Mercurial > hg
changeset 26504:875e5d89dc86
merge with stable
author | Matt Mackall <mpm@selenic.com> |
---|---|
date | Wed, 07 Oct 2015 13:44:48 -0500 |
parents | 0748083f2898 (diff) 5a84a453b503 (current diff) |
children | 6086ddc1fdc6 |
files | mercurial/templater.py tests/test-command-template.t |
diffstat | 299 files changed, 7843 insertions(+), 3677 deletions(-) [+] |
line wrap: on
line diff
--- a/Makefile Mon Oct 05 10:43:16 2015 -0600 +++ b/Makefile Wed Oct 07 13:44:48 2015 -0500 @@ -157,14 +157,12 @@ N=`cd dist && echo mercurial-*.mpkg | sed 's,\.mpkg$$,,'` && hdiutil create -srcfolder dist/$$N.mpkg/ -scrub -volname "$$N" -ov packages/osx/$$N.dmg rm -rf dist/mercurial-*.mpkg -debian-jessie: - mkdir -p packages/debian-jessie - contrib/builddeb - mv debbuild/*.deb packages/debian-jessie - rm -rf debbuild +deb: + mkdir -p packages/debian-unknown + contrib/builddeb --release unknown docker-debian-jessie: - mkdir -p packages/debian/jessie + mkdir -p packages/debian-jessie contrib/dockerdeb jessie fedora20:
--- a/README Mon Oct 05 10:43:16 2015 -0600 +++ b/README Wed Oct 07 13:44:48 2015 -0500 @@ -16,5 +16,5 @@ $ make local # build for inplace usage $ ./hg --version # should show the latest version -See http://mercurial.selenic.com/ for detailed installation +See https://mercurial-scm.org/ for detailed installation instructions, platform-specific notes, and Mercurial user information.
--- a/contrib/builddeb Mon Oct 05 10:43:16 2015 -0600 +++ b/contrib/builddeb Wed Oct 07 13:44:48 2015 -0500 @@ -7,13 +7,23 @@ . $(dirname $0)/packagelib.sh BUILD=1 -DEBBUILDDIR="$PWD/debbuild" +CLEANUP=1 +DEBVERSION=jessie while [ "$1" ]; do case "$1" in - --prepare ) + --release ) + shift + DEBVERSION="$1" + shift + ;; + --cleanup ) shift BUILD= ;; + --build ) + shift + CLEANUP= + ;; --debbuilddir ) shift DEBBUILDDIR="$1" @@ -26,10 +36,9 @@ esac done -set -u +trap "if [ '$CLEANUP' ] ; then rm -r '$PWD/debian' ; fi" EXIT -rm -rf $DEBBUILDDIR -mkdir -p $DEBBUILDDIR +set -u if [ ! -d .hg ]; then echo 'You are not inside a Mercurial repository!' 1>&2 @@ -38,25 +47,38 @@ gethgversion -cp -r $PWD/contrib/debian $DEBBUILDDIR/DEBIAN -chmod -R 0755 $DEBBUILDDIR/DEBIAN - -control=$DEBBUILDDIR/DEBIAN/control - -# This looks like sed -i, but sed -i behaves just differently enough -# between BSD and GNU sed that I gave up and did the dumb thing. -sed "s/__VERSION__/$version/" < $control > $control.tmp -mv $control.tmp $control +control=debian/control +changelog=debian/changelog if [ "$BUILD" ]; then - dpkg-deb --build $DEBBUILDDIR - mv $DEBBUILDDIR.deb $DEBBUILDDIR/mercurial-$version-$release.deb - if [ $? = 0 ]; then - echo - echo "Built packages for $version-$release:" - find $DEBBUILDDIR/ -type f -newer $control + if [ -d debian ] ; then + echo "Error! debian control directory already exists!" + exit 1 fi -else - echo "Prepared sources for $version-$release $control are in $DEBBUILDDIR - use like:" - echo "dpkg-deb --build $DEBBUILDDIR" + + cp -r $PWD/contrib/debian debian + chmod -R 0755 debian + + # This looks like sed -i, but sed -i behaves just differently enough + # between BSD and GNU sed that I gave up and did the dumb thing. + sed "s/__VERSION__/$version/" < $changelog > $changelog.tmp + date=$(date --rfc-2822) + sed "s/__DATE__/$date/" < $changelog.tmp > $changelog + rm $changelog.tmp + + debuild -us -uc -b + if [ $? != 0 ]; then + echo 'debuild failed!' + exit 1 + fi + fi +if [ "$CLEANUP" ] ; then + echo + OUTPUTDIR=${OUTPUTDIR:=packages/debian-$DEBVERSION} + find ../mercurial*.deb ../mercurial_*.build ../mercurial_*.changes \ + -type f -newer $control -print0 | \ + xargs -Inarf -0 mv narf "$OUTPUTDIR" + echo "Built packages for $version-$release:" + find "$OUTPUTDIR" -type f -newer $control -name '*.deb' +fi
--- a/contrib/buildrpm Mon Oct 05 10:43:16 2015 -0600 +++ b/contrib/buildrpm Wed Oct 07 13:44:48 2015 -0500 @@ -56,6 +56,7 @@ RPMPYTHONVER=%{nil} fi +mkdir -p $RPMBUILDDIR/SOURCES $HG archive -t tgz $RPMBUILDDIR/SOURCES/mercurial-$version-$release.tar.gz if [ "$PYTHONVER" ]; then ( @@ -79,6 +80,7 @@ ) fi +mkdir -p $RPMBUILDDIR/SPECS rpmspec=$RPMBUILDDIR/SPECS/mercurial.spec sed -e "s,^Version:.*,Version: $version," \
--- a/contrib/check-code.py Mon Oct 05 10:43:16 2015 -0600 +++ b/contrib/check-code.py Wed Oct 07 13:44:48 2015 -0500 @@ -291,6 +291,8 @@ (r'os\.path\.join\(.*, *(""|\'\')\)', "use pathutil.normasprefix(path) instead of os.path.join(path, '')"), (r'\s0[0-7]+\b', 'legacy octal syntax; use "0o" prefix instead of "0"'), + # XXX only catch mutable arguments on the first line of the definition + (r'def.*[( ]\w+=\{\}', "don't use mutable default arguments"), ], # warnings [
--- a/contrib/check-commit Mon Oct 05 10:43:16 2015 -0600 +++ b/contrib/check-commit Wed Oct 07 13:44:48 2015 -0500 @@ -13,7 +13,7 @@ # # $ BYPASS= hg commit # -# See also: http://mercurial.selenic.com/wiki/ContributingChanges +# See also: https://mercurial-scm.org/wiki/ContributingChanges import re, sys, os
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/contrib/debian/cacerts.rc Wed Oct 07 13:44:48 2015 -0500 @@ -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/debian/changelog Wed Oct 07 13:44:48 2015 -0500 @@ -0,0 +1,5 @@ +mercurial (__VERSION__) unstable; urgency=medium + + * Automated build performed by upstream. + + -- Mercurial Devel <mercurial-devel@selenic.com> __DATE__
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/contrib/debian/compat Wed Oct 07 13:44:48 2015 -0500 @@ -0,0 +1,1 @@ +9
--- a/contrib/debian/control Mon Oct 05 10:43:16 2015 -0600 +++ b/contrib/debian/control Wed Oct 07 13:44:48 2015 -0500 @@ -1,9 +1,47 @@ -Package: mercurial -Version: __VERSION__ +Source: mercurial Section: vcs Priority: optional +Maintainer: Mercurial Developers <mercurial-devel@selenic.com> +Build-Depends: + debhelper (>= 7), + dh-python, + python-all +Standards-Version: 3.9.4 +X-Python-Version: >= 2.6 + +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: python -Conflicts: mercurial-common -Maintainer: Mercurial Developers <mercurial-devel@selenic.com> -Description: Mercurial (probably nightly) package built by upstream. +Depends: + ${misc:Depends}, + ${python:Depends}, +Recommends: mercurial (= ${source:Version}), ca-certificates +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/debian/copyright Wed Oct 07 13:44:48 2015 -0500 @@ -0,0 +1,27 @@ +Format: http://www.debian.org/doc/packaging-manuals/copyright-format/1.0/ +Upstream-Name: mercurial +Source: http://www.selenic.com/mercurial/ + +Files: * +Copyright: 2005-2015, 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/debian/hgkpath.rc Wed Oct 07 13:44:48 2015 -0500 @@ -0,0 +1,2 @@ +[hgk] +path = /usr/share/mercurial/hgk
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/contrib/debian/rules Wed Oct 07 13:44:48 2015 -0500 @@ -0,0 +1,36 @@ +#!/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 + # 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/ + mkdir -p $(CURDIR)/debian/mercurial-common/usr/share/bash-completion/completions + cp contrib/bash_completion $(CURDIR)/debian/mercurial-common/usr/share/bash-completion/completions/mercurial + rm $(CURDIR)/debian/mercurial-common/usr/bin/hg
--- a/contrib/dockerdeb Mon Oct 05 10:43:16 2015 -0600 +++ b/contrib/dockerdeb Wed Oct 07 13:44:48 2015 -0500 @@ -8,32 +8,27 @@ checkdocker +DEBPLATFORM="$1" PLATFORM="debian-$1" shift # extra params are passed to build process +OUTPUTDIR=${OUTPUTDIR:=$ROOTDIR/packages/$PLATFORM} + initcontainer $PLATFORM -DEBBUILDDIR=$ROOTDIR/packages/$PLATFORM -contrib/builddeb --debbuilddir $DEBBUILDDIR/staged --prepare +# 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) -DSHARED=/mnt/shared/ if [ $(uname) = "Darwin" ] ; then - $DOCKER run -u $DBUILDUSER --rm -v $DEBBUILDDIR:$DSHARED -v $PWD:/mnt/hg $CONTAINER \ - sh -c "cd /mnt/hg && make clean && make local" + $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 $DEBBUILDDIR:$DSHARED -v $PWD:/mnt/hg $CONTAINER \ - sh -c "cd /mnt/hg && make PREFIX=$DSHARED/staged/usr install" -$DOCKER run -u $DBUILDUSER --rm -v $DEBBUILDDIR:$DSHARED $CONTAINER \ - dpkg-deb --build $DSHARED/staged +$DOCKER run -u $DBUILDUSER --rm -v $PWD/..:/mnt $CONTAINER \ + sh -c "cd /mnt/$dn && DEB_BUILD_OPTIONS='${DEB_BUILD_OPTIONS:=}' contrib/builddeb --build --release $DEBPLATFORM" +contrib/builddeb --cleanup --release $DEBPLATFORM if [ $(uname) = "Darwin" ] ; then - $DOCKER run -u $DBUILDUSER --rm -v $DEBBUILDDIR:$DSHARED -v $PWD:/mnt/hg $CONTAINER \ - sh -c "cd /mnt/hg && make clean" + $DOCKER run -u $DBUILDUSER --rm -v $PWD/..:/mnt $CONTAINER \ + sh -c "cd /mnt/$dn && make clean" fi - -gethgversion - -rm -r $DEBBUILDDIR/staged -mv $DEBBUILDDIR/staged.deb $DEBBUILDDIR/mercurial-$version-$release.deb - -echo -echo "Build complete - results can be found in $DEBBUILDDIR"
--- a/contrib/editmerge Mon Oct 05 10:43:16 2015 -0600 +++ b/contrib/editmerge Wed Oct 07 13:44:48 2015 -0500 @@ -16,7 +16,7 @@ grep -n "<<<<<<" $FILE | cut -f1 -d: } -# editor preference loosely based on http://mercurial.selenic.com/wiki/editor +# editor preference loosely based on https://mercurial-scm.org/wiki/editor # hg showconfig is at the bottom though, since it's slow to run (0.15 seconds) ED=$HGEDITOR if [ "$ED" = "" ] ; then
--- a/contrib/hgweb.wsgi Mon Oct 05 10:43:16 2015 -0600 +++ b/contrib/hgweb.wsgi Wed Oct 07 13:44:48 2015 -0500 @@ -1,5 +1,5 @@ # An example WSGI for use with mod_wsgi, edit as necessary -# See http://mercurial.selenic.com/wiki/modwsgi for more information +# See https://mercurial-scm.org/wiki/modwsgi for more information # Path to repo or hgweb config to serve (see 'hg help hgweb') config = "/path/to/repo/or/config"
--- a/contrib/import-checker.py Mon Oct 05 10:43:16 2015 -0600 +++ b/contrib/import-checker.py Wed Oct 07 13:44:48 2015 -0500 @@ -164,7 +164,7 @@ for m in ['msvcrt', '_winreg']: yield m # These get missed too - for m in 'ctypes', 'email': + for m in 'ctypes', 'email', 'multiprocessing': yield m yield 'builtins' # python3 only for m in 'fcntl', 'grp', 'pwd', 'termios': # Unix only @@ -200,8 +200,7 @@ for name in files: if name == '__init__.py': continue - if not (name.endswith('.py') or name.endswith('.so') - or name.endswith('.pyd')): + if not name.endswith(('.py', '.so', '.pyc', '.pyo', '.pyd')): continue full_path = os.path.join(top, name) rel_path = full_path[len(libpath) + 1:]
--- a/contrib/macosx/Readme.html Mon Oct 05 10:43:16 2015 -0600 +++ b/contrib/macosx/Readme.html Wed Oct 07 13:44:48 2015 -0500 @@ -25,7 +25,7 @@ <p class="p2"><br></p> <p class="p1"><b>Documentation</b></p> <p class="p2"><br></p> -<p class="p3">Visit the <a href="http://mercurial.selenic.com/">Mercurial web site and wiki</a></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="http://hgbook.red-bean.com/">Distributed revision control with Mercurial</a></p> <p class="p2"><br></p>
--- a/contrib/macosx/Welcome.html Mon Oct 05 10:43:16 2015 -0600 +++ b/contrib/macosx/Welcome.html Wed Oct 07 13:44:48 2015 -0500 @@ -11,10 +11,10 @@ </style> </head> <body> -<p class="p1">This is a prepackaged release of <a href="http://mercurial.selenic.com/">Mercurial</a> for Mac OS X.</p> +<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="http://mercurial.selenic.com/wiki/WhatsNew">release notes</a>.</p> +Please be sure to read the latest <a href="https://mercurial-scm.org/wiki/WhatsNew">release notes</a>.</p> </body> </html>
--- a/contrib/mercurial.spec Mon Oct 05 10:43:16 2015 -0600 +++ b/contrib/mercurial.spec Wed Oct 07 13:44:48 2015 -0500 @@ -25,7 +25,7 @@ Release: 0 License: GPLv2+ Group: Development/Tools -URL: http://mercurial.selenic.com/ +URL: https://mercurial-scm.org/ Source0: %{name}-%{version}-%{release}.tar.gz %if "%{?withpython}" Source1: %{pythonname}.tgz
--- a/contrib/plan9/README Mon Oct 05 10:43:16 2015 -0600 +++ b/contrib/plan9/README Wed Oct 07 13:44:48 2015 -0500 @@ -35,5 +35,5 @@ A proto(2) file is included in this directory as an example of how a binary distribution could be packaged, ostensibly with contrib(1). -See http://mercurial.selenic.com/ for detailed installation +See https://mercurial-scm.org/ for detailed installation instructions, platform-specific notes, and Mercurial user information.
--- a/contrib/revsetbenchmarks.py Mon Oct 05 10:43:16 2015 -0600 +++ b/contrib/revsetbenchmarks.py Wed Oct 07 13:44:48 2015 -0500 @@ -33,6 +33,8 @@ """update the repo to a revision""" try: check_call(['hg', 'update', '--quiet', '--check', str(rev)]) + check_output(['make', 'local'], + stderr=None) # suppress output except for error/warning except CalledProcessError as exc: print >> sys.stderr, 'update to revision %s failed, aborting' % rev sys.exit(exc.returncode)
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/contrib/showstack.py Wed Oct 07 13:44:48 2015 -0500 @@ -0,0 +1,17 @@ +# showstack.py - extension to dump a Python stack trace on signal +# +# binds to both SIGQUIT (Ctrl-\) and SIGINFO (Ctrl-T on BSDs) + +import sys, signal, traceback + +def sigshow(*args): + sys.stderr.write("\n") + traceback.print_stack(args[1], limit=10, file=sys.stderr) + sys.stderr.write("----\n") + +def extsetup(ui): + signal.signal(signal.SIGQUIT, sigshow) + try: + signal.signal(signal.SIGINFO, sigshow) + except AttributeError: + pass
--- a/contrib/vagrant/Vagrantfile Mon Oct 05 10:43:16 2015 -0600 +++ b/contrib/vagrant/Vagrantfile Wed Oct 07 13:44:48 2015 -0500 @@ -1,9 +1,8 @@ # -*- mode: ruby -*- Vagrant.configure('2') do |config| - # Debian 7.4 32-bit i386 without configuration management software - config.vm.box = "puppetlabs/debian-7.4-32-nocm" - #config.vm.box = "pnd/debian-wheezy32-basebox" + # Debian 8.1 x86_64 without configuration management software + config.vm.box = "debian/jessie64" config.vm.hostname = "tests" config.vm.define "tests" do |conf|
--- a/contrib/vim/hgcommand.vim Mon Oct 05 10:43:16 2015 -0600 +++ b/contrib/vim/hgcommand.vim Wed Oct 07 13:44:48 2015 -0500 @@ -1226,7 +1226,7 @@ Author: Mathieu Clabaut <mathieu.clabaut@gmail.com> Credits: Bob Hiestand <bob.hiestand@gmail.com> -Mercurial: http://mercurial.selenic.com/ +Mercurial: https://mercurial-scm.org/ Mercurial (noted Hg) is a fast, lightweight Source Control Management system designed for efficient handling of very large distributed projects.
--- a/contrib/vim/patchreview.txt Mon Oct 05 10:43:16 2015 -0600 +++ b/contrib/vim/patchreview.txt Wed Oct 07 13:44:48 2015 -0500 @@ -30,7 +30,7 @@ software development projects. This plugin provides that missing functionality. -It also improves on |:diffpatch|'s behaviour of creating the patched files in +It also improves on |:diffpatch|'s behavior of creating the patched files in the same directory as original file which can lead to project workspace pollution.
--- a/contrib/win32/ReadMe.html Mon Oct 05 10:43:16 2015 -0600 +++ b/contrib/win32/ReadMe.html Wed Oct 07 13:44:48 2015 -0500 @@ -49,7 +49,7 @@ <p> For documentation, please visit the <a - href="http://mercurial.selenic.com/">Mercurial web site</a>. + href="https://mercurial-scm.org/">Mercurial web site</a>. You can also download a free book, <a href="http://hgbook.red-bean.com/">Mercurial: The Definitive Guide</a>. @@ -100,7 +100,7 @@ By default, Mercurial will use the merge program defined by the <tt>HGMERGE</tt> environment variable, or uses the one defined in the <tt>mercurial.ini</tt> file. (see <a - href="http://mercurial.selenic.com/wiki/MergeProgram">MergeProgram</a> + href="https://mercurial-scm.org/wiki/MergeProgram">MergeProgram</a> on the Mercurial Wiki for more information) </p> @@ -108,9 +108,9 @@ <p> Before you report any problems, please consult the <a - href="http://mercurial.selenic.com/">Mercurial web site</a> + href="https://mercurial-scm.org/">Mercurial web site</a> and see if your question is already in our list of <a - href="http://mercurial.selenic.com/wiki/FAQ">Frequently + href="https://mercurial-scm.org/wiki/FAQ">Frequently Answered Questions</a> (the "FAQ"). </p>
--- a/contrib/win32/mercurial.iss Mon Oct 05 10:43:16 2015 -0600 +++ b/contrib/win32/mercurial.iss Wed Oct 07 13:44:48 2015 -0500 @@ -36,9 +36,9 @@ LicenseFile=COPYING ShowLanguageDialog=yes AppPublisher=Matt Mackall and others -AppPublisherURL=http://mercurial.selenic.com/ -AppSupportURL=http://mercurial.selenic.com/ -AppUpdatesURL=http://mercurial.selenic.com/ +AppPublisherURL=https://mercurial-scm.org/ +AppSupportURL=https://mercurial-scm.org/ +AppUpdatesURL=https://mercurial-scm.org/ AppID={{4B95A5F1-EF59-4B08-BED8-C891C46121B3} AppContact=mercurial@selenic.com DefaultDirName={pf}\Mercurial @@ -90,7 +90,7 @@ Source: COPYING; DestDir: {app}; DestName: Copying.txt [INI] -Filename: {app}\Mercurial.url; Section: InternetShortcut; Key: URL; String: http://mercurial.selenic.com/ +Filename: {app}\Mercurial.url; Section: InternetShortcut; Key: URL; String: https://mercurial-scm.org/ Filename: {app}\default.d\editor.rc; Section: ui; Key: editor; String: notepad [UninstallDelete]
--- a/contrib/win32/postinstall.txt Mon Oct 05 10:43:16 2015 -0600 +++ b/contrib/win32/postinstall.txt Wed Oct 07 13:44:48 2015 -0500 @@ -6,4 +6,4 @@ Also check the release notes at: - http://mercurial.selenic.com/wiki/WhatsNew + https://mercurial-scm.org/wiki/WhatsNew
--- a/contrib/wix/mercurial.wxs Mon Oct 05 10:43:16 2015 -0600 +++ b/contrib/wix/mercurial.wxs Wed Oct 07 13:44:48 2015 -0500 @@ -44,10 +44,10 @@ <!--Property Id='ARPCOMMENTS'>any comments</Property--> <Property Id='ARPCONTACT'>mercurial@selenic.com</Property> - <Property Id='ARPHELPLINK'>http://mercurial.selenic.com/wiki/</Property> - <Property Id='ARPURLINFOABOUT'>http://mercurial.selenic.com/about/</Property> - <Property Id='ARPURLUPDATEINFO'>http://mercurial.selenic.com/downloads/</Property> - <Property Id='ARPHELPTELEPHONE'>http://mercurial.selenic.com/wiki/Support</Property> + <Property Id='ARPHELPLINK'>https://mercurial-scm.org/wiki/</Property> + <Property Id='ARPURLINFOABOUT'>https://mercurial-scm.org/about/</Property> + <Property Id='ARPURLUPDATEINFO'>https://mercurial-scm.org/downloads/</Property> + <Property Id='ARPHELPTELEPHONE'>https://mercurial-scm.org/wiki/Support</Property> <Property Id='ARPPRODUCTICON'>hgIcon.ico</Property> <Property Id='INSTALLEDMERCURIALPRODUCTS' Secure='yes'></Property>
--- a/doc/check-seclevel.py Mon Oct 05 10:43:16 2015 -0600 +++ b/doc/check-seclevel.py Wed Oct 07 13:44:48 2015 -0500 @@ -1,6 +1,6 @@ #!/usr/bin/env python # -# checkseclevel - checking section title levels in each online help documents +# checkseclevel - checking section title levels in each online help document import sys, os import optparse @@ -14,15 +14,7 @@ from mercurial.help import helptable from mercurial import extensions from mercurial import minirst - -_verbose = False - -def verbose(msg): - if _verbose: - print msg - -def error(msg): - sys.stderr.write('%s\n' % msg) +from mercurial import ui as uimod level2mark = ['"', '=', '-', '.', '#'] reservedmarks = ['"'] @@ -37,12 +29,12 @@ initlevel_ext = 1 initlevel_ext_cmd = 3 -def showavailables(initlevel): - error(' available marks and order of them in this help: %s' % - (', '.join(['%r' % (m * 4) for m in level2mark[initlevel + 1:]]))) +def showavailables(ui, initlevel): + ui.warn((' available marks and order of them in this help: %s\n') % + (', '.join(['%r' % (m * 4) for m in level2mark[initlevel + 1:]]))) -def checkseclevel(doc, name, initlevel): - verbose('checking "%s"' % name) +def checkseclevel(ui, doc, name, initlevel): + ui.note(('checking "%s"\n') % name) blocks, pruned = minirst.parse(doc, 0, ['verbose']) errorcnt = 0 curlevel = initlevel @@ -52,66 +44,66 @@ mark = block['underline'] title = block['lines'][0] if (mark not in mark2level) or (mark2level[mark] <= initlevel): - error('invalid section mark %r for "%s" of %s' % - (mark * 4, title, name)) - showavailables(initlevel) + ui.warn(('invalid section mark %r for "%s" of %s\n') % + (mark * 4, title, name)) + showavailables(ui, initlevel) errorcnt += 1 continue nextlevel = mark2level[mark] if curlevel < nextlevel and curlevel + 1 != nextlevel: - error('gap of section level at "%s" of %s' % - (title, name)) - showavailables(initlevel) + ui.warn(('gap of section level at "%s" of %s\n') % + (title, name)) + showavailables(ui, initlevel) errorcnt += 1 continue - verbose('appropriate section level for "%s %s"' % + ui.note(('appropriate section level for "%s %s"\n') % (mark * (nextlevel * 2), title)) curlevel = nextlevel return errorcnt -def checkcmdtable(cmdtable, namefmt, initlevel): +def checkcmdtable(ui, cmdtable, namefmt, initlevel): errorcnt = 0 for k, entry in cmdtable.items(): name = k.split("|")[0].lstrip("^") if not entry[0].__doc__: - verbose('skip checking %s: no help document' % + ui.note(('skip checking %s: no help document\n') % (namefmt % name)) continue - errorcnt += checkseclevel(entry[0].__doc__, + errorcnt += checkseclevel(ui, entry[0].__doc__, namefmt % name, initlevel) return errorcnt -def checkhghelps(): +def checkhghelps(ui): errorcnt = 0 for names, sec, doc in helptable: if callable(doc): - doc = doc() - errorcnt += checkseclevel(doc, + doc = doc(ui) + errorcnt += checkseclevel(ui, doc, '%s help topic' % names[0], initlevel_topic) - errorcnt += checkcmdtable(table, '%s command', initlevel_cmd) + errorcnt += checkcmdtable(ui, table, '%s command', initlevel_cmd) for name in sorted(extensions.enabled().keys() + extensions.disabled().keys()): mod = extensions.load(None, name, None) if not mod.__doc__: - verbose('skip checking %s extension: no help document' % name) + ui.note(('skip checking %s extension: no help document\n') % name) continue - errorcnt += checkseclevel(mod.__doc__, + errorcnt += checkseclevel(ui, mod.__doc__, '%s extension' % name, initlevel_ext) cmdtable = getattr(mod, 'cmdtable', None) if cmdtable: - errorcnt += checkcmdtable(cmdtable, + errorcnt += checkcmdtable(ui, cmdtable, '%s command of ' + name + ' extension', initlevel_ext_cmd) return errorcnt -def checkfile(filename, initlevel): +def checkfile(ui, filename, initlevel): if filename == '-': filename = 'stdin' doc = sys.stdin.read() @@ -122,11 +114,11 @@ finally: fp.close() - verbose('checking input from %s with initlevel %d' % + ui.note(('checking input from %s with initlevel %d\n') % (filename, initlevel)) - return checkseclevel(doc, 'input from %s' % filename, initlevel) + return checkseclevel(ui, doc, 'input from %s' % filename, initlevel) -if __name__ == "__main__": +def main(): optparser = optparse.OptionParser("""%prog [options] This checks all help documents of Mercurial (topics, commands, @@ -159,11 +151,15 @@ (options, args) = optparser.parse_args() - _verbose = options.verbose + ui = uimod.ui() + ui.setconfig('ui', 'verbose', options.verbose, '--verbose') if options.file: - if checkfile(options.file, options.initlevel): + if checkfile(ui, options.file, options.initlevel): sys.exit(1) else: - if checkhghelps(): + if checkhghelps(ui): sys.exit(1) + +if __name__ == "__main__": + main()
--- a/doc/gendoc.py Mon Oct 05 10:43:16 2015 -0600 +++ b/doc/gendoc.py Wed Oct 07 13:44:48 2015 -0500 @@ -14,6 +14,7 @@ from mercurial.i18n import gettext, _ from mercurial.help import helptable, loaddoc from mercurial import extensions +from mercurial import ui as uimod def get_desc(docstr): if not docstr: @@ -137,7 +138,7 @@ if sectionfunc: ui.write(sectionfunc(sec)) if callable(doc): - doc = doc() + doc = doc(ui) ui.write(doc) ui.write("\n") @@ -198,7 +199,8 @@ if len(sys.argv) > 1: doc = sys.argv[1] + ui = uimod.ui() if doc == 'hg.1.gendoc': - showdoc(sys.stdout) + showdoc(ui) else: - showtopic(sys.stdout, sys.argv[1]) + showtopic(ui, sys.argv[1])
--- a/doc/hgmanpage.py Mon Oct 05 10:43:16 2015 -0600 +++ b/doc/hgmanpage.py Wed Oct 07 13:44:48 2015 -0500 @@ -18,11 +18,11 @@ 7 miscellaneous 8 system administration -Man pages are written *troff*, a text file formatting system. +Man pages are written in *troff*, a text file formatting system. See http://www.tldp.org/HOWTO/Man-Page for a start. -Man pages have no subsection only parts. +Man pages have no subsections only parts. Standard parts NAME , @@ -317,7 +317,7 @@ self._cnt = 0 self._indent = 2 if style == 'arabic': - # indentation depends on number of childrens + # indentation depends on number of children # and start value. self._indent = len(str(len(node.children))) self._indent += len(str(self._cnt)) + 1
--- a/doc/style.css Mon Oct 05 10:43:16 2015 -0600 +++ b/doc/style.css Wed Oct 07 13:44:48 2015 -0500 @@ -1,8 +1,8 @@ /* - * Styles for man pages, which match with http://mercurial.selenic.com/ + * Styles for man pages, which match with https://mercurial-scm.org/ * * Color scheme & layout are borrowed from - * http://mercurial.selenic.com/css/styles.css + * https://mercurial-scm.org/css/styles.css * * Some styles are from html4css1.css from Docutils, which is in the * public domain.
--- a/hgext/blackbox.py Mon Oct 05 10:43:16 2015 -0600 +++ b/hgext/blackbox.py Wed Oct 07 13:44:48 2015 -0500 @@ -107,9 +107,11 @@ if blackbox: date = util.datestr(None, '%Y/%m/%d %H:%M:%S') user = util.getuser() + pid = str(os.getpid()) formattedmsg = msg[0] % msg[1:] try: - blackbox.write('%s %s> %s' % (date, user, formattedmsg)) + blackbox.write('%s %s (%s)> %s' % + (date, user, pid, formattedmsg)) except IOError as err: self.debug('warning: cannot write to blackbox.log: %s\n' % err.strerror)
--- a/hgext/convert/__init__.py Mon Oct 05 10:43:16 2015 -0600 +++ b/hgext/convert/__init__.py Wed Oct 07 13:44:48 2015 -0500 @@ -25,7 +25,7 @@ @command('convert', [('', 'authors', '', - _('username mapping filename (DEPRECATED, use --authormap instead)'), + _('username mapping filename (DEPRECATED) (use --authormap instead)'), _('FILE')), ('s', 'source-type', '', _('source repository type'), _('TYPE')), ('d', 'dest-type', '', _('destination repository type'), _('TYPE')), @@ -316,6 +316,9 @@ ``convert.git.remoteprefix`` as a prefix followed by a /. The default is 'remote'. + :convert.git.skipsubmodules: does not convert root level .gitmodules files + or files with 160000 mode indicating a submodule. Default is False. + Perforce Source ###############
--- a/hgext/convert/common.py Mon Oct 05 10:43:16 2015 -0600 +++ b/hgext/convert/common.py Wed Oct 07 13:44:48 2015 -0500 @@ -82,6 +82,13 @@ def after(self): pass + def targetfilebelongstosource(self, targetfilename): + """Returns true if the given targetfile belongs to the source repo. This + is useful when only a subdirectory of the target belongs to the source + repo.""" + # For normal full repo converts, this is always True. + return True + def setrevmap(self, revmap): """set the map of already-converted revisions""" pass
--- a/hgext/convert/convcmd.py Mon Oct 05 10:43:16 2015 -0600 +++ b/hgext/convert/convcmd.py Wed Oct 07 13:44:48 2015 -0500 @@ -120,6 +120,9 @@ item=file, total=self.filecount) return self.source.getfile(file, rev) + def targetfilebelongstosource(self, targetfilename): + return self.source.targetfilebelongstosource(targetfilename) + def lookuprev(self, rev): return self.source.lookuprev(rev)
--- a/hgext/convert/filemap.py Mon Oct 05 10:43:16 2015 -0600 +++ b/hgext/convert/filemap.py Wed Oct 07 13:44:48 2015 -0500 @@ -42,6 +42,7 @@ self.include = {} self.exclude = {} self.rename = {} + self.targetprefixes = None if path: if self.parse(path): raise util.Abort(_('errors in filemap')) @@ -100,6 +101,30 @@ pass return '', name, '' + def istargetfile(self, filename): + """Return true if the given target filename is covered as a destination + of the filemap. This is useful for identifying what parts of the target + repo belong to the source repo and what parts don't.""" + if self.targetprefixes is None: + self.targetprefixes = set() + for before, after in self.rename.iteritems(): + self.targetprefixes.add(after) + + # If "." is a target, then all target files are considered from the + # source. + if not self.targetprefixes or '.' in self.targetprefixes: + return True + + filename = normalize(filename) + for pre, suf in rpairs(filename): + # This check is imperfect since it doesn't account for the + # include/exclude list, but it should work in filemaps that don't + # apply include/exclude to the same source directories they are + # renaming. + if pre in self.targetprefixes: + return True + return False + def __call__(self, name): if self.include: inc = self.lookup(name, self.include)[0] @@ -410,6 +435,9 @@ return files, ncopies, ncleanp2 + def targetfilebelongstosource(self, targetfilename): + return self.filemapper.istargetfile(targetfilename) + def getfile(self, name, rev): realname, realrev = rev return self.base.getfile(realname, realrev)
--- a/hgext/convert/git.py Mon Oct 05 10:43:16 2015 -0600 +++ b/hgext/convert/git.py Wed Oct 07 13:44:48 2015 -0500 @@ -224,6 +224,8 @@ lcount = len(difftree) i = 0 + skipsubmodules = self.ui.configbool('convert', 'git.skipsubmodules', + False) def add(entry, f, isdest): seen.add(f) h = entry[3] @@ -232,6 +234,9 @@ renamesource = (not isdest and entry[4][0] == 'R') if f == '.gitmodules': + if skipsubmodules: + return + subexists[0] = True if entry[4] == 'D' or renamesource: subdeleted[0] = True @@ -239,7 +244,8 @@ else: changes.append(('.hgsub', '')) elif entry[1] == '160000' or entry[0] == ':160000': - subexists[0] = True + if not skipsubmodules: + subexists[0] = True else: if renamesource: h = hex(nullid) @@ -377,28 +383,31 @@ def getbookmarks(self): bookmarks = {} - # Interesting references in git are prefixed - prefix = 'refs/heads/' - prefixlen = len(prefix) + # Handle local and remote branches + remoteprefix = self.ui.config('convert', 'git.remoteprefix', 'remote') + reftypes = [ + # (git prefix, hg prefix) + ('refs/remotes/origin/', remoteprefix + '/'), + ('refs/heads/', '') + ] - # factor two commands - remoteprefix = self.ui.config('convert', 'git.remoteprefix', 'remote') - gitcmd = { remoteprefix + '/': 'git ls-remote --heads origin', - '': 'git show-ref'} + exclude = set([ + 'refs/remotes/origin/HEAD', + ]) - # Origin heads - for reftype in gitcmd: - try: - fh = self.gitopen(gitcmd[reftype], err=subprocess.PIPE) - for line in fh: - line = line.strip() - rev, name = line.split(None, 1) - if not name.startswith(prefix): + try: + fh = self.gitopen('git show-ref', err=subprocess.PIPE) + for line in fh: + line = line.strip() + rev, name = line.split(None, 1) + # Process each type of branch + for gitprefix, hgprefix in reftypes: + if not name.startswith(gitprefix) or name in exclude: continue - name = '%s%s' % (reftype, name[prefixlen:]) + name = '%s%s' % (hgprefix, name[len(gitprefix):]) bookmarks[name] = rev - except Exception: - pass + except Exception: + pass return bookmarks
--- a/hgext/convert/hg.py Mon Oct 05 10:43:16 2015 -0600 +++ b/hgext/convert/hg.py Wed Oct 07 13:44:48 2015 -0500 @@ -23,6 +23,7 @@ from mercurial.node import bin, hex, nullid from mercurial import hg, util, context, bookmarks, error, scmutil, exchange from mercurial import phases +from mercurial import merge as mergemod from common import NoRepo, commit, converter_source, converter_sink, mapfile @@ -176,14 +177,58 @@ return fp.getvalue() + def _calculatemergedfiles(self, source, p1ctx, p2ctx): + """Calculates the files from p2 that we need to pull in when merging p1 + and p2, given that the merge is coming from the given source. + + This prevents us from losing files that only exist in the target p2 and + that don't come from the source repo (like if you're merging multiple + repositories together). + """ + anc = [p1ctx.ancestor(p2ctx)] + # Calculate what files are coming from p2 + actions, diverge, rename = mergemod.calculateupdates( + self.repo, p1ctx, p2ctx, anc, + True, # branchmerge + True, # force + False, # partial + False, # acceptremote + False, # followcopies + ) + + for file, (action, info, msg) in actions.iteritems(): + if source.targetfilebelongstosource(file): + # If the file belongs to the source repo, ignore the p2 + # since it will be covered by the existing fileset. + continue + + # If the file requires actual merging, abort. We don't have enough + # context to resolve merges correctly. + if action in ['m', 'dm', 'cd', 'dc']: + raise util.Abort(_("unable to convert merge commit " + "since target parents do not merge cleanly (file " + "%s, parents %s and %s)") % (file, p1ctx, + p2ctx)) + elif action == 'k': + # 'keep' means nothing changed from p1 + continue + else: + # Any other change means we want to take the p2 version + yield file + def putcommit(self, files, copies, parents, commit, source, revmap, full, cleanp2): files = dict(files) def getfilectx(repo, memctx, f): - if p2ctx and f in cleanp2 and f not in copies: + if p2ctx and f in p2files and f not in copies: self.ui.debug('reusing %s from p2\n' % f) - return p2ctx[f] + try: + return p2ctx[f] + except error.ManifestLookupError: + # If the file doesn't exist in p2, then we're syncing a + # delete, so just return None. + return None try: v = files[f] except KeyError: @@ -255,6 +300,7 @@ while parents: p1 = p2 p2 = parents.pop(0) + p1ctx = self.repo[p1] p2ctx = None if p2 != nullid: p2ctx = self.repo[p2] @@ -262,6 +308,13 @@ if full: fileset.update(self.repo[p1]) fileset.update(self.repo[p2]) + + if p2ctx: + p2files = set(cleanp2) + for file in self._calculatemergedfiles(source, p1ctx, p2ctx): + p2files.add(file) + fileset.add(file) + ctx = context.memctx(self.repo, (p1, p2), text, fileset, getfilectx, commit.author, commit.date, extra) @@ -378,9 +431,6 @@ class mercurial_source(converter_source): def __init__(self, ui, path, revs=None): converter_source.__init__(self, ui, path, revs) - if revs and len(revs) > 1: - raise util.Abort(_("mercurial source does not support specifying " - "multiple revisions")) self.ignoreerrors = ui.configbool('convert', 'hg.ignoreerrors', False) self.ignored = set() self.saverev = ui.configbool('convert', 'hg.saverev', False) @@ -415,7 +465,7 @@ else: self.keep = util.always if revs: - self._heads = [self.repo[revs[0]].node()] + self._heads = [self.repo[r].node() for r in revs] else: self._heads = self.repo.heads() else:
--- a/hgext/eol.py Mon Oct 05 10:43:16 2015 -0600 +++ b/hgext/eol.py Wed Oct 07 13:44:48 2015 -0500 @@ -23,7 +23,7 @@ ``native`` is an alias for checking out in the platform's default line ending: ``LF`` on Unix (including Mac OS X) and ``CRLF`` on Windows. Note that ``BIN`` (do nothing to line endings) is Mercurial's -default behaviour; it is only needed if you need to override a later, +default behavior; it is only needed if you need to override a later, more general pattern. The optional ``[repository]`` section specifies the line endings to
--- a/hgext/extdiff.py Mon Oct 05 10:43:16 2015 -0600 +++ b/hgext/extdiff.py Wed Oct 07 13:44:48 2015 -0500 @@ -146,72 +146,94 @@ subrepos=opts.get('subrepos') matcher = scmutil.match(repo[node2], pats, opts) - mod_a, add_a, rem_a = map(set, repo.status(node1a, node2, matcher, - listsubrepos=subrepos)[:3]) - if do3way: - mod_b, add_b, rem_b = map(set, repo.status(node1b, node2, matcher, - listsubrepos=subrepos)[:3]) + + if opts.get('patch'): + if subrepos: + raise util.Abort(_('--patch cannot be used with --subrepos')) + if node2 is None: + raise util.Abort(_('--patch requires two revisions')) else: - mod_b, add_b, rem_b = set(), set(), set() - modadd = mod_a | add_a | mod_b | add_b - common = modadd | rem_a | rem_b - if not common: - return 0 + mod_a, add_a, rem_a = map(set, repo.status(node1a, node2, matcher, + listsubrepos=subrepos)[:3]) + if do3way: + mod_b, add_b, rem_b = map(set, + repo.status(node1b, node2, matcher, + listsubrepos=subrepos)[:3]) + else: + mod_b, add_b, rem_b = set(), set(), set() + modadd = mod_a | add_a | mod_b | add_b + common = modadd | rem_a | rem_b + if not common: + return 0 tmproot = tempfile.mkdtemp(prefix='extdiff.') try: - # Always make a copy of node1a (and node1b, if applicable) - dir1a_files = mod_a | rem_a | ((mod_b | add_b) - add_a) - dir1a = snapshot(ui, repo, dir1a_files, node1a, tmproot, subrepos)[0] - rev1a = '@%d' % repo[node1a].rev() - if do3way: - dir1b_files = mod_b | rem_b | ((mod_a | add_a) - add_b) - dir1b = snapshot(ui, repo, dir1b_files, node1b, tmproot, + if not opts.get('patch'): + # Always make a copy of node1a (and node1b, if applicable) + dir1a_files = mod_a | rem_a | ((mod_b | add_b) - add_a) + dir1a = snapshot(ui, repo, dir1a_files, node1a, tmproot, subrepos)[0] - rev1b = '@%d' % repo[node1b].rev() - else: - dir1b = None - rev1b = '' + rev1a = '@%d' % repo[node1a].rev() + if do3way: + dir1b_files = mod_b | rem_b | ((mod_a | add_a) - add_b) + dir1b = snapshot(ui, repo, dir1b_files, node1b, tmproot, + subrepos)[0] + rev1b = '@%d' % repo[node1b].rev() + else: + dir1b = None + rev1b = '' - fns_and_mtime = [] + fns_and_mtime = [] - # If node2 in not the wc or there is >1 change, copy it - dir2root = '' - rev2 = '' - if node2: - dir2 = snapshot(ui, repo, modadd, node2, tmproot, subrepos)[0] - rev2 = '@%d' % repo[node2].rev() - elif len(common) > 1: - #we only actually need to get the files to copy back to - #the working dir in this case (because the other cases - #are: diffing 2 revisions or single file -- in which case - #the file is already directly passed to the diff tool). - dir2, fns_and_mtime = snapshot(ui, repo, modadd, None, tmproot, - subrepos) - else: - # This lets the diff tool open the changed file directly - dir2 = '' - dir2root = repo.root + # If node2 in not the wc or there is >1 change, copy it + dir2root = '' + rev2 = '' + if node2: + dir2 = snapshot(ui, repo, modadd, node2, tmproot, subrepos)[0] + rev2 = '@%d' % repo[node2].rev() + elif len(common) > 1: + #we only actually need to get the files to copy back to + #the working dir in this case (because the other cases + #are: diffing 2 revisions or single file -- in which case + #the file is already directly passed to the diff tool). + dir2, fns_and_mtime = snapshot(ui, repo, modadd, None, tmproot, + subrepos) + else: + # This lets the diff tool open the changed file directly + dir2 = '' + dir2root = repo.root + + label1a = rev1a + label1b = rev1b + label2 = rev2 - label1a = rev1a - label1b = rev1b - label2 = rev2 - - # If only one change, diff the files instead of the directories - # Handle bogus modifies correctly by checking if the files exist - if len(common) == 1: - common_file = util.localpath(common.pop()) - dir1a = os.path.join(tmproot, dir1a, common_file) - label1a = common_file + rev1a - if not os.path.isfile(dir1a): - dir1a = os.devnull - if do3way: - dir1b = os.path.join(tmproot, dir1b, common_file) - label1b = common_file + rev1b - if not os.path.isfile(dir1b): - dir1b = os.devnull - dir2 = os.path.join(dir2root, dir2, common_file) - label2 = common_file + rev2 + # If only one change, diff the files instead of the directories + # Handle bogus modifies correctly by checking if the files exist + if len(common) == 1: + common_file = util.localpath(common.pop()) + dir1a = os.path.join(tmproot, dir1a, common_file) + label1a = common_file + rev1a + if not os.path.isfile(dir1a): + dir1a = os.devnull + if do3way: + dir1b = os.path.join(tmproot, dir1b, common_file) + label1b = common_file + rev1b + if not os.path.isfile(dir1b): + dir1b = os.devnull + dir2 = os.path.join(dir2root, dir2, common_file) + label2 = common_file + rev2 + else: + template = 'hg-%h.patch' + cmdutil.export(repo, [repo[node1a].rev(), repo[node2].rev()], + template=repo.vfs.reljoin(tmproot, template), + match=matcher) + label1a = cmdutil.makefilename(repo, template, node1a) + label2 = cmdutil.makefilename(repo, template, node2) + dir1a = repo.vfs.reljoin(tmproot, label1a) + dir2 = repo.vfs.reljoin(tmproot, label2) + dir1b = None + label1b = None + fns_and_mtime = [] # Function to quote file/dir names in the argument string. # When not operating in 3-way mode, an empty string is @@ -255,6 +277,7 @@ _('pass option to comparison program'), _('OPT')), ('r', 'rev', [], _('revision'), _('REV')), ('c', 'change', '', _('change made by revision'), _('REV')), + ('', 'patch', None, _('compare patches for two revisions')) ] + commands.walkopts + commands.subrepoopts, _('hg extdiff [OPT]... [FILE]...'), inferrepo=True)
--- a/hgext/highlight/__init__.py Mon Oct 05 10:43:16 2015 -0600 +++ b/hgext/highlight/__init__.py Wed Oct 07 13:44:48 2015 -0500 @@ -13,23 +13,28 @@ It depends on the Pygments syntax highlighting library: http://pygments.org/ -There is a single configuration option:: +There are two configuration options:: [web] - pygments_style = <style> - -The default is 'colorful'. + pygments_style = <style> (default: colorful) + highlightfiles = <fileset> (default: size('<5M')) """ import highlight from mercurial.hgweb import webcommands, webutil, common -from mercurial import extensions, encoding +from mercurial import extensions, encoding, fileset # Note for extension authors: ONLY specify testedwith = 'internal' 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 = 'internal' +def checkfctx(fctx, expr): + ctx = fctx.changectx() + tree = fileset.parse(expr) + mctx = fileset.matchctx(ctx, subset=[fctx.path()], status=None) + return fctx.path() in fileset.getset(mctx, tree) + def filerevision_highlight(orig, web, req, tmpl, fctx): mt = ''.join(tmpl('mimetype', encoding=encoding.encoding)) # only pygmentize for mimetype containing 'html' so we both match @@ -41,7 +46,9 @@ # pygmentize a html file if 'html' in mt: style = web.config('web', 'pygments_style', 'colorful') - highlight.pygmentize('fileline', fctx, style, tmpl) + expr = web.config('web', 'highlightfiles', "size('<5M')") + if checkfctx(fctx, expr): + highlight.pygmentize('fileline', fctx, style, tmpl) return orig(web, req, tmpl, fctx) def annotate_highlight(orig, web, req, tmpl): @@ -49,7 +56,9 @@ if 'html' in mt: fctx = webutil.filectx(web.repo, req) style = web.config('web', 'pygments_style', 'colorful') - highlight.pygmentize('annotateline', fctx, style, tmpl) + expr = web.config('web', 'highlightfiles', "size('<5M')") + if checkfctx(fctx, expr): + highlight.pygmentize('annotateline', fctx, style, tmpl) return orig(web, req, tmpl) def generate_css(web, req, tmpl):
--- a/hgext/highlight/highlight.py Mon Oct 05 10:43:16 2015 -0600 +++ b/hgext/highlight/highlight.py Wed Oct 07 13:44:48 2015 -0500 @@ -49,7 +49,12 @@ try: lexer = guess_lexer(text[:1024], stripnl=False) except (ClassNotFound, ValueError): - lexer = TextLexer(stripnl=False) + # Don't highlight unknown files + return + + # Don't highlight text files + if isinstance(lexer, TextLexer): + return formatter = HtmlFormatter(nowrap=True, style=style)
--- a/hgext/histedit.py Mon Oct 05 10:43:16 2015 -0600 +++ b/hgext/histedit.py Wed Oct 07 13:44:48 2015 -0500 @@ -38,7 +38,7 @@ # f, fold = use commit, but combine it with the one above # r, roll = like fold, but discard this commit's description # d, drop = remove commit from history - # m, mess = edit message without changing commit content + # m, mess = edit commit message without changing commit content # In this file, lines beginning with ``#`` are ignored. You must specify a rule @@ -60,7 +60,7 @@ # f, fold = use commit, but combine it with the one above # r, roll = like fold, but discard this commit's description # d, drop = remove commit from history - # m, mess = edit message without changing commit content + # m, mess = edit commit message without changing commit content # At which point you close the editor and ``histedit`` starts working. When you @@ -144,7 +144,7 @@ repo, you can add a ``--force`` option. Histedit rule lines are truncated to 80 characters by default. You -can customise this behaviour by setting a different length in your +can customize this behavior by setting a different length in your configuration file:: [histedit] @@ -198,7 +198,7 @@ # f, fold = use commit, but combine it with the one above # r, roll = like fold, but discard this commit's description # d, drop = remove commit from history -# m, mess = edit message without changing commit content +# m, mess = edit commit message without changing commit content # """) @@ -406,7 +406,7 @@ """Merge changeset from ctx (only) in the current working directory""" wcpar = repo.dirstate.parents()[0] if ctx.p1().node() == wcpar: - # edition ar "in place" we do not need to make any merge, + # edits are "in place" we do not need to make any merge, # just applies changes on parent for edition cmdutil.revert(ui, repo, ctx, (wcpar, node.nullid), all=True) stats = None @@ -557,8 +557,21 @@ middlecommits) def skipprompt(self): + """Returns true if the rule should skip the message editor. + + For example, 'fold' wants to show an editor, but 'rollup' + doesn't want to. + """ return False + def mergedescs(self): + """Returns true if the rule should merge messages of multiple changes. + + This exists mainly so that 'rollup' rules can be a subclass of + 'fold'. + """ + return True + def finishfold(self, ui, repo, ctx, oldctx, newnode, internalchanges): parent = ctx.parents()[0].node() hg.update(repo, parent) @@ -566,7 +579,7 @@ commitopts = {} commitopts['user'] = ctx.user() # commit message - if self.skipprompt(): + if not self.mergedescs(): newmessage = ctx.description() else: newmessage = '\n***\n'.join( @@ -601,7 +614,22 @@ replacements.append((ich, (n,))) return repo[n], replacements +class _multifold(fold): + """fold subclass used for when multiple folds happen in a row + + We only want to fire the editor for the folded message once when + (say) four changes are folded down into a single change. This is + similar to rollup, but we should preserve both messages so that + when the last fold operation runs we can show the user all the + commit messages in their editor. + """ + def skipprompt(self): + return True + class rollup(fold): + def mergedescs(self): + return False + def skipprompt(self): return True @@ -614,10 +642,12 @@ def commiteditor(self): return cmdutil.getcommiteditor(edit=True, editform='histedit.mess') -def findoutgoing(ui, repo, remote=None, force=False, opts={}): +def findoutgoing(ui, repo, remote=None, force=False, opts=None): """utility function to find the first outgoing changeset - Used by initialisation code""" + Used by initialization code""" + if opts is None: + opts = {} dest = ui.expandpath(remote or 'default-push', remote or 'default') dest, revs = hg.parseurl(dest, None)[:2] ui.status(_('comparing with %s\n') % util.hidepassword(dest)) @@ -644,6 +674,7 @@ 'edit': edit, 'f': fold, 'fold': fold, + '_multifold': _multifold, 'r': rollup, 'roll': rollup, 'd': drop, @@ -675,9 +706,9 @@ destination repository. If URL of the destination is omitted, the 'default-push' (or 'default') path will be used. - For safety, this command is aborted, also if there are ambiguous - outgoing revisions which may confuse users: for example, there are - multiple branches containing outgoing revisions. + For safety, this command is also aborted if there are ambiguous + outgoing revisions which may confuse users: for example, if there + are multiple branches containing outgoing revisions. Use "min(outgoing() and ::.)" or similar revset specification instead of --outgoing to specify edit target revision exactly in @@ -778,7 +809,7 @@ return elif goal == 'abort': state.read() - mapping, tmpnodes, leafs, _ntm = processreplacement(state) + tmpnodes, leafs = newnodestoabort(state) ui.debug('restore wc to old parent %s\n' % node.short(state.topmost)) # Recover our old commits if necessary @@ -791,13 +822,9 @@ os.remove(backupfile) # check whether we should update away - parentnodes = [c.node() for c in repo[None].parents()] - for n in leafs | set([state.parentctxnode]): - if n in parentnodes: - hg.clean(repo, state.topmost) - break - else: - pass + if repo.unfiltered().revs('parents() and (%n or %ln::)', + state.parentctxnode, leafs | tmpnodes): + hg.clean(repo, state.topmost) cleanupnode(ui, repo, 'created', tmpnodes) cleanupnode(ui, repo, 'temp', leafs) state.clear() @@ -854,6 +881,14 @@ 'histedit') state.backupfile = backupfile + # preprocess rules so that we can hide inner folds from the user + # and only show one editor + rules = state.rules[:] + for idx, ((action, ha), (nextact, unused)) in enumerate( + zip(rules, rules[1:] + [(None, None)])): + if action == 'fold' and nextact == 'fold': + state.rules[idx] = '_multifold', ha + while state.rules: state.write() action, ha = state.rules.pop(0) @@ -999,7 +1034,7 @@ raise util.Abort(_('duplicated command for changeset %s') % ha[:12]) seen.add(ha) - if action not in actiontable: + if action not in actiontable or action.startswith('_'): raise util.Abort(_('unknown action "%s"') % action) parsed.append([action, ha]) missing = sorted(expected - seen) # sort to stabilize output @@ -1009,6 +1044,25 @@ hint=_('do you want to use the drop action?')) return parsed +def newnodestoabort(state): + """process the list of replacements to return + + 1) the list of final node + 2) the list of temporary node + + This meant to be used on abort as less data are required in this case. + """ + replacements = state.replacements + allsuccs = set() + replaced = set() + for rep in replacements: + allsuccs.update(rep[1]) + replaced.add(rep[0]) + newnodes = allsuccs - replaced + tmpnodes = allsuccs & replaced + return newnodes, tmpnodes + + def processreplacement(state): """process the list of replacements to return @@ -1019,15 +1073,15 @@ allsuccs = set() replaced = set() fullmapping = {} - # initialise basic set - # fullmapping record all operation recorded in replacement + # initialize basic set + # fullmapping records all operations recorded in replacement for rep in replacements: allsuccs.update(rep[1]) replaced.add(rep[0]) fullmapping.setdefault(rep[0], set()).update(rep[1]) new = allsuccs - replaced tmpnodes = allsuccs & replaced - # Reduce content fullmapping into direct relation between original nodes + # Reduce content fullmapping into direct relation between original nodes # and final node created during history edition # Dropped changeset are replaced by an empty list toproceed = set(fullmapping) @@ -1113,8 +1167,12 @@ lock = None try: lock = repo.lock() - # Find all node that need to be stripped - # (we hg %lr instead of %ln to silently ignore unknown item + # do not let filtering get in the way of the cleanse + # we should probably get rid of obsolescence marker created during the + # histedit, but we currently do not have such information. + repo = repo.unfiltered() + # Find all nodes that need to be stripped + # (we use %lr instead of %ln to silently ignore unknown items) nm = repo.changelog.nodemap nodes = sorted(n for n in nodes if n in nm) roots = [c.node() for c in repo.set("roots(%ln)", nodes)]
--- a/hgext/keyword.py Mon Oct 05 10:43:16 2015 -0600 +++ b/hgext/keyword.py Wed Oct 07 13:44:48 2015 -0500 @@ -15,7 +15,7 @@ # audience not running a version control system. # # For in-depth discussion refer to -# <http://mercurial.selenic.com/wiki/KeywordPlan>. +# <https://mercurial-scm.org/wiki/KeywordPlan>. # # Keyword expansion is based on Mercurial's changeset template mappings. # @@ -623,6 +623,7 @@ def rollback(self, dryrun=False, force=False): wlock = self.wlock() + origrestrict = kwt.restrict try: if not dryrun: changed = self['.'].files() @@ -630,10 +631,12 @@ if not dryrun: ctx = self['.'] modified, added = _preselect(ctx.status(), changed) + kwt.restrict = False kwt.overwrite(ctx, modified, True, True) kwt.overwrite(ctx, added, True, False) return ret finally: + kwt.restrict = origrestrict wlock.release() # monkeypatches
--- a/hgext/largefiles/overrides.py Mon Oct 05 10:43:16 2015 -0600 +++ b/hgext/largefiles/overrides.py Wed Oct 07 13:44:48 2015 -0500 @@ -50,8 +50,10 @@ def installnormalfilesmatchfn(manifest): '''installmatchfn with a matchfn that ignores all largefiles''' - def overridematch(ctx, pats=[], opts={}, globbed=False, + def overridematch(ctx, pats=(), opts=None, globbed=False, default='relpath', badfn=None): + if opts is None: + opts = {} match = oldmatch(ctx, pats, opts, globbed, default, badfn=badfn) return composenormalfilematcher(match, manifest) oldmatch = installmatchfn(overridematch) @@ -287,13 +289,15 @@ repo._repo.lfstatus = False def overridelog(orig, ui, repo, *pats, **opts): - def overridematchandpats(ctx, pats=[], opts={}, globbed=False, + def overridematchandpats(ctx, pats=(), opts=None, globbed=False, default='relpath', badfn=None): """Matcher that merges root directory with .hglf, suitable for log. It is still possible to match .hglf directly. For any listed files run log on the standin too. matchfn tries both the given filename and with .hglf stripped. """ + if opts is None: + opts = {} matchandpats = oldmatchandpats(ctx, pats, opts, globbed, default, badfn=badfn) m, p = copy.copy(matchandpats) @@ -613,8 +617,10 @@ wlock = repo.wlock() manifest = repo[None].manifest() - def overridematch(ctx, pats=[], opts={}, globbed=False, + def overridematch(ctx, pats=(), opts=None, globbed=False, default='relpath', badfn=None): + if opts is None: + opts = {} newpats = [] # The patterns were previously mangled to add the standin # directory; we need to remove that now @@ -722,8 +728,10 @@ oldstandins = lfutil.getstandinsstate(repo) - def overridematch(mctx, pats=[], opts={}, globbed=False, + def overridematch(mctx, pats=(), opts=None, globbed=False, default='relpath', badfn=None): + if opts is None: + opts = {} match = oldmatch(mctx, pats, opts, globbed, default, badfn=badfn) m = copy.copy(match) @@ -1174,8 +1182,10 @@ finally: repo.lfstatus = False -def scmutiladdremove(orig, repo, matcher, prefix, opts={}, dry_run=None, +def scmutiladdremove(orig, repo, matcher, prefix, opts=None, dry_run=None, similarity=None): + if opts is None: + opts = {} if not lfutil.islfilesrepo(repo): return orig(repo, matcher, prefix, opts, dry_run, similarity) # Get the list of missing largefiles so we can remove them
--- a/hgext/mq.py Mon Oct 05 10:43:16 2015 -0600 +++ b/hgext/mq.py Wed Oct 07 13:44:48 2015 -0500 @@ -28,7 +28,7 @@ By default, mq will automatically use git patches when required to avoid losing file mode changes, copy records, binary files or empty -files creations or deletions. This behaviour can be configured with:: +files creations or deletions. This behavior can be configured with:: [mq] git = auto/keep/yes/no @@ -483,7 +483,7 @@ self.guardsdirty = False self.activeguards = None - def diffopts(self, opts={}, patchfn=None): + def diffopts(self, opts=None, patchfn=None): diffopts = patchmod.diffopts(self.ui, opts) if self.gitmode == 'auto': diffopts.upgrade = True
--- a/hgext/pager.py Mon Oct 05 10:43:16 2015 -0600 +++ b/hgext/pager.py Wed Oct 07 13:44:48 2015 -0500 @@ -65,13 +65,19 @@ # leave the attribute unspecified. testedwith = 'internal' -def _pagersubprocess(ui, p): +def _runpager(ui, p): pager = subprocess.Popen(p, shell=True, bufsize=-1, close_fds=util.closefds, stdin=subprocess.PIPE, stdout=sys.stdout, stderr=sys.stderr) - stdout = os.dup(sys.stdout.fileno()) - stderr = os.dup(sys.stderr.fileno()) + # back up original file objects and descriptors + olduifout = ui.fout + oldstdout = sys.stdout + stdoutfd = os.dup(sys.stdout.fileno()) + stderrfd = os.dup(sys.stderr.fileno()) + + # create new line-buffered stdout so that output can show up immediately + ui.fout = sys.stdout = newstdout = os.fdopen(sys.stdout.fileno(), 'wb', 1) os.dup2(pager.stdin.fileno(), sys.stdout.fileno()) if ui._isatty(sys.stderr): os.dup2(pager.stdin.fileno(), sys.stderr.fileno()) @@ -81,13 +87,16 @@ if util.safehasattr(signal, "SIGINT"): signal.signal(signal.SIGINT, signal.SIG_IGN) pager.stdin.close() - os.dup2(stdout, sys.stdout.fileno()) - os.dup2(stderr, sys.stderr.fileno()) + ui.fout = olduifout + sys.stdout = oldstdout + # close new stdout while it's associated with pager; otherwise stdout + # fd would be closed when newstdout is deleted + newstdout.close() + # restore original fds: stdout is open again + os.dup2(stdoutfd, sys.stdout.fileno()) + os.dup2(stderrfd, sys.stderr.fileno()) pager.wait() -def _runpager(ui, p): - _pagersubprocess(ui, p) - def uisetup(ui): if '--debugger' in sys.argv or not ui.formatted(): return
--- a/hgext/progress.py Mon Oct 05 10:43:16 2015 -0600 +++ b/hgext/progress.py Wed Oct 07 13:44:48 2015 -0500 @@ -10,3 +10,8 @@ This extension has been merged into core, you can remove it from your config. See hg help config.progress for configuration options. """ +# Note for extension authors: ONLY specify testedwith = 'internal' 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 = 'internal'
--- a/hgext/purge.py Mon Oct 05 10:43:16 2015 -0600 +++ b/hgext/purge.py Wed Oct 07 13:44:48 2015 -0500 @@ -1,6 +1,6 @@ # Copyright (C) 2006 - Marco Barisione <marco@barisione.org> # -# This is a small extension for Mercurial (http://mercurial.selenic.com/) +# This is a small extension for Mercurial (https://mercurial-scm.org/) # that removes files not known to mercurial # # This program was inspired by the "cvspurge" script contained in CVS
--- a/hgext/rebase.py Mon Oct 05 10:43:16 2015 -0600 +++ b/hgext/rebase.py Wed Oct 07 13:44:48 2015 -0500 @@ -11,12 +11,12 @@ repository. For more information: -http://mercurial.selenic.com/wiki/RebaseExtension +https://mercurial-scm.org/wiki/RebaseExtension ''' from mercurial import hg, util, repair, merge, cmdutil, commands, bookmarks from mercurial import extensions, patch, scmutil, phases, obsolete, error -from mercurial import copies, repoview +from mercurial import copies, repoview, revset from mercurial.commands import templateopts from mercurial.node import nullrev, nullid, hex, short from mercurial.lock import release @@ -26,6 +26,7 @@ revtodo = -1 nullmerge = -2 revignored = -3 +revprecursor = -4 cmdtable = {} command = cmdutil.command(cmdtable) @@ -54,6 +55,21 @@ c(ctx, extra) return extrafn +def _rebasedefaultdest(repo, subset, x): + # ``_rebasedefaultdest()`` + + # default destination for rebase. + # # XXX: Currently private because I expect the signature to change. + # # XXX: - taking rev as arguments, + # # XXX: - bailing out in case of ambiguity vs returning all data. + # # XXX: - probably merging with the merge destination. + # i18n: "_rebasedefaultdest" is a keyword + revset.getargs(x, 0, 0, _("_rebasedefaultdest takes no arguments")) + # Destination defaults to the latest revision in the + # current branch + branch = repo[None].branch() + return subset & revset.baseset([repo[branch].rev()]) + @command('rebase', [('s', 'source', '', _('rebase the specified changeset and descendants'), _('REV')), @@ -201,8 +217,14 @@ keepopen = opts.get('keepopen', False) if opts.get('interactive'): + try: + if extensions.find('histedit'): + enablehistedit = '' + except KeyError: + enablehistedit = " --config extensions.histedit=" + help = "hg%s help -e histedit" % enablehistedit msg = _("interactive history editing is supported by the " - "'histedit' extension (see \"hg help histedit\")") + "'histedit' extension (see \"%s\")") % help raise util.Abort(msg) if collapsemsg and not collapsef: @@ -218,7 +240,7 @@ if srcf or basef or destf: raise util.Abort( _('abort and continue do not allow specifying revisions')) - if opts.get('tool', False): + if abortf and opts.get('tool', False): ui.warn(_('tool option will be ignored\n')) try: @@ -252,12 +274,8 @@ cmdutil.bailifchanged(repo) if not destf: - # Destination defaults to the latest revision in the - # current branch - branch = repo[None].branch() - dest = repo[branch] - else: - dest = scmutil.revsingle(repo, destf) + destf = '_rebasedefaultdest()' + dest = scmutil.revsingle(repo, destf) if revf: rebaseset = scmutil.revrange(repo, revf) @@ -322,7 +340,20 @@ " unrebased descendants"), hint=_('use --keep to keep original changesets')) - result = buildstate(repo, dest, rebaseset, collapsef) + obsoletenotrebased = {} + if ui.configbool('experimental', 'rebaseskipobsolete'): + rebasesetrevs = set(rebaseset) + obsoletenotrebased = _computeobsoletenotrebased(repo, + rebasesetrevs, + dest) + + # - plain prune (no successor) changesets are rebased + # - split changesets are not rebased if at least one of the + # changeset resulting from the split is an ancestor of dest + rebaseset = rebasesetrevs - set(obsoletenotrebased) + result = buildstate(repo, dest, rebaseset, collapsef, + obsoletenotrebased) + if not result: # Empty state built, nothing to rebase ui.status(_('nothing to rebase\n')) @@ -406,7 +437,8 @@ editform = cmdutil.mergeeditform(merging, 'rebase') editor = cmdutil.getcommiteditor(editform=editform, **opts) newnode = concludenode(repo, rev, p1, p2, extrafn=extrafn, - editor=editor) + editor=editor, + keepbranches=keepbranchesf) else: # Skip commit if we are collapsing repo.dirstate.beginparentchange() @@ -428,6 +460,12 @@ ui.debug('ignoring null merge rebase of %s\n' % rev) elif state[rev] == revignored: ui.status(_('not rebasing ignored %s\n') % desc) + elif state[rev] == revprecursor: + targetctx = repo[obsoletenotrebased[rev]] + desctarget = '%d:%s "%s"' % (targetctx.rev(), targetctx, + targetctx.description().split('\n', 1)[0]) + msg = _('note: not rebasing %s, already in destination as %s\n') + ui.status(msg % (desc, desctarget)) else: ui.status(_('already rebased %s as %s\n') % (desc, repo[state[rev]])) @@ -450,7 +488,8 @@ editopt = True editor = cmdutil.getcommiteditor(edit=editopt, editform=editform) newnode = concludenode(repo, rev, p1, external, commitmsg=commitmsg, - extrafn=extrafn, editor=editor) + extrafn=extrafn, editor=editor, + keepbranches=keepbranchesf) if newnode is None: newrev = target else: @@ -530,7 +569,8 @@ (max(targetancestors), ', '.join(str(p) for p in sorted(parents)))) -def concludenode(repo, rev, p1, p2, commitmsg=None, editor=None, extrafn=None): +def concludenode(repo, rev, p1, p2, commitmsg=None, editor=None, extrafn=None, + keepbranches=False): '''Commit the wd changes with parents p1 and p2. Reuse commit info from rev but also store useful information in extra. Return node of committed revision.''' @@ -540,6 +580,7 @@ ctx = repo[rev] if commitmsg is None: commitmsg = ctx.description() + keepbranch = keepbranches and repo[p1].branch() != ctx.branch() extra = {'rebase_source': ctx.hex()} if extrafn: extrafn(ctx, extra) @@ -548,6 +589,8 @@ try: targetphase = max(ctx.phase(), phases.draft) repo.ui.setconfig('phases', 'new-commit', targetphase, 'rebase') + if keepbranch: + repo.ui.setconfig('ui', 'allowemptycommit', True) # Commit might fail if unresolved files exist newnode = repo.commit(text=commitmsg, user=ctx.user(), date=ctx.date(), extra=extra, editor=editor) @@ -609,7 +652,7 @@ elif p1n in state: if state[p1n] == nullmerge: p1 = target - elif state[p1n] == revignored: + elif state[p1n] in (revignored, revprecursor): p1 = nearestrebased(repo, p1n, state) if p1 is None: p1 = target @@ -625,7 +668,7 @@ if p2n in state: if p1 == target: # p1n in targetancestors or external p1 = state[p2n] - elif state[p2n] == revignored: + elif state[p2n] in (revignored, revprecursor): p2 = nearestrebased(repo, p2n, state) if p2 is None: # no ancestors rebased yet, detach @@ -813,7 +856,8 @@ activebookmark = l else: oldrev, newrev = l.split(':') - if newrev in (str(nullmerge), str(revignored)): + if newrev in (str(nullmerge), str(revignored), + str(revprecursor)): state[repo[oldrev].rev()] = int(newrev) elif newrev == nullid: state[repo[oldrev].rev()] = revtodo @@ -901,7 +945,7 @@ repo.ui.warn(_('rebase aborted\n')) return 0 -def buildstate(repo, dest, rebaseset, collapse): +def buildstate(repo, dest, rebaseset, collapse, obsoletenotrebased): '''Define which revisions are going to be rebased and where repo: repo @@ -988,6 +1032,8 @@ rebasedomain = set(repo.revs('%ld::%ld', rebaseset, rebaseset)) for ignored in set(rebasedomain) - set(rebaseset): state[ignored] = revignored + for r in obsoletenotrebased: + state[r] = revprecursor return repo['.'].rev(), dest.rev(), state def clearrebased(ui, repo, state, skipped, collapsedas=None): @@ -1096,6 +1142,32 @@ blockers.update(getattr(repo, '_rebaseset', ())) return blockers +def _computeobsoletenotrebased(repo, rebasesetrevs, dest): + """return a mapping obsolete => successor for all obsolete nodes to be + rebased that have a successors in the destination""" + obsoletenotrebased = {} + + # Build a mapping succesor => obsolete nodes for the obsolete + # nodes to be rebased + allsuccessors = {} + for r in rebasesetrevs: + n = repo[r] + if n.obsolete(): + node = repo.changelog.node(r) + for s in obsolete.allsuccessors(repo.obsstore, [node]): + allsuccessors[repo.changelog.rev(s)] = repo.changelog.rev(node) + + if allsuccessors: + # Look for successors of obsolete nodes to be rebased among + # the ancestors of dest + ancs = repo.changelog.ancestors([repo[dest].rev()], + stoprev=min(allsuccessors), + inclusive=True) + for s in allsuccessors: + if s in ancs: + obsoletenotrebased[allsuccessors[s]] = s + return obsoletenotrebased + def summaryhook(ui, repo): if not os.path.exists(repo.join('rebasestate')): return @@ -1126,3 +1198,4 @@ _("use 'hg rebase --continue' or 'hg rebase --abort'")]) # ensure rebased rev are not hidden extensions.wrapfunction(repoview, '_getdynamicblockers', _rebasedvisible) + revset.symbols['_rebasedefaultdest'] = _rebasedefaultdest
--- a/hgext/transplant.py Mon Oct 05 10:43:16 2015 -0600 +++ b/hgext/transplant.py Wed Oct 07 13:44:48 2015 -0500 @@ -117,8 +117,10 @@ return True return False - def apply(self, repo, source, revmap, merges, opts={}): + def apply(self, repo, source, revmap, merges, opts=None): '''apply the revisions in revmap one by one in revision order''' + if opts is None: + opts = {} revs = sorted(revmap) p1, p2 = repo.dirstate.parents() pulls = []
--- a/hgext/win32text.py Mon Oct 05 10:43:16 2015 -0600 +++ b/hgext/win32text.py Wed Oct 07 13:44:48 2015 -0500 @@ -62,7 +62,7 @@ # warn if already has 'newline' in repository. # it might cause unexpected eol conversion. # see issue 302: - # http://mercurial.selenic.com/bts/issue302 + # https://bz.mercurial-scm.org/302 if newline in s and ui and filename and repo: ui.warn(_('WARNING: %s already has %s line endings\n' 'and does not need EOL conversion by the win32text plugin.\n' @@ -174,4 +174,4 @@ # deprecated config: win32text.warn if ui.configbool('win32text', 'warn', True): ui.warn(_("win32text is deprecated: " - "http://mercurial.selenic.com/wiki/Win32TextExtension\n")) + "https://mercurial-scm.org/wiki/Win32TextExtension\n"))
--- a/hgweb.cgi Mon Oct 05 10:43:16 2015 -0600 +++ b/hgweb.cgi Wed Oct 07 13:44:48 2015 -0500 @@ -1,7 +1,7 @@ #!/usr/bin/env python # # An example hgweb CGI script, edit as necessary -# See also http://mercurial.selenic.com/wiki/PublishingRepositories +# See also https://mercurial-scm.org/wiki/PublishingRepositories # Path to repo or hgweb config to serve (see 'hg help hgweb') config = "/path/to/repo/or/config"
--- a/i18n/check-translation.py Mon Oct 05 10:43:16 2015 -0600 +++ b/i18n/check-translation.py Wed Oct 07 13:44:48 2015 -0500 @@ -5,8 +5,15 @@ import polib import re +scanners = [] checkers = [] +def scanner(): + def decorator(func): + scanners.append(func) + return func + return decorator + def levelchecker(level, msgidpat): def decorator(func): if msgidpat: @@ -61,6 +68,46 @@ if [c for c, i in indices if len(c) == i + 1]: yield "msgstr has invalid '&' followed by none" +deprecatedpe = None +@scanner() +def deprecatedsetup(pofile): + pes = [p for p in pofile if p.msgid == 'DEPRECATED'] + if len(pes): + global deprecatedpe + deprecatedpe = pes[0] + +@fatalchecker('(DEPRECATED)') +def deprecated(pe): + """Check for DEPRECATED + >>> ped = polib.POEntry( + ... msgid = 'DEPRECATED', + ... msgstr= 'DETACERPED') + >>> deprecatedsetup([ped]) + >>> pe = polib.POEntry( + ... msgid = 'Something (DEPRECATED)', + ... msgstr= 'something (DEPRECATED)') + >>> match(deprecated, pe) + True + >>> for e in deprecated(pe): print e + >>> pe = polib.POEntry( + ... msgid = 'Something (DEPRECATED)', + ... msgstr= 'something (DETACERPED)') + >>> match(deprecated, pe) + True + >>> for e in deprecated(pe): print e + >>> pe = polib.POEntry( + ... msgid = 'Something (DEPRECATED)', + ... msgstr= 'something') + >>> match(deprecated, pe) + True + >>> for e in deprecated(pe): print e + msgstr inconsistently translated (DEPRECATED) + """ + if not ('(DEPRECATED)' in pe.msgstr or + (deprecatedpe and deprecatedpe.msgstr and + deprecatedpe.msgstr in pe.msgstr)): + yield "msgstr inconsistently translated (DEPRECATED)" + #################### def warningchecker(msgidpat=None): @@ -117,6 +164,8 @@ return [] detected = [] + for checker in scanners: + checker(pofile) for pe in pofile.translated_entries(): errors = [] for checker, level in targetcheckers:
--- a/i18n/da.po Mon Oct 05 10:43:16 2015 -0600 +++ b/i18n/da.po Wed Oct 07 13:44:48 2015 -0500 @@ -9367,7 +9367,7 @@ msgstr "" msgid "DEPRECATED" -msgstr "" +msgstr "FORÆLDET" msgid "" "\n"
--- a/i18n/fr.po Mon Oct 05 10:43:16 2015 -0600 +++ b/i18n/fr.po Wed Oct 07 13:44:48 2015 -0500 @@ -4014,7 +4014,7 @@ msgstr "garder le fichier du patch" msgid "stop managing a revision (DEPRECATED)" -msgstr "arrêter de gérer une révision" +msgstr "" msgid "hg qdelete [-k] [-r REV]... [PATCH]..." msgstr "hg qdelete [-k] [-r REV]... [PATCH]..."
--- a/i18n/it.po Mon Oct 05 10:43:16 2015 -0600 +++ b/i18n/it.po Wed Oct 07 13:44:48 2015 -0500 @@ -7506,7 +7506,7 @@ msgstr "" msgid "DEPRECATED" -msgstr "" +msgstr "DEPRECATO" msgid "" "\n"
--- a/i18n/pt_BR.po Mon Oct 05 10:43:16 2015 -0600 +++ b/i18n/pt_BR.po Wed Oct 07 13:44:48 2015 -0500 @@ -3821,7 +3821,7 @@ msgstr "factotum não está respondendo" msgid "pull, update and merge in one command (DEPRECATED)" -msgstr "pull, update e merge em um comando (OBSOLETA)" +msgstr "pull, update e merge em um comando (OBSOLETO)" msgid "a specific revision you would like to pull" msgstr "uma revisão específica que você gostaria de trazer" @@ -4082,7 +4082,7 @@ msgstr "revisões de autoria do usuário" msgid "show only changesets within the given named branch (DEPRECATED)" -msgstr "mostra apenas revisões dentro do ramo nomeado especificado (OBSOLETA)" +msgstr "mostra apenas revisões dentro do ramo nomeado especificado (OBSOLETO)" msgid "BRANCH" msgstr "RAMO" @@ -8342,7 +8342,7 @@ msgstr "escrevendo" msgid "show progress bars for some actions (DEPRECATED)" -msgstr "mostra barras de progresso para algumas ações (OBSOLETA)" +msgstr "mostra barras de progresso para algumas ações (OBSOLETO)" msgid "" "This extension has been merged into core, you can remove it from your config.\n" @@ -8505,7 +8505,7 @@ msgstr "mantém nomes de ramos originais" msgid "(DEPRECATED)" -msgstr "(OBSOLETA)" +msgstr "(OBSOLETO)" msgid "specify merge tool" msgstr "especifica o utilitário de mesclagem" @@ -11140,7 +11140,7 @@ msgstr "faz um annotate da revisão especificada" msgid "follow copies/renames and list the filename (DEPRECATED)" -msgstr "segue cópias e renomeações e lista o nome de arquivo (OBSOLETA)" +msgstr "segue cópias e renomeações e lista o nome de arquivo (OBSOLETO)" msgid "don't follow copies and renames" msgstr "não segue cópias e renomeações" @@ -12862,7 +12862,7 @@ msgid "backwards compatibility with old bash completion scripts (DEPRECATED)" msgstr "" "compatibilidade retroativa com antigos scripts bash de completação " -"(OBSOLETA)" +"(OBSOLETO)" msgid "NAME..." msgstr "NOME..." @@ -13783,7 +13783,7 @@ msgstr "mostra apenas cabeças topológicas" msgid "show active branchheads only (DEPRECATED)" -msgstr "mostra apenas cabeças de ramo ativas (OBSOLETA)" +msgstr "mostra apenas cabeças de ramo ativas (OBSOLETO)" msgid "show normal and closed branch heads" msgstr "mostra cabeças de ramo normais e fechadas" @@ -15606,7 +15606,7 @@ msgstr "nome do arquivo de configuração do hgweb (veja \"hg help hgweb\")" msgid "name of the hgweb config file (DEPRECATED)" -msgstr "nome do arquivo de configuração do hgweb (OBSOLETA)" +msgstr "nome do arquivo de configuração do hgweb (OBSOLETO)" msgid "name of file to write process ID to" msgstr "nome do arquivo no qual escrever o ID do processo" @@ -21151,7 +21151,7 @@ " Default is False." msgstr "" "``allowbz2``\n" -" (OBSOLETA) Determina se revisões estarão disponíveis para download\n" +" (OBSOLETO) Determina se revisões estarão disponíveis para download\n" " em formato .tar.bz2.\n" " O padrão é False." @@ -21162,7 +21162,7 @@ " Default is False." msgstr "" "``allowgz``\n" -" (OBSOLETA) Determina se revisões estarão disponíveis para download\n" +" (OBSOLETO) Determina se revisões estarão disponíveis para download\n" " em formato .tar.gz.\n" " O padrão é False." @@ -21223,7 +21223,7 @@ " revisions. Default is False. This feature creates temporary files." msgstr "" "``allowzip``\n" -" (OBSOLETA) Determina se revisões estarão disponíveis para download\n" +" (OBSOLETO) Determina se revisões estarão disponíveis para download\n" " em formato .zip.\n" " O padrão é False."
--- a/i18n/ro.po Mon Oct 05 10:43:16 2015 -0600 +++ b/i18n/ro.po Wed Oct 07 13:44:48 2015 -0500 @@ -8063,7 +8063,7 @@ msgstr "afișează doar capetele topologice" msgid "show active branchheads only (DEPRECATED)" -msgstr "afișează doar capetele de ramură active [ÎNVECHIT]" +msgstr "afișează doar capetele de ramură active (ÎNVECHIT)" msgid "show normal and closed branch heads" msgstr "afișează capetele de ramură normale și închise" @@ -8259,7 +8259,7 @@ msgstr "VALOARE" msgid "DEPRECATED" -msgstr "" +msgstr "ÎNVECHIT" msgid "" "\n"
--- a/i18n/sv.po Mon Oct 05 10:43:16 2015 -0600 +++ b/i18n/sv.po Wed Oct 07 13:44:48 2015 -0500 @@ -826,7 +826,7 @@ msgstr "Bugzilla-fel: %s" msgid "command to display child changesets (DEPRECATED)" -msgstr "kommando för att visa barnändringar (FÖRLEGAD)" +msgstr "kommando för att visa barnändringar (FÖRÅLDRAD)" msgid "" "This extension is deprecated. You should use :hg:`log -r\n" @@ -2741,7 +2741,7 @@ "följ ändringshistorik, eller filhistorik över kopieringar och namnbyten" msgid "only follow the first parent of merge changesets (DEPRECATED)" -msgstr "följ bara den första föräldern vid sammanfogningar (FÖRLEGAD)" +msgstr "följ bara den första föräldern vid sammanfogningar (FÖRÅLDRAD)" msgid "show revisions matching date spec" msgstr "visa revisioner som matchar datumspecen" @@ -2759,7 +2759,7 @@ msgstr "inkludera revisioner där filer togs bort" msgid "show only merges (DEPRECATED)" -msgstr "visa bara sammanfogningar (FÖRLEGAD)" +msgstr "visa bara sammanfogningar (FÖRÅLDRAD)" msgid "USER" msgstr "ANVÄNDARE" @@ -2768,7 +2768,7 @@ msgstr "revisioner arkiverade av användare" msgid "show only changesets within the given named branch (DEPRECATED)" -msgstr "visa bara ändringar i den namngivna grenen (FÖRLEGAD)" +msgstr "visa bara ändringar i den namngivna grenen (FÖRÅLDRAD)" msgid "BRANCH" msgstr "GREN" @@ -2780,7 +2780,7 @@ msgstr "visa inte revision eller någon av dess föräldrar" msgid "show hidden changesets (DEPRECATED)" -msgstr "visa dolda ändringar (FÖRLEGAD)" +msgstr "visa dolda ändringar (FÖRÅLDRAD)" msgid "[OPTION]... [FILE]" msgstr "[FLAGGA]... [FIL]" @@ -5011,7 +5011,7 @@ msgstr "skriv ut namnet på föregående applicerade patch" msgid "import uncommitted changes (DEPRECATED)" -msgstr "importera icke arkiverade ändringar (FÖRLEGAD)" +msgstr "importera icke arkiverade ändringar (FÖRÅLDRAD)" msgid "add \"From: <current user>\" to patch" msgstr "lägg till \"From: <denna användare>\" i patch" @@ -5306,7 +5306,7 @@ msgstr "poppa alla patchar" msgid "queue name to pop (DEPRECATED)" -msgstr "könamn att poppa (FÖRLEGAD)" +msgstr "könamn att poppa (FÖRÅLDRAD)" msgid "forget any local changes to patched files" msgstr "" @@ -5404,7 +5404,7 @@ "not descendants of REV (DEPRECATED)" msgstr "" "bunta bara ändringar med lokala revisionsnummer större än REV som inte är " -"ättling till REV (FÖRLEGAD)" +"ättling till REV (FÖRÅLDRAD)" msgid "no backups" msgstr "inga säkerhetskopior" @@ -6537,7 +6537,7 @@ msgstr "" msgid "(DEPRECATED)" -msgstr "(FÖRLEGAD)" +msgstr "(FÖRÅLDRAD)" msgid "specify merge tool" msgstr "ange sammanfogningsverktyg" @@ -8088,7 +8088,7 @@ msgstr "annotera den angivna revisionen" msgid "follow copies/renames and list the filename (DEPRECATED)" -msgstr "följ kopieringar/namnbyten och visa filnamnet (FÖRLEGAD)" +msgstr "följ kopieringar/namnbyten och visa filnamnet (FÖRÅLDRAD)" msgid "don't follow copies and renames" msgstr "följ inte kopieringar och namnbyten" @@ -8247,7 +8247,7 @@ msgstr "sammanfoga med gamla dirstate-föräldern efter återkallning" msgid "parent to choose when backing out merge (DEPRECATED)" -msgstr "förälder att välja när en sammanfogning återkallas (FÖRLEGAD)" +msgstr "förälder att välja när en sammanfogning återkallas (FÖRÅLDRAD)" msgid "revision to backout" msgstr "revision att återkalla" @@ -10105,7 +10105,7 @@ msgstr "visa bara topologiska huvuden" msgid "show active branchheads only (DEPRECATED)" -msgstr "visa bara aktiva grenhuvuden (FÖRLEGAD)" +msgstr "visa bara aktiva grenhuvuden (FÖRÅLDRAD)" msgid "show normal and closed branch heads" msgstr "visa normala och stängda grenhuvuden" @@ -11764,7 +11764,7 @@ msgstr "namn på webdir-konfigurationsfil (se \"hg help hgweb\")" msgid "name of the hgweb config file (DEPRECATED)" -msgstr "namn på webdir-konfigurationsfil (FÖRLEGAD)" +msgstr "namn på webdir-konfigurationsfil (FÖRÅLDRAD)" msgid "for remote clients" msgstr "för fjärrklienter" @@ -13116,7 +13116,7 @@ msgstr "VÄRDE" msgid "DEPRECATED" -msgstr "FÖRLEGAD" +msgstr "FÖRÅLDRAD" msgid "" "\n"
--- a/i18n/zh_CN.po Mon Oct 05 10:43:16 2015 -0600 +++ b/i18n/zh_CN.po Wed Oct 07 13:44:48 2015 -0500 @@ -5091,7 +5091,7 @@ msgid "HG: Leave message empty to abort commit." msgstr "" -#, fuzzy, python-format +#, fuzzy, python-format, broken msgid "HG: user: %s" msgstr "用户: %s\n" @@ -5102,7 +5102,7 @@ msgid "HG: branch '%s'" msgstr "" -#, fuzzy, python-format +#, fuzzy, python-format, broken msgid "HG: subrepo %s" msgstr "已删除" @@ -5114,11 +5114,11 @@ msgid "HG: changed %s" msgstr "" -#, fuzzy, python-format +#, fuzzy, python-format, broken msgid "HG: removed %s" msgstr "已删除" -#, fuzzy +#, fuzzy, broken msgid "HG: no files changed" msgstr "正在增加文件改变\n" @@ -10443,3 +10443,6 @@ msgid "user name not available - set USERNAME environment variable" msgstr "" + +msgid "DEPRECATED" +msgstr "不赞成"
--- a/mercurial/ancestor.py Mon Oct 05 10:43:16 2015 -0600 +++ b/mercurial/ancestor.py Wed Oct 07 13:44:48 2015 -0500 @@ -5,9 +5,12 @@ # 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 collections import heapq -from node import nullrev + +from .node import nullrev def commonancestorsheads(pfunc, *nodes): """Returns a set with the heads of all common ancestors of all nodes,
--- a/mercurial/archival.py Mon Oct 05 10:43:16 2015 -0600 +++ b/mercurial/archival.py Wed Oct 07 13:44:48 2015 -0500 @@ -5,14 +5,27 @@ # This software may be used and distributed according to the terms of the # GNU General Public License version 2 or any later version. -from i18n import _ -import match as matchmod -import cmdutil -import scmutil, util, encoding -import cStringIO, os, tarfile, time, zipfile -import zlib, gzip +from __future__ import absolute_import + +import cStringIO +import gzip +import os import struct -import error +import tarfile +import time +import zipfile +import zlib + +from .i18n import _ + +from . import ( + cmdutil, + encoding, + error, + match as matchmod, + scmutil, + util, +) # from unzip source code: _UNX_IFREG = 0x8000 @@ -111,11 +124,7 @@ def _write_gzip_header(self): self.fileobj.write('\037\213') # magic header self.fileobj.write('\010') # compression method - # Python 2.6 introduced self.name and deprecated self.filename - try: - fname = self.name - except AttributeError: - fname = self.filename + fname = self.name if fname and fname.endswith('.gz'): fname = fname[:-3] flags = 0
--- a/mercurial/base85.c Mon Oct 05 10:43:16 2015 -0600 +++ b/mercurial/base85.c Wed Oct 07 13:44:48 2015 -0500 @@ -21,7 +21,7 @@ static void b85prep(void) { - int i; + unsigned i; memset(b85dec, 0, sizeof(b85dec)); for (i = 0; i < sizeof(b85chars); i++)
--- a/mercurial/bookmarks.py Mon Oct 05 10:43:16 2015 -0600 +++ b/mercurial/bookmarks.py Wed Oct 07 13:44:48 2015 -0500 @@ -5,11 +5,22 @@ # 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 errno import os -from mercurial.i18n import _ -from mercurial.node import hex, bin -from mercurial import encoding, util, obsolete, lock as lockmod -import errno + +from .i18n import _ +from .node import ( + bin, + hex, +) +from . import ( + encoding, + lock as lockmod, + obsolete, + util, +) class bmstore(dict): """Storage for bookmarks. @@ -79,6 +90,11 @@ can be copied back on rollback. ''' repo = self._repo + if (repo.ui.configbool('devel', 'all-warnings') + or repo.ui.configbool('devel', 'check-locks')): + l = repo._wlockref and repo._wlockref() + if l is None or not l.held: + repo.ui.develwarn('bookmarks write with no wlock') self._writerepo(repo) repo.invalidatevolatilesets()
--- a/mercurial/branchmap.py Mon Oct 05 10:43:16 2015 -0600 +++ b/mercurial/branchmap.py Wed Oct 07 13:44:48 2015 -0500 @@ -5,13 +5,28 @@ # This software may be used and distributed according to the terms of the # GNU General Public License version 2 or any later version. -from node import bin, hex, nullid, nullrev -import encoding -import scmutil -import util +from __future__ import absolute_import + +import array +import struct import time -from array import array -from struct import calcsize, pack, unpack + +from .node import ( + bin, + hex, + nullid, + nullrev, +) +from . import ( + encoding, + scmutil, + util, +) + +array = array.array +calcsize = struct.calcsize +pack = struct.pack +unpack = struct.unpack def _filename(repo): """name of a branchcache file for a given repo or repoview""" @@ -101,6 +116,38 @@ assert partial.validfor(repo), filtername repo._branchcaches[repo.filtername] = partial +def replacecache(repo, bm): + """Replace the branchmap cache for a repo with a branch mapping. + + This is likely only called during clone with a branch map from a remote. + """ + rbheads = [] + closed = [] + for bheads in bm.itervalues(): + rbheads.extend(bheads) + for h in bheads: + r = repo.changelog.rev(h) + b, c = repo.changelog.branchinfo(r) + if c: + closed.append(h) + + if rbheads: + rtiprev = max((int(repo.changelog.rev(node)) + for node in rbheads)) + cache = branchcache(bm, + repo[rtiprev].node(), + rtiprev, + closednodes=closed) + + # Try to stick it as low as possible + # filter above served are unlikely to be fetch from a clone + for candidate in ('base', 'immutable', 'served'): + rview = repo.filtered(candidate) + if cache.validfor(rview): + repo._branchcaches[candidate] = cache + cache.write(rview) + break + class branchcache(dict): """A dict like object that hold branches heads cache.
--- a/mercurial/bundle2.py Mon Oct 05 10:43:16 2015 -0600 +++ b/mercurial/bundle2.py Wed Oct 07 13:44:48 2015 -0500 @@ -145,19 +145,25 @@ preserve. """ +from __future__ import absolute_import + import errno -import sys -import util -import struct -import urllib +import re import string -import obsolete -import pushkey -import url -import re +import struct +import sys +import urllib -import changegroup, error, tags -from i18n import _ +from .i18n import _ +from . import ( + changegroup, + error, + obsolete, + pushkey, + tags, + url, + util, +) _pack = struct.pack _unpack = struct.unpack @@ -370,17 +376,17 @@ handler = parthandlermapping.get(part.type) if handler is None: status = 'unsupported-type' - raise error.UnsupportedPartError(parttype=part.type) + raise error.BundleUnknownFeatureError(parttype=part.type) indebug(op.ui, 'found a handler for part %r' % part.type) unknownparams = part.mandatorykeys - handler.params if unknownparams: unknownparams = list(unknownparams) unknownparams.sort() status = 'unsupported-params (%s)' % unknownparams - raise error.UnsupportedPartError(parttype=part.type, - params=unknownparams) + raise error.BundleUnknownFeatureError(parttype=part.type, + params=unknownparams) status = 'supported' - except error.UnsupportedPartError as exc: + except error.BundleUnknownFeatureError as exc: if part.mandatory: # mandatory parts raise indebug(op.ui, 'ignoring unsupported advisory part %s' % exc) @@ -473,6 +479,15 @@ self._params = [] self._parts = [] self.capabilities = dict(capabilities) + self._compressor = util.compressors[None]() + + def setcompression(self, alg): + """setup core part compression to <alg>""" + if alg is None: + return + assert not any(n.lower() == 'Compression' for n, v in self._params) + self.addparam('Compression', alg) + self._compressor = util.compressors[alg]() @property def nbparts(self): @@ -524,14 +539,10 @@ yield _pack(_fstreamparamsize, len(param)) if param: yield param - - outdebug(self.ui, 'start of parts') - for part in self._parts: - outdebug(self.ui, 'bundle part: "%s"' % part.type) - for chunk in part.getchunks(ui=self.ui): - yield chunk - outdebug(self.ui, 'end of bundle') - yield _pack(_fpartheadersize, 0) + # starting compression + for chunk in self._getcorechunk(): + yield self._compressor.compress(chunk) + yield self._compressor.flush() def _paramchunk(self): """return a encoded version of all stream parameters""" @@ -544,6 +555,19 @@ blocks.append(par) return ' '.join(blocks) + def _getcorechunk(self): + """yield chunk for the core part of the bundle + + (all but headers and parameters)""" + outdebug(self.ui, 'start of parts') + for part in self._parts: + outdebug(self.ui, 'bundle part: "%s"' % part.type) + for chunk in part.getchunks(ui=self.ui): + yield chunk + outdebug(self.ui, 'end of bundle') + yield _pack(_fpartheadersize, 0) + + def salvageoutput(self): """return a list with a copy of all output parts in the bundle @@ -620,6 +644,7 @@ def __init__(self, ui, fp): """If header is specified, we do not read it out of the stream.""" self.ui = ui + self._decompressor = util.decompressors[None] super(unbundle20, self).__init__(fp) @util.propertycache @@ -655,18 +680,22 @@ raise ValueError('empty parameter name') if name[0] not in string.letters: raise ValueError('non letter first character: %r' % name) - # Some logic will be later added here to try to process the option for - # a dict of known parameter. - if name[0].islower(): - indebug(self.ui, "ignoring unknown parameter %r" % name) + try: + handler = b2streamparamsmap[name.lower()] + except KeyError: + if name[0].islower(): + indebug(self.ui, "ignoring unknown parameter %r" % name) + else: + raise error.BundleUnknownFeatureError(params=(name,)) else: - raise error.UnsupportedPartError(params=(name,)) - + handler(self, name, value) def iterparts(self): """yield all parts contained in the stream""" # make sure param have been loaded self.params + # From there, payload need to be decompressed + self._fp = self._decompressor(self._fp) indebug(self.ui, 'start extraction of bundle2 parts') headerblock = self._readpartheader() while headerblock is not None: @@ -694,6 +723,24 @@ formatmap = {'20': unbundle20} +b2streamparamsmap = {} + +def b2streamparamhandler(name): + """register a handler for a stream level parameter""" + def decorator(func): + assert name not in formatmap + b2streamparamsmap[name] = func + return func + return decorator + +@b2streamparamhandler('compression') +def processcompression(unbundler, param, value): + """read compression parameter and install payload decompression""" + if value not in util.decompressors: + raise error.BundleUnknownFeatureError(params=(param,), + values=(value,)) + unbundler._decompressor = util.decompressors[value] + class bundlepart(object): """A bundle2 part contains application level payload @@ -841,6 +888,12 @@ outdebug(ui, 'payload chunk size: %i' % len(chunk)) yield _pack(_fpayloadsize, len(chunk)) yield chunk + except GeneratorExit: + # GeneratorExit means that nobody is listening for our + # results anyway, so just bail quickly rather than trying + # to produce an error part. + ui.debug('bundle2-generatorexit\n') + raise except BaseException as exc: # backup exception data for later ui.debug('bundle2-input-stream-interrupt: encoding exception %s' @@ -1162,7 +1215,7 @@ unpackerversion = inpart.params.get('version', '01') # We should raise an appropriate exception here unpacker = changegroup.packermap[unpackerversion][1] - cg = unpacker(inpart, 'UN') + cg = unpacker(inpart, None) # the source and url passed here are overwritten by the one contained in # the transaction.hookargs argument. So 'bundle2' is a placeholder nbchangesets = None @@ -1233,7 +1286,7 @@ # we need to make sure we trigger the creation of a transaction object used # for the whole processing scope. op.gettransaction() - import exchange + from . import exchange cg = exchange.readbundle(op.repo.ui, real_part, raw_url) if not isinstance(cg, changegroup.cg1unpacker): raise util.Abort(_('%s: not a bundle version 1.0') % @@ -1317,7 +1370,7 @@ if params is not None: kwargs['params'] = params.split('\0') - raise error.UnsupportedPartError(**kwargs) + raise error.BundleUnknownFeatureError(**kwargs) @parthandler('error:pushraced', ('message',)) def handleerrorpushraced(op, inpart):
--- a/mercurial/bundlerepo.py Mon Oct 05 10:43:16 2015 -0600 +++ b/mercurial/bundlerepo.py Wed Oct 07 13:44:48 2015 -0500 @@ -11,12 +11,33 @@ were part of the actual repository. """ -from node import nullid -from i18n import _ -import os, tempfile, shutil -import changegroup, util, mdiff, discovery, cmdutil, scmutil, exchange -import localrepo, changelog, manifest, filelog, revlog, error, phases, bundle2 -import pathutil +from __future__ import absolute_import + +import os +import shutil +import tempfile + +from .i18n import _ +from .node import nullid + +from . import ( + bundle2, + changegroup, + changelog, + cmdutil, + discovery, + error, + exchange, + filelog, + localrepo, + manifest, + mdiff, + pathutil, + phases, + revlog, + scmutil, + util, +) class bundlerevlog(revlog.revlog): def __init__(self, opener, indexfile, bundle, linkmapper): @@ -174,7 +195,15 @@ linkmapper) def baserevision(self, nodeorrev): - return manifest.manifest.revision(self, nodeorrev) + node = nodeorrev + if isinstance(node, int): + node = self.node(node) + + if node in self._mancache: + result = self._mancache[node][0].text() + else: + result = manifest.manifest.revision(self, nodeorrev) + return result class bundlefilelog(bundlerevlog, filelog.filelog): def __init__(self, opener, path, bundle, linkmapper):
--- a/mercurial/changegroup.py Mon Oct 05 10:43:16 2015 -0600 +++ b/mercurial/changegroup.py Wed Oct 07 13:44:48 2015 -0500 @@ -5,12 +5,30 @@ # 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 os +import struct +import tempfile import weakref -from i18n import _ -from node import nullrev, nullid, hex, short -import mdiff, util, dagutil -import struct, os, bz2, zlib, tempfile -import discovery, error, phases, branchmap + +from .i18n import _ +from .node import ( + hex, + nullid, + nullrev, + short, +) + +from . import ( + branchmap, + dagutil, + discovery, + error, + mdiff, + phases, + util, +) _CHANGEGROUPV1_DELTA_HEADER = "20s20s20s20s" _CHANGEGROUPV2_DELTA_HEADER = "20s20s20s20s20s" @@ -61,26 +79,20 @@ result = -1 + changedheads return result -class nocompress(object): - def compress(self, x): - return x - def flush(self): - return "" - bundletypes = { - "": ("", nocompress), # only when using unbundle on ssh and old http servers + "": ("", None), # only when using unbundle on ssh and old http servers # since the unification ssh accepts a header but there # is no capability signaling it. "HG20": (), # special-cased below - "HG10UN": ("HG10UN", nocompress), - "HG10BZ": ("HG10", lambda: bz2.BZ2Compressor()), - "HG10GZ": ("HG10GZ", lambda: zlib.compressobj()), + "HG10UN": ("HG10UN", None), + "HG10BZ": ("HG10", 'BZ'), + "HG10GZ": ("HG10GZ", 'GZ'), } # hgweb uses this list to communicate its preferred type bundlepriority = ['HG10GZ', 'HG10BZ', 'HG10UN'] -def writebundle(ui, cg, filename, bundletype, vfs=None): +def writebundle(ui, cg, filename, bundletype, vfs=None, compression=None): """Write a bundle file and return its filename. Existing files will not be overwritten. @@ -103,19 +115,25 @@ cleanup = filename if bundletype == "HG20": - import bundle2 + from . import bundle2 bundle = bundle2.bundle20(ui) + bundle.setcompression(compression) part = bundle.newpart('changegroup', data=cg.getchunks()) part.addparam('version', cg.version) - z = nocompress() + z = util.compressors[None]() chunkiter = bundle.getchunks() else: + # compression argument is only for the bundle2 case + assert compression is None if cg.version != '01': raise util.Abort(_('old bundle types only supports v1 ' 'changegroups')) - header, compressor = bundletypes[bundletype] + header, comp = bundletypes[bundletype] fh.write(header) - z = compressor() + if comp not in util.compressors: + raise util.Abort(_('unknown stream compression type: %s') + % comp) + z = util.compressors[comp]() chunkiter = cg.getchunks() # parse the changegroup data, otherwise we will block @@ -138,34 +156,23 @@ else: os.unlink(cleanup) -def decompressor(fh, alg): - if alg == 'UN': - return fh - elif alg == 'GZ': - def generator(f): - zd = zlib.decompressobj() - for chunk in util.filechunkiter(f): - yield zd.decompress(chunk) - elif alg == 'BZ': - def generator(f): - zd = bz2.BZ2Decompressor() - zd.decompress("BZ") - for chunk in util.filechunkiter(f, 4096): - yield zd.decompress(chunk) - else: - raise util.Abort("unknown bundle compression '%s'" % alg) - return util.chunkbuffer(generator(fh)) - class cg1unpacker(object): deltaheader = _CHANGEGROUPV1_DELTA_HEADER deltaheadersize = struct.calcsize(deltaheader) version = '01' def __init__(self, fh, alg): - self._stream = decompressor(fh, alg) + if alg == 'UN': + alg = None # get more modern without breaking too much + if not alg in util.decompressors: + raise util.Abort(_('unknown stream compression type: %s') + % alg) + if alg == 'BZ': + alg = '_truncatedBZ' + self._stream = util.decompressors[alg](fh) self._type = alg self.callback = None def compressed(self): - return self._type != 'UN' + return self._type is not None def read(self, l): return self._stream.read(l) def seek(self, pos): @@ -568,7 +575,7 @@ def getsubset(repo, outgoing, bundler, source, fastpath=False, version='01'): gengroup = getsubsetraw(repo, outgoing, bundler, source, fastpath) - return packermap[version][1](util.chunkbuffer(gengroup), 'UN') + return packermap[version][1](util.chunkbuffer(gengroup), None) def changegroupsubset(repo, roots, heads, source, version='01'): """Compute a changegroup consisting of all the nodes that are
--- a/mercurial/changelog.py Mon Oct 05 10:43:16 2015 -0600 +++ b/mercurial/changelog.py Wed Oct 07 13:44:48 2015 -0500 @@ -5,9 +5,21 @@ # This software may be used and distributed according to the terms of the # GNU General Public License version 2 or any later version. -from node import bin, hex, nullid -from i18n import _ -import util, error, revlog, encoding +from __future__ import absolute_import + +from .i18n import _ +from .node import ( + bin, + hex, + nullid, +) + +from . import ( + encoding, + error, + revlog, + util, +) _defaultextra = {'branch': 'default'} @@ -172,6 +184,9 @@ self.rev(self.node(0)) return self._nodecache + def reachableroots(self, minroot, heads, roots, includepath=False): + return self.index.reachableroots2(minroot, heads, roots, includepath) + def headrevs(self): if self.filteredrevs: try:
--- a/mercurial/cmdutil.py Mon Oct 05 10:43:16 2015 -0600 +++ b/mercurial/cmdutil.py Wed Oct 07 13:44:48 2015 -0500 @@ -10,7 +10,7 @@ import os, sys, errno, re, tempfile, cStringIO, shutil import util, scmutil, templater, patch, error, templatekw, revlog, copies import match as matchmod -import context, repair, graphmod, revset, phases, obsolete, pathutil +import repair, graphmod, revset, phases, obsolete, pathutil import changelog import bookmarks import encoding @@ -848,6 +848,8 @@ :updatefunc: a function that update a repo to a given node updatefunc(<repo>, <node>) """ + # avoid cycle context -> subrepo -> cmdutil + import context tmpname, message, user, date, branch, nodeid, p1, p2 = \ patch.extract(ui, hunk) @@ -990,7 +992,7 @@ os.unlink(tmpname) def export(repo, revs, template='hg-%h.patch', fp=None, switch_parent=False, - opts=None): + opts=None, match=None): '''export changesets as hg patches.''' total = len(revs) @@ -1041,7 +1043,7 @@ write(ctx.description().rstrip()) write("\n\n") - for chunk, label in patch.diffui(repo, prev, node, opts=opts): + for chunk, label in patch.diffui(repo, prev, node, match, opts=opts): write(chunk, label=label) if shouldclose: @@ -1193,7 +1195,7 @@ # i18n: column positioning for "hg log" self.ui.write(_("phase: %s\n") % ctx.phasestr(), label='log.phase') - for pctx in self._meaningful_parentrevs(ctx): + for pctx in scmutil.meaningfulparents(self.repo, ctx): label = 'log.parent changeset.%s' % pctx.phasestr() # i18n: column positioning for "hg log" self.ui.write(_("parent: %d:%s\n") @@ -1277,22 +1279,6 @@ match=matchfn, stat=False) self.ui.write("\n") - def _meaningful_parentrevs(self, ctx): - """Return list of meaningful (or all if debug) parentrevs for rev. - - For merges (two non-nullrev revisions) both parents are meaningful. - Otherwise the first parent revision is considered meaningful if it - is not the preceding revision. - """ - parents = ctx.parents() - if len(parents) > 1: - return parents - if self.ui.debugflag: - return [parents[0], self.repo['null']] - if parents[0].rev() >= scmutil.intrev(ctx.rev()) - 1: - return [] - return parents - class jsonchangeset(changeset_printer): '''format changeset information.''' @@ -1412,34 +1398,7 @@ self.cache = {} - def _show(self, ctx, copies, matchfn, props): - '''show a single changeset or file revision''' - - showlist = templatekw.showlist - - # showparents() behaviour depends on ui trace level which - # causes unexpected behaviours at templating level and makes - # it harder to extract it in a standalone function. Its - # behaviour cannot be changed so leave it here for now. - def showparents(**args): - ctx = args['ctx'] - parents = [[('rev', p.rev()), - ('node', p.hex()), - ('phase', p.phasestr())] - for p in self._meaningful_parentrevs(ctx)] - return showlist('parent', parents, **args) - - props = props.copy() - props.update(templatekw.keywords) - props['parents'] = showparents - props['templ'] = self.t - props['ctx'] = ctx - props['repo'] = self.repo - props['revcache'] = {'copies': copies} - props['cache'] = self.cache - # find correct templates for current mode - tmplmodes = [ (True, None), (self.ui.verbose, 'verbose'), @@ -1447,18 +1406,40 @@ (self.ui.debugflag, 'debug'), ] - types = {'header': '', 'footer':'', 'changeset': 'changeset'} - for mode, postfix in tmplmodes: - for type in types: - cur = postfix and ('%s_%s' % (type, postfix)) or type + self._parts = {'header': '', 'footer': '', 'changeset': 'changeset', + 'docheader': '', 'docfooter': ''} + for mode, postfix in tmplmodes: + for t in self._parts: + cur = t + if postfix: + cur += "_" + postfix if mode and cur in self.t: - types[type] = cur + self._parts[t] = cur + + if self._parts['docheader']: + self.ui.write(templater.stringify(self.t(self._parts['docheader']))) + + def close(self): + if self._parts['docfooter']: + if not self.footer: + self.footer = "" + self.footer += templater.stringify(self.t(self._parts['docfooter'])) + return super(changeset_templater, self).close() + + def _show(self, ctx, copies, matchfn, props): + '''show a single changeset or file revision''' + props = props.copy() + props.update(templatekw.keywords) + props['templ'] = self.t + props['ctx'] = ctx + props['repo'] = self.repo + props['revcache'] = {'copies': copies} + props['cache'] = self.cache try: - # write header - if types['header']: - h = templater.stringify(self.t(types['header'], **props)) + if self._parts['header']: + h = templater.stringify(self.t(self._parts['header'], **props)) if self.buffered: self.header[ctx.rev()] = h else: @@ -1467,15 +1448,14 @@ self.ui.write(h) # write changeset metadata, then patch if requested - key = types['changeset'] + key = self._parts['changeset'] self.ui.write(templater.stringify(self.t(key, **props))) self.showpatch(ctx.node(), matchfn) - if types['footer']: + if self._parts['footer']: if not self.footer: - self.footer = templater.stringify(self.t(types['footer'], - **props)) - + self.footer = templater.stringify( + self.t(self._parts['footer'], **props)) except KeyError as inst: msg = _("%s: no key named '%s'") raise util.Abort(msg % (self.t.mapfile, inst.args[0])) @@ -1928,7 +1908,7 @@ followfirst = 1 else: followfirst = 0 - # --follow with FILE behaviour depends on revs... + # --follow with FILE behavior depends on revs... it = iter(revs) startrev = it.next() followdescendants = startrev < next(it, startrev) @@ -2049,7 +2029,7 @@ return expr, filematcher def _logrevs(repo, opts): - # Default --rev value depends on --follow but --follow behaviour + # Default --rev value depends on --follow but --follow behavior # depends on revisions resolved from --rev... follow = opts.get('follow') or opts.get('follow_first') if opts.get('rev'): @@ -2207,7 +2187,12 @@ if abort or warn: cca = scmutil.casecollisionauditor(ui, abort, repo.dirstate) - for f in wctx.walk(matchmod.badmatch(match, badfn)): + badmatch = matchmod.badmatch(match, badfn) + dirstate = repo.dirstate + # We don't want to just call wctx.walk here, since it would return a lot of + # clean files, which we aren't interested in and takes time. + for f in sorted(dirstate.walk(badmatch, sorted(wctx.substate), + True, False, full=False)): exact = match.exact(f) if exact or not explicitonly and f not in wctx and repo.wvfs.lexists(f): if cca: @@ -2464,6 +2449,9 @@ return commitfunc(ui, repo, message, matcher, opts) def amend(ui, repo, commitfunc, old, extra, pats, opts): + # avoid cycle context -> subrepo -> cmdutil + import context + # amend will reuse the existing user if not specified, but the obsolete # marker creation requires that the current user's name is specified. if obsolete.isenabled(repo, obsolete.createmarkersopt): @@ -2715,6 +2703,9 @@ t.show(ctx, extramsg=extramsg) return ui.popbuffer() +def hgprefix(msg): + return "\n".join(["HG: %s" % a for a in msg.split("\n") if a]) + def buildcommittext(repo, ctx, subs, extramsg): edittext = [] modified, added, removed = ctx.modified(), ctx.added(), ctx.removed() @@ -2722,28 +2713,30 @@ edittext.append(ctx.description()) edittext.append("") edittext.append("") # Empty line between message and comments. - edittext.append(_("HG: Enter commit message." - " Lines beginning with 'HG:' are removed.")) - edittext.append("HG: %s" % extramsg) + edittext.append(hgprefix(_("Enter commit message." + " Lines beginning with 'HG:' are removed."))) + edittext.append(hgprefix(extramsg)) edittext.append("HG: --") - edittext.append(_("HG: user: %s") % ctx.user()) + edittext.append(hgprefix(_("user: %s") % ctx.user())) if ctx.p2(): - edittext.append(_("HG: branch merge")) + edittext.append(hgprefix(_("branch merge"))) if ctx.branch(): - edittext.append(_("HG: branch '%s'") % ctx.branch()) + edittext.append(hgprefix(_("branch '%s'") % ctx.branch())) if bookmarks.isactivewdirparent(repo): - edittext.append(_("HG: bookmark '%s'") % repo._activebookmark) - edittext.extend([_("HG: subrepo %s") % s for s in subs]) - edittext.extend([_("HG: added %s") % f for f in added]) - edittext.extend([_("HG: changed %s") % f for f in modified]) - edittext.extend([_("HG: removed %s") % f for f in removed]) + edittext.append(hgprefix(_("bookmark '%s'") % repo._activebookmark)) + edittext.extend([hgprefix(_("subrepo %s") % s) for s in subs]) + edittext.extend([hgprefix(_("added %s") % f) for f in added]) + edittext.extend([hgprefix(_("changed %s") % f) for f in modified]) + edittext.extend([hgprefix(_("removed %s") % f) for f in removed]) if not added and not modified and not removed: - edittext.append(_("HG: no files changed")) + edittext.append(hgprefix(_("no files changed"))) edittext.append("") return "\n".join(edittext) -def commitstatus(repo, node, branch, bheads=None, opts={}): +def commitstatus(repo, node, branch, bheads=None, opts=None): + if opts is None: + opts = {} ctx = repo[node] parents = ctx.parents()
--- a/mercurial/commands.py Mon Oct 05 10:43:16 2015 -0600 +++ b/mercurial/commands.py Wed Oct 07 13:44:48 2015 -0500 @@ -13,13 +13,13 @@ import hg, scmutil, util, revlog, copies, error, bookmarks import patch, help, encoding, templatekw, discovery import archival, changegroup, cmdutil, hbisect -import sshserver, hgweb, commandserver +import sshserver, hgweb import extensions from hgweb import server as hgweb_server import merge as mergemod import minirst, revset, fileset import dagparser, context, simplemerge, graphmod, copies -import random +import random, operator import setdiscovery, treediscovery, dagutil, pvec, localrepo import phases, obsolete, exchange, bundle2, repair, lock as lockmod import ui as uimod @@ -497,6 +497,9 @@ See :hg:`help dates` for a list of formats valid for -d/--date. + See :hg:`help revert` for a way to restore files to the state + of another revision. + Returns 0 on success, 1 if nothing to backout or there are unresolved files. ''' @@ -875,7 +878,7 @@ [('f', 'force', False, _('force')), ('r', 'rev', '', _('revision'), _('REV')), ('d', 'delete', False, _('delete a given bookmark')), - ('m', 'rename', '', _('rename a given bookmark'), _('NAME')), + ('m', 'rename', '', _('rename a given bookmark'), _('OLD')), ('i', 'inactive', False, _('mark a bookmark inactive')), ] + formatteropts, _('hg bookmarks [OPTIONS]... [NAME]...')) @@ -916,6 +919,10 @@ hg book -r .^ tested + - rename bookmark turkey to dinner:: + + hg book -m turkey dinner + - move the '@' bookmark from another branch:: hg book -f @ @@ -2167,6 +2174,45 @@ localrevs = opts.get('local_head') doit(localrevs, remoterevs) +@command('debugextensions', formatteropts, [], norepo=True) +def debugextensions(ui, **opts): + '''show information about active extensions''' + exts = extensions.extensions(ui) + fm = ui.formatter('debugextensions', opts) + for extname, extmod in sorted(exts, key=operator.itemgetter(0)): + extsource = extmod.__file__ + exttestedwith = getattr(extmod, 'testedwith', None) + if exttestedwith is not None: + exttestedwith = exttestedwith.split() + extbuglink = getattr(extmod, 'buglink', None) + + fm.startitem() + + if ui.quiet or ui.verbose: + fm.write('name', '%s\n', extname) + else: + fm.write('name', '%s', extname) + if not exttestedwith: + fm.plain(_(' (untested!)\n')) + else: + if exttestedwith == ['internal'] or \ + util.version() in exttestedwith: + fm.plain('\n') + else: + lasttestedversion = exttestedwith[-1] + fm.plain(' (%s!)\n' % lasttestedversion) + + fm.condwrite(ui.verbose and extsource, 'source', + _(' location: %s\n'), extsource or "") + + fm.condwrite(ui.verbose and exttestedwith, 'testedwith', + _(' tested with: %s\n'), ' '.join(exttestedwith or [])) + + fm.condwrite(ui.verbose and extbuglink, 'buglink', + _(' bug reporting: %s\n'), extbuglink or "") + + fm.end() + @command('debugfileset', [('r', 'rev', '', _('apply the filespec on this revision'), _('REV'))], _('[-r REV] FILESPEC')) @@ -2430,6 +2476,71 @@ '''backwards compatibility with old bash completion scripts (DEPRECATED)''' debugnamecomplete(ui, repo, *args) +@command('debugmergestate', [], '') +def debugmergestate(ui, repo, *args): + """print merge state + + Use --verbose to print out information about whether v1 or v2 merge state + was chosen.""" + def printrecords(version): + ui.write(('* version %s records\n') % version) + if version == 1: + records = v1records + else: + records = v2records + + for rtype, record in records: + # pretty print some record types + if rtype == 'L': + ui.write(('local: %s\n') % record) + elif rtype == 'O': + ui.write(('other: %s\n') % record) + elif rtype == 'F': + r = record.split('\0') + f, state, hash, lfile, afile, anode, ofile = r[0:7] + if version == 1: + onode = 'not stored in v1 format' + flags = r[7] + else: + onode, flags = r[7:9] + ui.write(('file: %s (state "%s", hash %s)\n') + % (f, state, hash)) + ui.write((' local path: %s (flags "%s")\n') % (lfile, flags)) + ui.write((' ancestor path: %s (node %s)\n') % (afile, anode)) + ui.write((' other path: %s (node %s)\n') % (ofile, onode)) + else: + ui.write(('unrecognized entry: %s\t%s\n') + % (rtype, record.replace('\0', '\t'))) + + ms = mergemod.mergestate(repo) + + # sort so that reasonable information is on top + v1records = ms._readrecordsv1() + v2records = ms._readrecordsv2() + order = 'LO' + def key(r): + idx = order.find(r[0]) + if idx == -1: + return (1, r[1]) + else: + return (0, idx) + v1records.sort(key=key) + v2records.sort(key=key) + + if not v1records and not v2records: + ui.write(('no merge state found\n')) + elif not v2records: + ui.note(('no version 2 merge state\n')) + printrecords(1) + elif ms._v1v2match(v1records, v2records): + ui.note(('v1 and v2 states match: using v2\n')) + printrecords(2) + else: + ui.note(('v1 and v2 states mismatch: using v1\n')) + printrecords(1) + if ui.verbose: + printrecords(2) + @command('debugnamecomplete', [], _('NAME...')) def debugnamecomplete(ui, repo, *args): '''complete "names" - tags, open branch names, bookmark names''' @@ -2700,9 +2811,12 @@ pa.distance(pb), rel)) @command('debugrebuilddirstate|debugrebuildstate', - [('r', 'rev', '', _('revision to rebuild to'), _('REV'))], + [('r', 'rev', '', _('revision to rebuild to'), _('REV')), + ('', 'minimal', None, _('only rebuild files that are inconsistent with ' + 'the working copy parent')), + ], _('[-r REV]')) -def debugrebuilddirstate(ui, repo, rev): +def debugrebuilddirstate(ui, repo, rev, **opts): """rebuild the dirstate as it would look like for the given revision If no revision is specified the first current parent will be used. @@ -2711,13 +2825,33 @@ The actual working directory content or existing dirstate information such as adds or removes is not considered. + ``minimal`` will only rebuild the dirstate status for files that claim to be + tracked but are not in the parent manifest, or that exist in the parent + manifest but are not in the dirstate. It will not change adds, removes, or + modified files that are in the working copy parent. + One use of this command is to make the next :hg:`status` invocation check the actual file content. """ ctx = scmutil.revsingle(repo, rev) wlock = repo.wlock() try: - repo.dirstate.rebuild(ctx.node(), ctx.manifest()) + dirstate = repo.dirstate + + # See command doc for what minimal does. + if opts.get('minimal'): + dirstatefiles = set(dirstate) + ctxfiles = set(ctx.manifest().keys()) + for file in (dirstatefiles | ctxfiles): + indirstate = file in dirstatefiles + inctx = file in ctxfiles + + if indirstate and not inctx and dirstate[file] != 'a': + dirstate.drop(file) + elif inctx and not indirstate: + dirstate.normallookup(file) + else: + dirstate.rebuild(ctx.node(), ctx.manifest()) finally: wlock.release() @@ -2933,7 +3067,7 @@ expansion. """ if ui.verbose: - tree = revset.parse(expr) + tree = revset.parse(expr, lookup=repo.__contains__) ui.note(revset.prettyformat(tree), "\n") newtree = revset.findaliases(ui, tree) if newtree != tree: @@ -2945,7 +3079,7 @@ if opts["optimize"]: weight, optimizedtree = revset.optimize(newtree, True) ui.note("* optimized:\n", revset.prettyformat(optimizedtree), "\n") - func = revset.match(ui, expr) + func = revset.match(ui, expr, repo) revs = func(repo) if ui.verbose: ui.note("* set:\n", revset.prettyformatset(revs), "\n") @@ -3915,9 +4049,9 @@ @command('help', [('e', 'extension', None, _('show only help for extensions')), ('c', 'command', None, _('show only help for commands')), - ('k', 'keyword', '', _('show topics matching keyword')), + ('k', 'keyword', None, _('show topics matching keyword')), ], - _('[-ec] [TOPIC]'), + _('[-eck] [TOPIC]'), norepo=True) def help_(ui, name=None, **opts): """show help for a given topic or a help overview @@ -3948,12 +4082,17 @@ section = None if name and '.' in name: name, section = name.split('.', 1) + section = section.lower() text = help.help_(ui, name, **opts) formatted, pruned = minirst.format(text, textwidth, keep=keep, section=section) - if section and not formatted: + + # We could have been given a weird ".foo" section without a name + # to look for, or we could have simply failed to found "foo.bar" + # because bar isn't a section of foo + if section and not (formatted and name): raise util.Abort(_("help section not found")) if 'verbose' in pruned: @@ -4740,58 +4879,8 @@ if node: node = scmutil.revsingle(repo, node).node() - if not node and repo._activebookmark: - bmheads = repo.bookmarkheads(repo._activebookmark) - curhead = repo[repo._activebookmark].node() - if len(bmheads) == 2: - if curhead == bmheads[0]: - node = bmheads[1] - else: - node = bmheads[0] - elif len(bmheads) > 2: - raise util.Abort(_("multiple matching bookmarks to merge - " - "please merge with an explicit rev or bookmark"), - hint=_("run 'hg heads' to see all heads")) - elif len(bmheads) <= 1: - raise util.Abort(_("no matching bookmark to merge - " - "please merge with an explicit rev or bookmark"), - hint=_("run 'hg heads' to see all heads")) - - if not node and not repo._activebookmark: - branch = repo[None].branch() - bheads = repo.branchheads(branch) - nbhs = [bh for bh in bheads if not repo[bh].bookmarks()] - - if len(nbhs) > 2: - raise util.Abort(_("branch '%s' has %d heads - " - "please merge with an explicit rev") - % (branch, len(bheads)), - hint=_("run 'hg heads .' to see heads")) - - parent = repo.dirstate.p1() - if len(nbhs) <= 1: - if len(bheads) > 1: - raise util.Abort(_("heads are bookmarked - " - "please merge with an explicit rev"), - hint=_("run 'hg heads' to see all heads")) - if len(repo.heads()) > 1: - raise util.Abort(_("branch '%s' has one head - " - "please merge with an explicit rev") - % branch, - hint=_("run 'hg heads' to see all heads")) - msg, hint = _('nothing to merge'), None - if parent != repo.lookup(branch): - hint = _("use 'hg update' instead") - raise util.Abort(msg, hint=hint) - - if parent not in bheads: - raise util.Abort(_('working directory not at a head revision'), - hint=_("use 'hg update' or merge with an " - "explicit revision")) - if parent == nbhs[0]: - node = nbhs[-1] - else: - node = nbhs[0] + if not node: + node = scmutil.revsingle(repo, '_mergedefaultdest()').node() if opts.get('preview'): # find nodes that are ancestors of p2 but not of p1 @@ -5004,8 +5093,7 @@ public < draft < secret - Returns 0 on success, 1 if no phases were changed or some could not - be changed. + Returns 0 on success, 1 if some phases could not be changed. (For more information about the phases concept, see :hg:`help phases`.) """ @@ -5074,7 +5162,6 @@ ui.note(msg) else: ui.warn(_('no phases changed\n')) - ret = 1 return ret def postincoming(ui, repo, modheads, optupdate, checkout): @@ -5251,18 +5338,14 @@ # this lets simultaneous -r, -b options continue working opts.setdefault('rev', []).append("null") - dest = ui.expandpath(dest or 'default-push', dest or 'default') - dest, branches = hg.parseurl(dest, opts.get('branch')) + path = ui.paths.getpath(dest, default='default') + if not path: + raise util.Abort(_('default repository not configured!'), + hint=_('see the "path" section in "hg help config"')) + dest, branches = path.pushloc, (path.branch, opts.get('branch') or []) ui.status(_('pushing to %s\n') % util.hidepassword(dest)) revs, checkout = hg.addbranchrevs(repo, repo, branches, opts.get('rev')) - try: - other = hg.peer(repo, opts, dest) - except error.RepoError: - if dest == "default-push": - raise util.Abort(_("default repository not configured!"), - hint=_('see the "path" section in "hg help config"')) - else: - raise + other = hg.peer(repo, opts, dest) if revs: revs = [repo.lookup(r) for r in scmutil.revrange(repo, revs)] @@ -5446,7 +5529,7 @@ raise util.Abort(_("can't specify --all and patterns")) if not (all or pats or show or mark or unmark): raise util.Abort(_('no files or directories specified'), - hint=('use --all to remerge all files')) + hint=('use --all to re-merge all unresolved files')) if show: fm = ui.formatter('resolve', opts) @@ -5555,6 +5638,9 @@ See :hg:`help dates` for a list of formats valid for -d/--date. + See :hg:`help backout` for a way to reverse the effect of an + earlier changeset. + Returns 0 on success. """ @@ -5713,6 +5799,7 @@ s.serve_forever() if opts["cmdserver"]: + import commandserver service = commandserver.createservice(ui, repo, opts) return cmdutil.service(opts, initfn=service.init, runfn=service.run) @@ -6248,7 +6335,7 @@ raise util.Abort(_("tag '%s' is not a global tag") % n) else: raise util.Abort(_("tag '%s' is not a local tag") % n) - rev_ = nullid + rev_ = 'null' if not message: # we don't translate commit messages message = 'Removed tag %s' % ', '.join(names) @@ -6372,10 +6459,10 @@ try: op = bundle2.processbundle(repo, gen, lambda: tr) tr.close() - except error.UnsupportedPartError as exc: + except error.BundleUnknownFeatureError as exc: raise util.Abort(_('%s: unknown bundle feature, %s') % (fname, exc), - hint=_("see https://mercurial.selenic.com/" + hint=_("see https://mercurial-scm.org/" "wiki/BundleFeature for more " "information")) finally: @@ -6461,6 +6548,11 @@ try: cmdutil.clearunfinished(repo) + if date: + if rev is not None: + raise util.Abort(_("you can't specify a revision and a date")) + rev = cmdutil.finddate(ui, repo, date) + # with no argument, we also move the active bookmark, if any rev, movemarkfrom = bookmarks.calculateupdate(ui, repo, rev) @@ -6471,11 +6563,6 @@ if check and clean: raise util.Abort(_("cannot specify both -c/--check and -C/--clean")) - if date: - if rev is not None: - raise util.Abort(_("you can't specify a revision and a date")) - rev = cmdutil.finddate(ui, repo, date) - if check: cmdutil.bailifchanged(repo, merge=False) if rev is None: @@ -6520,7 +6607,7 @@ the changelog, manifest, and tracked files, as well as the integrity of their crosslinks and indices. - Please see http://mercurial.selenic.com/wiki/RepositoryCorruption + Please see https://mercurial-scm.org/wiki/RepositoryCorruption for more information about recovery from corruption of the repository. @@ -6534,7 +6621,7 @@ ui.write(_("Mercurial Distributed SCM (version %s)\n") % util.version()) ui.status(_( - "(see http://mercurial.selenic.com for more information)\n" + "(see https://mercurial-scm.org for more information)\n" "\nCopyright (C) 2005-2015 Matt Mackall and others\n" "This is free software; see the source for copying conditions. " "There is NO\nwarranty; "
--- a/mercurial/config.py Mon Oct 05 10:43:16 2015 -0600 +++ b/mercurial/config.py Wed Oct 07 13:44:48 2015 -0500 @@ -5,9 +5,16 @@ # This software may be used and distributed according to the terms of the # GNU General Public License version 2 or any later version. -from i18n import _ -import error, util -import os, errno +from __future__ import absolute_import + +import errno +import os + +from .i18n import _ +from . import ( + error, + util, +) class config(object): def __init__(self, data=None, includepaths=[]):
--- a/mercurial/context.py Mon Oct 05 10:43:16 2015 -0600 +++ b/mercurial/context.py Wed Oct 07 13:44:48 2015 -0500 @@ -1690,7 +1690,7 @@ def date(self): t, tz = self._changectx.date() try: - return (int(self._repo.wvfs.lstat(self._path).st_mtime), tz) + return (util.statmtimesec(self._repo.wvfs.lstat(self._path)), tz) except OSError as err: if err.errno != errno.ENOENT: raise
--- a/mercurial/copies.py Mon Oct 05 10:43:16 2015 -0600 +++ b/mercurial/copies.py Wed Oct 07 13:44:48 2015 -0500 @@ -5,9 +5,15 @@ # This software may be used and distributed according to the terms of the # GNU General Public License version 2 or any later version. -import util, pathutil +from __future__ import absolute_import + import heapq +from . import ( + pathutil, + util, +) + def _findlimit(repo, a, b): """ Find the last revision that needs to be checked to ensure that a full @@ -185,6 +191,9 @@ return cm def _backwardrenames(a, b): + if a._repo.ui.configbool('experimental', 'disablecopytrace'): + return {} + # Even though we're not taking copies into account, 1:n rename situations # can still exist (e.g. hg cp a b; hg mv a c). In those cases we # arbitrarily pick one of the renames. @@ -258,17 +267,25 @@ if c2.node() is None and c1.node() == repo.dirstate.p1(): return repo.dirstate.copies(), {}, {}, {} + # Copy trace disabling is explicitly below the node == p1 logic above + # because the logic above is required for a simple copy to be kept across a + # rebase. + if repo.ui.configbool('experimental', 'disablecopytrace'): + return {}, {}, {}, {} + limit = _findlimit(repo, c1.rev(), c2.rev()) if limit is None: # no common ancestor, no copies return {}, {}, {}, {} + repo.ui.debug(" searching for copies back to rev %d\n" % limit) + m1 = c1.manifest() m2 = c2.manifest() ma = ca.manifest() def setupctx(ctx): - """return a 'makectx' function suitable for checkcopies usage from ctx + """return a 'getfctx' function suitable for checkcopies usage We have to re-setup the function building 'filectx' for each 'checkcopies' to ensure the linkrev adjustement is properly setup for @@ -301,28 +318,30 @@ return fctx return util.lrucachefunc(makectx) - copy = {} - movewithdir = {} - fullcopy = {} + copy1, copy2, = {}, {} + movewithdir1, movewithdir2 = {}, {} + fullcopy1, fullcopy2 = {}, {} diverge = {} - repo.ui.debug(" searching for copies back to rev %d\n" % limit) - addedinm1 = m1.filesnotin(ma) addedinm2 = m2.filesnotin(ma) u1, u2 = _computenonoverlap(repo, c1, c2, addedinm1, addedinm2) for f in u1: - ctx = setupctx(c1) - checkcopies(ctx, f, m1, m2, ca, limit, diverge, copy, fullcopy) + getfctx = setupctx(c1) + checkcopies(getfctx, f, m1, m2, ca, limit, diverge, copy1, fullcopy1) for f in u2: - ctx = setupctx(c2) - checkcopies(ctx, f, m2, m1, ca, limit, diverge, copy, fullcopy) + getfctx = setupctx(c2) + checkcopies(getfctx, f, m2, m1, ca, limit, diverge, copy2, fullcopy2) + + copy = dict(copy1.items() + copy2.items()) + movewithdir = dict(movewithdir1.items() + movewithdir2.items()) + fullcopy = dict(fullcopy1.items() + fullcopy2.items()) renamedelete = {} renamedelete2 = set() - diverge2 = set() + divergeset = set() for of, fl in diverge.items(): if len(fl) == 1 or of in c1 or of in c2: del diverge[of] # not actually divergent, or not a rename @@ -332,7 +351,7 @@ renamedelete[of] = [f for f in fl if f in c1 or f in c2] renamedelete2.update(fl) # reverse map for below else: - diverge2.update(fl) # reverse map for below + divergeset.update(fl) # reverse map for below bothnew = sorted(addedinm1 & addedinm2) if bothnew: @@ -340,10 +359,12 @@ % "\n ".join(bothnew)) bothdiverge, _copy, _fullcopy = {}, {}, {} for f in bothnew: - ctx = setupctx(c1) - checkcopies(ctx, f, m1, m2, ca, limit, bothdiverge, _copy, _fullcopy) - ctx = setupctx(c2) - checkcopies(ctx, f, m2, m1, ca, limit, bothdiverge, _copy, _fullcopy) + getfctx = setupctx(c1) + checkcopies(getfctx, f, m1, m2, ca, limit, bothdiverge, + _copy, _fullcopy) + getfctx = setupctx(c2) + checkcopies(getfctx, f, m2, m1, ca, limit, bothdiverge, + _copy, _fullcopy) for of, fl in bothdiverge.items(): if len(fl) == 2 and fl[0] == fl[1]: copy[fl[0]] = of # not actually divergent, just matching renames @@ -355,13 +376,13 @@ note = "" if f in copy: note += "*" - if f in diverge2: + if f in divergeset: note += "!" if f in renamedelete2: note += "%" repo.ui.debug(" src: '%s' -> dst: '%s' %s\n" % (fullcopy[f], f, note)) - del diverge2 + del divergeset if not fullcopy: return copy, movewithdir, diverge, renamedelete @@ -423,11 +444,11 @@ return copy, movewithdir, diverge, renamedelete -def checkcopies(ctx, f, m1, m2, ca, limit, diverge, copy, fullcopy): +def checkcopies(getfctx, f, m1, m2, ca, limit, diverge, copy, fullcopy): """ check possible copies of f from m1 to m2 - ctx = function accepting (filename, node) that returns a filectx. + getfctx = function accepting (filename, node) that returns a filectx. f = the filename to check m1 = the source manifest m2 = the destination manifest @@ -473,7 +494,7 @@ of = None seen = set([f]) - for oc in ctx(f, m1[f]).ancestors(): + for oc in getfctx(f, m1[f]).ancestors(): ocr = oc.linkrev() of = oc.path() if of in seen: @@ -488,7 +509,7 @@ continue # no match, keep looking if m2[of] == ma.get(of): break # no merge needed, quit early - c2 = ctx(of, m2[of]) + c2 = getfctx(of, m2[of]) cr = _related(oc, c2, ca.rev()) if cr and (of == f or of == c2.path()): # non-divergent copy[f] = of @@ -507,7 +528,12 @@ copies between fromrev and rev. ''' exclude = {} - if skiprev is not None: + if (skiprev is not None and + not repo.ui.configbool('experimental', 'disablecopytrace')): + # disablecopytrace skips this line, but not the entire function because + # the line below is O(size of the repo) during a rebase, while the rest + # of the function is much faster (and is required for carrying copy + # metadata across the rebase anyway). exclude = pathcopies(repo[fromrev], repo[skiprev]) for dst, src in pathcopies(repo[fromrev], repo[rev]).iteritems(): # copies.pathcopies returns backward renames, so dst might not
--- a/mercurial/crecord.py Mon Oct 05 10:43:16 2015 -0600 +++ b/mercurial/crecord.py Wed Oct 07 13:44:48 2015 -0500 @@ -8,11 +8,23 @@ # This code is based on the Mark Edgington's crecord extension. # (Itself based on Bryan O'Sullivan's record extension.) -from i18n import _ -import patch as patchmod -import util, encoding +from __future__ import absolute_import -import os, re, sys, struct, signal, tempfile, locale, cStringIO +import cStringIO +import locale +import os +import re +import signal +import struct +import sys +import tempfile + +from .i18n import _ +from . import ( + encoding, + patch as patchmod, + util, +) # This is required for ncurses to display non-ASCII characters in default user # locale encoding correctly. --immerrr @@ -21,7 +33,8 @@ # os.name is one of: 'posix', 'nt', 'dos', 'os2', 'mac', or 'ce' if os.name == 'posix': import curses - import fcntl, termios + import fcntl + import termios else: # I have no idea if wcurses works with crecord... try: @@ -1449,7 +1462,7 @@ self.ui.write("\n") return None # patch comment based on the git one (based on comment at end of - # http://mercurial.selenic.com/wiki/recordextension) + # https://mercurial-scm.org/wiki/recordextension) phelp = '---' + _(""" to remove '-' lines, make them ' ' lines (context). to remove '+' lines, delete them.
--- a/mercurial/dagparser.py Mon Oct 05 10:43:16 2015 -0600 +++ b/mercurial/dagparser.py Wed Oct 07 13:44:48 2015 -0500 @@ -5,9 +5,13 @@ # This software may be used and distributed according to the terms of the # GNU General Public License version 2 or any later version. -import re, string -import util -from i18n import _ +from __future__ import absolute_import + +import re +import string + +from .i18n import _ +from . import util def parsedag(desc): '''parses a DAG from a concise textual description; generates events
--- a/mercurial/dagutil.py Mon Oct 05 10:43:16 2015 -0600 +++ b/mercurial/dagutil.py Wed Oct 07 13:44:48 2015 -0500 @@ -6,9 +6,10 @@ # This software may be used and distributed according to the terms of the # GNU General Public License version 2 or any later version. -from node import nullrev -from i18n import _ +from __future__ import absolute_import +from .i18n import _ +from .node import nullrev class basedag(object): '''generic interface for DAGs
--- a/mercurial/demandimport.py Mon Oct 05 10:43:16 2015 -0600 +++ b/mercurial/demandimport.py Wed Oct 07 13:44:48 2015 -0500 @@ -24,8 +24,11 @@ b = __import__(a) ''' -import os, sys -from contextlib import contextmanager +from __future__ import absolute_import + +import contextlib +import os +import sys # __builtin__ in Python 2, builtins in Python 3. try: @@ -33,26 +36,21 @@ except ImportError: import builtins +contextmanager = contextlib.contextmanager + _origimport = __import__ nothing = object() -try: - # Python 3 doesn't have relative imports nor level -1. - level = -1 - if sys.version_info[0] >= 3: - level = 0 - _origimport(builtins.__name__, {}, {}, None, level) -except TypeError: # no level argument - def _import(name, globals, locals, fromlist, level): - "call _origimport with no level argument" - return _origimport(name, globals, locals, fromlist) -else: - _import = _origimport +# Python 3 doesn't have relative imports nor level -1. +level = -1 +if sys.version_info[0] >= 3: + level = 0 +_import = _origimport -def _hgextimport(importfunc, name, globals, *args): +def _hgextimport(importfunc, name, globals, *args, **kwargs): try: - return importfunc(name, globals, *args) + return importfunc(name, globals, *args, **kwargs) except ImportError: if not globals: raise @@ -63,7 +61,7 @@ if nameroot != contextroot: raise # retry to import with "hgext_" prefix - return importfunc(hgextname, globals, *args) + return importfunc(hgextname, globals, *args, **kwargs) class _demandmod(object): """module demand-loader and proxy""" @@ -75,14 +73,26 @@ head = name after = [] object.__setattr__(self, "_data", - (head, globals, locals, after, level)) + (head, globals, locals, after, level, set())) object.__setattr__(self, "_module", None) def _extend(self, name): """add to the list of submodules to load""" self._data[3].append(name) + + def _addref(self, name): + """Record that the named module ``name`` imports this module. + + References to this proxy class having the name of this module will be + replaced at module load time. We assume the symbol inside the importing + module is identical to the "head" name of this module. We don't + actually know if "as X" syntax is being used to change the symbol name + because this information isn't exposed to __import__. + """ + self._data[5].add(name) + def _load(self): if not self._module: - head, globals, locals, after, level = self._data + head, globals, locals, after, level, modrefs = self._data mod = _hgextimport(_import, head, globals, locals, None, level) # load submodules def subload(mod, p): @@ -97,9 +107,15 @@ for x in after: subload(mod, x) - # are we in the locals dictionary still? + # Replace references to this proxy instance with the actual module. if locals and locals.get(head) == self: locals[head] = mod + + for modname in modrefs: + modref = sys.modules.get(modname, None) + if modref and getattr(modref, head, None) == self: + setattr(modref, head, mod) + object.__setattr__(self, "_module", mod) def __repr__(self): @@ -109,7 +125,7 @@ def __call__(self, *args, **kwargs): raise TypeError("%s object is not callable" % repr(self)) def __getattribute__(self, attr): - if attr in ('_data', '_extend', '_load', '_module'): + if attr in ('_data', '_extend', '_load', '_module', '_addref'): return object.__getattribute__(self, attr) self._load() return getattr(self._module, attr) @@ -135,23 +151,70 @@ return locals[base] return _demandmod(name, globals, locals, level) else: - if level != -1: - # from . import b,c,d or from .a import b,c,d - return _origimport(name, globals, locals, fromlist, level) + # There is a fromlist. # from a import b,c,d + # from . import b,c,d + # from .a import b,c,d + + # level == -1: relative and absolute attempted (Python 2 only). + # level >= 0: absolute only (Python 2 w/ absolute_import and Python 3). + # The modern Mercurial convention is to use absolute_import everywhere, + # so modern Mercurial code will have level >= 0. + + # The name of the module the import statement is located in. + globalname = globals.get('__name__') + + def processfromitem(mod, attr, **kwargs): + """Process an imported symbol in the import statement. + + If the symbol doesn't exist in the parent module, it must be a + module. We set missing modules up as _demandmod instances. + """ + symbol = getattr(mod, attr, nothing) + if symbol is nothing: + symbol = _demandmod(attr, mod.__dict__, locals, **kwargs) + setattr(mod, attr, symbol) + + # Record the importing module references this symbol so we can + # replace the symbol with the actual module instance at load + # time. + if globalname and isinstance(symbol, _demandmod): + symbol._addref(globalname) + + if level >= 0: + # Mercurial's enforced import style does not use + # "from a import b,c,d" or "from .a import b,c,d" syntax. In + # addition, this appears to be giving errors with some modules + # for unknown reasons. Since we shouldn't be using this syntax + # much, work around the problems. + if name: + return _hgextimport(_origimport, name, globals, locals, + fromlist, level) + + mod = _hgextimport(_origimport, name, globals, locals, level=level) + + for x in fromlist: + processfromitem(mod, x, level=level) + + return mod + + # But, we still need to support lazy loading of standard library and 3rd + # party modules. So handle level == -1. mod = _hgextimport(_origimport, name, globals, locals) # recurse down the module chain for comp in name.split('.')[1:]: if getattr(mod, comp, nothing) is nothing: - setattr(mod, comp, _demandmod(comp, mod.__dict__, mod.__dict__)) + setattr(mod, comp, + _demandmod(comp, mod.__dict__, mod.__dict__)) mod = getattr(mod, comp) + for x in fromlist: - # set requested submodules for demand load - if getattr(mod, x, nothing) is nothing: - setattr(mod, x, _demandmod(x, mod.__dict__, locals)) + processfromitem(mod, x) + return mod ignore = [ + '__future__', '_hashlib', '_xmlplus', 'fcntl',
--- a/mercurial/dirstate.py Mon Oct 05 10:43:16 2015 -0600 +++ b/mercurial/dirstate.py Wed Oct 07 13:44:48 2015 -0500 @@ -42,6 +42,10 @@ # ntpath.join(root, '') of Python 2.7.9 does not add sep if root is # UNC path pointing to root share (issue4557) self._rootdir = pathutil.normasprefix(root) + # internal config: ui.forcecwd + forcecwd = ui.config('ui', 'forcecwd') + if forcecwd: + self._cwd = forcecwd self._dirty = False self._dirtypl = False self._lastnormaltime = 0 @@ -220,6 +224,12 @@ return os.getcwd() def getcwd(self): + '''Return the path from which a canonical path is calculated. + + This path should be used to resolve file patterns or to convert + canonical paths back to file paths for display. It shouldn't be + used to get real file paths. Use vfs functions instead. + ''' cwd = self._cwd if cwd == self._root: return '' @@ -418,7 +428,7 @@ def normal(self, f): '''Mark a file normal and clean.''' s = os.lstat(self._join(f)) - mtime = int(s.st_mtime) + mtime = util.statmtimesec(s) self._addpath(f, 'n', s.st_mode, s.st_size & _rangemask, mtime & _rangemask) if f in self._copymap: @@ -988,7 +998,7 @@ if not st and state in "nma": dadd(fn) elif state == 'n': - mtime = int(st.st_mtime) + mtime = util.statmtimesec(st) if (size >= 0 and ((size != st.st_size and size != st.st_size & _rangemask) or ((mode ^ st.st_mode) & 0o100 and checkexec))
--- a/mercurial/discovery.py Mon Oct 05 10:43:16 2015 -0600 +++ b/mercurial/discovery.py Wed Oct 07 13:44:48 2015 -0500 @@ -5,10 +5,23 @@ # This software may be used and distributed according to the terms of the # GNU General Public License version 2 or any later version. -from node import nullid, short -from i18n import _ -import util, setdiscovery, treediscovery, phases, obsolete, bookmarks -import branchmap +from __future__ import absolute_import + +from .i18n import _ +from .node import ( + nullid, + short, +) + +from . import ( + bookmarks, + branchmap, + obsolete, + phases, + setdiscovery, + treediscovery, + util, +) def findcommonincoming(repo, remote, heads=None, force=False): """Return a tuple (common, anyincoming, heads) used to identify the common
--- a/mercurial/dispatch.py Mon Oct 05 10:43:16 2015 -0600 +++ b/mercurial/dispatch.py Wed Oct 07 13:44:48 2015 -0500 @@ -5,13 +5,37 @@ # This software may be used and distributed according to the terms of the # GNU General Public License version 2 or any later version. -from i18n import _ -import os, sys, atexit, signal, pdb, socket, errno, shlex, time, traceback, re +from __future__ import absolute_import + +import atexit import difflib -import util, commands, hg, fancyopts, extensions, hook, error -import cmdutil, encoding -import ui as uimod -import demandimport +import errno +import os +import pdb +import re +import shlex +import signal +import socket +import sys +import time +import traceback + + +from .i18n import _ + +from . import ( + cmdutil, + commands, + demandimport, + encoding, + error, + extensions, + fancyopts, + hg, + hook, + ui as uimod, + util, +) class request(object): def __init__(self, args, ui=None, repo=None, fin=None, fout=None, @@ -157,8 +181,8 @@ debugtrace[debugger] == debugtrace['pdb']): ui.warn(_("%s debugger specified " "but its module was not found\n") % debugger) - - debugtrace[debugger]() + with demandimport.deactivated(): + debugtrace[debugger]() try: return _dispatch(req) finally: @@ -229,7 +253,7 @@ # check if the command is in a disabled extension # (but don't check for extensions themselves) commands.help_(ui, inst.args[0], unknowncmd=True) - except error.UnknownCommand: + except (error.UnknownCommand, util.Abort): suggested = False if len(inst.args) == 2: sim = _getsimilar(inst.args[1], inst.args[0]) @@ -268,8 +292,7 @@ ui.warn(_("abort: error: %s\n") % reason) elif (util.safehasattr(inst, "args") and inst.args and inst.args[0] == errno.EPIPE): - if ui.debugflag: - ui.warn(_("broken pipe\n")) + pass elif getattr(inst, "strerror", None): if getattr(inst, "filename", None): ui.warn(_("abort: %s: %s\n") % (inst.strerror, inst.filename)) @@ -286,10 +309,7 @@ try: ui.warn(_("interrupted!\n")) except IOError as inst: - if inst.errno == errno.EPIPE: - if ui.debugflag: - ui.warn(_("\nbroken pipe\n")) - else: + if inst.errno != errno.EPIPE: raise except MemoryError: ui.warn(_("abort: out of memory\n")) @@ -311,26 +331,27 @@ compare = myver.split('+')[0] ct = tuplever(compare) worst = None, ct, '' - for name, mod in extensions.extensions(): - testedwith = getattr(mod, 'testedwith', '') - report = getattr(mod, 'buglink', _('the extension author.')) - if not testedwith.strip(): - # We found an untested extension. It's likely the culprit. - worst = name, 'unknown', report - break + if ui.config('ui', 'supportcontact', None) is None: + for name, mod in extensions.extensions(): + testedwith = getattr(mod, 'testedwith', '') + report = getattr(mod, 'buglink', _('the extension author.')) + if not testedwith.strip(): + # We found an untested extension. It's likely the culprit. + worst = name, 'unknown', report + break - # Never blame on extensions bundled with Mercurial. - if testedwith == 'internal': - continue + # Never blame on extensions bundled with Mercurial. + if testedwith == 'internal': + continue - tested = [tuplever(t) for t in testedwith.split()] - if ct in tested: - continue + tested = [tuplever(t) for t in testedwith.split()] + if ct in tested: + continue - lower = [t for t in tested if t < ct] - nearest = max(lower or tested) - if worst[0] is None or nearest < worst[1]: - worst = name, nearest, report + lower = [t for t in tested if t < ct] + nearest = max(lower or tested) + if worst[0] is None or nearest < worst[1]: + worst = name, nearest, report if worst[0] is not None: name, testedwith, report = worst if not isinstance(testedwith, str): @@ -342,9 +363,11 @@ '** If that fixes the bug please report it to %s\n') % (name, testedwith, name, report)) else: + bugtracker = ui.config('ui', 'supportcontact', None) + if bugtracker is None: + bugtracker = _("https://mercurial-scm.org/wiki/BugTracker") warning = (_("** unknown exception encountered, " - "please report by visiting\n") + - _("** http://mercurial.selenic.com/wiki/BugTracker\n")) + "please report by visiting\n** ") + bugtracker + '\n') warning += ((_("** Python %s\n") % sys.version.replace('\n', '')) + (_("** Mercurial Distributed SCM (version %s)\n") % myver) + (_("** Extensions loaded: %s\n") % @@ -866,6 +889,8 @@ except error.RequirementError: raise except error.RepoError: + if rpath and rpath[-1]: # invalid -R path + raise if cmd not in commands.optionalrepo.split(): if (cmd in commands.inferrepo.split() and args and not path): # try to infer -R from command args @@ -909,7 +934,7 @@ format = 'text' try: - from mercurial import lsprof + from . import lsprof except ImportError: raise util.Abort(_( 'lsprof not available - install from ' @@ -922,7 +947,7 @@ p.disable() if format == 'kcachegrind': - import lsprofcalltree + from . import lsprofcalltree calltree = lsprofcalltree.KCacheGrind(p) calltree.output(fp) else: @@ -977,13 +1002,17 @@ statprof.display(fp) def _runcommand(ui, options, cmd, cmdfunc): + """Enables the profiler if applicable. + + ``profiling.enabled`` - boolean config that enables or disables profiling + """ def checkargs(): try: return cmdfunc() except error.SignatureError: raise error.CommandError(cmd, _("invalid arguments")) - if options['profile']: + if options['profile'] or ui.configbool('profiling', 'enabled'): profiler = os.getenv('HGPROF') if profiler is None: profiler = ui.config('profiling', 'type', default='ls') @@ -993,7 +1022,10 @@ output = ui.config('profiling', 'output') - if output: + if output == 'blackbox': + import StringIO + fp = StringIO.StringIO() + elif output: path = ui.expandpath(output) fp = open(path, 'wb') else: @@ -1008,6 +1040,12 @@ return statprofile(ui, checkargs, fp) finally: if output: + if output == 'blackbox': + val = "Profile:\n%s" % fp.getvalue() + # ui.log treats the input as a format string, + # so we need to escape any % signs. + val = val.replace('%', '%%') + ui.log('profile', val) fp.close() else: return checkargs()
--- a/mercurial/error.py Mon Oct 05 10:43:16 2015 -0600 +++ b/mercurial/error.py Wed Oct 07 13:44:48 2015 -0500 @@ -11,6 +11,8 @@ imports. """ +from __future__ import absolute_import + # Do not import anything here, please class HintException(Exception): @@ -32,7 +34,7 @@ # Python 2.6+ complain about the 'message' property being deprecated self.lookupmessage = message if isinstance(name, str) and len(name) == 20: - from node import short + from .node import short name = short(name) RevlogError.__init__(self, '%s@%s: %s' % (index, name, message)) @@ -78,7 +80,7 @@ """Exception raised when a {rev,file}set references an unknown identifier""" def __init__(self, function, symbols): - from i18n import _ + from .i18n import _ ParseError.__init__(self, _("unknown identifier: %s") % function) self.function = function self.symbols = symbols @@ -112,6 +114,10 @@ class LockUnavailable(LockError): pass +# LockError is for errors while acquiring the lock -- this is unrelated +class LockInheritanceContractViolation(RuntimeError): + pass + class ResponseError(Exception): """Raised to print an error with part of output and exit.""" @@ -135,16 +141,27 @@ class BundleValueError(ValueError): """error raised when bundle2 cannot be processed""" -class UnsupportedPartError(BundleValueError): - def __init__(self, parttype=None, params=()): +class BundleUnknownFeatureError(BundleValueError): + def __init__(self, parttype=None, params=(), values=()): self.parttype = parttype self.params = params + self.values = values if self.parttype is None: msg = 'Stream Parameter' else: msg = parttype - if self.params: - msg = '%s - %s' % (msg, ', '.join(self.params)) + entries = self.params + if self.params and self.values: + assert len(self.params) == len(self.values) + entries = [] + for idx, par in enumerate(self.params): + val = self.values[idx] + if val is None: + entries.append(val) + else: + entries.append("%s=%r" % (par, val)) + if entries: + msg = '%s - %s' % (msg, ', '.join(entries)) ValueError.__init__(self, msg) class ReadOnlyPartError(RuntimeError): @@ -173,7 +190,7 @@ """ def __init__(self, filename, node, tombstone): - from node import short + from .node import short RevlogError.__init__(self, '%s:%s' % (filename, short(node))) self.tombstone = tombstone
--- a/mercurial/exchange.py Mon Oct 05 10:43:16 2015 -0600 +++ b/mercurial/exchange.py Wed Oct 07 13:44:48 2015 -0500 @@ -5,13 +5,13 @@ # This software may be used and distributed according to the terms of the # GNU General Public License version 2 or any later version. -import time from i18n import _ from node import hex, nullid import errno, urllib -import util, scmutil, changegroup, base85, error, store +import util, scmutil, changegroup, base85, error import discovery, phases, obsolete, bookmarks as bookmod, bundle2, pushkey import lock as lockmod +import streamclone import tags def readbundle(ui, fh, fname, vfs=None): @@ -147,7 +147,7 @@ # # We can pick: # * missingheads part of common (::commonheads) - common = set(self.outgoing.common) + common = self.outgoing.common nm = self.repo.changelog.nodemap cheads = [node for node in self.revs if nm[node] in common] # and @@ -475,6 +475,14 @@ return func return dec +def _pushb2ctxcheckheads(pushop, bundler): + """Generate race condition checking parts + + Exists as an indepedent function to aid extensions + """ + if not pushop.force: + bundler.newpart('check:heads', data=iter(pushop.remoteheads)) + @b2partsgenerator('changeset') def _pushb2ctx(pushop, bundler): """handle changegroup push through bundle2 @@ -490,8 +498,9 @@ pushop.repo.prepushoutgoinghooks(pushop.repo, pushop.remote, pushop.outgoing) - if not pushop.force: - bundler.newpart('check:heads', data=iter(pushop.remoteheads)) + + _pushb2ctxcheckheads(pushop, bundler) + b2caps = bundle2.bundle2caps(pushop.remote) version = None cgversions = b2caps.get('changegroup') @@ -571,7 +580,7 @@ @b2partsgenerator('bookmarks') def _pushb2bookmarks(pushop, bundler): - """handle phase push through bundle2""" + """handle bookmark push through bundle2""" if 'bookmarks' in pushop.stepsdone: return b2caps = bundle2.bundle2caps(pushop.remote) @@ -838,7 +847,7 @@ """ def __init__(self, repo, remote, heads=None, force=False, bookmarks=(), - remotebookmarks=None): + remotebookmarks=None, streamclonerequested=None): # repo we pull into self.repo = repo # repo we pull from @@ -849,6 +858,8 @@ self.explicitbookmarks = bookmarks # do we force pull? self.force = force + # whether a streaming clone was requested + self.streamclonerequested = streamclonerequested # transaction manager self.trmanager = None # set of common changeset between local and remote before pull @@ -882,6 +893,14 @@ # sync on this subset return self.heads + @util.propertycache + def canusebundle2(self): + return _canusebundle2(self) + + @util.propertycache + def remotebundle2caps(self): + return bundle2.bundle2caps(self.remote) + def gettransaction(self): # deprecated; talk to trmanager directly return self.trmanager.transaction() @@ -916,11 +935,32 @@ if self._tr is not None: self._tr.release() -def pull(repo, remote, heads=None, force=False, bookmarks=(), opargs=None): +def pull(repo, remote, heads=None, force=False, bookmarks=(), opargs=None, + streamclonerequested=None): + """Fetch repository data from a remote. + + This is the main function used to retrieve data from a remote repository. + + ``repo`` is the local repository to clone into. + ``remote`` is a peer instance. + ``heads`` is an iterable of revisions we want to pull. ``None`` (the + default) means to pull everything from the remote. + ``bookmarks`` is an iterable of bookmarks requesting to be pulled. By + default, all remote bookmarks are pulled. + ``opargs`` are additional keyword arguments to pass to ``pulloperation`` + initialization. + ``streamclonerequested`` is a boolean indicating whether a "streaming + clone" is requested. A "streaming clone" is essentially a raw file copy + of revlogs from the server. This only works when the local repository is + empty. The default value of ``None`` means to respect the server + configuration for preferring stream clones. + + Returns the ``pulloperation`` created for this pull. + """ if opargs is None: opargs = {} pullop = pulloperation(repo, remote, heads, force, bookmarks=bookmarks, - **opargs) + streamclonerequested=streamclonerequested, **opargs) if pullop.remote.local(): missing = set(pullop.remote.requirements) - pullop.repo.supported if missing: @@ -932,8 +972,9 @@ lock = pullop.repo.lock() try: pullop.trmanager = transactionmanager(repo, 'pull', remote.url()) + streamclone.maybeperformlegacystreamclone(pullop) _pulldiscovery(pullop) - if _canusebundle2(pullop): + if pullop.canusebundle2: _pullbundle2(pullop) _pullchangeset(pullop) _pullphase(pullop) @@ -984,8 +1025,7 @@ discovery to reduce the chance and impact of race conditions.""" if pullop.remotebookmarks is not None: return - if (_canusebundle2(pullop) - and 'listkeys' in bundle2.bundle2caps(pullop.remote)): + if pullop.canusebundle2 and 'listkeys' in pullop.remotebundle2caps: # all known bundle2 servers now support listkeys, but lets be nice with # new implementation. return @@ -1034,28 +1074,32 @@ """pull data using bundle2 For now, the only supported data are changegroup.""" - remotecaps = bundle2.bundle2caps(pullop.remote) kwargs = {'bundlecaps': caps20to10(pullop.repo)} + + streaming, streamreqs = streamclone.canperformstreamclone(pullop) + # pulling changegroup pullop.stepsdone.add('changegroup') kwargs['common'] = pullop.common kwargs['heads'] = pullop.heads or pullop.rheads kwargs['cg'] = pullop.fetch - if 'listkeys' in remotecaps: + if 'listkeys' in pullop.remotebundle2caps: kwargs['listkeys'] = ['phase'] if pullop.remotebookmarks is None: # make sure to always includes bookmark data when migrating # `hg incoming --bundle` to using this function. kwargs['listkeys'].append('bookmarks') - if not pullop.fetch: + if streaming: + pullop.repo.ui.status(_('streaming all changes\n')) + elif not pullop.fetch: pullop.repo.ui.status(_("no changes found\n")) pullop.cgresult = 0 else: if pullop.heads is None and list(pullop.common) == [nullid]: pullop.repo.ui.status(_("requesting all changes\n")) if obsolete.isenabled(pullop.repo, obsolete.exchangeopt): - remoteversions = bundle2.obsmarkersversion(remotecaps) + remoteversions = bundle2.obsmarkersversion(pullop.remotebundle2caps) if obsolete.commonversion(remoteversions) is not None: kwargs['obsmarkers'] = True pullop.stepsdone.add('obsmarkers') @@ -1419,7 +1463,7 @@ op = bundle2.bundleoperation(repo, lambda: tr, captureoutput=captureoutput) try: - r = bundle2.processbundle(repo, cg, op=op) + op = bundle2.processbundle(repo, cg, op=op) finally: r = op.reply if captureoutput and r is not None: @@ -1444,131 +1488,3 @@ if recordout is not None: recordout(repo.ui.popbuffer()) return r - -# This is it's own function so extensions can override it. -def _walkstreamfiles(repo): - return repo.store.walk() - -def generatestreamclone(repo): - """Emit content for a streaming clone. - - This is a generator of raw chunks that constitute a streaming clone. - - The stream begins with a line of 2 space-delimited integers containing the - number of entries and total bytes size. - - Next, are N entries for each file being transferred. Each file entry starts - as a line with the file name and integer size delimited by a null byte. - The raw file data follows. Following the raw file data is the next file - entry, or EOF. - - When used on the wire protocol, an additional line indicating protocol - success will be prepended to the stream. This function is not responsible - for adding it. - - This function will obtain a repository lock to ensure a consistent view of - the store is captured. It therefore may raise LockError. - """ - entries = [] - total_bytes = 0 - # Get consistent snapshot of repo, lock during scan. - lock = repo.lock() - try: - repo.ui.debug('scanning\n') - for name, ename, size in _walkstreamfiles(repo): - if size: - entries.append((name, size)) - total_bytes += size - finally: - lock.release() - - repo.ui.debug('%d files, %d bytes to transfer\n' % - (len(entries), total_bytes)) - yield '%d %d\n' % (len(entries), total_bytes) - - svfs = repo.svfs - oldaudit = svfs.mustaudit - debugflag = repo.ui.debugflag - svfs.mustaudit = False - - try: - for name, size in entries: - if debugflag: - repo.ui.debug('sending %s (%d bytes)\n' % (name, size)) - # partially encode name over the wire for backwards compat - yield '%s\0%d\n' % (store.encodedir(name), size) - if size <= 65536: - fp = svfs(name) - try: - data = fp.read(size) - finally: - fp.close() - yield data - else: - for chunk in util.filechunkiter(svfs(name), limit=size): - yield chunk - finally: - svfs.mustaudit = oldaudit - -def consumestreamclone(repo, fp): - """Apply the contents from a streaming clone file. - - This takes the output from "streamout" and applies it to the specified - repository. - - Like "streamout," the status line added by the wire protocol is not handled - by this function. - """ - lock = repo.lock() - try: - repo.ui.status(_('streaming all changes\n')) - l = fp.readline() - try: - total_files, total_bytes = map(int, l.split(' ', 1)) - except (ValueError, TypeError): - raise error.ResponseError( - _('unexpected response from remote server:'), l) - repo.ui.status(_('%d files to transfer, %s of data\n') % - (total_files, util.bytecount(total_bytes))) - handled_bytes = 0 - repo.ui.progress(_('clone'), 0, total=total_bytes) - start = time.time() - - tr = repo.transaction(_('clone')) - try: - for i in xrange(total_files): - # XXX doesn't support '\n' or '\r' in filenames - l = fp.readline() - try: - name, size = l.split('\0', 1) - size = int(size) - except (ValueError, TypeError): - raise error.ResponseError( - _('unexpected response from remote server:'), l) - if repo.ui.debugflag: - repo.ui.debug('adding %s (%s)\n' % - (name, util.bytecount(size))) - # for backwards compat, name was partially encoded - ofp = repo.svfs(store.decodedir(name), 'w') - for chunk in util.filechunkiter(fp, limit=size): - handled_bytes += len(chunk) - repo.ui.progress(_('clone'), handled_bytes, - total=total_bytes) - ofp.write(chunk) - ofp.close() - tr.close() - finally: - tr.release() - - # Writing straight to files circumvented the inmemory caches - repo.invalidate() - - elapsed = time.time() - start - if elapsed <= 0: - elapsed = 0.001 - repo.ui.progress(_('clone'), None) - repo.ui.status(_('transferred %s in %.1f seconds (%s/sec)\n') % - (util.bytecount(total_bytes), elapsed, - util.bytecount(total_bytes / elapsed))) - finally: - lock.release()
--- a/mercurial/extensions.py Mon Oct 05 10:43:16 2015 -0600 +++ b/mercurial/extensions.py Wed Oct 07 13:44:48 2015 -0500 @@ -5,9 +5,21 @@ # This software may be used and distributed according to the terms of the # GNU General Public License version 2 or any later version. -import imp, os -import util, cmdutil, error -from i18n import _, gettext +from __future__ import absolute_import + +import imp +import os + +from .i18n import ( + _, + gettext, +) + +from . import ( + cmdutil, + error, + util, +) _extensions = {} _aftercallbacks = {}
--- a/mercurial/fancyopts.py Mon Oct 05 10:43:16 2015 -0600 +++ b/mercurial/fancyopts.py Wed Oct 07 13:44:48 2015 -0500 @@ -5,9 +5,12 @@ # 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 getopt -import util -from i18n import _ + +from .i18n import _ +from . import util def gnugetopt(args, options, longoptions): """Parse options mostly like getopt.gnu_getopt.
--- a/mercurial/filelog.py Mon Oct 05 10:43:16 2015 -0600 +++ b/mercurial/filelog.py Wed Oct 07 13:44:48 2015 -0500 @@ -5,8 +5,16 @@ # This software may be used and distributed according to the terms of the # GNU General Public License version 2 or any later version. -import error, mdiff, revlog -import re, struct +from __future__ import absolute_import + +import re +import struct + +from . import ( + error, + mdiff, + revlog, +) _mdre = re.compile('\1\n') def parsemeta(text):
--- a/mercurial/filemerge.py Mon Oct 05 10:43:16 2015 -0600 +++ b/mercurial/filemerge.py Wed Oct 07 13:44:48 2015 -0500 @@ -5,11 +5,25 @@ # This software may be used and distributed according to the terms of the # GNU General Public License version 2 or any later version. -from node import short -from i18n import _ -import util, simplemerge, match, error, templater, templatekw -import os, tempfile, re, filecmp -import tagmerge +from __future__ import absolute_import + +import filecmp +import os +import re +import tempfile + +from .i18n import _ +from .node import short + +from . import ( + error, + match, + simplemerge, + tagmerge, + templatekw, + templater, + util, +) def _toolstr(ui, tool, part, default=""): return ui.config("merge-tools", tool + "." + part, default) @@ -213,15 +227,12 @@ util.copyfile(back, a) # restore from backup and try again return 1 # continue merging -@internaltool('merge', True, - _("merging %s incomplete! " - "(edit conflicts, then use 'hg resolve --mark')\n")) -def _imerge(repo, mynode, orig, fcd, fco, fca, toolconf, files, labels=None): +def _merge(repo, mynode, orig, fcd, fco, fca, toolconf, files, labels, mode): """ Uses the internal non-interactive simple merge algorithm for merging files. It will fail if there are any conflicts and leave markers in the partially merged file. Markers will have two sections, one for each side - of merge.""" + of merge, unless mode equals 'union' which suppresses the markers.""" tool, toolpath, binary, symlink = toolconf if symlink: repo.ui.warn(_('warning: internal :merge cannot merge symlinks ' @@ -233,10 +244,33 @@ ui = repo.ui - r = simplemerge.simplemerge(ui, a, b, c, label=labels) + r = simplemerge.simplemerge(ui, a, b, c, label=labels, mode=mode) return True, r return False, 0 +@internaltool('union', True, + _("merging %s incomplete! " + "(edit conflicts, then use 'hg resolve --mark')\n")) +def _iunion(repo, mynode, orig, fcd, fco, fca, toolconf, files, labels=None): + """ + Uses the internal non-interactive simple merge algorithm for merging + files. It will use both left and right sides for conflict regions. + No markers are inserted.""" + return _merge(repo, mynode, orig, fcd, fco, fca, toolconf, + files, labels, 'union') + +@internaltool('merge', True, + _("merging %s incomplete! " + "(edit conflicts, then use 'hg resolve --mark')\n")) +def _imerge(repo, mynode, orig, fcd, fco, fca, toolconf, files, labels=None): + """ + Uses the internal non-interactive simple merge algorithm for merging + files. It will fail if there are any conflicts and leave markers in + the partially merged file. Markers will have two sections, one for each side + of merge.""" + return _merge(repo, mynode, orig, fcd, fco, fca, toolconf, + files, labels, 'merge') + @internaltool('merge3', True, _("merging %s incomplete! " "(edit conflicts, then use 'hg resolve --mark')\n")) @@ -252,6 +286,38 @@ labels.append('base') return _imerge(repo, mynode, orig, fcd, fco, fca, toolconf, files, labels) +def _imergeauto(repo, mynode, orig, fcd, fco, fca, toolconf, files, + labels=None, localorother=None): + """ + Generic driver for _imergelocal and _imergeother + """ + assert localorother is not None + tool, toolpath, binary, symlink = toolconf + if symlink: + repo.ui.warn(_('warning: :merge-%s cannot merge symlinks ' + 'for %s\n') % (localorother, fcd.path())) + return False, 1 + a, b, c, back = files + r = simplemerge.simplemerge(repo.ui, a, b, c, label=labels, + localorother=localorother) + return True, r + +@internaltool('merge-local', True) +def _imergelocal(*args, **kwargs): + """ + Like :merge, but resolve all conflicts non-interactively in favor + of the local changes.""" + success, status = _imergeauto(localorother='local', *args, **kwargs) + return success, status + +@internaltool('merge-other', True) +def _imergeother(*args, **kwargs): + """ + Like :merge, but resolve all conflicts non-interactively in favor + of the other changes.""" + success, status = _imergeauto(localorother='other', *args, **kwargs) + return success, status + @internaltool('tagmerge', True, _("automatic tag merging of %s failed! " "(use 'hg resolve --tool :merge' or another merge "
--- a/mercurial/fileset.py Mon Oct 05 10:43:16 2015 -0600 +++ b/mercurial/fileset.py Wed Oct 07 13:44:48 2015 -0500 @@ -5,9 +5,17 @@ # 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 re -import parser, error, util, merge -from i18n import _ + +from .i18n import _ +from . import ( + error, + merge, + parser, + util, +) elements = { # token-type: binding-strength, primary, prefix, infix, suffix @@ -46,7 +54,7 @@ c = program[pos] decode = lambda x: x else: - decode = lambda x: x.decode('string-escape') + decode = parser.unescapestr pos += 1 s = pos while pos < l: # find closing quote @@ -124,7 +132,7 @@ def modified(mctx, x): """``modified()`` - File that is modified according to status. + File that is modified according to :hg:`status`. """ # i18n: "modified" is a keyword getargs(x, 0, 0, _("modified takes no arguments")) @@ -133,7 +141,7 @@ def added(mctx, x): """``added()`` - File that is added according to status. + File that is added according to :hg:`status`. """ # i18n: "added" is a keyword getargs(x, 0, 0, _("added takes no arguments")) @@ -142,7 +150,7 @@ def removed(mctx, x): """``removed()`` - File that is removed according to status. + File that is removed according to :hg:`status`. """ # i18n: "removed" is a keyword getargs(x, 0, 0, _("removed takes no arguments")) @@ -151,7 +159,7 @@ def deleted(mctx, x): """``deleted()`` - File that is deleted according to status. + File that is deleted according to :hg:`status`. """ # i18n: "deleted" is a keyword getargs(x, 0, 0, _("deleted takes no arguments")) @@ -160,7 +168,7 @@ def unknown(mctx, x): """``unknown()`` - File that is unknown according to status. These files will only be + File that is unknown according to :hg:`status`. These files will only be considered if this predicate is used. """ # i18n: "unknown" is a keyword @@ -170,7 +178,7 @@ def ignored(mctx, x): """``ignored()`` - File that is ignored according to status. These files will only be + File that is ignored according to :hg:`status`. These files will only be considered if this predicate is used. """ # i18n: "ignored" is a keyword @@ -180,7 +188,7 @@ def clean(mctx, x): """``clean()`` - File that is clean according to status. + File that is clean according to :hg:`status`. """ # i18n: "clean" is a keyword getargs(x, 0, 0, _("clean takes no arguments")) @@ -235,7 +243,7 @@ def resolved(mctx, x): """``resolved()`` - File that is marked resolved according to the resolve state. + File that is marked resolved according to :hg:`resolve -l`. """ # i18n: "resolved" is a keyword getargs(x, 0, 0, _("resolved takes no arguments")) @@ -246,7 +254,7 @@ def unresolved(mctx, x): """``unresolved()`` - File that is marked unresolved according to the resolve state. + File that is marked unresolved according to :hg:`resolve -l`. """ # i18n: "unresolved" is a keyword getargs(x, 0, 0, _("unresolved takes no arguments")) @@ -410,7 +418,7 @@ # i18n: "subrepo" is a keyword pat = getstring(x, _("subrepo requires a pattern or no arguments")) - import match as matchmod # avoid circular import issues + from . import match as matchmod # avoid circular import issues fast = not matchmod.patkind(pat) if fast: def m(s):
--- a/mercurial/formatter.py Mon Oct 05 10:43:16 2015 -0600 +++ b/mercurial/formatter.py Wed Oct 07 13:44:48 2015 -0500 @@ -5,13 +5,23 @@ # 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 cPickle -from node import hex, short -from i18n import _ -import encoding, util -import templater import os +from .i18n import _ +from .node import ( + hex, + short, +) + +from . import ( + encoding, + templater, + util, +) + class baseformatter(object): def __init__(self, ui, topic, opts): self._ui = ui @@ -38,12 +48,14 @@ self._item.update(data) def write(self, fields, deftext, *fielddata, **opts): '''do default text output while assigning data to item''' - for k, v in zip(fields.split(), fielddata): - self._item[k] = v + fieldkeys = fields.split() + assert len(fieldkeys) == len(fielddata) + self._item.update(zip(fieldkeys, fielddata)) def condwrite(self, cond, fields, deftext, *fielddata, **opts): '''do conditional write (primarily for plain formatter)''' - for k, v in zip(fields.split(), fielddata): - self._item[k] = v + fieldkeys = fields.split() + assert len(fieldkeys) == len(fielddata) + self._item.update(zip(fieldkeys, fielddata)) def plain(self, text, **opts): '''show raw text for non-templated mode''' pass
--- a/mercurial/graphmod.py Mon Oct 05 10:43:16 2015 -0600 +++ b/mercurial/graphmod.py Wed Oct 07 13:44:48 2015 -0500 @@ -17,11 +17,16 @@ Data depends on type. """ -from mercurial.node import nullrev -import util +from __future__ import absolute_import import heapq +from .node import nullrev +from . import ( + revset, + util, +) + CHANGESET = 'C' def groupbranchiter(revs, parentsfunc, firstbranch=()): @@ -233,8 +238,6 @@ if not revs: return - cl = repo.changelog - lowestrev = revs.min() gpcache = {} if repo.ui.configbool('experimental', 'graph-group-branches', False): @@ -244,7 +247,8 @@ if firstbranchrevset: firstbranch = repo.revs(firstbranchrevset) parentrevs = repo.changelog.parentrevs - revs = list(groupbranchiter(revs, parentrevs, firstbranch)) + revs = groupbranchiter(revs, parentrevs, firstbranch) + revs = revset.baseset(revs) for rev in revs: ctx = repo[rev] @@ -256,7 +260,11 @@ for mpar in mpars: gp = gpcache.get(mpar) if gp is None: - gp = gpcache[mpar] = grandparent(cl, lowestrev, revs, mpar) + # precompute slow query as we know reachableroots() goes + # through all revs (issue4782) + if not isinstance(revs, revset.baseset): + revs = revset.baseset(revs) + gp = gpcache[mpar] = revset.reachableroots(repo, revs, [mpar]) if not gp: parents.append(mpar) else: @@ -354,24 +362,6 @@ yield (cur, type, data, (col, color), edges) seen = next -def grandparent(cl, lowestrev, roots, head): - """Return all ancestors of head in roots which revision is - greater or equal to lowestrev. - """ - pending = set([head]) - seen = set() - kept = set() - llowestrev = max(nullrev, lowestrev) - while pending: - r = pending.pop() - if r >= llowestrev and r not in seen: - if r in roots: - kept.add(r) - else: - pending.update([p for p in cl.parentrevs(r)]) - seen.add(r) - return sorted(kept) - def asciiedges(type, char, lines, seen, rev, parents): """adds edge info to changelog DAG walk suitable for ascii()""" if rev not in seen:
--- a/mercurial/hbisect.py Mon Oct 05 10:43:16 2015 -0600 +++ b/mercurial/hbisect.py Wed Oct 07 13:44:48 2015 -0500 @@ -8,12 +8,20 @@ # 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 collections import os -import error -from i18n import _ -from node import short, hex -import util + +from .i18n import _ +from .node import ( + hex, + short, +) +from . import ( + error, + util, +) def bisect(changelog, state): """find the next node (if any) for testing during a bisect search.
--- a/mercurial/help.py Mon Oct 05 10:43:16 2015 -0600 +++ b/mercurial/help.py Wed Oct 07 13:44:48 2015 -0500 @@ -14,19 +14,28 @@ import cmdutil import hgweb.webcommands as webcommands +_exclkeywords = [ + "(DEPRECATED)", + "(EXPERIMENTAL)", + # i18n: "(DEPRECATED)" is a keyword, must be translated consistently + _("(DEPRECATED)"), + # i18n: "(EXPERIMENTAL)" is a keyword, must be translated consistently + _("(EXPERIMENTAL)"), + ] + def listexts(header, exts, indent=1, showdeprecated=False): '''return a text listing of the given extensions''' rst = [] if exts: rst.append('\n%s\n\n' % header) for name, desc in sorted(exts.iteritems()): - if '(DEPRECATED)' in desc and not showdeprecated: + if not showdeprecated and any(w in desc for w in _exclkeywords): continue rst.append('%s:%s: %s\n' % (' ' * indent, name, desc)) return rst -def extshelp(): - rst = loaddoc('extensions')().splitlines(True) +def extshelp(ui): + rst = loaddoc('extensions')(ui).splitlines(True) rst.extend(listexts( _('enabled extensions:'), extensions.enabled(), showdeprecated=True)) rst.extend(listexts(_('disabled extensions:'), extensions.disabled())) @@ -43,9 +52,7 @@ shortopt, longopt, default, desc = option optlabel = _("VALUE") # default label - if not verbose and ("DEPRECATED" in desc or _("DEPRECATED") in desc or - "EXPERIMENTAL" in desc or - _("EXPERIMENTAL") in desc): + if not verbose and any(w in desc for w in _exclkeywords): continue so = '' @@ -76,7 +83,7 @@ if notomitted: rst.append('\n\n.. container:: notomitted\n\n %s\n\n' % notomitted) -def topicmatch(kw): +def topicmatch(ui, kw): """Return help topics matching kw. Returns {'section': [(name, summary), ...], ...} where section is @@ -94,7 +101,7 @@ # Old extensions may use a str as doc. if (sum(map(lowercontains, names)) or lowercontains(header) - or (callable(doc) and lowercontains(doc()))): + or (callable(doc) and lowercontains(doc(ui)))): results['topics'].append((names[0], header)) import commands # avoid cycle for cmd, entry in commands.table.iteritems(): @@ -132,12 +139,12 @@ def loaddoc(topic): """Return a delayed loader for help/topic.txt.""" - def loader(): + def loader(ui): docdir = os.path.join(util.datapath, 'help') path = os.path.join(docdir, topic + ".txt") doc = gettext(util.readfile(path)) for rewriter in helphooks.get(topic, []): - doc = rewriter(topic, doc) + doc = rewriter(ui, topic, doc) return doc return loader @@ -177,14 +184,15 @@ def addtopichook(topic, rewriter): helphooks.setdefault(topic, []).append(rewriter) -def makeitemsdoc(topic, doc, marker, items, dedent=False): +def makeitemsdoc(ui, topic, doc, marker, items, dedent=False): """Extract docstring from the items key to function mapping, build a - .single documentation block and use it to overwrite the marker in doc + single documentation block and use it to overwrite the marker in doc. """ entries = [] for name in sorted(items): text = (items[name].__doc__ or '').rstrip() - if not text: + if (not text + or not ui.verbose and any(w in text for w in _exclkeywords)): continue text = gettext(text) if dedent: @@ -204,15 +212,15 @@ return doc.replace(marker, entries) def addtopicsymbols(topic, marker, symbols, dedent=False): - def add(topic, doc): - return makeitemsdoc(topic, doc, marker, symbols, dedent=dedent) + def add(ui, topic, doc): + return makeitemsdoc(ui, topic, doc, marker, symbols, dedent=dedent) addtopichook(topic, add) addtopicsymbols('filesets', '.. predicatesmarker', fileset.symbols) addtopicsymbols('merge-tools', '.. internaltoolsmarker', filemerge.internalsdoc) addtopicsymbols('revsets', '.. predicatesmarker', revset.symbols) -addtopicsymbols('templates', '.. keywordsmarker', templatekw.dockeywords) +addtopicsymbols('templates', '.. keywordsmarker', templatekw.keywords) addtopicsymbols('templates', '.. filtersmarker', templatefilters.filters) addtopicsymbols('templates', '.. functionsmarker', templater.funcs) addtopicsymbols('hgweb', '.. webcommandsmarker', webcommands.commands, @@ -334,7 +342,7 @@ if not ui.debugflag and f.startswith("debug") and name != "debug": continue doc = e[0].__doc__ - if doc and 'DEPRECATED' in doc and not ui.verbose: + if not ui.verbose and doc and any(w in doc for w in _exclkeywords): continue doc = gettext(doc) if not doc: @@ -408,7 +416,7 @@ if not doc: rst.append(" %s\n" % _("(no help text available)")) if callable(doc): - rst += [" %s\n" % l for l in doc().splitlines()] + rst += [" %s\n" % l for l in doc(ui).splitlines()] if not ui.verbose: omitted = _('(some details hidden, use --verbose' @@ -475,11 +483,18 @@ rst = [] kw = opts.get('keyword') if kw: - matches = topicmatch(kw) - for t, title in (('topics', _('Topics')), + matches = topicmatch(ui, name) + helpareas = [] + if opts.get('extension'): + helpareas += [('extensions', _('Extensions'))] + if opts.get('command'): + helpareas += [('commands', _('Commands'))] + if not helpareas: + helpareas = [('topics', _('Topics')), ('commands', _('Commands')), ('extensions', _('Extensions')), - ('extensioncommands', _('Extension Commands'))): + ('extensioncommands', _('Extension Commands'))] + for t, title in helpareas: if matches[t]: rst.append('%s:\n\n' % title) rst.extend(minirst.maketable(sorted(matches[t]), 1)) @@ -489,13 +504,14 @@ hint = _('try "hg help" for a list of topics') raise util.Abort(msg, hint=hint) elif name and name != 'shortlist': + queries = [] if unknowncmd: - queries = (helpextcmd,) - elif opts.get('extension'): - queries = (helpext,) - elif opts.get('command'): - queries = (helpcmd,) - else: + queries += [helpextcmd] + if opts.get('extension'): + queries += [helpext] + if opts.get('command'): + queries += [helpcmd] + if not queries: queries = (helptopic, helpcmd, helpext, helpextcmd) for f in queries: try:
--- a/mercurial/help/config.txt Mon Oct 05 10:43:16 2015 -0600 +++ b/mercurial/help/config.txt Wed Oct 07 13:44:48 2015 -0500 @@ -1,6 +1,19 @@ The Mercurial system uses a set of configuration files to control aspects of its behavior. +Troubleshooting +=============== + +If you're having problems with your configuration, +:hg:`config --debug` can help you understand what is introducing +a setting into your environment. + +See :hg:`help config.syntax` and :hg:`help config.files` +for information about how and where to override things. + +Format +====== + The configuration files use a simple ini-file format. A configuration file consists of sections, led by a ``[section]`` header and followed by ``name = value`` entries:: @@ -10,7 +23,7 @@ verbose = True The above entries will be referred to as ``ui.username`` and -``ui.verbose``, respectively. See the Syntax section below. +``ui.verbose``, respectively. See :hg:`help config.syntax`. Files ===== @@ -76,8 +89,8 @@ will not get transferred during a "clone" operation. Options in this file override options in all other configuration files. On Plan 9 and Unix, most of this file will be ignored if it doesn't -belong to a trusted user or to a trusted group. See the documentation -for the ``[trusted]`` section below for more details. +belong to a trusted user or to a trusted group. See +:hg:`help config.trusted` for more details. Per-user configuration file(s) are for the user running Mercurial. On Windows 9x, ``%HOME%`` is replaced by ``%APPDATA%``. Options in these @@ -208,9 +221,10 @@ --------- Defines command aliases. + Aliases allow you to define your own commands in terms of other commands (or aliases), optionally including arguments. Positional -arguments in the form of ``$1``, ``$2``, etc in the alias definition +arguments in the form of ``$1``, ``$2``, etc. in the alias definition are expanded by Mercurial before execution. Positional arguments not already used by ``$N`` in the definition are put at the end of the command to be executed. @@ -273,8 +287,8 @@ ------------ Settings used when displaying file annotations. All values are -Booleans and default to False. See ``diff`` section for related -options for the diff command. +Booleans and default to False. See :hg:`help config.diff` for +related options for the diff command. ``ignorews`` Ignore white space when comparing lines. @@ -291,7 +305,7 @@ Authentication credentials for HTTP authentication. This section allows you to store usernames and passwords for use when logging -*into* HTTP servers. See the ``[web]`` configuration section if +*into* HTTP servers. See :hg:`help config.web` if you want to configure *who* can login to your HTTP server. Each line has the following format:: @@ -347,7 +361,7 @@ authentication entry with. Only used if the prefix doesn't include a scheme. Supported schemes are http and https. They will match static-http and static-https respectively, as well. - Default: https. + (default: https) If no suitable authentication entry is found, the user is prompted for credentials as usual if required by the remote. @@ -356,8 +370,9 @@ ``committemplate`` ------------------ -``changeset`` configuration in this section is used as the template to -customize the text shown in the editor when committing. +``changeset`` + String: configuration in this section is used as the template to + customize the text shown in the editor when committing. In addition to pre-defined template keywords, commit log specific one below can be used for customization: @@ -390,10 +405,10 @@ detail), this customization should be configured carefully, to avoid showing broken characters. - For example, if multibyte character ending with backslash (0x5c) is - followed by ASCII character 'n' in the customized template, - sequence of backslash and 'n' is treated as line-feed unexpectedly - (and multibyte character is broken, too). + For example, if a multibyte character ending with backslash (0x5c) is + followed by the ASCII character 'n' in the customized template, + the sequence of backslash and 'n' is treated as line-feed unexpectedly + (and the multibyte character is broken, too). Customized template is used for commands below (``--edit`` may be required): @@ -447,9 +462,10 @@ only for :hg:`tag --remove`, but ``changeset.tag`` customizes the commit message for :hg:`tag` regardless of ``--remove`` option. -At the external editor invocation for committing, corresponding -dot-separated list of names without ``changeset.`` prefix -(e.g. ``commit.normal.normal``) is in ``HGEDITFORM`` environment variable. +When the external editor is invoked for a commit, the corresponding +dot-separated list of names without the ``changeset.`` prefix +(e.g. ``commit.normal.normal``) is in the ``HGEDITFORM`` environment +variable. In this section, items other than ``changeset`` can be referred from others. For example, the configuration to list committed files up @@ -514,7 +530,7 @@ ``defaults`` ------------ -(defaults are deprecated. Don't use them. Use aliases instead) +(defaults are deprecated. Don't use them. Use aliases instead.) Use the ``[defaults]`` section to define command defaults, i.e. the default options/arguments to pass to the specified commands. @@ -535,8 +551,8 @@ -------- Settings used when displaying diffs. Everything except for ``unified`` -is a Boolean and defaults to False. See ``annotate`` section for -related options for the annotate command. +is a Boolean and defaults to False. See :hg:`help config.annotate` +for related options for the annotate command. ``git`` Use git extended diff format. @@ -599,8 +615,8 @@ containing patches of outgoing messages will be encoded in the first character set to which conversion from local encoding (``$HGENCODING``, ``ui.fallbackencoding``) succeeds. If correct - conversion fails, the text in question is sent as is. Defaults to - empty (explicit) list. + conversion fails, the text in question is sent as is. + (default: '') Order of outgoing email character sets: @@ -711,8 +727,7 @@ action. Overriding a site-wide hook can be done by changing its value or setting it to an empty string. Hooks can be prioritized by adding a prefix of ``priority`` to the hook name on a new line -and setting the priority. The default priority is 0 if -not specified. +and setting the priority. The default priority is 0. Example ``.hg/hgrc``:: @@ -748,7 +763,7 @@ ``outgoing`` Run after sending changes from local repository to another. ID of first changeset sent is in ``$HG_NODE``. Source of operation is in - ``$HG_SOURCE``; see "preoutgoing" hook for description. + ``$HG_SOURCE``; Also see :hg:`help config.preoutgoing` hook. ``post-<command>`` Run after successful invocations of the associated command. The @@ -830,12 +845,12 @@ ``txnclose`` Run after any repository transaction has been committed. At this point, the transaction can no longer be rolled back. The hook will run - after the lock is released. See ``pretxnclose`` docs for details about - available variables. + after the lock is released. See :hg:`help config.pretxnclose` docs for + details about available variables. ``txnabort`` - Run when a transaction is aborted. See ``pretxnclose`` docs for details about - available variables. + Run when a transaction is aborted. See :hg:`help config.pretxnclose` + docs for details about available variables. ``pretxnchangegroup`` Run after a changegroup has been added via push, pull or unbundle, @@ -954,7 +969,7 @@ ``always`` Optional. Always use the proxy, even for localhost and any entries - in ``http_proxy.no``. True or False. Default: False. + in ``http_proxy.no``. (default: False) ``merge-patterns`` ------------------ @@ -1000,12 +1015,12 @@ ``priority`` The priority in which to evaluate this tool. - Default: 0. + (default: 0) ``executable`` Either just the name of the executable or its pathname. On Windows, the path can use environment variables with ${ProgramFiles} syntax. - Default: the tool name. + (default: the tool name) ``args`` The arguments to pass to the tool executable. You can refer to the @@ -1017,7 +1032,7 @@ to or the commit you are merging with. During a rebase ``$local`` represents the destination of the rebase, and ``$other`` represents the commit being rebased. - Default: ``$local $base $other`` + (default: ``$local $base $other``) ``premerge`` Attempt to run internal non-interactive 3-way merge tool before @@ -1026,15 +1041,14 @@ premerge fails. The ``keep-merge3`` will do the same but include information about the base of the merge in the marker (see internal :merge3 in :hg:`help merge-tools`). - Default: True + (default: True) ``binary`` - This tool can merge binary files. Defaults to False, unless tool - was selected by file pattern match. + This tool can merge binary files. (default: False, unless tool + was selected by file pattern match) ``symlink`` - This tool can merge symlinks. Defaults to False, even if tool was - selected by file pattern match. + This tool can merge symlinks. (default: False) ``check`` A list of merge success-checking options: @@ -1048,32 +1062,32 @@ ``fixeol`` Attempt to fix up EOL changes caused by the merge tool. - Default: False + (default: False) ``gui`` - This tool requires a graphical interface to run. Default: False + This tool requires a graphical interface to run. (default: False) ``regkey`` Windows registry key which describes install location of this tool. Mercurial will search for this key first under ``HKEY_CURRENT_USER`` and then under ``HKEY_LOCAL_MACHINE``. - Default: None + (default: None) ``regkeyalt`` An alternate Windows registry key to try if the first key is not found. The alternate key uses the same ``regname`` and ``regappend`` semantics of the primary key. The most common use for this key is to search for 32bit applications on 64bit operating systems. - Default: None + (default: None) ``regname`` - Name of value to read from specified registry key. Defaults to the - unnamed (default) value. + Name of value to read from specified registry key. + (default: the unnamed (default) value) ``regappend`` String to append to the value read from the registry, typically the executable name of the tool. - Default: None + (default: None) ``patch`` @@ -1091,13 +1105,13 @@ endings in patched files are normalized to their original setting on a per-file basis. If target file does not exist or has no end of line, patch line endings are preserved. - Default: strict. + (default: strict) ``fuzz`` The number of lines of 'fuzz' to allow when applying patches. This controls how much context the patcher is allowed to ignore when trying to apply a patch. - Default: 2 + (default: 2) ``paths`` --------- @@ -1109,8 +1123,7 @@ ``default`` Directory or URL to use when pulling if no source is specified. - Default is set to repository from which the current repository was - cloned. + (default: repository from which the current repository was cloned) ``default-push`` Optional. Directory or URL to use when pushing if no destination @@ -1137,11 +1150,11 @@ Controls draft phase behavior when working as a server. When true, pushed changesets are set to public in both client and server and pulled or cloned changesets are set to public in the client. - Default: True + (default: True) ``new-commit`` Phase of newly-created commits. - Default: draft + (default: draft) ``checksubrepos`` Check the phase of the current revision of each subrepository. Allowed @@ -1152,7 +1165,7 @@ "secret" phase while the parent repo is in "draft" phase), the commit is either aborted (if checksubrepos is set to "abort") or the higher phase is used for the parent repository commit (if set to "follow"). - Default: "follow" + (default: follow) ``profiling`` @@ -1169,7 +1182,7 @@ ``type`` The type of profiler to use. - Default: ls. + (default: ls) ``ls`` Use Python's built-in instrumenting profiler. This profiler @@ -1183,7 +1196,7 @@ ``format`` Profiling format. Specific to the ``ls`` instrumenting profiler. - Default: text. + (default: text) ``text`` Generate a profiling report. When saving to a file, it should be @@ -1196,28 +1209,28 @@ ``frequency`` Sampling frequency. Specific to the ``stat`` sampling profiler. - Default: 1000. + (default: 1000) ``output`` File path where profiling data or report should be saved. If the - file exists, it is replaced. Default: None, data is printed on - stderr + file exists, it is replaced. (default: None, data is printed on + stderr) ``sort`` Sort field. Specific to the ``ls`` instrumenting profiler. One of ``callcount``, ``reccallcount``, ``totaltime`` and ``inlinetime``. - Default: inlinetime. + (default: inlinetime) ``limit`` Number of lines to show. Specific to the ``ls`` instrumenting profiler. - Default: 30. + (default: 30) ``nested`` Show at most this number of lines of drill-down info after each main entry. This can help explain the difference between Total and Inline. Specific to the ``ls`` instrumenting profiler. - Default: 5. + (default: 5) ``progress`` ------------ @@ -1249,16 +1262,16 @@ ``width`` If set, the maximum width of the progress information (that is, min(width, - term width) will be used) + term width) will be used). ``clear-complete`` - clear the progress bar after it's done (default to True) + Clear the progress bar after it's done. (default: True) ``disable`` - If true, don't show a progress bar + If true, don't show a progress bar. ``assume-tty`` - If true, ALWAYS show a progress bar, unless disable is given + If true, ALWAYS show a progress bar, unless disable is given. ``revsetalias`` --------------- @@ -1280,20 +1293,20 @@ about 6 Mbps), uncompressed streaming is slower, because of the extra data transfer overhead. This mode will also temporarily hold the write lock while determining what data to transfer. - Default is True. + (default: True) ``preferuncompressed`` When set, clients will try to use the uncompressed streaming - protocol. Default is False. + protocol. (default: False) ``validate`` Whether to validate the completeness of pushed changesets by checking that all new file revisions specified in manifests are - present. Default is False. + present. (default: False) ``maxhttpheaderlen`` Instruct HTTP clients not to send request headers longer than this - many bytes. Default is 1024. + many bytes. (default: 1024) ``smtp`` -------- @@ -1304,12 +1317,12 @@ Host name of mail server, e.g. "mail.example.com". ``port`` - Optional. Port to connect to on mail server. Default: 465 (if - ``tls`` is smtps) or 25 (otherwise). + Optional. Port to connect to on mail server. (default: 465 if + ``tls`` is smtps; 25 otherwise) ``tls`` Optional. Method to enable TLS when connecting to mail server: starttls, - smtps or none. Default: none. + smtps or none. (default: none) ``verifycert`` Optional. Verification for the certificate of mail server, when @@ -1319,19 +1332,19 @@ ``[web] cacerts`` also). For "strict", sending email is also aborted, if there is no configuration for mail server in ``[hostfingerprints]`` and ``[web] cacerts``. --insecure for - :hg:`email` overwrites this as "loose". Default: "strict". + :hg:`email` overwrites this as "loose". (default: strict) ``username`` Optional. User name for authenticating with the SMTP server. - Default: none. + (default: None) ``password`` Optional. Password for authenticating with the SMTP server. If not specified, interactive sessions will prompt the user for a - password; non-interactive sessions will fail. Default: none. + password; non-interactive sessions will fail. (default: None) ``local_hostname`` - Optional. It's the hostname that the sender can use to identify + Optional. The hostname that the sender can use to identify itself to the MTA. @@ -1390,30 +1403,30 @@ Whether to include the .hg_archival.txt file containing meta data (hashes for the repository base and for tip) in archives created by the :hg:`archive` command or downloaded via hgweb. - Default is True. + (default: True) ``askusername`` Whether to prompt for a username when committing. If True, and neither ``$HGUSER`` nor ``$EMAIL`` has been specified, then the user will be prompted to enter a username. If no username is entered, the default ``USER@HOST`` is used instead. - Default is False. + (default: False) ``commitsubrepos`` Whether to commit modified subrepositories when committing the parent repository. If False and one subrepository has uncommitted changes, abort the commit. - Default is False. + (default: False) ``debug`` - Print debugging information. True or False. Default is False. + Print debugging information. (default: False) ``editor`` - The editor to use during a commit. Default is ``$EDITOR`` or ``vi``. + The editor to use during a commit. (default: ``$EDITOR`` or ``vi``) ``fallbackencoding`` Encoding to try if it's not possible to decode the changelog using - UTF-8. Default is ISO-8859-1. + UTF-8. (default: ISO-8859-1) ``ignore`` A file to read per-user ignore patterns from. This file should be @@ -1424,7 +1437,7 @@ of the ignore file format, see the ``hgignore(5)`` man page. ``interactive`` - Allow to prompt the user. True or False. Default is True. + Allow to prompt the user. (default: True) ``logtemplate`` Template string for commands that print changesets. @@ -1439,14 +1452,16 @@ style uses the ``mergemarkertemplate`` setting to style the labels. The ``basic`` style just uses 'local' and 'other' as the marker label. One of ``basic`` or ``detailed``. - Default is ``basic``. + (default: ``basic``) ``mergemarkertemplate`` The template used to print the commit description next to each conflict marker during merge conflicts. See :hg:`help templates` for the template format. + Defaults to showing the hash, tags, branches, bookmarks, author, and the first line of the commit description. + If you use non-ASCII characters in names for tags, branches, bookmarks, authors, and/or commit descriptions, you must pay attention to encodings of managed files. At template expansion, non-ASCII characters use the encoding @@ -1470,7 +1485,7 @@ ``portablefilenames`` Check for portable filenames. Can be ``warn``, ``ignore`` or ``abort``. - Default is ``warn``. + (default: ``warn``) If set to ``warn`` (or ``true``), a warning message is printed on POSIX platforms, if a file with a non-portable filename is added (e.g. a file with a name that can't be created on Windows because it contains reserved @@ -1481,56 +1496,63 @@ On Windows, this configuration option is ignored and the command aborted. ``quiet`` - Reduce the amount of output printed. True or False. Default is False. + Reduce the amount of output printed. (default: False) ``remotecmd`` - remote command to use for clone/push/pull operations. Default is ``hg``. + Remote command to use for clone/push/pull operations. (default: ``hg``) ``report_untrusted`` Warn if a ``.hg/hgrc`` file is ignored due to not being owned by a - trusted user or group. True or False. Default is True. + trusted user or group. (default: True) ``slash`` Display paths using a slash (``/``) as the path separator. This only makes a difference on systems where the default path separator is not the slash character (e.g. Windows uses the backslash character (``\``)). - Default is False. + (default: False) ``statuscopies`` Display copies in the status command. ``ssh`` - command to use for SSH connections. Default is ``ssh``. + Command to use for SSH connections. (default: ``ssh``) ``strict`` Require exact command names, instead of allowing unambiguous - abbreviations. True or False. Default is False. + abbreviations. (default: False) ``style`` Name of style to use for command output. +``supportcontact`` + A URL where users should report a Mercurial traceback. Use this if you are a + large organisation with its own Mercurial deployment process and crash + reports should be addressed to your internal support. + ``timeout`` The timeout used when a lock is held (in seconds), a negative value - means no timeout. Default is 600. + means no timeout. (default: 600) ``traceback`` Mercurial always prints a traceback when an unknown exception occurs. Setting this to True will make Mercurial print a traceback on all exceptions, even those recognized by Mercurial (such as - IOError or MemoryError). Default is False. + IOError or MemoryError). (default: False) ``username`` The committer of a changeset created when running "commit". Typically a person's name and email address, e.g. ``Fred Widget - <fred@example.com>``. Default is ``$EMAIL`` or ``username@hostname``. If - the username in hgrc is empty, it has to be specified manually or - in a different hgrc file (e.g. ``$HOME/.hgrc``, if the admin set - ``username =`` in the system hgrc). Environment variables in the + <fred@example.com>``. Environment variables in the username are expanded. + (default: ``$EMAIL`` or ``username@hostname``. If the username in + hgrc is empty, e.g. if the system admin set ``username =`` in the + system hgrc, it has to be specified manually or in a different + hgrc file) + ``verbose`` - Increase the amount of output printed. True or False. Default is False. + Increase the amount of output printed. (default: False) ``web`` @@ -1560,35 +1582,35 @@ The full set of options is: ``accesslog`` - Where to output the access log. Default is stdout. + Where to output the access log. (default: stdout) ``address`` - Interface address to bind to. Default is all. + Interface address to bind to. (default: all) ``allow_archive`` List of archive format (bz2, gz, zip) allowed for downloading. - Default is empty. + (default: empty) ``allowbz2`` (DEPRECATED) Whether to allow .tar.bz2 downloading of repository revisions. - Default is False. + (default: False) ``allowgz`` (DEPRECATED) Whether to allow .tar.gz downloading of repository revisions. - Default is False. + (default: False) ``allowpull`` - Whether to allow pulling from the repository. Default is True. + Whether to allow pulling from the repository. (default: True) ``allow_push`` Whether to allow pushing to the repository. If empty or not set, - push is not allowed. If the special value ``*``, any remote user can - push, including unauthenticated users. Otherwise, the remote user - must have been authenticated, and the authenticated user name must - be present in this list. The contents of the allow_push list are - examined after the deny_push list. + pushing is not allowed. If the special value ``*``, any remote + user can push, including unauthenticated users. Otherwise, the + remote user must have been authenticated, and the authenticated + user name must be present in this list. The contents of the + allow_push list are examined after the deny_push list. ``allow_read`` If the user has not already been denied repository access due to @@ -1603,11 +1625,12 @@ ``allowzip`` (DEPRECATED) Whether to allow .zip downloading of repository - revisions. Default is False. This feature creates temporary files. + revisions. This feature creates temporary files. + (default: False) ``archivesubrepos`` - Whether to recurse into subrepositories when archiving. Default is - False. + Whether to recurse into subrepositories when archiving. + (default: False) ``baseurl`` Base URL to use when publishing URLs in other locations, so @@ -1642,7 +1665,7 @@ -----END CERTIFICATE----- ``cache`` - Whether to support caching in hgweb. Defaults to True. + Whether to support caching in hgweb. (default: True) ``certificate`` Certificate to use when running :hg:`serve`. @@ -1654,17 +1677,18 @@ the current path are grouped behind navigable directory entries that lead to the locations of these repositories. In effect, this setting collapses each collection of repositories found within a subdirectory - into a single entry for that subdirectory. Default is False. + into a single entry for that subdirectory. (default: False) ``comparisoncontext`` Number of lines of context to show in side-by-side file comparison. If - negative or the value ``full``, whole files are shown. Default is 5. + negative or the value ``full``, whole files are shown. (default: 5) + This setting can be overridden by a ``context`` request parameter to the ``comparison`` command, taking the same values. ``contact`` Name or email address of the person in charge of the repository. - Defaults to ui.username or ``$EMAIL`` or "unknown" if unset or empty. + (default: ui.username or ``$EMAIL`` or "unknown" if unset or empty) ``deny_push`` Whether to deny pushing to the repository. If empty or not set, @@ -1695,28 +1719,28 @@ ``description`` Textual description of the repository's purpose or contents. - Default is "unknown". + (default: "unknown") ``encoding`` - Character encoding name. Default is the current locale charset. - Example: "UTF-8" + Character encoding name. (default: the current locale charset) + Example: "UTF-8". ``errorlog`` - Where to output the error log. Default is stderr. + Where to output the error log. (default: stderr) ``guessmime`` Control MIME types for raw download of file content. Set to True to let hgweb guess the content type from the file extension. This will serve HTML files as ``text/html`` and might allow cross-site scripting attacks when serving untrusted - repositories. Default is False. + repositories. (default: False) ``hidden`` Whether to hide the repository in the hgwebdir index. - Default is False. + (default: False) ``ipv6`` - Whether to use IPv6. Default is False. + Whether to use IPv6. (default: False) ``logoimg`` File name of the logo image that some templates display on each page. @@ -1725,32 +1749,41 @@ If unset, ``hglogo.png`` will be used. ``logourl`` - Base URL to use for logos. If unset, ``http://mercurial.selenic.com/`` + Base URL to use for logos. If unset, ``https://mercurial-scm.org/`` will be used. ``maxchanges`` - Maximum number of changes to list on the changelog. Default is 10. + Maximum number of changes to list on the changelog. (default: 10) ``maxfiles`` - Maximum number of files to list per changeset. Default is 10. + Maximum number of files to list per changeset. (default: 10) ``maxshortchanges`` Maximum number of changes to list on the shortlog, graph or filelog - pages. Default is 60. + pages. (default: 60) ``name`` - Repository name to use in the web interface. Default is current - working directory. + Repository name to use in the web interface. + (default: current working directory) ``port`` - Port to listen on. Default is 8000. + Port to listen on. (default: 8000) ``prefix`` - Prefix path to serve from. Default is '' (server root). + Prefix path to serve from. (default: '' (server root)) ``push_ssl`` Whether to require that inbound pushes be transported over SSL to - prevent password sniffing. Default is True. + prevent password sniffing. (default: True) + +``refreshinterval`` + How frequently directory listings re-scan the filesystem for new + repositories, in seconds. This is relevant when wildcards are used + to define paths. Depending on how much filesystem traversal is + required, refreshing may negatively impact performance. + + Values less than or equal to 0 always refresh. + (default: 20) ``staticurl`` Base URL to use for static files. If unset, static files (e.g. the @@ -1760,12 +1793,12 @@ ``stripes`` How many lines a "zebra stripe" should span in multi-line output. - Default is 1; set to 0 to disable. + Set to 0 to disable. (default: 1) ``style`` Which template map style to use. The available options are the names of - subdirectories in the HTML templates path. Default is ``paper``. - Example: ``monoblue`` + subdirectories in the HTML templates path. (default: ``paper``) + Example: ``monoblue``. ``templates`` Where to find the HTML templates. The default path to the HTML templates @@ -1812,6 +1845,6 @@ helps performance. ``numcpus`` - Number of CPUs to use for parallel operations. Default is 4 or the - number of CPUs on the system, whichever is larger. A zero or + Number of CPUs to use for parallel operations. A zero or negative value is treated as ``use the default``. + (default: 4 or the number of CPUs on the system, whichever is larger)
--- a/mercurial/help/glossary.txt Mon Oct 05 10:43:16 2015 -0600 +++ b/mercurial/help/glossary.txt Wed Oct 07 13:44:48 2015 -0500 @@ -32,12 +32,12 @@ explicitly with a named branch, but it can also be done locally, using bookmarks or clones and anonymous branches. - Example: "The experimental branch". + Example: "The experimental branch." (Verb) The action of creating a child changeset which results in its parent having more than one child. - Example: "I'm going to branch at X". + Example: "I'm going to branch at X." Branch, anonymous Every time a new child changeset is created from a parent that is not @@ -135,7 +135,7 @@ See 'Changeset, child'. Close changeset - See 'Head, closed branch' + See 'Head, closed branch'. Closed branch See 'Branch, closed'. @@ -144,11 +144,11 @@ (Noun) An entire or partial copy of a repository. The partial clone must be in the form of a revision and its ancestors. - Example: "Is your clone up to date?". + Example: "Is your clone up to date?" (Verb) The process of creating a clone, using :hg:`clone`. - Example: "I'm going to clone the repository". + Example: "I'm going to clone the repository." Closed branch head See 'Head, closed branch'. @@ -398,13 +398,13 @@ Update (Noun) Another synonym of changeset. - Example: "I've pushed an update". + Example: "I've pushed an update." (Verb) This term is usually used to describe updating the state of the working directory to that of a specific changeset. See :hg:`help update`. - Example: "You should update". + Example: "You should update." Working directory See 'Directory, working'.
--- a/mercurial/help/hg.1.txt Mon Oct 05 10:43:16 2015 -0600 +++ b/mercurial/help/hg.1.txt Wed Oct 07 13:44:48 2015 -0500 @@ -104,7 +104,7 @@ Resources """"""""" -Main Web Site: http://mercurial.selenic.com/ +Main Web Site: https://mercurial-scm.org/ Source code repository: http://selenic.com/hg
--- a/mercurial/help/scripting.txt Mon Oct 05 10:43:16 2015 -0600 +++ b/mercurial/help/scripting.txt Wed Oct 07 13:44:48 2015 -0500 @@ -50,15 +50,15 @@ invoking ``hg`` processes. HGENCODING - If not set, the locale used by Mercurial will be detected from the - environment. If the determined locale does not support display of - certain characters, Mercurial may render these character sequences - incorrectly (often by using "?" as a placeholder for invalid - characters in the current locale). + If not set, the locale used by Mercurial will be detected from the + environment. If the determined locale does not support display of + certain characters, Mercurial may render these character sequences + incorrectly (often by using "?" as a placeholder for invalid + characters in the current locale). - Explicitly setting this environment variable is a good practice to - guarantee consistent results. "utf-8" is a good choice on UNIX-like - environments. + Explicitly setting this environment variable is a good practice to + guarantee consistent results. "utf-8" is a good choice on UNIX-like + environments. HGRCPATH If not set, Mercurial will inherit config options from config files
--- a/mercurial/help/templates.txt Mon Oct 05 10:43:16 2015 -0600 +++ b/mercurial/help/templates.txt Wed Oct 07 13:44:48 2015 -0500 @@ -69,6 +69,10 @@ $ hg log -r 0 --template "{date(date, '%Y')}\n" +- Display date in UTC:: + + $ hg log -r 0 --template "{localdate(date, 'UTC')|date}\n" + - Output the description set to a fill-width of 30:: $ hg log -r 0 --template "{fill(desc, 30)}" @@ -98,10 +102,18 @@ $ hg log --template "{bookmarks % '{bookmark}{ifeq(bookmark, active, '*')} '}\n" +- Find the previous release candidate tag, the distance and changes since the tag:: + + $ hg log -r . --template "{latesttag('re:^.*-rc$') % '{tag}, {changes}, {distance}'}\n" + - Mark the working copy parent with '@':: $ hg log --template "{ifcontains(rev, revset('.'), '@')}\n" +- Show details of parent revisions:: + + $ hg log --template "{revset('parents(%d)', rev) % '{desc|firstline}\n'}" + - Show only commit descriptions that start with "template":: $ hg log --template "{startswith('template', firstline(desc))}\n"
--- a/mercurial/hg.py Mon Oct 05 10:43:16 2015 -0600 +++ b/mercurial/hg.py Wed Oct 07 13:44:48 2015 -0500 @@ -6,17 +6,41 @@ # This software may be used and distributed according to the terms of the # GNU General Public License version 2 or any later version. -from i18n import _ -from lock import release -from node import nullid +from __future__ import absolute_import + +import errno +import os +import shutil + +from .i18n import _ +from .node import nullid -import localrepo, bundlerepo, unionrepo, httppeer, sshpeer, statichttprepo -import bookmarks, lock, util, extensions, error, node, scmutil, phases, url -import cmdutil, discovery, repoview, exchange -import ui as uimod -import merge as mergemod -import verify as verifymod -import errno, os, shutil +from . import ( + bookmarks, + bundlerepo, + cmdutil, + discovery, + error, + exchange, + extensions, + httppeer, + localrepo, + lock, + merge as mergemod, + node, + phases, + repoview, + scmutil, + sshpeer, + statichttprepo, + ui as uimod, + unionrepo, + url, + util, + verify as verifymod, +) + +release = lock.release def _local(path): path = util.expandpath(util.urllocalpath(path)) @@ -558,7 +582,11 @@ try: uprev = destrepo.lookup(checkout) except error.RepoLookupError: - pass + if update is not True: + try: + uprev = destrepo.lookup(update) + except error.RepoLookupError: + pass if uprev is None: try: uprev = destrepo._bookmarks['@'] @@ -568,7 +596,7 @@ status = _("updating to bookmark @\n") else: status = (_("updating to bookmark @ on branch %s\n") - % bn) + % bn) except KeyError: try: uprev = destrepo.branchtip('default') @@ -796,3 +824,77 @@ dst.setconfig('web', 'cacerts', util.expandpath(v), 'copied') return dst + +# Files of interest +# Used to check if the repository has changed looking at mtime and size of +# theses files. +foi = [('spath', '00changelog.i'), + ('spath', 'phaseroots'), # ! phase can change content at the same size + ('spath', 'obsstore'), + ('path', 'bookmarks'), # ! bookmark can change content at the same size + ] + +class cachedlocalrepo(object): + """Holds a localrepository that can be cached and reused.""" + + def __init__(self, repo): + """Create a new cached repo from an existing repo. + + We assume the passed in repo was recently created. If the + repo has changed between when it was created and when it was + turned into a cache, it may not refresh properly. + """ + assert isinstance(repo, localrepo.localrepository) + self._repo = repo + self._state, self.mtime = self._repostate() + + def fetch(self): + """Refresh (if necessary) and return a repository. + + If the cached instance is out of date, it will be recreated + automatically and returned. + + Returns a tuple of the repo and a boolean indicating whether a new + repo instance was created. + """ + # We compare the mtimes and sizes of some well-known files to + # determine if the repo changed. This is not precise, as mtimes + # are susceptible to clock skew and imprecise filesystems and + # file content can change while maintaining the same size. + + state, mtime = self._repostate() + if state == self._state: + return self._repo, False + + self._repo = repository(self._repo.baseui, self._repo.url()) + self._state = state + self.mtime = mtime + + return self._repo, True + + def _repostate(self): + state = [] + maxmtime = -1 + for attr, fname in foi: + prefix = getattr(self._repo, attr) + p = os.path.join(prefix, fname) + try: + st = os.stat(p) + except OSError: + st = os.stat(prefix) + state.append((st.st_mtime, st.st_size)) + maxmtime = max(maxmtime, st.st_mtime) + + return tuple(state), maxmtime + + def copy(self): + """Obtain a copy of this class instance. + + A new localrepository instance is obtained. The new instance should be + completely independent of the original. + """ + repo = repository(self._repo.baseui, self._repo.origroot) + c = cachedlocalrepo(repo) + c._state = self._state + c.mtime = self.mtime + return c
--- a/mercurial/hgweb/common.py Mon Oct 05 10:43:16 2015 -0600 +++ b/mercurial/hgweb/common.py Wed Oct 07 13:44:48 2015 -0500 @@ -80,12 +80,9 @@ def __init__(self, code, message=None, headers=[]): if message is None: message = _statusmessage(code) - Exception.__init__(self) + Exception.__init__(self, message) self.code = code - self.message = message self.headers = headers - def __str__(self): - return self.message class continuereader(object): def __init__(self, f, write):
--- a/mercurial/hgweb/hgweb_mod.py Mon Oct 05 10:43:16 2015 -0600 +++ b/mercurial/hgweb/hgweb_mod.py Wed Oct 07 13:44:48 2015 -0500 @@ -6,11 +6,11 @@ # This software may be used and distributed according to the terms of the # GNU General Public License version 2 or any later version. -import os, re +import contextlib +import os from mercurial import ui, hg, hook, error, encoding, templater, util, repoview from mercurial.templatefilters import websub -from mercurial.i18n import _ -from common import get_stat, ErrorResponse, permhooks, caching +from common import ErrorResponse, permhooks, caching from common import HTTP_OK, HTTP_NOT_MODIFIED, HTTP_BAD_REQUEST from common import HTTP_NOT_FOUND, HTTP_SERVER_ERROR from request import wsgirequest @@ -26,15 +26,6 @@ 'pushkey': 'push', } -## Files of interest -# Used to check if the repository has changed looking at mtime and size of -# theses files. This should probably be relocated a bit higher in core. -foi = [('spath', '00changelog.i'), - ('spath', 'phaseroots'), # ! phase can change content at the same size - ('spath', 'obsstore'), - ('path', 'bookmarks'), # ! bookmark can change content at the same size - ] - def makebreadcrumb(url, prefix=''): '''Return a 'URL breadcrumb' list @@ -60,8 +51,145 @@ urlel = os.path.dirname(urlel) return reversed(breadcrumb) +class requestcontext(object): + """Holds state/context for an individual request. + + Servers can be multi-threaded. Holding state on the WSGI application + is prone to race conditions. Instances of this class exist to hold + mutable and race-free state for requests. + """ + def __init__(self, app, repo): + self.repo = repo + self.reponame = app.reponame + + self.archives = ('zip', 'gz', 'bz2') + + self.maxchanges = self.configint('web', 'maxchanges', 10) + self.stripecount = self.configint('web', 'stripes', 1) + self.maxshortchanges = self.configint('web', 'maxshortchanges', 60) + self.maxfiles = self.configint('web', 'maxfiles', 10) + self.allowpull = self.configbool('web', 'allowpull', True) + + # we use untrusted=False to prevent a repo owner from using + # web.templates in .hg/hgrc to get access to any file readable + # by the user running the CGI script + self.templatepath = self.config('web', 'templates', untrusted=False) + + # This object is more expensive to build than simple config values. + # It is shared across requests. The app will replace the object + # if it is updated. Since this is a reference and nothing should + # modify the underlying object, it should be constant for the lifetime + # of the request. + self.websubtable = app.websubtable + + # Trust the settings from the .hg/hgrc files by default. + def config(self, section, name, default=None, untrusted=True): + return self.repo.ui.config(section, name, default, + untrusted=untrusted) + + def configbool(self, section, name, default=False, untrusted=True): + return self.repo.ui.configbool(section, name, default, + untrusted=untrusted) + + def configint(self, section, name, default=None, untrusted=True): + return self.repo.ui.configint(section, name, default, + untrusted=untrusted) + + def configlist(self, section, name, default=None, untrusted=True): + return self.repo.ui.configlist(section, name, default, + untrusted=untrusted) + + archivespecs = { + 'bz2': ('application/x-bzip2', 'tbz2', '.tar.bz2', None), + 'gz': ('application/x-gzip', 'tgz', '.tar.gz', None), + 'zip': ('application/zip', 'zip', '.zip', None), + } + + def archivelist(self, nodeid): + allowed = self.configlist('web', 'allow_archive') + for typ, spec in self.archivespecs.iteritems(): + if typ in allowed or self.configbool('web', 'allow%s' % typ): + yield {'type': typ, 'extension': spec[2], 'node': nodeid} + + def templater(self, req): + # determine scheme, port and server name + # this is needed to create absolute urls + + proto = req.env.get('wsgi.url_scheme') + if proto == 'https': + proto = 'https' + default_port = '443' + else: + proto = 'http' + default_port = '80' + + port = req.env['SERVER_PORT'] + port = port != default_port and (':' + port) or '' + urlbase = '%s://%s%s' % (proto, req.env['SERVER_NAME'], port) + logourl = self.config('web', 'logourl', 'https://mercurial-scm.org/') + logoimg = self.config('web', 'logoimg', 'hglogo.png') + staticurl = self.config('web', 'staticurl') or req.url + 'static/' + if not staticurl.endswith('/'): + staticurl += '/' + + # some functions for the templater + + def motd(**map): + yield self.config('web', 'motd', '') + + # figure out which style to use + + vars = {} + styles = ( + req.form.get('style', [None])[0], + self.config('web', 'style'), + 'paper', + ) + style, mapfile = templater.stylemap(styles, self.templatepath) + if style == styles[0]: + vars['style'] = style + + start = req.url[-1] == '?' and '&' or '?' + sessionvars = webutil.sessionvars(vars, start) + + if not self.reponame: + self.reponame = (self.config('web', 'name') + or req.env.get('REPO_NAME') + or req.url.strip('/') or self.repo.root) + + def websubfilter(text): + return websub(text, self.websubtable) + + # create the templater + + tmpl = templater.templater(mapfile, + filters={'websub': websubfilter}, + defaults={'url': req.url, + 'logourl': logourl, + 'logoimg': logoimg, + 'staticurl': staticurl, + 'urlbase': urlbase, + 'repo': self.reponame, + 'encoding': encoding.encoding, + 'motd': motd, + 'sessionvars': sessionvars, + 'pathdef': makebreadcrumb(req.url), + 'style': style, + }) + return tmpl + class hgweb(object): + """HTTP server for individual repositories. + + Instances of this class serve HTTP responses for a particular + repository. + + Instances are typically used as WSGI applications. + + Some servers are multi-threaded. On these servers, there may + be multiple active threads inside __call__. + """ def __init__(self, repo, name=None, baseui=None): if isinstance(repo, str): if baseui: @@ -73,93 +201,62 @@ # we trust caller to give us a private copy r = repo - r = self._getview(r) r.ui.setconfig('ui', 'report_untrusted', 'off', 'hgweb') r.baseui.setconfig('ui', 'report_untrusted', 'off', 'hgweb') r.ui.setconfig('ui', 'nontty', 'true', 'hgweb') r.baseui.setconfig('ui', 'nontty', 'true', 'hgweb') + # resolve file patterns relative to repo root + r.ui.setconfig('ui', 'forcecwd', r.root, 'hgweb') + r.baseui.setconfig('ui', 'forcecwd', r.root, 'hgweb') # displaying bundling progress bar while serving feel wrong and may # break some wsgi implementation. r.ui.setconfig('progress', 'disable', 'true', 'hgweb') r.baseui.setconfig('progress', 'disable', 'true', 'hgweb') - self.repo = r + self._repos = [hg.cachedlocalrepo(self._webifyrepo(r))] + self._lastrepo = self._repos[0] hook.redirect(True) - self.repostate = ((-1, -1), (-1, -1)) - self.mtime = -1 self.reponame = name - self.archives = 'zip', 'gz', 'bz2' - self.stripecount = 1 - # we use untrusted=False to prevent a repo owner from using - # web.templates in .hg/hgrc to get access to any file readable - # by the user running the CGI script - self.templatepath = self.config('web', 'templates', untrusted=False) - self.websubtable = self.loadwebsub() - # The CGI scripts are often run by a user different from the repo owner. - # Trust the settings from the .hg/hgrc files by default. - def config(self, section, name, default=None, untrusted=True): - return self.repo.ui.config(section, name, default, - untrusted=untrusted) - - def configbool(self, section, name, default=False, untrusted=True): - return self.repo.ui.configbool(section, name, default, - untrusted=untrusted) + def _webifyrepo(self, repo): + repo = getwebview(repo) + self.websubtable = webutil.getwebsubs(repo) + return repo - def configlist(self, section, name, default=None, untrusted=True): - return self.repo.ui.configlist(section, name, default, - untrusted=untrusted) + @contextlib.contextmanager + def _obtainrepo(self): + """Obtain a repo unique to the caller. - def _getview(self, repo): - """The 'web.view' config controls changeset filter to hgweb. Possible - values are ``served``, ``visible`` and ``all``. Default is ``served``. - The ``served`` filter only shows changesets that can be pulled from the - hgweb instance. The``visible`` filter includes secret changesets but - still excludes "hidden" one. - - See the repoview module for details. + Internally we maintain a stack of cachedlocalrepo instances + to be handed out. If one is available, we pop it and return it, + ensuring it is up to date in the process. If one is not available, + we clone the most recently used repo instance and return it. - The option has been around undocumented since Mercurial 2.5, but no - user ever asked about it. So we better keep it undocumented for now.""" - viewconfig = repo.ui.config('web', 'view', 'served', - untrusted=True) - if viewconfig == 'all': - return repo.unfiltered() - elif viewconfig in repoview.filtertable: - return repo.filtered(viewconfig) + It is currently possible for the stack to grow without bounds + if the server allows infinite threads. However, servers should + have a thread limit, thus establishing our limit. + """ + if self._repos: + cached = self._repos.pop() + r, created = cached.fetch() else: - return repo.filtered('served') + cached = self._lastrepo.copy() + r, created = cached.fetch() + if created: + r = self._webifyrepo(r) - def refresh(self, request=None): - repostate = [] - mtime = 0 - # file of interrests mtime and size - for meth, fname in foi: - prefix = getattr(self.repo, meth) - st = get_stat(prefix, fname) - repostate.append((st.st_mtime, st.st_size)) - mtime = max(mtime, st.st_mtime) - repostate = tuple(repostate) - # we need to compare file size in addition to mtime to catch - # changes made less than a second ago - if repostate != self.repostate: - r = hg.repository(self.repo.baseui, self.repo.url()) - self.repo = self._getview(r) - self.maxchanges = int(self.config("web", "maxchanges", 10)) - self.stripecount = int(self.config("web", "stripes", 1)) - self.maxshortchanges = int(self.config("web", "maxshortchanges", - 60)) - self.maxfiles = int(self.config("web", "maxfiles", 10)) - self.allowpull = self.configbool("web", "allowpull", True) - encoding.encoding = self.config("web", "encoding", - encoding.encoding) - # update these last to avoid threads seeing empty settings - self.repostate = repostate - # mtime is needed for ETag - self.mtime = mtime - if request: - self.repo.ui.environ = request.env + self._lastrepo = cached + self.mtime = cached.mtime + try: + yield r + finally: + self._repos.append(cached) def run(self): + """Start a server from CGI environment. + + Modern servers should be using WSGI and should avoid this + method, if possible. + """ if not os.environ.get('GATEWAY_INTERFACE', '').startswith("CGI/1."): raise RuntimeError("This function is only intended to be " "called while running as a CGI script.") @@ -167,12 +264,29 @@ wsgicgi.launch(self) def __call__(self, env, respond): + """Run the WSGI application. + + This may be called by multiple threads. + """ req = wsgirequest(env, respond) return self.run_wsgi(req) def run_wsgi(self, req): + """Internal method to run the WSGI application. - self.refresh(req) + This is typically only called by Mercurial. External consumers + should be using instances of this class as the WSGI application. + """ + with self._obtainrepo() as repo: + for r in self._runwsgi(req, repo): + yield r + + def _runwsgi(self, req, repo): + rctx = requestcontext(self, repo) + + # This state is global across all threads. + encoding.encoding = rctx.config('web', 'encoding', encoding.encoding) + rctx.repo.ui.environ = req.env # work with CGI variables to create coherent structure # use SCRIPT_NAME, PATH_INFO and QUERY_STRING as well as our REPO_NAME @@ -203,8 +317,8 @@ if query: raise ErrorResponse(HTTP_NOT_FOUND) if cmd in perms: - self.check_perm(req, perms[cmd]) - return protocol.call(self.repo, req, cmd) + self.check_perm(rctx, req, perms[cmd]) + return protocol.call(rctx.repo, req, cmd) except ErrorResponse as inst: # A client that sends unbundle without 100-continue will # break if we respond early. @@ -216,7 +330,7 @@ else: req.headers.append(('Connection', 'Close')) req.respond(inst, protocol.HGTYPE, - body='0\n%s\n' % inst.message) + body='0\n%s\n' % inst) return '' # translate user-visible url structure to internal structure @@ -249,7 +363,7 @@ if cmd == 'archive': fn = req.form['node'][0] - for type_, spec in self.archive_specs.iteritems(): + for type_, spec in rctx.archivespecs.iteritems(): ext = spec[2] if fn.endswith(ext): req.form['node'] = [fn[:-len(ext)]] @@ -258,28 +372,28 @@ # process the web interface request try: - tmpl = self.templater(req) + tmpl = rctx.templater(req) ctype = tmpl('mimetype', encoding=encoding.encoding) ctype = templater.stringify(ctype) # check read permissions non-static content if cmd != 'static': - self.check_perm(req, None) + self.check_perm(rctx, req, None) if cmd == '': req.form['cmd'] = [tmpl.cache['default']] cmd = req.form['cmd'][0] - if self.configbool('web', 'cache', True): + if rctx.configbool('web', 'cache', True): caching(self, req) # sets ETag header or raises NOT_MODIFIED if cmd not in webcommands.__all__: msg = 'no such method: %s' % cmd raise ErrorResponse(HTTP_BAD_REQUEST, msg) elif cmd == 'file' and 'raw' in req.form.get('style', []): - self.ctype = ctype - content = webcommands.rawfile(self, req, tmpl) + rctx.ctype = ctype + content = webcommands.rawfile(rctx, req, tmpl) else: - content = getattr(webcommands, cmd)(self, req, tmpl) + content = getattr(webcommands, cmd)(rctx, req, tmpl) req.respond(HTTP_OK, ctype) return content @@ -299,129 +413,29 @@ if inst.code == HTTP_NOT_MODIFIED: # Not allowed to return a body on a 304 return [''] - return tmpl('error', error=inst.message) - - def loadwebsub(self): - websubtable = [] - websubdefs = self.repo.ui.configitems('websub') - # we must maintain interhg backwards compatibility - websubdefs += self.repo.ui.configitems('interhg') - for key, pattern in websubdefs: - # grab the delimiter from the character after the "s" - unesc = pattern[1] - delim = re.escape(unesc) + return tmpl('error', error=str(inst)) - # identify portions of the pattern, taking care to avoid escaped - # delimiters. the replace format and flags are optional, but - # delimiters are required. - match = re.match( - r'^s%s(.+)(?:(?<=\\\\)|(?<!\\))%s(.*)%s([ilmsux])*$' - % (delim, delim, delim), pattern) - if not match: - self.repo.ui.warn(_("websub: invalid pattern for %s: %s\n") - % (key, pattern)) - continue - - # we need to unescape the delimiter for regexp and format - delim_re = re.compile(r'(?<!\\)\\%s' % delim) - regexp = delim_re.sub(unesc, match.group(1)) - format = delim_re.sub(unesc, match.group(2)) + def check_perm(self, rctx, req, op): + for permhook in permhooks: + permhook(rctx, req, op) - # the pattern allows for 6 regexp flags, so set them if necessary - flagin = match.group(3) - flags = 0 - if flagin: - for flag in flagin.upper(): - flags |= re.__dict__[flag] - - try: - regexp = re.compile(regexp, flags) - websubtable.append((regexp, format)) - except re.error: - self.repo.ui.warn(_("websub: invalid regexp for %s: %s\n") - % (key, regexp)) - return websubtable - - def templater(self, req): - - # determine scheme, port and server name - # this is needed to create absolute urls - - proto = req.env.get('wsgi.url_scheme') - if proto == 'https': - proto = 'https' - default_port = "443" - else: - proto = 'http' - default_port = "80" +def getwebview(repo): + """The 'web.view' config controls changeset filter to hgweb. Possible + values are ``served``, ``visible`` and ``all``. Default is ``served``. + The ``served`` filter only shows changesets that can be pulled from the + hgweb instance. The``visible`` filter includes secret changesets but + still excludes "hidden" one. - port = req.env["SERVER_PORT"] - port = port != default_port and (":" + port) or "" - urlbase = '%s://%s%s' % (proto, req.env['SERVER_NAME'], port) - logourl = self.config("web", "logourl", "http://mercurial.selenic.com/") - logoimg = self.config("web", "logoimg", "hglogo.png") - staticurl = self.config("web", "staticurl") or req.url + 'static/' - if not staticurl.endswith('/'): - staticurl += '/' - - # some functions for the templater - - def motd(**map): - yield self.config("web", "motd", "") - - # figure out which style to use - - vars = {} - styles = ( - req.form.get('style', [None])[0], - self.config('web', 'style'), - 'paper', - ) - style, mapfile = templater.stylemap(styles, self.templatepath) - if style == styles[0]: - vars['style'] = style - - start = req.url[-1] == '?' and '&' or '?' - sessionvars = webutil.sessionvars(vars, start) - - if not self.reponame: - self.reponame = (self.config("web", "name") - or req.env.get('REPO_NAME') - or req.url.strip('/') or self.repo.root) + See the repoview module for details. - def websubfilter(text): - return websub(text, self.websubtable) - - # create the templater + The option has been around undocumented since Mercurial 2.5, but no + user ever asked about it. So we better keep it undocumented for now.""" + viewconfig = repo.ui.config('web', 'view', 'served', + untrusted=True) + if viewconfig == 'all': + return repo.unfiltered() + elif viewconfig in repoview.filtertable: + return repo.filtered(viewconfig) + else: + return repo.filtered('served') - tmpl = templater.templater(mapfile, - filters={"websub": websubfilter}, - defaults={"url": req.url, - "logourl": logourl, - "logoimg": logoimg, - "staticurl": staticurl, - "urlbase": urlbase, - "repo": self.reponame, - "encoding": encoding.encoding, - "motd": motd, - "sessionvars": sessionvars, - "pathdef": makebreadcrumb(req.url), - "style": style, - }) - return tmpl - - def archivelist(self, nodeid): - allowed = self.configlist("web", "allow_archive") - for i, spec in self.archive_specs.iteritems(): - if i in allowed or self.configbool("web", "allow" + i): - yield {"type" : i, "extension" : spec[2], "node" : nodeid} - - archive_specs = { - 'bz2': ('application/x-bzip2', 'tbz2', '.tar.bz2', None), - 'gz': ('application/x-gzip', 'tgz', '.tar.gz', None), - 'zip': ('application/zip', 'zip', '.zip', None), - } - - def check_perm(self, req, op): - for permhook in permhooks: - permhook(self, req, op)
--- a/mercurial/hgweb/hgwebdir_mod.py Mon Oct 05 10:43:16 2015 -0600 +++ b/mercurial/hgweb/hgwebdir_mod.py Wed Oct 07 13:44:48 2015 -0500 @@ -79,17 +79,30 @@ return name, str(port), path class hgwebdir(object): - refreshinterval = 20 + """HTTP server for multiple repositories. + Given a configuration, different repositories will be served depending + on the request path. + + Instances are typically used as WSGI applications. + """ def __init__(self, conf, baseui=None): self.conf = conf self.baseui = baseui + self.ui = None self.lastrefresh = 0 self.motd = None self.refresh() def refresh(self): - if self.lastrefresh + self.refreshinterval > time.time(): + refreshinterval = 20 + if self.ui: + refreshinterval = self.ui.configint('web', 'refreshinterval', + refreshinterval) + + # refreshinterval <= 0 means to always refresh. + if (refreshinterval > 0 and + self.lastrefresh + refreshinterval > time.time()): return if self.baseui: @@ -446,7 +459,7 @@ start = url[-1] == '?' and '&' or '?' sessionvars = webutil.sessionvars(vars, start) - logourl = config('web', 'logourl', 'http://mercurial.selenic.com/') + logourl = config('web', 'logourl', 'https://mercurial-scm.org/') logoimg = config('web', 'logoimg', 'hglogo.png') staticurl = config('web', 'staticurl') or url + 'static/' if not staticurl.endswith('/'):
--- a/mercurial/hgweb/request.py Mon Oct 05 10:43:16 2015 -0600 +++ b/mercurial/hgweb/request.py Wed Oct 07 13:44:48 2015 -0500 @@ -40,6 +40,12 @@ return form class wsgirequest(object): + """Higher-level API for a WSGI request. + + WSGI applications are invoked with 2 arguments. They are used to + instantiate instances of this class, which provides higher-level APIs + for obtaining request parameters, writing HTTP output, etc. + """ def __init__(self, wsgienv, start_response): version = wsgienv['wsgi.version'] if (version < (1, 0)) or (version >= (2, 0)): @@ -94,7 +100,7 @@ self.headers = [(k, v) for (k, v) in self.headers if k in ('Date', 'ETag', 'Expires', 'Cache-Control', 'Vary')] - status = statusmessage(status.code, status.message) + status = statusmessage(status.code, str(status)) elif status == 200: status = '200 Script output follows' elif isinstance(status, int):
--- a/mercurial/hgweb/server.py Mon Oct 05 10:43:16 2015 -0600 +++ b/mercurial/hgweb/server.py Wed Oct 07 13:44:48 2015 -0500 @@ -239,7 +239,7 @@ pass class _httprequesthandlerssl(_httprequesthandler): - """HTTPS handler based on Pythons ssl module (introduced in 2.6)""" + """HTTPS handler based on Python's ssl module""" url_scheme = 'https'
--- a/mercurial/hgweb/webcommands.py Mon Oct 05 10:43:16 2015 -0600 +++ b/mercurial/hgweb/webcommands.py Wed Oct 07 13:44:48 2015 -0500 @@ -639,35 +639,10 @@ The ``branches`` template is rendered. """ - tips = [] - heads = web.repo.heads() - parity = paritygen(web.stripecount) - sortkey = lambda item: (not item[1], item[0].rev()) - - def entries(limit, **map): - count = 0 - if not tips: - for tag, hs, tip, closed in web.repo.branchmap().iterbranches(): - tips.append((web.repo[tip], closed)) - for ctx, closed in sorted(tips, key=sortkey, reverse=True): - if limit > 0 and count >= limit: - return - count += 1 - if closed: - status = 'closed' - elif ctx.node() not in heads: - status = 'inactive' - else: - status = 'open' - yield {'parity': parity.next(), - 'branch': ctx.branch(), - 'status': status, - 'node': ctx.hex(), - 'date': ctx.date()} - + entries = webutil.branchentries(web.repo, web.stripecount) + latestentry = webutil.branchentries(web.repo, web.stripecount, 1) return tmpl('branches', node=hex(web.repo.changelog.tip()), - entries=lambda **x: entries(0, **x), - latestentry=lambda **x: entries(1, **x)) + entries=entries, latestentry=latestentry) @webcommand('summary') def summary(web, req, tmpl): @@ -710,18 +685,6 @@ 'date': web.repo[n].date(), 'node': hex(n)} - def branches(**map): - parity = paritygen(web.stripecount) - - b = web.repo.branchmap() - l = [(-web.repo.changelog.rev(tip), tip, tag) - for tag, heads, tip, closed in b.iterbranches()] - for r, n, t in sorted(l): - yield {'parity': parity.next(), - 'branch': t, - 'node': hex(n), - 'date': web.repo[n].date()} - def changelist(**map): parity = paritygen(web.stripecount, offset=start - end) l = [] # build a list in forward order for efficiency @@ -761,7 +724,7 @@ lastchange=tip.date(), tags=tagentries, bookmarks=bookmarks, - branches=branches, + branches=webutil.branchentries(web.repo, web.stripecount, 10), shortlog=changelist, node=tip.hex(), symrev='tip', @@ -1115,7 +1078,7 @@ raise ErrorResponse(HTTP_NOT_FOUND, 'file(s) not found: %s' % file[0]) - mimetype, artype, extension, encoding = web.archive_specs[type_] + mimetype, artype, extension, encoding = web.archivespecs[type_] headers = [ ('Content-Disposition', 'attachment; filename=%s%s' % (name, extension)) ]
--- a/mercurial/hgweb/webutil.py Mon Oct 05 10:43:16 2015 -0600 +++ b/mercurial/hgweb/webutil.py Wed Oct 07 13:44:48 2015 -0500 @@ -7,6 +7,7 @@ # GNU General Public License version 2 or any later version. import os, copy +import re from mercurial import match, patch, error, ui, util, pathutil, context from mercurial.i18n import _ from mercurial.node import hex, nullid, short @@ -199,11 +200,42 @@ for t in repo.nodebookmarks(node): yield tmpl(t1, bookmark=t, **args) +def branchentries(repo, stripecount, limit=0): + tips = [] + heads = repo.heads() + parity = paritygen(stripecount) + sortkey = lambda item: (not item[1], item[0].rev()) + + def entries(**map): + count = 0 + if not tips: + for tag, hs, tip, closed in repo.branchmap().iterbranches(): + tips.append((repo[tip], closed)) + for ctx, closed in sorted(tips, key=sortkey, reverse=True): + if limit > 0 and count >= limit: + return + count += 1 + if closed: + status = 'closed' + elif ctx.node() not in heads: + status = 'inactive' + else: + status = 'open' + yield { + 'parity': parity.next(), + 'branch': ctx.branch(), + 'status': status, + 'node': ctx.hex(), + 'date': ctx.date() + } + + return entries + def cleanpath(repo, path): path = path.lstrip('/') return pathutil.canonpath(repo.root, '', path) -def changeidctx (repo, changeid): +def changeidctx(repo, changeid): try: ctx = repo[changeid] except error.RepoError: @@ -212,11 +244,11 @@ return ctx -def changectx (repo, req): +def changectx(repo, req): changeid = "tip" if 'node' in req.form: changeid = req.form['node'][0] - ipos=changeid.find(':') + ipos = changeid.find(':') if ipos != -1: changeid = changeid[(ipos + 1):] elif 'manifest' in req.form: @@ -227,7 +259,7 @@ def basechangectx(repo, req): if 'node' in req.form: changeid = req.form['node'][0] - ipos=changeid.find(':') + ipos = changeid.find(':') if ipos != -1: changeid = changeid[:ipos] return changeidctx(repo, changeid) @@ -509,3 +541,44 @@ # default termwidth breaks under mod_wsgi def termwidth(self): return 80 + +def getwebsubs(repo): + websubtable = [] + websubdefs = repo.ui.configitems('websub') + # we must maintain interhg backwards compatibility + websubdefs += repo.ui.configitems('interhg') + for key, pattern in websubdefs: + # grab the delimiter from the character after the "s" + unesc = pattern[1] + delim = re.escape(unesc) + + # identify portions of the pattern, taking care to avoid escaped + # delimiters. the replace format and flags are optional, but + # delimiters are required. + match = re.match( + r'^s%s(.+)(?:(?<=\\\\)|(?<!\\))%s(.*)%s([ilmsux])*$' + % (delim, delim, delim), pattern) + if not match: + repo.ui.warn(_("websub: invalid pattern for %s: %s\n") + % (key, pattern)) + continue + + # we need to unescape the delimiter for regexp and format + delim_re = re.compile(r'(?<!\\)\\%s' % delim) + regexp = delim_re.sub(unesc, match.group(1)) + format = delim_re.sub(unesc, match.group(2)) + + # the pattern allows for 6 regexp flags, so set them if necessary + flagin = match.group(3) + flags = 0 + if flagin: + for flag in flagin.upper(): + flags |= re.__dict__[flag] + + try: + regexp = re.compile(regexp, flags) + websubtable.append((regexp, format)) + except re.error: + repo.ui.warn(_("websub: invalid regexp for %s: %s\n") + % (key, regexp)) + return websubtable
--- a/mercurial/hook.py Mon Oct 05 10:43:16 2015 -0600 +++ b/mercurial/hook.py Wed Oct 07 13:44:48 2015 -0500 @@ -5,9 +5,19 @@ # This software may be used and distributed according to the terms of the # GNU General Public License version 2 or any later version. -from i18n import _ -import os, sys, time -import extensions, util, demandimport, error +from __future__ import absolute_import + +import os +import sys +import time + +from .i18n import _ +from . import ( + demandimport, + error, + extensions, + util, +) def _pythonhook(ui, repo, name, hname, funcname, args, throw): '''call python hook. hook is callable object, looked up as
--- a/mercurial/httpconnection.py Mon Oct 05 10:43:16 2015 -0600 +++ b/mercurial/httpconnection.py Wed Oct 07 13:44:48 2015 -0500 @@ -107,7 +107,9 @@ class HTTPConnection(httpclient.HTTPConnection): response_class = HTTPResponse - def request(self, method, uri, body=None, headers={}): + def request(self, method, uri, body=None, headers=None): + if headers is None: + headers = {} if isinstance(body, httpsendfile): body.seek(0) httpclient.HTTPConnection.request(self, method, uri, body=body,
--- a/mercurial/httppeer.py Mon Oct 05 10:43:16 2015 -0600 +++ b/mercurial/httppeer.py Wed Oct 07 13:44:48 2015 -0500 @@ -6,12 +6,28 @@ # This software may be used and distributed according to the terms of the # GNU General Public License version 2 or any later version. -from node import nullid -from i18n import _ +from __future__ import absolute_import + +import errno +import httplib +import os +import socket import tempfile -import changegroup, statichttprepo, error, httpconnection, url, util, wireproto -import os, urllib, urllib2, zlib, httplib -import errno, socket +import urllib +import urllib2 +import zlib + +from .i18n import _ +from .node import nullid +from . import ( + changegroup, + error, + httpconnection, + statichttprepo, + url, + util, + wireproto, +) def zgenerator(f): zd = zlib.decompressobj()
--- a/mercurial/i18n.py Mon Oct 05 10:43:16 2015 -0600 +++ b/mercurial/i18n.py Wed Oct 07 13:44:48 2015 -0500 @@ -5,8 +5,14 @@ # This software may be used and distributed according to the terms of the # GNU General Public License version 2 or any later version. -import encoding -import gettext as gettextmod, sys, os, locale +from __future__ import absolute_import + +import gettext as gettextmod +import locale +import os +import sys + +from . import encoding # modelled after templater.templatepath: if getattr(sys, 'frozen', None) is not None:
--- a/mercurial/localrepo.py Mon Oct 05 10:43:16 2015 -0600 +++ b/mercurial/localrepo.py Wed Oct 07 13:44:48 2015 -0500 @@ -300,6 +300,7 @@ if create: self._writerequirements() + self._dirstatevalidatewarned = False self._branchcaches = {} self._revbranchcache = None @@ -354,6 +355,10 @@ 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', False) + self.svfs.options['aggressivemergedeltas'] = aggressivemergedeltas def _writerequirements(self): scmutil.writerequires(self.vfs, self.requirements) @@ -472,19 +477,19 @@ @repofilecache('dirstate') def dirstate(self): - warned = [0] - def validate(node): - try: - self.changelog.rev(node) - return node - except error.LookupError: - if not warned[0]: - warned[0] = True - self.ui.warn(_("warning: ignoring unknown" - " working parent %s!\n") % short(node)) - return nullid + return dirstate.dirstate(self.vfs, self.ui, self.root, + self._dirstatevalidate) - return dirstate.dirstate(self.vfs, self.ui, self.root, validate) + def _dirstatevalidate(self, node): + try: + self.changelog.rev(node) + return node + except error.LookupError: + if not self._dirstatevalidatewarned: + self._dirstatevalidatewarned = True + self.ui.warn(_("warning: ignoring unknown" + " working parent %s!\n") % short(node)) + return nullid def __getitem__(self, changeid): if changeid is None or changeid == wdirrev: @@ -538,7 +543,7 @@ return hook.hook(self.ui, self, name, throw, **args) @unfilteredmethod - def _tag(self, names, node, message, local, user, date, extra={}, + def _tag(self, names, node, message, local, user, date, extra=None, editor=False): if isinstance(names, str): names = (names,) @@ -1019,6 +1024,9 @@ reporef().hook('txnabort', throw=False, txnname=desc, **tr2.hookargs) tr.addabort('txnabort-hook', txnaborthook) + # avoid eager cache invalidation. in-memory data should be identical + # to stored data if transaction has no error. + tr.addpostclose('refresh-filecachestats', self._refreshfilecachestats) self._transref = weakref.ref(tr) return tr @@ -1196,9 +1204,25 @@ self.invalidate() self.invalidatedirstate() - def _lock(self, vfs, lockname, wait, releasefn, acquirefn, desc): + def _refreshfilecachestats(self, tr): + """Reload stats of cached files so that they are flagged as valid""" + for k, ce in self._filecache.items(): + if k == 'dirstate' or k not in self.__dict__: + continue + ce.refresh() + + def _lock(self, vfs, lockname, wait, releasefn, acquirefn, desc, + inheritchecker=None, parentenvvar=None): + parentlock = None + # the contents of parentenvvar are used by the underlying lock to + # determine whether it can be inherited + if parentenvvar is not None: + parentlock = os.environ.get(parentenvvar) try: - l = lockmod.lock(vfs, lockname, 0, releasefn, desc=desc) + l = lockmod.lock(vfs, lockname, 0, releasefn=releasefn, + acquirefn=acquirefn, desc=desc, + inheritchecker=inheritchecker, + parentlock=parentlock) except error.LockHeld as inst: if not wait: raise @@ -1207,10 +1231,9 @@ # default to 600 seconds timeout l = lockmod.lock(vfs, lockname, int(self.ui.config("ui", "timeout", "600")), - releasefn, desc=desc) + releasefn=releasefn, acquirefn=acquirefn, + desc=desc) self.ui.warn(_("got lock after %s seconds\n") % l.delay) - if acquirefn: - acquirefn() return l def _afterlock(self, callback): @@ -1238,17 +1261,16 @@ l.lock() return l - def unlock(): - for k, ce in self._filecache.items(): - if k == 'dirstate' or k not in self.__dict__: - continue - ce.refresh() - - l = self._lock(self.svfs, "lock", wait, unlock, + l = self._lock(self.svfs, "lock", wait, None, self.invalidate, _('repository %s') % self.origroot) self._lockref = weakref.ref(l) return l + def _wlockchecktransaction(self): + if self.currenttransaction() is not None: + raise error.LockInheritanceContractViolation( + 'wlock cannot be inherited in the middle of a transaction') + def wlock(self, wait=True): '''Lock the non-store parts of the repository (everything under .hg except .hg/store) and return a weak reference to the lock. @@ -1280,10 +1302,25 @@ l = self._lock(self.vfs, "wlock", wait, unlock, self.invalidatedirstate, _('working directory of %s') % - self.origroot) + self.origroot, + inheritchecker=self._wlockchecktransaction, + parentenvvar='HG_WLOCK_LOCKER') self._wlockref = weakref.ref(l) return l + def _currentlock(self, lockref): + """Returns the lock if it's held, or None if it's not.""" + if lockref is None: + return None + l = lockref() + if l is None or not l.held: + return None + return l + + def currentwlock(self): + """Returns the wlock if it's held, or None if it's not.""" + return self._currentlock(self._wlockref) + def _filecommit(self, fctx, manifest1, manifest2, linkrev, tr, changelist): """ commit an individual file as part of a larger transaction @@ -1372,13 +1409,15 @@ @unfilteredmethod def commit(self, text="", user=None, date=None, match=None, force=False, - editor=False, extra={}): + editor=False, extra=None): """Add a new revision to current repository. Revision information is gathered from the working directory, match can be used to filter the committed files. If editor is supplied, it is called to get a commit message. """ + if extra is None: + extra = {} def fail(f, msg): raise util.Abort('%s: %s' % (f, msg)) @@ -1771,121 +1810,21 @@ """ return util.hooks() - def stream_in(self, remote, remotereqs): - # Save remote branchmap. We will use it later - # to speed up branchcache creation - rbranchmap = None - if remote.capable("branchmap"): - rbranchmap = remote.branchmap() - - fp = remote.stream_out() - l = fp.readline() - try: - resp = int(l) - except ValueError: - raise error.ResponseError( - _('unexpected response from remote server:'), l) - if resp == 1: - raise util.Abort(_('operation forbidden by server')) - elif resp == 2: - raise util.Abort(_('locking the remote repository failed')) - elif resp != 0: - raise util.Abort(_('the server sent an unknown error code')) - - self.applystreamclone(remotereqs, rbranchmap, fp) - return len(self.heads()) + 1 - - def applystreamclone(self, remotereqs, remotebranchmap, fp): - """Apply stream clone data to this repository. - - "remotereqs" is a set of requirements to handle the incoming data. - "remotebranchmap" is the result of a branchmap lookup on the remote. It - can be None. - "fp" is a file object containing the raw stream data, suitable for - feeding into exchange.consumestreamclone. - """ - lock = self.lock() - try: - exchange.consumestreamclone(self, fp) - - # new requirements = old non-format requirements + - # new format-related remote requirements - # requirements from the streamed-in repository - self.requirements = remotereqs | ( - self.requirements - self.supportedformats) - self._applyopenerreqs() - self._writerequirements() - - if remotebranchmap: - rbheads = [] - closed = [] - for bheads in remotebranchmap.itervalues(): - rbheads.extend(bheads) - for h in bheads: - r = self.changelog.rev(h) - b, c = self.changelog.branchinfo(r) - if c: - closed.append(h) - - if rbheads: - rtiprev = max((int(self.changelog.rev(node)) - for node in rbheads)) - cache = branchmap.branchcache(remotebranchmap, - self[rtiprev].node(), - rtiprev, - closednodes=closed) - # Try to stick it as low as possible - # filter above served are unlikely to be fetch from a clone - for candidate in ('base', 'immutable', 'served'): - rview = self.filtered(candidate) - if cache.validfor(rview): - self._branchcaches[candidate] = cache - cache.write(rview) - break - self.invalidate() - finally: - lock.release() - def clone(self, remote, heads=[], stream=None): '''clone remote repository. keyword arguments: heads: list of revs to clone (forces use of pull) stream: use streaming clone if possible''' - - # now, all clients that can request uncompressed clones can - # read repo formats supported by all servers that can serve - # them. - - # if revlog format changes, client will have to check version - # and format flags on "stream" capability, and use - # uncompressed only if compatible. - - if stream is None: - # if the server explicitly prefers to stream (for fast LANs) - stream = remote.capable('stream-preferred') - - if stream and not heads: - # 'stream' means remote revlog format is revlogv1 only - if remote.capable('stream'): - self.stream_in(remote, set(('revlogv1',))) - else: - # otherwise, 'streamreqs' contains the remote revlog format - streamreqs = remote.capable('streamreqs') - if streamreqs: - streamreqs = set(streamreqs.split(',')) - # if we support it, stream in and adjust our requirements - if not streamreqs - self.supportedformats: - self.stream_in(remote, streamreqs) - # internal config: ui.quietbookmarkmove quiet = self.ui.backupconfig('ui', 'quietbookmarkmove') try: self.ui.setconfig('ui', 'quietbookmarkmove', True, 'clone') - ret = exchange.pull(self, remote, heads).cgresult + pullop = exchange.pull(self, remote, heads, + streamclonerequested=stream) + return pullop.cgresult finally: self.ui.restoreconfig(quiet) - return ret def pushkey(self, namespace, key, old, new): try:
--- a/mercurial/lock.py Mon Oct 05 10:43:16 2015 -0600 +++ b/mercurial/lock.py Wed Oct 07 13:44:48 2015 -0500 @@ -5,10 +5,20 @@ # This software may be used and distributed according to the terms of the # GNU General Public License version 2 or any later version. -import util, error -import errno, os, socket, time +from __future__ import absolute_import + +import contextlib +import errno +import os +import socket +import time import warnings +from . import ( + error, + util, +) + class lock(object): '''An advisory lock held by one process to control access to a set of files. Non-cooperating processes or incorrectly written scripts @@ -29,16 +39,24 @@ _host = None - def __init__(self, vfs, file, timeout=-1, releasefn=None, desc=None): + def __init__(self, vfs, file, timeout=-1, releasefn=None, acquirefn=None, + desc=None, inheritchecker=None, parentlock=None): self.vfs = vfs self.f = file self.held = 0 self.timeout = timeout self.releasefn = releasefn + self.acquirefn = acquirefn self.desc = desc + self._inheritchecker = inheritchecker + self.parentlock = parentlock + self._parentheld = False + self._inherited = False self.postrelease = [] - self.pid = os.getpid() + self.pid = self._getpid() self.delay = self.lock() + if self.acquirefn: + self.acquirefn() def __del__(self): if self.held: @@ -52,11 +70,15 @@ self.release() + def _getpid(self): + # wrapper around os.getpid() to make testing easier + return os.getpid() + def lock(self): timeout = self.timeout while True: try: - self.trylock() + self._trylock() return self.timeout - timeout except error.LockHeld as inst: if timeout != 0: @@ -67,20 +89,31 @@ raise error.LockHeld(errno.ETIMEDOUT, inst.filename, self.desc, inst.locker) - def trylock(self): + def _trylock(self): if self.held: self.held += 1 return if lock._host is None: lock._host = socket.gethostname() lockname = '%s:%s' % (lock._host, self.pid) - while not self.held: + retry = 5 + while not self.held and retry: + retry -= 1 try: self.vfs.makelock(lockname, self.f) self.held = 1 except (OSError, IOError) as why: if why.errno == errno.EEXIST: - locker = self.testlock() + locker = self._readlock() + # special case where a parent process holds the lock -- this + # is different from the pid being different because we do + # want the unlock and postrelease functions to be called, + # but the lockfile to not be removed. + if locker == self.parentlock: + self._parentheld = True + self.held = 1 + return + locker = self._testlock(locker) if locker is not None: raise error.LockHeld(errno.EAGAIN, self.vfs.join(self.f), self.desc, @@ -89,23 +122,22 @@ raise error.LockUnavailable(why.errno, why.strerror, why.filename, self.desc) - def testlock(self): - """return id of locker if lock is valid, else None. + def _readlock(self): + """read lock and return its value - If old-style lock, we cannot tell what machine locker is on. - with new-style lock, if locker is on this machine, we can - see if locker is alive. If locker is on this machine but - not alive, we can safely break lock. - - The lock file is only deleted when None is returned. - + Returns None if no lock exists, pid for old-style locks, and host:pid + for new-style locks. """ try: - locker = self.vfs.readlock(self.f) + return self.vfs.readlock(self.f) except (OSError, IOError) as why: if why.errno == errno.ENOENT: return None raise + + def _testlock(self, locker): + if locker is None: + return None try: host, pid = locker.split(":", 1) except ValueError: @@ -127,6 +159,50 @@ except error.LockError: return locker + def testlock(self): + """return id of locker if lock is valid, else None. + + If old-style lock, we cannot tell what machine locker is on. + with new-style lock, if locker is on this machine, we can + see if locker is alive. If locker is on this machine but + not alive, we can safely break lock. + + The lock file is only deleted when None is returned. + + """ + locker = self._readlock() + return self._testlock(locker) + + @contextlib.contextmanager + def inherit(self): + """context for the lock to be inherited by a Mercurial subprocess. + + Yields a string that will be recognized by the lock in the subprocess. + Communicating this string to the subprocess needs to be done separately + -- typically by an environment variable. + """ + if not self.held: + raise error.LockInheritanceContractViolation( + 'inherit can only be called while lock is held') + if self._inherited: + raise error.LockInheritanceContractViolation( + 'inherit cannot be called while lock is already inherited') + if self._inheritchecker is not None: + self._inheritchecker() + if self.releasefn: + self.releasefn() + if self._parentheld: + lockname = self.parentlock + else: + lockname = '%s:%s' % (lock._host, self.pid) + self._inherited = True + try: + yield lockname + finally: + if self.acquirefn: + self.acquirefn() + self._inherited = False + def release(self): """release the lock and execute callback function if any @@ -136,19 +212,23 @@ self.held -= 1 elif self.held == 1: self.held = 0 - if os.getpid() != self.pid: + if self._getpid() != self.pid: # we forked, and are not the parent return try: if self.releasefn: self.releasefn() finally: - try: - self.vfs.unlink(self.f) - except OSError: - pass - for callback in self.postrelease: - callback() + if not self._parentheld: + try: + self.vfs.unlink(self.f) + except OSError: + pass + # The postrelease functions typically assume the lock is not held + # at all. + if not self._parentheld: + for callback in self.postrelease: + callback() def release(*locks): for lock in locks:
--- a/mercurial/mail.py Mon Oct 05 10:43:16 2015 -0600 +++ b/mercurial/mail.py Wed Oct 07 13:44:48 2015 -0500 @@ -5,10 +5,22 @@ # This software may be used and distributed according to the terms of the # GNU General Public License version 2 or any later version. -from i18n import _ -import util, encoding, sslutil -import os, smtplib, socket, quopri, time, sys +from __future__ import absolute_import + import email +import os +import quopri +import smtplib +import socket +import sys +import time + +from .i18n import _ +from . import ( + encoding, + sslutil, + util, +) _oldheaderinit = email.Header.Header.__init__ def _unifiedheaderinit(self, *args, **kw): @@ -19,10 +31,10 @@ constructor, and 2.7 removed this parameter. Default argument is continuation_ws=' ', which means that the - behaviour is different in <2.7 and 2.7 + behavior is different in <2.7 and 2.7 - We consider the 2.7 behaviour to be preferable, but need - to have an unified behaviour for versions 2.4 to 2.7 + We consider the 2.7 behavior to be preferable, but need + to have an unified behavior for versions 2.4 to 2.7 """ # override continuation_ws kw['continuation_ws'] = ' '
--- a/mercurial/manifest.py Mon Oct 05 10:43:16 2015 -0600 +++ b/mercurial/manifest.py Wed Oct 07 13:44:48 2015 -0500 @@ -9,6 +9,7 @@ import mdiff, parsers, error, revlog, util import array, struct import os +import heapq propertycache = util.propertycache @@ -441,13 +442,14 @@ else: return '', f -_noop = lambda: None +_noop = lambda s: None class treemanifest(object): def __init__(self, dir='', text=''): self._dir = dir self._node = revlog.nullid - self._load = _noop + self._loadfunc = _noop + self._copyfunc = _noop self._dirty = False self._dirs = {} # Using _lazymanifest here is a little slower than plain old dicts @@ -475,11 +477,11 @@ return (not self._files and (not self._dirs or all(m._isempty() for m in self._dirs.values()))) - def __str__(self): - return ('<treemanifest dir=%s, node=%s, loaded=%s, dirty=%s>' % + def __repr__(self): + return ('<treemanifest dir=%s, node=%s, loaded=%s, dirty=%s at 0x%x>' % (self._dir, revlog.hex(self._node), - bool(self._load is _noop), - self._dirty)) + bool(self._loadfunc is _noop), + self._dirty, id(self))) def dir(self): '''The directory that this tree manifest represents, including a @@ -597,6 +599,14 @@ self._files[f] = n[:21] # to match manifestdict's behavior self._dirty = True + def _load(self): + if self._loadfunc is not _noop: + lf, self._loadfunc = self._loadfunc, _noop + lf(self) + elif self._copyfunc is not _noop: + cf, self._copyfunc = self._copyfunc, _noop + cf(self) + def setflag(self, f, flags): """Set the flags (symlink, executable) for path f.""" assert 'd' not in flags @@ -614,19 +624,19 @@ copy = treemanifest(self._dir) copy._node = self._node copy._dirty = self._dirty - def _load(): - self._load() - for d in self._dirs: - copy._dirs[d] = self._dirs[d].copy() - copy._files = dict.copy(self._files) - copy._flags = dict.copy(self._flags) - copy._load = _noop - copy._load = _load - if self._load == _noop: - # Chaining _load if it's _noop is functionally correct, but the - # chain may end up excessively long (stack overflow), and - # will prevent garbage collection of 'self'. - copy._load() + if self._copyfunc is _noop: + def _copyfunc(s): + self._load() + for d in self._dirs: + s._dirs[d] = self._dirs[d].copy() + s._files = dict.copy(self._files) + s._flags = dict.copy(self._flags) + if self._loadfunc is _noop: + _copyfunc(copy) + else: + copy._copyfunc = _copyfunc + else: + copy._copyfunc = self._copyfunc return copy def filesnotin(self, m2): @@ -833,13 +843,10 @@ return _text(sorted(dirs + files), usemanifestv2) def read(self, gettext, readsubtree): - def _load(): - # Mark as loaded already here, so __setitem__ and setflag() don't - # cause infinite loops when they try to load. - self._load = _noop - self.parse(gettext(), readsubtree) - self._dirty = False - self._load = _load + def _load_for_read(s): + s.parse(gettext(), readsubtree) + s._dirty = False + self._loadfunc = _load_for_read def writesubtrees(self, m1, m2, writesubtree): self._load() # for consistency; should never have any effect here @@ -970,12 +977,9 @@ # revlog layer. _checkforbidden(added) - # combine the changed lists into one list for sorting - work = [(x, False) for x in added] - work.extend((x, True) for x in removed) - # this could use heapq.merge() (from Python 2.6+) or equivalent - # since the lists are already sorted - work.sort() + # combine the changed lists into one sorted iterator + work = heapq.merge([(x, False) for x in added], + [(x, True) for x in removed]) arraytext, deltatext = m.fastdelta(self._mancache[p1][1], work) cachedelta = self.rev(p1), deltatext
--- a/mercurial/match.py Mon Oct 05 10:43:16 2015 -0600 +++ b/mercurial/match.py Wed Oct 07 13:44:48 2015 -0500 @@ -5,9 +5,17 @@ # This software may be used and distributed according to the terms of the # GNU General Public License version 2 or any later version. -import copy, os, re -import util, pathutil -from i18n import _ +from __future__ import absolute_import + +import copy +import os +import re + +from .i18n import _ +from . import ( + pathutil, + util, +) propertycache = util.propertycache
--- a/mercurial/merge.py Mon Oct 05 10:43:16 2015 -0600 +++ b/mercurial/merge.py Wed Oct 07 13:44:48 2015 -0500 @@ -5,13 +5,28 @@ # 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 errno +import os +import shutil import struct -from node import nullid, nullrev, hex, bin -from i18n import _ -from mercurial import obsolete -import error as errormod, util, filemerge, copies, subrepo, worker -import errno, os, shutil +from .i18n import _ +from .node import ( + bin, + hex, + nullid, + nullrev, +) +from . import ( + copies, + filemerge, + obsolete, + subrepo, + util, + worker, +) _pack = struct.pack _unpack = struct.unpack @@ -27,7 +42,7 @@ it is stored on disk when needed. Two file are used, one with an old format, one with a new format. Both contains similar data, but the new - format can store new kind of field. + format can store new kinds of field. Current new format is a list of arbitrary record of the form: @@ -102,6 +117,25 @@ returns list of record [(TYPE, data), ...]""" v1records = self._readrecordsv1() v2records = self._readrecordsv2() + if self._v1v2match(v1records, v2records): + return v2records + else: + # v1 file is newer than v2 file, use it + # we have to infer the "other" changeset of the merge + # we cannot do better than that with v1 of the format + mctx = self._repo[None].parents()[-1] + v1records.append(('O', mctx.hex())) + # add place holder "other" file node information + # nobody is using it yet so we do no need to fetch the data + # if mctx was wrong `mctx[bits[-2]]` may fails. + for idx, r in enumerate(v1records): + if r[0] == 'F': + bits = r[1].split('\0') + bits.insert(-2, '') + v1records[idx] = (r[0], '\0'.join(bits)) + return v1records + + def _v1v2match(self, v1records, v2records): oldv2 = set() # old format version of v2 record for rec in v2records: if rec[0] == 'L': @@ -111,22 +145,9 @@ oldv2.add(('F', _droponode(rec[1]))) for rec in v1records: if rec not in oldv2: - # v1 file is newer than v2 file, use it - # we have to infer the "other" changeset of the merge - # we cannot do better than that with v1 of the format - mctx = self._repo[None].parents()[-1] - v1records.append(('O', mctx.hex())) - # add place holder "other" file node information - # nobody is using it yet so we do no need to fetch the data - # if mctx was wrong `mctx[bits[-2]]` may fails. - for idx, r in enumerate(v1records): - if r[0] == 'F': - bits = r[1].split('\0') - bits.insert(-2, '') - v1records[idx] = (r[0], '\0'.join(bits)) - return v1records + return False else: - return v2records + return True def _readrecordsv1(self): """read on disk merge state for version 1 file @@ -581,10 +602,14 @@ repo, wctx, mctx, ancestor, branchmerge, force, partial, acceptremote, followcopies) _checkunknownfiles(repo, wctx, mctx, force, actions) - if diverge is None: # and renamedelete is None. - # Arbitrarily pick warnings from first iteration + + # Track the shortest set of warning on the theory that bid + # merge will correctly incorporate more information + if diverge is None or len(diverge1) < len(diverge): diverge = diverge1 + if renamedelete is None or len(renamedelete) < len(renamedelete1): renamedelete = renamedelete1 + for f, a in sorted(actions.iteritems()): m, args, msg = a repo.ui.debug(' %s: %s -> %s\n' % (f, msg, m)) @@ -779,25 +804,6 @@ repo.ui.debug(" %s: %s -> k\n" % (f, msg)) # no progress - # merge - for f, args, msg in actions['m']: - repo.ui.debug(" %s: %s -> m\n" % (f, msg)) - z += 1 - progress(_updating, z, item=f, total=numupdates, unit=_files) - if f == '.hgsubstate': # subrepo states need updating - subrepo.submerge(repo, wctx, mctx, wctx.ancestor(mctx), - overwrite) - continue - audit(f) - r = ms.resolve(f, wctx, labels=labels) - if r is not None and r > 0: - unresolved += 1 - else: - if r is None: - updated += 1 - else: - merged += 1 - # directory rename, move local for f, args, msg in actions['dm']: repo.ui.debug(" %s: %s -> dm\n" % (f, msg)) @@ -830,6 +836,25 @@ util.setflags(repo.wjoin(f), 'l' in flags, 'x' in flags) updated += 1 + # merge + for f, args, msg in actions['m']: + repo.ui.debug(" %s: %s -> m\n" % (f, msg)) + z += 1 + progress(_updating, z, item=f, total=numupdates, unit=_files) + if f == '.hgsubstate': # subrepo states need updating + subrepo.submerge(repo, wctx, mctx, wctx.ancestor(mctx), + overwrite) + continue + audit(f) + r = ms.resolve(f, wctx, labels=labels) + if r is not None and r > 0: + unresolved += 1 + else: + if r is None: + updated += 1 + else: + merged += 1 + ms.commit() progress(_updating, None, total=numupdates, unit=_files) @@ -969,42 +994,10 @@ pas = [repo[ancestor]] if node is None: - # Here is where we should consider bookmarks, divergent bookmarks, - # foreground changesets (successors), and tip of current branch; - # but currently we are only checking the branch tips. - try: - node = repo.branchtip(wc.branch()) - except errormod.RepoLookupError: - if wc.branch() == 'default': # no default branch! - node = repo.lookup('tip') # update to tip - else: - raise util.Abort(_("branch %s not found") % wc.branch()) - - if p1.obsolete() and not p1.children(): - # allow updating to successors - successors = obsolete.successorssets(repo, p1.node()) - - # behavior of certain cases is as follows, - # - # divergent changesets: update to highest rev, similar to what - # is currently done when there are more than one head - # (i.e. 'tip') - # - # replaced changesets: same as divergent except we know there - # is no conflict - # - # pruned changeset: no update is done; though, we could - # consider updating to the first non-obsolete parent, - # similar to what is current done for 'hg prune' - - if successors: - # flatten the list here handles both divergent (len > 1) - # and the usual case (len = 1) - successors = [n for sub in successors for n in sub] - - # get the max revision for the given successors set, - # i.e. the 'tip' of a set - node = repo.revs('max(%ln)', successors).first() + nodes = list(repo.set('_updatedefaultdest()')) + if nodes: + node = nodes[0].node() + if p1.obsolete() and not p1.children(): pas = [p1] overwrite = force and not branchmerge
--- a/mercurial/minirst.py Mon Oct 05 10:43:16 2015 -0600 +++ b/mercurial/minirst.py Wed Oct 07 13:44:48 2015 -0500 @@ -14,15 +14,20 @@ are just indented blocks that look like they are nested. This relies on the user to keep the right indentation for the blocks. -Remember to update http://mercurial.selenic.com/wiki/HelpStyleGuide +Remember to update https://mercurial-scm.org/wiki/HelpStyleGuide when adding support for new constructs. """ -import re -import util, encoding -from i18n import _ +from __future__ import absolute_import import cgi +import re + +from .i18n import _ +from . import ( + encoding, + util, +) def section(s): return "%s\n%s\n\n" % (s, "\"" * encoding.colwidth(s)) @@ -651,13 +656,17 @@ def format(text, width=80, indent=0, keep=None, style='plain', section=None): """Parse and format the text according to width.""" blocks, pruned = parse(text, indent, keep or []) + parents = [] if section: sections = getsections(blocks) blocks = [] i = 0 while i < len(sections): name, nest, b = sections[i] + del parents[nest:] + parents.append(name) if name == section: + b[0]['path'] = parents[3:] blocks.extend(b) ## Also show all subnested sections @@ -669,6 +678,14 @@ if style == 'html': text = formathtml(blocks) else: + if len([b for b in blocks if b['type'] == 'definition']) > 1: + i = 0 + while i < len(blocks): + if blocks[i]['type'] == 'definition': + if 'path' in blocks[i]: + blocks[i]['lines'][0] = '"%s"' % '.'.join( + blocks[i]['path']) + i += 1 text = ''.join(formatblock(b, width) for b in blocks) if keep is None: return text @@ -705,11 +722,43 @@ nest += i level = nest.index(i) + 1 nest = nest[:level] + for i in range(1, len(secs) + 1): + sec = secs[-i] + if sec[1] < level: + break + siblings = [a for a in sec[2] if a['type'] == 'definition'] + if siblings: + siblingindent = siblings[-1]['indent'] + indent = b['indent'] + if siblingindent < indent: + level += 1 + break + elif siblingindent == indent: + level = sec[1] + break secs.append((getname(b), level, [b])) else: if not secs: # add an initial empty section secs = [('', 0, [])] + if b['type'] != 'margin': + pointer = 1 + bindent = b['indent'] + while pointer < len(secs): + section = secs[-pointer][2][0] + if section['type'] != 'margin': + sindent = section['indent'] + if len(section['lines']) > 1: + sindent += len(section['lines'][1]) - \ + len(section['lines'][1].lstrip(' ')) + if bindent >= sindent: + break + pointer += 1 + if pointer > 1: + blevel = secs[-pointer][1] + if section['type'] != b['type']: + blevel += 1 + secs.append(('', blevel, [])) secs[-1][2].append(b) return secs
--- a/mercurial/namespaces.py Mon Oct 05 10:43:16 2015 -0600 +++ b/mercurial/namespaces.py Wed Oct 07 13:44:48 2015 -0500 @@ -1,6 +1,10 @@ -from i18n import _ -from mercurial import util -import templatekw +from __future__ import absolute_import + +from .i18n import _ +from . import ( + templatekw, + util, +) def tolist(val): """
--- a/mercurial/node.py Mon Oct 05 10:43:16 2015 -0600 +++ b/mercurial/node.py Wed Oct 07 13:44:48 2015 -0500 @@ -5,6 +5,8 @@ # 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 binascii nullrev = -1
--- a/mercurial/obsolete.py Mon Oct 05 10:43:16 2015 -0600 +++ b/mercurial/obsolete.py Wed Oct 07 13:44:48 2015 -0500 @@ -67,7 +67,7 @@ comment associated with each format for details. """ -import struct +import errno, struct import util, base85, node, parsers import phases from i18n import _ @@ -520,14 +520,9 @@ def __init__(self, svfs, defaultformat=_fm1version, readonly=False): # caches for various obsolescence related cache self.caches = {} - self._all = [] self.svfs = svfs - data = svfs.tryread('obsstore') self._version = defaultformat self._readonly = readonly - if data: - self._version, markers = _readmarkers(data) - self._addmarkers(markers) def __iter__(self): return iter(self._all) @@ -536,6 +531,15 @@ return len(self._all) def __nonzero__(self): + if not self._cached('_all'): + try: + return self.svfs.stat('obsstore').st_size > 1 + except OSError as inst: + if inst.errno != errno.ENOENT: + raise + # just build an empty _all list if no obsstore exists, which + # avoids further stat() syscalls + pass return bool(self._all) def create(self, transaction, prec, succs=(), flag=0, parents=None, @@ -615,6 +619,16 @@ return self.add(transaction, markers) @propertycache + def _all(self): + data = self.svfs.tryread('obsstore') + if not data: + return [] + self._version, markers = _readmarkers(data) + markers = list(markers) + _checkinvalidmarkers(markers) + return markers + + @propertycache def successors(self): successors = {} _addsuccessors(successors, self._all) @@ -841,15 +855,15 @@ def successorssets(repo, initialnode, cache=None): - """Return all set of successors of initial nodes + """Return set of all latest successors of initial nodes - The successors set of a changeset A are a group of revisions that succeed + The successors set of a changeset A are the group of revisions that succeed A. It succeeds A as a consistent whole, each revision being only a partial replacement. The successors set contains non-obsolete changesets only. This function returns the full list of successor sets which is why it returns a list of tuples and not just a single tuple. Each tuple is a valid - successors set. Not that (A,) may be a valid successors set for changeset A + successors set. Note that (A,) may be a valid successors set for changeset A (see below). In most cases, a changeset A will have a single element (e.g. the changeset @@ -865,7 +879,7 @@ If a changeset A is not obsolete, then it will conceptually have no successors set. To distinguish this from a pruned changeset, the successor - set will only contain itself, i.e. [(A,)]. + set will contain itself only, i.e. [(A,)]. Finally, successors unknown locally are considered to be pruned (obsoleted without any successors). @@ -873,10 +887,9 @@ The optional `cache` parameter is a dictionary that may contain precomputed successors sets. It is meant to reuse the computation of a previous call to `successorssets` when multiple calls are made at the same time. The cache - dictionary is updated in place. The caller is responsible for its live - spawn. Code that makes multiple calls to `successorssets` *must* use this - cache mechanism or suffer terrible performances. - + dictionary is updated in place. The caller is responsible for its life + span. Code that makes multiple calls to `successorssets` *must* use this + cache mechanism or suffer terrible performance. """ succmarkers = repo.obsstore.successors @@ -1046,16 +1059,6 @@ cache[current] = final return cache[initialnode] -def _knownrevs(repo, nodes): - """yield revision numbers of known nodes passed in parameters - - Unknown revisions are silently ignored.""" - torev = repo.changelog.nodemap.get - for n in nodes: - rev = torev(n) - if rev is not None: - yield rev - # mapping of 'set-name' -> <function to compute this set> cachefuncs = {} def cachefor(name):
--- a/mercurial/parser.py Mon Oct 05 10:43:16 2015 -0600 +++ b/mercurial/parser.py Wed Oct 07 13:44:48 2015 -0500 @@ -16,8 +16,10 @@ # an action is a tree node name, a tree label, and an optional match # __call__(program) parses program into a labeled tree -import error -from i18n import _ +from __future__ import absolute_import + +from .i18n import _ +from . import error class parser(object): def __init__(self, elements, methods=None): @@ -120,6 +122,13 @@ args[k] = x[2] return args +def unescapestr(s): + try: + return s.decode("string_escape") + except ValueError as e: + # mangle Python's exception into our format + raise error.ParseError(str(e).lower()) + def _prettyformat(tree, leafnodes, level, lines): if not isinstance(tree, tuple) or tree[0] in leafnodes: lines.append((level, str(tree)))
--- a/mercurial/parsers.c Mon Oct 05 10:43:16 2015 -0600 +++ b/mercurial/parsers.c Wed Oct 07 13:44:48 2015 -0500 @@ -253,8 +253,11 @@ if (normed == NULL) goto quit; - if (PyDict_SetItem(file_foldmap, normed, k) == -1) + if (PyDict_SetItem(file_foldmap, normed, k) == -1) { + Py_DECREF(normed); goto quit; + } + Py_DECREF(normed); } } return file_foldmap; @@ -475,14 +478,14 @@ &str, &readlen)) goto quit; - if (readlen < 0) - goto quit; - len = readlen; /* read parents */ - if (len < 40) + if (len < 40) { + PyErr_SetString( + PyExc_ValueError, "too little data for parents"); goto quit; + } parents = Py_BuildValue("s#s#", str, 20, str + 20, 20); if (!parents) @@ -679,7 +682,7 @@ } nodetree; /* - * This class has two behaviours. + * This class has two behaviors. * * When used in a list-like way (with integer keys), we decode an * entry in a RevlogNG index file on demand. Our last entry is a @@ -702,8 +705,8 @@ PyObject *headrevs; /* cache, invalidated on changes */ PyObject *filteredrevs;/* filtered revs set */ nodetree *nt; /* base-16 trie */ - int ntlength; /* # nodes in use */ - int ntcapacity; /* # nodes allocated */ + unsigned ntlength; /* # nodes in use */ + unsigned ntcapacity; /* # nodes allocated */ int ntdepth; /* maximum depth of tree */ int ntsplits; /* # splits performed */ int ntrev; /* last rev scanned */ @@ -1043,13 +1046,12 @@ return newlist; } -/* arg should be Py_ssize_t but Python 2.4 do not support the n format */ -static int check_filter(PyObject *filter, unsigned long arg) { +static int check_filter(PyObject *filter, Py_ssize_t arg) { if (filter) { PyObject *arglist, *result; int isfiltered; - arglist = Py_BuildValue("(k)", arg); + arglist = Py_BuildValue("(n)", arg); if (!arglist) { return -1; } @@ -1105,6 +1107,162 @@ phases[i] = phases[parent_2]; } +static PyObject *reachableroots2(indexObject *self, PyObject *args) +{ + + /* Input */ + long minroot; + PyObject *includepatharg = NULL; + int includepath = 0; + /* heads and roots are lists */ + PyObject *heads = NULL; + PyObject *roots = NULL; + PyObject *reachable = NULL; + + PyObject *val; + Py_ssize_t len = index_length(self) - 1; + long revnum; + Py_ssize_t k; + Py_ssize_t i; + Py_ssize_t l; + int r; + int parents[2]; + + /* Internal data structure: + * tovisit: array of length len+1 (all revs + nullrev), filled upto lentovisit + * revstates: array of length len+1 (all revs + nullrev) */ + int *tovisit = NULL; + long lentovisit = 0; + enum { RS_SEEN = 1, RS_ROOT = 2, RS_REACHABLE = 4 }; + char *revstates = NULL; + + /* Get arguments */ + if (!PyArg_ParseTuple(args, "lO!O!O!", &minroot, &PyList_Type, &heads, + &PyList_Type, &roots, + &PyBool_Type, &includepatharg)) + goto bail; + + if (includepatharg == Py_True) + includepath = 1; + + /* Initialize return set */ + reachable = PyList_New(0); + if (reachable == NULL) + goto bail; + + /* Initialize internal datastructures */ + tovisit = (int *)malloc((len + 1) * sizeof(int)); + if (tovisit == NULL) { + PyErr_NoMemory(); + goto bail; + } + + revstates = (char *)calloc(len + 1, 1); + if (revstates == NULL) { + PyErr_NoMemory(); + goto bail; + } + + l = PyList_GET_SIZE(roots); + for (i = 0; i < l; i++) { + revnum = PyInt_AsLong(PyList_GET_ITEM(roots, i)); + if (revnum == -1 && PyErr_Occurred()) + goto bail; + /* If root is out of range, e.g. wdir(), it must be unreachable + * from heads. So we can just ignore it. */ + if (revnum + 1 < 0 || revnum + 1 >= len + 1) + continue; + revstates[revnum + 1] |= RS_ROOT; + } + + /* Populate tovisit with all the heads */ + l = PyList_GET_SIZE(heads); + for (i = 0; i < l; i++) { + revnum = PyInt_AsLong(PyList_GET_ITEM(heads, i)); + if (revnum == -1 && PyErr_Occurred()) + goto bail; + if (revnum + 1 < 0 || revnum + 1 >= len + 1) { + PyErr_SetString(PyExc_IndexError, "head out of range"); + goto bail; + } + if (!(revstates[revnum + 1] & RS_SEEN)) { + tovisit[lentovisit++] = (int)revnum; + revstates[revnum + 1] |= RS_SEEN; + } + } + + /* Visit the tovisit list and find the reachable roots */ + k = 0; + while (k < lentovisit) { + /* Add the node to reachable if it is a root*/ + revnum = tovisit[k++]; + if (revstates[revnum + 1] & RS_ROOT) { + revstates[revnum + 1] |= RS_REACHABLE; + val = PyInt_FromLong(revnum); + if (val == NULL) + goto bail; + r = PyList_Append(reachable, val); + Py_DECREF(val); + if (r < 0) + goto bail; + if (includepath == 0) + continue; + } + + /* Add its parents to the list of nodes to visit */ + if (revnum == -1) + continue; + r = index_get_parents(self, revnum, parents, (int)len - 1); + if (r < 0) + goto bail; + for (i = 0; i < 2; i++) { + if (!(revstates[parents[i] + 1] & RS_SEEN) + && parents[i] >= minroot) { + tovisit[lentovisit++] = parents[i]; + revstates[parents[i] + 1] |= RS_SEEN; + } + } + } + + /* Find all the nodes in between the roots we found and the heads + * and add them to the reachable set */ + if (includepath == 1) { + long minidx = minroot; + if (minidx < 0) + minidx = 0; + for (i = minidx; i < len; i++) { + if (!(revstates[i + 1] & RS_SEEN)) + continue; + r = index_get_parents(self, i, parents, (int)len - 1); + /* Corrupted index file, error is set from + * index_get_parents */ + if (r < 0) + goto bail; + if (((revstates[parents[0] + 1] | + revstates[parents[1] + 1]) & RS_REACHABLE) + && !(revstates[i + 1] & RS_REACHABLE)) { + revstates[i + 1] |= RS_REACHABLE; + val = PyInt_FromLong(i); + if (val == NULL) + goto bail; + r = PyList_Append(reachable, val); + Py_DECREF(val); + if (r < 0) + goto bail; + } + } + } + + free(revstates); + free(tovisit); + return reachable; +bail: + Py_XDECREF(reachable); + free(revstates); + free(tovisit); + return NULL; +} + static PyObject *compute_phases_map_sets(indexObject *self, PyObject *args) { PyObject *roots = Py_None; @@ -2011,16 +2169,18 @@ */ static PyObject *index_ancestors(indexObject *self, PyObject *args) { + PyObject *ret; PyObject *gca = index_commonancestorsheads(self, args); if (gca == NULL) return NULL; if (PyList_GET_SIZE(gca) <= 1) { - Py_INCREF(gca); return gca; } - return find_deepest(self, gca); + ret = find_deepest(self, gca); + Py_DECREF(gca); + return ret; } /* @@ -2282,6 +2442,8 @@ "get an index entry"}, {"computephasesmapsets", (PyCFunction)compute_phases_map_sets, METH_VARARGS, "compute phases"}, + {"reachableroots2", (PyCFunction)reachableroots2, METH_VARARGS, + "reachableroots"}, {"headrevs", (PyCFunction)index_headrevs, METH_VARARGS, "get head revisions"}, /* Can do filtering since 3.2 */ {"headrevsfiltered", (PyCFunction)index_headrevs, METH_VARARGS, @@ -2402,7 +2564,7 @@ Py_DECREF(list); return NULL; } - PyTuple_SetItem(list, i, hash); + PyTuple_SET_ITEM(list, i, hash); source += hashwidth; } return list; @@ -2474,18 +2636,16 @@ metasize = (unsigned char)(*data++); right = PyString_FromStringAndSize(meta, metasize); meta += metasize; - if (!left || !right) { + tmp = PyTuple_New(2); + if (!left || !right || !tmp) { Py_XDECREF(left); Py_XDECREF(right); + Py_XDECREF(tmp); goto bail; } - tmp = PyTuple_Pack(2, left, right); - Py_DECREF(left); - Py_DECREF(right); - if (!tmp) { - goto bail; - } - PyTuple_SetItem(metadata, i, tmp); + PyTuple_SET_ITEM(tmp, 0, left); + PyTuple_SET_ITEM(tmp, 1, right); + PyTuple_SET_ITEM(metadata, i, tmp); } ret = Py_BuildValue("(OOHO(di)O)", prec, succs, flags, metadata, mtime, (int)tz * 60, parents); @@ -2502,12 +2662,10 @@ static PyObject *fm1readmarkers(PyObject *self, PyObject *args) { const char *data; Py_ssize_t datalen; - /* only unsigned long because python 2.4, should be Py_ssize_t */ - unsigned long offset, stop; + Py_ssize_t offset, stop; PyObject *markers = NULL; - /* replace kk with nn when we drop Python 2.4 */ - if (!PyArg_ParseTuple(args, "s#kk", &data, &datalen, &offset, &stop)) { + if (!PyArg_ParseTuple(args, "s#nn", &data, &datalen, &offset, &stop)) { return NULL; } data += offset;
--- a/mercurial/patch.py Mon Oct 05 10:43:16 2015 -0600 +++ b/mercurial/patch.py Wed Oct 07 13:44:48 2015 -0500 @@ -995,7 +995,7 @@ ui.write("\n") continue # Patch comment based on the Git one (based on comment at end of - # http://mercurial.selenic.com/wiki/RecordExtension) + # https://mercurial-scm.org/wiki/RecordExtension) phelp = '---' + _(""" To remove '-' lines, make them ' ' lines (context). To remove '+' lines, delete them.
--- a/mercurial/pathencode.c Mon Oct 05 10:43:16 2015 -0600 +++ b/mercurial/pathencode.c Wed Oct 07 13:44:48 2015 -0500 @@ -684,6 +684,8 @@ hashobj = PyObject_CallMethod(shaobj, "digest", ""); Py_DECREF(shaobj); + if (hashobj == NULL) + return -1; if (!PyString_Check(hashobj) || PyString_GET_SIZE(hashobj) != 20) { PyErr_SetString(PyExc_TypeError,
--- a/mercurial/pathutil.py Mon Oct 05 10:43:16 2015 -0600 +++ b/mercurial/pathutil.py Wed Oct 07 13:44:48 2015 -0500 @@ -1,8 +1,15 @@ -import os, errno, stat, posixpath +from __future__ import absolute_import -import encoding -import util -from i18n import _ +import errno +import os +import posixpath +import stat + +from .i18n import _ +from . import ( + encoding, + util, +) def _lowerclean(s): return encoding.hfsignoreclean(s.lower())
--- a/mercurial/peer.py Mon Oct 05 10:43:16 2015 -0600 +++ b/mercurial/peer.py Wed Oct 07 13:44:48 2015 -0500 @@ -6,11 +6,91 @@ # This software may be used and distributed according to the terms of the # GNU General Public License version 2 or any later version. -from i18n import _ -import error +from __future__ import absolute_import + +from .i18n import _ +from . import ( + error, + util, +) + +# abstract batching support + +class future(object): + '''placeholder for a value to be set later''' + def set(self, value): + if util.safehasattr(self, 'value'): + raise error.RepoError("future is already set") + self.value = value + +class batcher(object): + '''base class for batches of commands submittable in a single request + + All methods invoked on instances of this class are simply queued and + return a a future for the result. Once you call submit(), all the queued + calls are performed and the results set in their respective futures. + ''' + def __init__(self): + self.calls = [] + def __getattr__(self, name): + def call(*args, **opts): + resref = future() + self.calls.append((name, args, opts, resref,)) + return resref + return call + def submit(self): + pass + +class localbatch(batcher): + '''performs the queued calls directly''' + def __init__(self, local): + batcher.__init__(self) + self.local = local + def submit(self): + for name, args, opts, resref in self.calls: + resref.set(getattr(self.local, name)(*args, **opts)) + +def batchable(f): + '''annotation for batchable methods + + Such methods must implement a coroutine as follows: + + @batchable + def sample(self, one, two=None): + # Handle locally computable results first: + if not one: + yield "a local result", None + # Build list of encoded arguments suitable for your wire protocol: + encargs = [('one', encode(one),), ('two', encode(two),)] + # Create future for injection of encoded result: + encresref = future() + # Return encoded arguments and future: + yield encargs, encresref + # Assuming the future to be filled with the result from the batched + # request now. Decode it: + yield decode(encresref.value) + + The decorator returns a function which wraps this coroutine as a plain + method, but adds the original method as an attribute called "batchable", + which is used by remotebatch to split the call into separate encoding and + decoding phases. + ''' + def plain(*args, **opts): + batchable = f(*args, **opts) + encargsorres, encresref = batchable.next() + if not encresref: + return encargsorres # a local result in this case + self = args[0] + encresref.set(self._submitone(f.func_name, encargsorres)) + return batchable.next() + setattr(plain, 'batchable', f) + return plain class peerrepository(object): + def batch(self): + return localbatch(self) + def capable(self, name): '''tell whether repo supports named capability. return False if not supported.
--- a/mercurial/phases.py Mon Oct 05 10:43:16 2015 -0600 +++ b/mercurial/phases.py Wed Oct 07 13:44:48 2015 -0500 @@ -100,11 +100,23 @@ """ -import os +from __future__ import absolute_import + import errno -from node import nullid, nullrev, bin, hex, short -from i18n import _ -import util, error +import os + +from .i18n import _ +from .node import ( + bin, + hex, + nullid, + nullrev, + short, +) +from . import ( + error, + util, +) allphases = public, draft, secret = range(3) trackedphases = allphases[1:]
--- a/mercurial/posix.py Mon Oct 05 10:43:16 2015 -0600 +++ b/mercurial/posix.py Wed Oct 07 13:44:48 2015 -0500 @@ -5,11 +5,26 @@ # This software may be used and distributed according to the terms of the # GNU General Public License version 2 or any later version. -from i18n import _ -import encoding -import os, sys, errno, stat, getpass, pwd, grp, socket, tempfile, unicodedata +from __future__ import absolute_import + +import errno +import fcntl +import getpass +import grp +import os +import pwd +import re import select -import fcntl, re +import socket +import stat +import sys +import tempfile +import unicodedata + +from .i18n import _ +from . import ( + encoding, +) posixfile = open normpath = os.path.normpath @@ -335,7 +350,7 @@ return '"%s"' % s global _needsshellquote if _needsshellquote is None: - _needsshellquote = re.compile(r'[^a-zA-Z0-9._/-]').search + _needsshellquote = re.compile(r'[^a-zA-Z0-9._/+-]').search if s and not _needsshellquote(s): # "s" shouldn't have to be quoted return s @@ -459,7 +474,8 @@ def termwidth(): try: - import termios, array + import array + import termios for dev in (sys.stderr, sys.stdout, sys.stdin): try: try:
--- a/mercurial/progress.py Mon Oct 05 10:43:16 2015 -0600 +++ b/mercurial/progress.py Wed Oct 07 13:44:48 2015 -0500 @@ -5,13 +5,14 @@ # 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 sys -import time import threading -from mercurial import encoding +import time -from mercurial.i18n import _ - +from .i18n import _ +from . import encoding def spacejoin(*args): return ' '.join(s for s in args if s) @@ -165,6 +166,9 @@ if not shouldprint(self.ui): return sys.stderr.write('\r%s\r' % (' ' * self.width())) + if self.printed: + # force immediate re-paint of progress bar + self.lastprint = 0 def complete(self): if not shouldprint(self.ui):
--- a/mercurial/pushkey.py Mon Oct 05 10:43:16 2015 -0600 +++ b/mercurial/pushkey.py Wed Oct 07 13:44:48 2015 -0500 @@ -5,7 +5,14 @@ # This software may be used and distributed according to the terms of the # GNU General Public License version 2 or any later version. -import bookmarks, phases, obsolete, encoding +from __future__ import absolute_import + +from . import ( + bookmarks, + encoding, + obsolete, + phases, +) def _nslist(repo): n = {}
--- a/mercurial/repair.py Mon Oct 05 10:43:16 2015 -0600 +++ b/mercurial/repair.py Wed Oct 07 13:44:48 2015 -0500 @@ -6,25 +6,24 @@ # This software may be used and distributed according to the terms of the # GNU General Public License version 2 or any later version. -from mercurial import changegroup, exchange, util, bundle2 -from mercurial.node import short -from mercurial.i18n import _ +from __future__ import absolute_import + import errno +from .i18n import _ +from .node import short +from . import ( + bundle2, + changegroup, + exchange, + util, +) + def _bundle(repo, bases, heads, node, suffix, compress=True): """create a bundle with the specified revisions as a backup""" - usebundle2 = (repo.ui.configbool('experimental', 'bundle2-exp', True) and - repo.ui.config('experimental', 'strip-bundle2-version')) - if usebundle2: - cgversion = repo.ui.config('experimental', 'strip-bundle2-version') - if cgversion not in changegroup.packermap: - repo.ui.warn(_('unknown strip-bundle2-version value %r; ' - 'should be one of %r\n') % - (cgversion, sorted(changegroup.packermap.keys()),)) - cgversion = '01' - usebundle2 = False - else: - cgversion = '01' + cgversion = '01' + if 'generaldelta' in repo.requirements: + cgversion = '02' cg = changegroup.changegroupsubset(repo, bases, heads, 'strip', version=cgversion) @@ -39,13 +38,17 @@ totalhash = util.sha1(''.join(allhashes)).hexdigest() name = "%s/%s-%s-%s.hg" % (backupdir, short(node), totalhash[:8], suffix) - if usebundle2: + comp = None + if cgversion != '01': bundletype = "HG20" + if compress: + comp = 'BZ' elif compress: bundletype = "HG10BZ" else: bundletype = "HG10UN" - return changegroup.writebundle(repo.ui, cg, name, bundletype, vfs) + return changegroup.writebundle(repo.ui, cg, name, bundletype, vfs, + compression=comp) def _collectfiles(repo, striprev): """find out the filelogs affected by the strip"""
--- a/mercurial/repoview.py Mon Oct 05 10:43:16 2015 -0600 +++ b/mercurial/repoview.py Wed Oct 07 13:44:48 2015 -0500 @@ -6,15 +6,20 @@ # This software may be used and distributed according to the terms of the # GNU General Public License version 2 or any later version. -import heapq +from __future__ import absolute_import + import copy -import error -import phases -import util -import obsolete +import heapq import struct -import tags as tagsmod -from node import nullrev + +from .node import nullrev +from . import ( + error, + obsolete, + phases, + tags as tagsmod, + util, +) def hideablerevs(repo): """Revisions candidates to be hidden
--- a/mercurial/revlog.py Mon Oct 05 10:43:16 2015 -0600 +++ b/mercurial/revlog.py Wed Oct 07 13:44:48 2015 -0500 @@ -100,11 +100,10 @@ # 4 bytes: compressed length # 4 bytes: base rev # 4 bytes: link rev -# 32 bytes: parent 1 nodeid -# 32 bytes: parent 2 nodeid -# 32 bytes: nodeid +# 20 bytes: parent 1 nodeid +# 20 bytes: parent 2 nodeid +# 20 bytes: nodeid indexformatv0 = ">4l20s20s20s" -v0shaoffset = 56 class revlogoldio(object): def __init__(self): @@ -150,7 +149,6 @@ # 4 bytes: parent 2 rev # 32 bytes: nodeid indexformatng = ">Qiiiiii20s12x" -ngshaoffset = 32 versionformat = ">I" # corresponds to uncompressed length of indexformatng (2 gigs, 4-byte @@ -212,6 +210,7 @@ self._chunkcache = (0, '') self._chunkcachesize = 65536 self._maxchainlen = None + self._aggressivemergedeltas = False self.index = [] self._pcache = {} self._nodecache = {nullid: nullrev} @@ -229,6 +228,8 @@ self._chunkcachesize = opts['chunkcachesize'] if 'maxchainlen' in opts: self._maxchainlen = opts['maxchainlen'] + if 'aggressivemergedeltas' in opts: + self._aggressivemergedeltas = opts['aggressivemergedeltas'] if self._chunkcachesize <= 0: raise RevlogError(_('revlog chunk cache size %r is not greater ' @@ -237,14 +238,14 @@ raise RevlogError(_('revlog chunk cache size %r is not a power ' 'of 2') % self._chunkcachesize) - i = '' + indexdata = '' self._initempty = True try: f = self.opener(self.indexfile) - i = f.read() + indexdata = f.read() f.close() - if len(i) > 0: - v = struct.unpack(versionformat, i[:4])[0] + if len(indexdata) > 0: + v = struct.unpack(versionformat, indexdata[:4])[0] self._initempty = False except IOError as inst: if inst.errno != errno.ENOENT: @@ -269,7 +270,7 @@ if self.version == REVLOGV0: self._io = revlogoldio() try: - d = self._io.parseindex(i, self._inline) + d = self._io.parseindex(indexdata, self._inline) except (ValueError, IndexError): raise RevlogError(_("index %s is corrupted") % (self.indexfile)) self.index, nodemap, self._chunkcache = d @@ -931,11 +932,23 @@ else: self._chunkcache = offset, data - def _loadchunk(self, offset, length): - if self._inline: - df = self.opener(self.indexfile) + def _loadchunk(self, offset, length, df=None): + """Load a chunk/segment from the revlog. + + Accepts absolute offset, length to read, and an optional existing + file handle to read from. + + If an existing file handle is passed, it will be seeked and the + original seek position will NOT be restored. + """ + if df is not None: + closehandle = False else: - df = self.opener(self.datafile) + if self._inline: + df = self.opener(self.indexfile) + else: + df = self.opener(self.datafile) + closehandle = True # Cache data both forward and backward around the requested # data, in a fixed size window. This helps speed up operations @@ -946,13 +959,14 @@ - realoffset) df.seek(realoffset) d = df.read(reallength) - df.close() + if closehandle: + df.close() self._addchunk(realoffset, d) if offset != realoffset or reallength != length: return util.buffer(d, offset - realoffset, length) return d - def _getchunk(self, offset, length): + def _getchunk(self, offset, length, df=None): o, d = self._chunkcache l = len(d) @@ -964,21 +978,21 @@ return d # avoid a copy return util.buffer(d, cachestart, cacheend - cachestart) - return self._loadchunk(offset, length) + return self._loadchunk(offset, length, df=df) - def _chunkraw(self, startrev, endrev): + def _chunkraw(self, startrev, endrev, df=None): start = self.start(startrev) end = self.end(endrev) if self._inline: start += (startrev + 1) * self._io.size end += (endrev + 1) * self._io.size length = end - start - return self._getchunk(start, length) + return self._getchunk(start, length, df=df) - def _chunk(self, rev): - return decompress(self._chunkraw(rev, rev)) + def _chunk(self, rev, df=None): + return decompress(self._chunkraw(rev, rev, df=df)) - def _chunks(self, revs): + def _chunks(self, revs, df=None): '''faster version of [self._chunk(rev) for rev in revs] Assumes that revs is in ascending order.''' @@ -998,14 +1012,14 @@ while True: # ensure that the cache doesn't change out from under us _cache = self._chunkcache - self._chunkraw(revs[0], revs[-1]) + self._chunkraw(revs[0], revs[-1], df=df) if _cache == self._chunkcache: break offset, data = _cache except OverflowError: # issue4215 - we can't cache a run of chunks greater than # 2G on Windows - return [self._chunk(rev) for rev in revs] + return [self._chunk(rev, df=df) for rev in revs] for rev in revs: chunkstart = start(rev) @@ -1037,9 +1051,12 @@ return mdiff.textdiff(self.revision(rev1), self.revision(rev2)) - def revision(self, nodeorrev): + def revision(self, nodeorrev, _df=None): """return an uncompressed revision of a given node or revision number. + + _df is an existing file handle to read from. It is meant to only be + used internally. """ if isinstance(nodeorrev, int): rev = nodeorrev @@ -1048,14 +1065,13 @@ node = nodeorrev rev = None - _cache = self._cache # grab local copy of cache to avoid thread race cachedrev = None if node == nullid: return "" - if _cache: - if _cache[0] == node: - return _cache[2] - cachedrev = _cache[1] + if self._cache: + if self._cache[0] == node: + return self._cache[2] + cachedrev = self._cache[1] # look up what we need to read text = None @@ -1083,7 +1099,7 @@ if iterrev == cachedrev: # cache hit - text = _cache[2] + text = self._cache[2] else: chain.append(iterrev) chain.reverse() @@ -1091,7 +1107,7 @@ # drop cache to save memory self._cache = None - bins = self._chunks(chain) + bins = self._chunks(chain, df=_df) if text is None: text = str(bins[0]) bins = bins[1:] @@ -1125,6 +1141,12 @@ % (self.indexfile, revornode)) def checkinlinesize(self, tr, fp=None): + """Check if the revlog is too big for inline and convert if so. + + This should be called after revisions are added to the revlog. If the + revlog has grown too large to be an inline revlog, it will convert it + to use multiple index and data files. + """ if not self._inline or (self.start(-2) + self.length(-2)) < _maxinline: return @@ -1196,7 +1218,7 @@ dfh = None if not self._inline: - dfh = self.opener(self.datafile, "a") + dfh = self.opener(self.datafile, "a+") ifh = self.opener(self.indexfile, "a+") try: return self._addrevision(node, text, transaction, link, p1, p2, @@ -1235,8 +1257,27 @@ return ('u', text) return ("", bin) + def _isgooddelta(self, d, textlen): + """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: + return False + + # - 'dist' is the distance from the base revision -- bounding it limits + # the amount of I/O we need to do. + # - 'compresseddeltalen' is the sum of the total size of deltas we need + # to apply -- bounding it limits the amount of CPU we consume. + dist, l, data, base, chainbase, chainlen, compresseddeltalen = d + if (dist > textlen * 4 or l > textlen or + compresseddeltalen > textlen * 2 or + (self._maxchainlen and chainlen > self._maxchainlen)): + return False + + return True + def _addrevision(self, node, text, transaction, link, p1, p2, flags, - cachedelta, ifh, dfh): + cachedelta, ifh, dfh, alwayscache=False): """internal function to add revisions to the log see addrevision for argument descriptions. @@ -1248,10 +1289,6 @@ def buildtext(): if btext[0] is not None: return btext[0] - # flush any pending writes here so we can read it in revision - if dfh: - dfh.flush() - ifh.flush() baserev = cachedelta[0] delta = cachedelta[1] # special case deltas which replace entire base; no need to decode @@ -1262,7 +1299,11 @@ len(delta) - hlen): btext[0] = delta[hlen:] else: - basetext = self.revision(self.node(baserev)) + if self._inline: + fh = ifh + else: + fh = dfh + basetext = self.revision(self.node(baserev), _df=fh) btext[0] = mdiff.patch(basetext, delta) try: self.checkhash(btext[0], p1, p2, node) @@ -1286,7 +1327,11 @@ header = mdiff.replacediffheader(self.rawsize(rev), len(t)) delta = header + t else: - ptext = self.revision(self.node(rev)) + if self._inline: + fh = ifh + else: + fh = dfh + ptext = self.revision(self.node(rev), _df=fh) delta = mdiff.textdiff(ptext, t) data = self.compress(delta) l = len(data[1]) + len(data[0]) @@ -1315,19 +1360,6 @@ basecache = self._basecache p1r, p2r = self.rev(p1), self.rev(p2) - # should we try to build a delta? - if prev != nullrev: - if self._generaldelta: - if p1r >= basecache[1]: - d = builddelta(p1r) - elif p2r >= basecache[1]: - d = builddelta(p2r) - else: - d = builddelta(prev) - else: - d = builddelta(prev) - dist, l, data, base, chainbase, chainlen, compresseddeltalen = d - # full versions are inserted when the needed deltas # become comparable to the uncompressed text if text is None: @@ -1336,13 +1368,42 @@ else: textlen = len(text) - # - 'dist' is the distance from the base revision -- bounding it limits - # the amount of I/O we need to do. - # - 'compresseddeltalen' is the sum of the total size of deltas we need - # to apply -- bounding it limits the amount of CPU we consume. - if (d is None or dist > textlen * 4 or l > textlen or - compresseddeltalen > textlen * 2 or - (self._maxchainlen and chainlen > self._maxchainlen)): + # should we try to build a delta? + if prev != nullrev: + if self._generaldelta: + if p2r != nullrev and self._aggressivemergedeltas: + d = builddelta(p1r) + d2 = builddelta(p2r) + p1good = self._isgooddelta(d, textlen) + p2good = self._isgooddelta(d2, textlen) + if p1good and p2good: + # If both are good deltas, choose the smallest + if d2[1] < d[1]: + d = d2 + elif p2good: + # If only p2 is good, use it + d = d2 + elif p1good: + pass + else: + # Neither is good, try against prev to hopefully save us + # a fulltext. + d = builddelta(prev) + else: + # Pick whichever parent is closer to us (to minimize the + # chance of having to build a fulltext). Since + # nullrev == -1, any non-merge commit will always pick p1r. + drev = p2r if p2r > p1r else p1r + d = builddelta(drev) + # If the chosen delta will result in us making a full text, + # give it one last try against prev. + if drev != prev and not self._isgooddelta(d, textlen): + d = builddelta(prev) + else: + d = builddelta(prev) + dist, l, data, base, chainbase, chainlen, compresseddeltalen = d + + if not self._isgooddelta(d, textlen): text = buildtext() data = self.compress(text) l = len(data[1]) + len(data[0]) @@ -1356,6 +1417,9 @@ entry = self._io.packentry(e, self.node, self.version, curr) self._writeentry(transaction, ifh, dfh, entry, data, link, offset) + if alwayscache and text is None: + text = buildtext() + if type(text) == str: # only accept immutable objects self._cache = (node, curr, text) self._basecache = (curr, chainbase) @@ -1369,7 +1433,6 @@ if data[0]: dfh.write(data[0]) dfh.write(data[1]) - dfh.flush() ifh.write(entry) else: offset += curr * self._io.size @@ -1407,7 +1470,7 @@ else: transaction.add(self.indexfile, isize, r) transaction.add(self.datafile, end) - dfh = self.opener(self.datafile, "a") + dfh = self.opener(self.datafile, "a+") def flush(): if dfh: dfh.flush() @@ -1459,23 +1522,24 @@ if self._peek_iscensored(baserev, delta, flush): flags |= REVIDX_ISCENSORED + # We assume consumers of addrevisioncb will want to retrieve + # the added revision, which will require a call to + # revision(). revision() will fast path if there is a cache + # hit. So, we tell _addrevision() to always cache in this case. chain = self._addrevision(node, None, transaction, link, p1, p2, flags, (baserev, delta), - ifh, dfh) + ifh, dfh, + alwayscache=bool(addrevisioncb)) if addrevisioncb: - # Data for added revision can't be read unless flushed - # because _loadchunk always opensa new file handle and - # there is no guarantee data was actually written yet. - flush() addrevisioncb(self, chain) if not dfh and not self._inline: # addrevision switched from inline to conventional # reopen the index ifh.close() - dfh = self.opener(self.datafile, "a") - ifh = self.opener(self.indexfile, "a") + dfh = self.opener(self.datafile, "a+") + ifh = self.opener(self.indexfile, "a+") finally: if dfh: dfh.close()
--- a/mercurial/revset.py Mon Oct 05 10:43:16 2015 -0600 +++ b/mercurial/revset.py Wed Oct 07 13:44:48 2015 -0500 @@ -5,16 +5,25 @@ # This software may be used and distributed according to the terms of the # GNU General Public License version 2 or any later version. -import re -import parser, util, error, hbisect, phases -import node +from __future__ import absolute_import + import heapq -import match as matchmod -from i18n import _ -import encoding -import obsolete as obsmod -import pathutil -import repoview +import re + +from .i18n import _ +from . import ( + encoding, + error, + hbisect, + match as matchmod, + node, + obsolete as obsmod, + parser, + pathutil, + phases, + repoview, + util, +) def _revancestors(repo, revs, followfirst): """Like revlog.ancestors(), but supports followfirst.""" @@ -78,19 +87,17 @@ return generatorset(iterate(), iterasc=True) -def _revsbetween(repo, roots, heads): - """Return all paths between roots and heads, inclusive of both endpoint - sets.""" +def _reachablerootspure(repo, minroot, roots, heads, includepath): + """return (heads(::<roots> and ::<heads>)) + + If includepath is True, return (<roots>::<heads>).""" if not roots: - return baseset() + return [] parentrevs = repo.changelog.parentrevs + roots = set(roots) visit = list(heads) reachable = set() seen = {} - # XXX this should be 'parentset.min()' assuming 'parentset' is a smartset - # (and if it is not, it should.) - minroot = min(roots) - roots = set(roots) # prefetch all the things! (because python is slow) reached = reachable.add dovisit = visit.append @@ -101,6 +108,8 @@ rev = nextvisit() if rev in roots: reached(rev) + if not includepath: + continue parents = parentrevs(rev) seen[rev] = parents for parent in parents: @@ -108,11 +117,30 @@ dovisit(parent) if not reachable: return baseset() + if not includepath: + return reachable for rev in sorted(seen): for parent in seen[rev]: if parent in reachable: reached(rev) - return baseset(sorted(reachable)) + return reachable + +def reachableroots(repo, roots, heads, includepath=False): + """return (heads(::<roots> and ::<heads>)) + + If includepath is True, return (<roots>::<heads>).""" + if not roots: + return baseset() + minroot = roots.min() + roots = list(roots) + heads = list(heads) + try: + revs = repo.changelog.reachableroots(minroot, heads, roots, includepath) + except AttributeError: + revs = _reachablerootspure(repo, minroot, roots, heads, includepath) + revs = baseset(revs) + revs.sort() + return revs elements = { # token-type: binding-strength, primary, prefix, infix, suffix @@ -178,6 +206,21 @@ if symletters is None: symletters = _symletters + if program and lookup: + # attempt to parse old-style ranges first to deal with + # things like old-tag which contain query metacharacters + parts = program.split(':', 1) + if all(lookup(sym) for sym in parts if sym): + if parts[0]: + yield ('symbol', parts[0], 0) + if len(parts) > 1: + s = len(parts[0]) + yield (':', None, s) + if parts[1]: + yield ('symbol', parts[1], s + 1) + yield ('end', None, len(program)) + return + pos, l = 0, len(program) while pos < l: c = program[pos] @@ -201,7 +244,7 @@ c = program[pos] decode = lambda x: x else: - decode = lambda x: x.decode('string-escape') + decode = parser.unescapestr pos += 1 s = pos while pos < l: # find closing quote @@ -382,7 +425,8 @@ def dagrange(repo, subset, x, y): r = fullreposet(repo) - xs = _revsbetween(repo, getset(repo, r, x), getset(repo, r, y)) + xs = reachableroots(repo, getset(repo, r, x), getset(repo, r, y), + includepath=True) # XXX We should combine with subset first: 'subset & baseset(...)'. This is # necessary to ensure we preserve the order in subset. return xs & subset @@ -391,8 +435,13 @@ return getset(repo, getset(repo, subset, x), y) def orset(repo, subset, *xs): - rs = [getset(repo, subset, x) for x in xs] - return _combinesets(rs) + assert xs + if len(xs) == 1: + return getset(repo, subset, xs[0]) + p = len(xs) // 2 + a = orset(repo, subset, *xs[:p]) + b = orset(repo, subset, *xs[p:]) + return a + b def notset(repo, subset, x): return subset - getset(repo, subset, x) @@ -414,6 +463,116 @@ # functions +def _mergedefaultdest(repo, subset, x): + # ``_mergedefaultdest()`` + + # default destination for merge. + # # XXX: Currently private because I expect the signature to change. + # # XXX: - taking rev as arguments, + # # XXX: - bailing out in case of ambiguity vs returning all data. + getargs(x, 0, 0, _("_mergedefaultdest takes no arguments")) + if repo._activebookmark: + bmheads = repo.bookmarkheads(repo._activebookmark) + curhead = repo[repo._activebookmark].node() + if len(bmheads) == 2: + if curhead == bmheads[0]: + node = bmheads[1] + else: + node = bmheads[0] + elif len(bmheads) > 2: + raise util.Abort(_("multiple matching bookmarks to merge - " + "please merge with an explicit rev or bookmark"), + hint=_("run 'hg heads' to see all heads")) + elif len(bmheads) <= 1: + raise util.Abort(_("no matching bookmark to merge - " + "please merge with an explicit rev or bookmark"), + hint=_("run 'hg heads' to see all heads")) + else: + branch = repo[None].branch() + bheads = repo.branchheads(branch) + nbhs = [bh for bh in bheads if not repo[bh].bookmarks()] + + if len(nbhs) > 2: + raise util.Abort(_("branch '%s' has %d heads - " + "please merge with an explicit rev") + % (branch, len(bheads)), + hint=_("run 'hg heads .' to see heads")) + + parent = repo.dirstate.p1() + if len(nbhs) <= 1: + if len(bheads) > 1: + raise util.Abort(_("heads are bookmarked - " + "please merge with an explicit rev"), + hint=_("run 'hg heads' to see all heads")) + if len(repo.heads()) > 1: + raise util.Abort(_("branch '%s' has one head - " + "please merge with an explicit rev") + % branch, + hint=_("run 'hg heads' to see all heads")) + msg, hint = _('nothing to merge'), None + if parent != repo.lookup(branch): + hint = _("use 'hg update' instead") + raise util.Abort(msg, hint=hint) + + if parent not in bheads: + raise util.Abort(_('working directory not at a head revision'), + hint=_("use 'hg update' or merge with an " + "explicit revision")) + if parent == nbhs[0]: + node = nbhs[-1] + else: + node = nbhs[0] + return subset & baseset([repo[node].rev()]) + +def _updatedefaultdest(repo, subset, x): + # ``_updatedefaultdest()`` + + # default destination for update. + # # XXX: Currently private because I expect the signature to change. + # # XXX: - taking rev as arguments, + # # XXX: - bailing out in case of ambiguity vs returning all data. + getargs(x, 0, 0, _("_updatedefaultdest takes no arguments")) + # Here is where we should consider bookmarks, divergent bookmarks, + # foreground changesets (successors), and tip of current branch; + # but currently we are only checking the branch tips. + node = None + wc = repo[None] + p1 = wc.p1() + try: + node = repo.branchtip(wc.branch()) + except error.RepoLookupError: + if wc.branch() == 'default': # no default branch! + node = repo.lookup('tip') # update to tip + else: + raise util.Abort(_("branch %s not found") % wc.branch()) + + if p1.obsolete() and not p1.children(): + # allow updating to successors + successors = obsmod.successorssets(repo, p1.node()) + + # behavior of certain cases is as follows, + # + # divergent changesets: update to highest rev, similar to what + # is currently done when there are more than one head + # (i.e. 'tip') + # + # replaced changesets: same as divergent except we know there + # is no conflict + # + # pruned changeset: no update is done; though, we could + # consider updating to the first non-obsolete parent, + # similar to what is current done for 'hg prune' + + if successors: + # flatten the list here handles both divergent (len > 1) + # and the usual case (len = 1) + successors = [n for sub in successors for n in sub] + + # get the max revision for the given successors set, + # i.e. the 'tip' of a set + node = repo.revs('max(%ln)', successors).first() + return subset & baseset([repo[node].rev()]) + def adds(repo, subset, x): """``adds(pattern)`` Changesets that add a file matching pattern. @@ -531,7 +690,7 @@ bm = getstring(args[0], # i18n: "bookmark" is a keyword _('the argument to bookmark must be a string')) - kind, pattern, matcher = _stringmatcher(bm) + kind, pattern, matcher = util.stringmatcher(bm) bms = set() if kind == 'literal': bmrev = repo._bookmarks.get(pattern, None) @@ -572,7 +731,7 @@ # not a string, but another revspec, e.g. tip() pass else: - kind, pattern, matcher = _stringmatcher(b) + kind, pattern, matcher = util.stringmatcher(b) if kind == 'literal': # note: falls through to the revspec case if no branch with # this name exists @@ -860,7 +1019,7 @@ # i18n: "extra" is a keyword value = getstring(args['value'], _('second argument to extra must be ' 'a string')) - kind, value, matcher = _stringmatcher(value) + kind, value, matcher = util.stringmatcher(value) def _matchvalue(r): extra = repo[r].extra() @@ -990,34 +1149,37 @@ return limit(repo, subset, x) def _follow(repo, subset, x, name, followfirst=False): - l = getargs(x, 0, 1, _("%s takes no arguments or a filename") % name) + l = getargs(x, 0, 1, _("%s takes no arguments or a pattern") % name) c = repo['.'] if l: - x = getstring(l[0], _("%s expected a filename") % name) - if x in c: - cx = c[x] - s = set(ctx.rev() for ctx in cx.ancestors(followfirst=followfirst)) - # include the revision responsible for the most recent version - s.add(cx.introrev()) - else: - return baseset() + x = getstring(l[0], _("%s expected a pattern") % name) + matcher = matchmod.match(repo.root, repo.getcwd(), [x], + ctx=repo[None], default='path') + + s = set() + for fname in c: + if matcher(fname): + fctx = c[fname] + s = s.union(set(c.rev() for c in fctx.ancestors(followfirst))) + # include the revision responsible for the most recent version + s.add(fctx.introrev()) else: s = _revancestors(repo, baseset([c.rev()]), followfirst) return subset & s def follow(repo, subset, x): - """``follow([file])`` + """``follow([pattern])`` An alias for ``::.`` (ancestors of the working directory's first parent). - If a filename is specified, the history of the given file is followed, - including copies. + If pattern is specified, the histories of files matching given + pattern is followed, including copies. """ return _follow(repo, subset, x, 'follow') def _followfirst(repo, subset, x): - # ``followfirst([file])`` - # Like ``follow([file])`` but follows only the first parent of - # every revision or file revision. + # ``followfirst([pattern])`` + # Like ``follow([pattern])`` but follows only the first parent of + # every revisions or files revisions. return _follow(repo, subset, x, '_followfirst', followfirst=True) def getall(repo, subset, x): @@ -1225,10 +1387,14 @@ Changeset with highest revision number in set. """ os = getset(repo, fullreposet(repo), x) - if os: + try: m = os.max() if m in subset: return baseset([m]) + except ValueError: + # os.max() throws a ValueError when the collection is empty. + # Same as python's max(). + pass return baseset() def merge(repo, subset, x): @@ -1264,10 +1430,14 @@ Changeset with lowest revision number in set. """ os = getset(repo, fullreposet(repo), x) - if os: + try: m = os.min() if m in subset: return baseset([m]) + except ValueError: + # os.min() throws a ValueError when the collection is empty. + # Same as python's min(). + pass return baseset() def modifies(repo, subset, x): @@ -1296,7 +1466,7 @@ ns = getstring(args[0], # i18n: "named" is a keyword _('the argument to named must be a string')) - kind, pattern, matcher = _stringmatcher(ns) + kind, pattern, matcher = util.stringmatcher(ns) namespaces = set() if kind == 'literal': if pattern not in repo.names: @@ -1415,8 +1585,10 @@ default push location. """ # Avoid cycles. - import discovery - import hg + from . import ( + discovery, + hg, + ) # i18n: "outgoing" is a keyword l = getargs(x, 0, 1, _("outgoing takes one or no arguments")) # i18n: "outgoing" is a keyword @@ -1597,7 +1769,7 @@ synonym for the current local branch. """ - import hg # avoid start-up nasties + from . import hg # avoid start-up nasties # i18n: "remote" is a keyword l = getargs(x, 0, 2, _("remote takes one, two or no arguments")) @@ -1862,7 +2034,7 @@ m = matchmod.exact(repo.root, repo.root, ['.hgsubstate']) def submatches(names): - k, p, m = _stringmatcher(pat) + k, p, m = util.stringmatcher(pat) for name in names: if m(name): yield name @@ -1892,47 +2064,8 @@ return subset.filter(matches) -def _stringmatcher(pattern): - """ - accepts a string, possibly starting with 're:' or 'literal:' prefix. - returns the matcher name, pattern, and matcher function. - missing or unknown prefixes are treated as literal matches. - - helper for tests: - >>> def test(pattern, *tests): - ... kind, pattern, matcher = _stringmatcher(pattern) - ... return (kind, pattern, [bool(matcher(t)) for t in tests]) - - exact matching (no prefix): - >>> test('abcdefg', 'abc', 'def', 'abcdefg') - ('literal', 'abcdefg', [False, False, True]) - - regex matching ('re:' prefix) - >>> test('re:a.+b', 'nomatch', 'fooadef', 'fooadefbar') - ('re', 'a.+b', [False, False, True]) - - force exact matches ('literal:' prefix) - >>> test('literal:re:foobar', 'foobar', 're:foobar') - ('literal', 're:foobar', [False, True]) - - unknown prefixes are ignored and treated as literals - >>> test('foo:bar', 'foo', 'bar', 'foo:bar') - ('literal', 'foo:bar', [False, False, True]) - """ - if pattern.startswith('re:'): - pattern = pattern[3:] - try: - regex = re.compile(pattern) - except re.error as e: - raise error.ParseError(_('invalid regular expression: %s') - % e) - return 're', pattern, regex.search - elif pattern.startswith('literal:'): - pattern = pattern[8:] - return 'literal', pattern, pattern.__eq__ - def _substringmatcher(pattern): - kind, pattern, matcher = _stringmatcher(pattern) + kind, pattern, matcher = util.stringmatcher(pattern) if kind == 'literal': matcher = lambda s: pattern in s return kind, pattern, matcher @@ -1952,7 +2085,7 @@ pattern = getstring(args[0], # i18n: "tag" is a keyword _('the argument to tag must be a string')) - kind, pattern, matcher = _stringmatcher(pattern) + kind, pattern, matcher = util.stringmatcher(pattern) if kind == 'literal': # avoid resolving all tags tn = repo._tagscache.tags.get(pattern, None) @@ -2013,14 +2146,17 @@ r = int(t) if str(r) != t or r not in cl: raise ValueError + revs = [r] except ValueError: - r = repo[t].rev() - if r in seen: - continue - if (r in subset - or r == node.nullrev and isinstance(subset, fullreposet)): - ls.append(r) - seen.add(r) + revs = stringset(repo, subset, t) + + for r in revs: + if r in seen: + continue + if (r in subset + or r == node.nullrev and isinstance(subset, fullreposet)): + ls.append(r) + seen.add(r) return baseset(ls) # for internal use @@ -2043,6 +2179,8 @@ return baseset([r for r in ls if r in s]) symbols = { + "_mergedefaultdest": _mergedefaultdest, + "_updatedefaultdest": _updatedefaultdest, "adds": adds, "all": getall, "ancestor": ancestor, @@ -2654,6 +2792,27 @@ if repo: lookup = repo.__contains__ tree = parse(spec, lookup) + return _makematcher(ui, tree, repo) + +def matchany(ui, specs, repo=None): + """Create a matcher that will include any revisions matching one of the + given specs""" + if not specs: + def mfunc(repo, subset=None): + return baseset() + return mfunc + if not all(specs): + raise error.ParseError(_("empty query")) + lookup = None + if repo: + lookup = repo.__contains__ + if len(specs) == 1: + tree = parse(specs[0], lookup) + else: + tree = ('or',) + tuple(parse(s, lookup) for s in specs) + return _makematcher(ui, tree, repo) + +def _makematcher(ui, tree, repo): if ui: tree = findaliases(ui, tree, showwarning=ui.warn) tree = foldconcat(tree) @@ -2813,6 +2972,7 @@ """True if the set will iterate in descending order""" raise NotImplementedError() + @util.cachefunc def min(self): """return the minimum element in the set""" if self.fastasc is not None: @@ -2821,6 +2981,7 @@ raise ValueError('arg is an empty sequence') return min(self) + @util.cachefunc def max(self): """return the maximum element in the set""" if self.fastdesc is not None: @@ -2896,6 +3057,8 @@ """ def __init__(self, data=()): if not isinstance(data, list): + if isinstance(data, set): + self._set = data data = list(data) self._list = data self._ascending = None @@ -2995,14 +3158,9 @@ """ self._subset = subset self._condition = condition - self._cache = {} def __contains__(self, x): - c = self._cache - if x not in c: - v = c[x] = x in self._subset and self._condition(x) - return v - return c[x] + return x in self._subset and self._condition(x) def __iter__(self): return self._iterfilter(self._subset) @@ -3028,7 +3186,15 @@ return lambda: self._iterfilter(it()) def __nonzero__(self): - for r in self: + fast = self.fastasc + if fast is None: + fast = self.fastdesc + if fast is not None: + it = fast() + else: + it = self + + for r in it: return True return False @@ -3073,20 +3239,6 @@ def __repr__(self): return '<%s %r>' % (type(self).__name__, self._subset) -# this function will be removed, or merged to addset or orset, when -# - scmutil.revrange() can be rewritten to not combine calculated smartsets -# - or addset can handle more than two sets without balanced tree -def _combinesets(subsets): - """Create balanced tree of addsets representing union of given sets""" - if not subsets: - return baseset() - if len(subsets) == 1: - return subsets[0] - p = len(subsets) // 2 - xs = _combinesets(subsets[:p]) - ys = _combinesets(subsets[p:]) - return addset(xs, ys) - def _iterordered(ascending, iter1, iter2): """produce an ordered iteration from two iterators with the same order
--- a/mercurial/scmutil.py Mon Oct 05 10:43:16 2015 -0600 +++ b/mercurial/scmutil.py Wed Oct 07 13:44:48 2015 -0500 @@ -6,7 +6,7 @@ # GNU General Public License version 2 or any later version. from i18n import _ -from mercurial.node import nullrev, wdirrev +from mercurial.node import wdirrev import util, error, osutil, revset, similar, encoding, phases import pathutil import match as matchmod @@ -586,6 +586,8 @@ raise util.Abort('this vfs is read only') return self.vfs(path, mode, *args, **kw) + def join(self, path, *insidef): + return self.vfs.join(path, *insidef) def walkrepos(path, followsym=False, seen_dirs=None, recurse=False): '''yield every hg repository under path, always recursively. @@ -690,6 +692,11 @@ raise util.Abort(_('empty revision set')) return repo[l.last()] +def _pairspec(revspec): + tree = revset.parse(revspec) + tree = revset.optimize(tree, True)[1] # fix up "x^:y" -> "(x^):y" + return tree and tree[0] in ('range', 'rangepre', 'rangepost', 'rangeall') + def revpair(repo, revs): if not revs: return repo.dirstate.p1(), None @@ -711,67 +718,37 @@ if first is None: raise util.Abort(_('empty revision range')) - if first == second and len(revs) == 1 and _revrangesep not in revs[0]: + # if top-level is range expression, the result must always be a pair + if first == second and len(revs) == 1 and not _pairspec(revs[0]): return repo.lookup(first), None return repo.lookup(first), repo.lookup(second) -_revrangesep = ':' - def revrange(repo, revs): """Yield revision as strings from a list of revision specifications.""" - - def revfix(repo, val, defval): - if not val and val != 0 and defval is not None: - return defval - return repo[val].rev() - - subsets = [] - - revsetaliases = [alias for (alias, _) in - repo.ui.configitems("revsetalias")] - + allspecs = [] for spec in revs: - # attempt to parse old-style ranges first to deal with - # things like old-tag which contain query metacharacters - try: - # ... except for revset aliases without arguments. These - # should be parsed as soon as possible, because they might - # clash with a hash prefix. - if spec in revsetaliases: - raise error.RepoLookupError + if isinstance(spec, int): + spec = revset.formatspec('rev(%d)', spec) + allspecs.append(spec) + m = revset.matchany(repo.ui, allspecs, repo) + return m(repo) - if isinstance(spec, int): - subsets.append(revset.baseset([spec])) - continue +def meaningfulparents(repo, ctx): + """Return list of meaningful (or all if debug) parentrevs for rev. - if _revrangesep in spec: - start, end = spec.split(_revrangesep, 1) - if start in revsetaliases or end in revsetaliases: - raise error.RepoLookupError - - start = revfix(repo, start, 0) - end = revfix(repo, end, len(repo) - 1) - if end == nullrev and start < 0: - start = nullrev - if start < end: - l = revset.spanset(repo, start, end + 1) - else: - l = revset.spanset(repo, start, end - 1) - subsets.append(l) - continue - elif spec and spec in repo: # single unquoted rev - rev = revfix(repo, spec, None) - subsets.append(revset.baseset([rev])) - continue - except error.RepoLookupError: - pass - - # fall through to new-style queries if old-style fails - m = revset.match(repo.ui, spec, repo) - subsets.append(m(repo)) - - return revset._combinesets(subsets) + For merges (two non-nullrev revisions) both parents are meaningful. + Otherwise the first parent revision is considered meaningful if it + is not the preceding revision. + """ + parents = ctx.parents() + if len(parents) > 1: + return parents + if repo.ui.debugflag: + return [parents[0], repo['null']] + if parents[0].rev() >= intrev(ctx.rev()) - 1: + return [] + return parents def expandpats(pats): '''Expand bare globs when running on windows. @@ -792,13 +769,15 @@ ret.append(kindpat) return ret -def matchandpats(ctx, pats=[], opts={}, globbed=False, default='relpath', +def matchandpats(ctx, pats=(), opts=None, globbed=False, default='relpath', badfn=None): '''Return a matcher and the patterns that were used. The matcher will warn about bad matches, unless an alternate badfn callback is provided.''' if pats == ("",): pats = [] + if opts is None: + opts = {} if not globbed and default == 'relpath': pats = expandpats(pats or []) @@ -815,7 +794,8 @@ pats = [] return m, pats -def match(ctx, pats=[], opts={}, globbed=False, default='relpath', badfn=None): +def match(ctx, pats=(), opts=None, globbed=False, default='relpath', + badfn=None): '''Return a matcher that will warn about bad matches.''' return matchandpats(ctx, pats, opts, globbed, default, badfn=badfn)[0] @@ -827,7 +807,9 @@ '''Return a matcher that will efficiently match exactly these files.''' return matchmod.exact(repo.root, repo.getcwd(), files, badfn=badfn) -def addremove(repo, matcher, prefix, opts={}, dry_run=None, similarity=None): +def addremove(repo, matcher, prefix, opts=None, dry_run=None, similarity=None): + if opts is None: + opts = {} m = matcher if dry_run is None: dry_run = opts.get('dry_run') @@ -1009,7 +991,7 @@ raise error.RequirementError( _("repository requires features unknown to this Mercurial: %s") % " ".join(missings), - hint=_("see http://mercurial.selenic.com/wiki/MissingRequirement" + hint=_("see https://mercurial-scm.org/wiki/MissingRequirement" " for more information")) return requirements @@ -1103,7 +1085,7 @@ 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 behaviour as + recreating the object on every call (essentially the same behavior as propertycache). ''' @@ -1166,3 +1148,22 @@ del obj.__dict__[self.name] except KeyError: raise AttributeError(self.name) + +def _locksub(repo, lock, envvar, cmd, environ=None, *args, **kwargs): + if lock is None: + raise error.LockInheritanceContractViolation( + 'lock can only be inherited while held') + if environ is None: + environ = {} + with lock.inherit() as locker: + environ[envvar] = locker + return repo.ui.system(cmd, environ=environ, *args, **kwargs) + +def wlocksub(repo, cmd, *args, **kwargs): + """run cmd as a subprocess that allows inheriting repo's wlock + + This can only be called while the wlock is held. This takes all the + arguments that ui.system does, and returns the exit code of the + subprocess.""" + return _locksub(repo, repo.currentwlock(), 'HG_WLOCK_LOCKER', cmd, *args, + **kwargs)
--- a/mercurial/setdiscovery.py Mon Oct 05 10:43:16 2015 -0600 +++ b/mercurial/setdiscovery.py Wed Oct 07 13:44:48 2015 -0500 @@ -40,11 +40,20 @@ classified with it (since all ancestors or descendants will be marked as well). """ +from __future__ import absolute_import + import collections -from node import nullid, nullrev -from i18n import _ import random -import util, dagutil + +from .i18n import _ +from .node import ( + nullid, + nullrev, +) +from . import ( + dagutil, + util, +) def _updatesample(dag, nodes, sample, quicksamplesize=0): """update an existing sample to match the expected size @@ -138,22 +147,12 @@ sample = _limitsample(ownheads, initialsamplesize) # indices between sample and externalized version must match sample = list(sample) - if remote.local(): - # stopgap until we have a proper localpeer that supports batch() - srvheadhashes = remote.heads() - yesno = remote.known(dag.externalizeall(sample)) - elif remote.capable('batch'): - batch = remote.batch() - srvheadhashesref = batch.heads() - yesnoref = batch.known(dag.externalizeall(sample)) - batch.submit() - srvheadhashes = srvheadhashesref.value - yesno = yesnoref.value - else: - # compatibility with pre-batch, but post-known remotes during 1.9 - # development - srvheadhashes = remote.heads() - sample = [] + batch = remote.batch() + srvheadhashesref = batch.heads() + yesnoref = batch.known(dag.externalizeall(sample)) + batch.submit() + srvheadhashes = srvheadhashesref.value + yesno = yesnoref.value if cl.tip() == nullid: if srvheadhashes != [nullid]:
--- a/mercurial/simplemerge.py Mon Oct 05 10:43:16 2015 -0600 +++ b/mercurial/simplemerge.py Wed Oct 07 13:44:48 2015 -0500 @@ -16,9 +16,17 @@ # mbp: "you know that thing where cvs gives you conflict markers?" # s: "i hate that." -from i18n import _ -import scmutil, util, mdiff -import sys, os +from __future__ import absolute_import + +import os +import sys + +from .i18n import _ +from . import ( + mdiff, + scmutil, + util, +) class CantReprocessAndShowBase(Exception): pass @@ -82,7 +90,8 @@ start_marker='<<<<<<<', mid_marker='=======', end_marker='>>>>>>>', - base_marker=None): + base_marker=None, + localorother=None): """Return merge in cvs-like form. """ self.conflicts = False @@ -92,9 +101,9 @@ newline = '\r\n' elif self.a[0].endswith('\r'): newline = '\r' - if name_a: + if name_a and start_marker: start_marker = start_marker + ' ' + name_a - if name_b: + if name_b and end_marker: end_marker = end_marker + ' ' + name_b if name_base and base_marker: base_marker = base_marker + ' ' + name_base @@ -111,18 +120,28 @@ for i in range(t[1], t[2]): yield self.b[i] elif what == 'conflict': - self.conflicts = True - yield start_marker + newline - for i in range(t[3], t[4]): - yield self.a[i] - if base_marker is not None: - yield base_marker + newline - for i in range(t[1], t[2]): - yield self.base[i] - yield mid_marker + newline - for i in range(t[5], t[6]): - yield self.b[i] - yield end_marker + newline + if localorother == 'local': + for i in range(t[3], t[4]): + yield self.a[i] + elif localorother == 'other': + for i in range(t[5], t[6]): + yield self.b[i] + else: + self.conflicts = True + if start_marker is not None: + yield start_marker + newline + for i in range(t[3], t[4]): + yield self.a[i] + if base_marker is not None: + yield base_marker + newline + for i in range(t[1], t[2]): + yield self.base[i] + if mid_marker is not None: + yield mid_marker + newline + for i in range(t[5], t[6]): + yield self.b[i] + if end_marker is not None: + yield end_marker + newline else: raise ValueError(what) @@ -345,18 +364,24 @@ raise util.Abort(msg) return text - name_a = local - name_b = other - name_base = None - labels = opts.get('label', []) - if len(labels) > 0: - name_a = labels[0] - if len(labels) > 1: - name_b = labels[1] - if len(labels) > 2: - name_base = labels[2] - if len(labels) > 3: - raise util.Abort(_("can only specify three labels.")) + mode = opts.get('mode','merge') + if mode == 'union': + name_a = None + name_b = None + name_base = None + else: + name_a = local + name_b = other + name_base = None + labels = opts.get('label', []) + if len(labels) > 0: + name_a = labels[0] + if len(labels) > 1: + name_b = labels[1] + if len(labels) > 2: + name_base = labels[2] + if len(labels) > 3: + raise util.Abort(_("can only specify three labels.")) try: localtext = readfile(local) @@ -373,8 +398,12 @@ out = sys.stdout m3 = Merge3Text(basetext, localtext, othertext) - extrakwargs = {} - if name_base is not None: + extrakwargs = {"localorother": opts.get("localorother", None)} + if mode == 'union': + extrakwargs['start_marker'] = None + extrakwargs['mid_marker'] = None + extrakwargs['end_marker'] = None + elif name_base is not None: extrakwargs['base_marker'] = '|||||||' extrakwargs['name_base'] = name_base for line in m3.merge_lines(name_a=name_a, name_b=name_b, **extrakwargs): @@ -383,7 +412,7 @@ if not opts.get('print'): out.close() - if m3.conflicts: + if m3.conflicts and not mode == 'union': if not opts.get('quiet'): ui.warn(_("warning: conflicts during merge.\n")) return 1
--- a/mercurial/sshpeer.py Mon Oct 05 10:43:16 2015 -0600 +++ b/mercurial/sshpeer.py Wed Oct 07 13:44:48 2015 -0500 @@ -5,9 +5,16 @@ # 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 re -from i18n import _ -import util, error, wireproto + +from .i18n import _ +from . import ( + error, + util, + wireproto, +) class remotelock(object): def __init__(self, repo):
--- a/mercurial/sshserver.py Mon Oct 05 10:43:16 2015 -0600 +++ b/mercurial/sshserver.py Wed Oct 07 13:44:48 2015 -0500 @@ -6,8 +6,16 @@ # This software may be used and distributed according to the terms of the # GNU General Public License version 2 or any later version. -import util, hook, wireproto -import os, sys +from __future__ import absolute_import + +import os +import sys + +from . import ( + hook, + util, + wireproto, +) class sshserver(wireproto.abstractserverproto): def __init__(self, ui, repo):
--- a/mercurial/sslutil.py Mon Oct 05 10:43:16 2015 -0600 +++ b/mercurial/sslutil.py Wed Oct 07 13:44:48 2015 -0500 @@ -6,10 +6,15 @@ # # This software may be used and distributed according to the terms of the # GNU General Public License version 2 or any later version. -import os, sys, ssl + +from __future__ import absolute_import -from mercurial import util -from mercurial.i18n import _ +import os +import ssl +import sys + +from .i18n import _ +from . import util _canloaddefaultcerts = False try:
--- a/mercurial/statichttprepo.py Mon Oct 05 10:43:16 2015 -0600 +++ b/mercurial/statichttprepo.py Wed Oct 07 13:44:48 2015 -0500 @@ -7,10 +7,26 @@ # This software may be used and distributed according to the terms of the # GNU General Public License version 2 or any later version. -from i18n import _ -import changelog, byterange, url, error, namespaces -import localrepo, manifest, util, scmutil, store -import urllib, urllib2, errno, os +from __future__ import absolute_import + +import errno +import os +import urllib +import urllib2 + +from .i18n import _ +from . import ( + byterange, + changelog, + error, + localrepo, + manifest, + namespaces, + scmutil, + store, + url, + util, +) class httprangereader(object): def __init__(self, url, opener):
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/mercurial/streamclone.py Wed Oct 07 13:44:48 2015 -0500 @@ -0,0 +1,292 @@ +# streamclone.py - producing and consuming streaming repository data +# +# 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 time + +from .i18n import _ +from . import ( + branchmap, + error, + store, + util, +) + +def canperformstreamclone(pullop, bailifbundle2supported=False): + """Whether it is possible to perform a streaming clone as part of pull. + + ``bailifbundle2supported`` will cause the function to return False if + bundle2 stream clones are supported. It should only be called by the + legacy stream clone code path. + + Returns a tuple of (supported, requirements). ``supported`` is True if + streaming clone is supported and False otherwise. ``requirements`` is + a set of repo requirements from the remote, or ``None`` if stream clone + isn't supported. + """ + repo = pullop.repo + remote = pullop.remote + + bundle2supported = False + if pullop.canusebundle2: + if 'v1' in pullop.remotebundle2caps.get('stream', []): + bundle2supported = True + # else + # Server doesn't support bundle2 stream clone or doesn't support + # the versions we support. Fall back and possibly allow legacy. + + # Ensures legacy code path uses available bundle2. + if bailifbundle2supported and bundle2supported: + return False, None + # Ensures bundle2 doesn't try to do a stream clone if it isn't supported. + #elif not bailifbundle2supported and not bundle2supported: + # return False, None + + # Streaming clone only works on empty repositories. + if len(repo): + return False, None + + # Streaming clone only works if all data is being requested. + if pullop.heads: + return False, None + + streamrequested = pullop.streamclonerequested + + # If we don't have a preference, let the server decide for us. This + # likely only comes into play in LANs. + if streamrequested is None: + # The server can advertise whether to prefer streaming clone. + streamrequested = remote.capable('stream-preferred') + + if not streamrequested: + return False, None + + # In order for stream clone to work, the client has to support all the + # requirements advertised by the server. + # + # The server advertises its requirements via the "stream" and "streamreqs" + # capability. "stream" (a value-less capability) is advertised if and only + # if the only requirement is "revlogv1." Else, the "streamreqs" capability + # is advertised and contains a comma-delimited list of requirements. + requirements = set() + if remote.capable('stream'): + requirements.add('revlogv1') + else: + streamreqs = remote.capable('streamreqs') + # This is weird and shouldn't happen with modern servers. + if not streamreqs: + return False, None + + streamreqs = set(streamreqs.split(',')) + # Server requires something we don't support. Bail. + if streamreqs - repo.supportedformats: + return False, None + requirements = streamreqs + + return True, requirements + +def maybeperformlegacystreamclone(pullop): + """Possibly perform a legacy stream clone operation. + + Legacy stream clones are performed as part of pull but before all other + operations. + + A legacy stream clone will not be performed if a bundle2 stream clone is + supported. + """ + supported, requirements = canperformstreamclone(pullop) + + if not supported: + return + + repo = pullop.repo + remote = pullop.remote + + # Save remote branchmap. We will use it later to speed up branchcache + # creation. + rbranchmap = None + if remote.capable('branchmap'): + rbranchmap = remote.branchmap() + + repo.ui.status(_('streaming all changes\n')) + + fp = remote.stream_out() + l = fp.readline() + try: + resp = int(l) + except ValueError: + raise error.ResponseError( + _('unexpected response from remote server:'), l) + if resp == 1: + raise util.Abort(_('operation forbidden by server')) + elif resp == 2: + raise util.Abort(_('locking the remote repository failed')) + elif resp != 0: + raise util.Abort(_('the server sent an unknown error code')) + + l = fp.readline() + try: + filecount, bytecount = map(int, l.split(' ', 1)) + except (ValueError, TypeError): + raise error.ResponseError( + _('unexpected response from remote server:'), l) + + lock = repo.lock() + try: + consumev1(repo, fp, filecount, bytecount) + + # new requirements = old non-format requirements + + # new format-related remote requirements + # requirements from the streamed-in repository + repo.requirements = requirements | ( + repo.requirements - repo.supportedformats) + repo._applyopenerreqs() + repo._writerequirements() + + if rbranchmap: + branchmap.replacecache(repo, rbranchmap) + + repo.invalidate() + finally: + lock.release() + +def allowservergeneration(ui): + """Whether streaming clones are allowed from the server.""" + return ui.configbool('server', 'uncompressed', True, untrusted=True) + +# This is it's own function so extensions can override it. +def _walkstreamfiles(repo): + return repo.store.walk() + +def generatev1(repo): + """Emit content for version 1 of a streaming clone. + + This returns a 3-tuple of (file count, byte size, data iterator). + + The data iterator consists of N entries for each file being transferred. + Each file entry starts as a line with the file name and integer size + delimited by a null byte. + + The raw file data follows. Following the raw file data is the next file + entry, or EOF. + + When used on the wire protocol, an additional line indicating protocol + success will be prepended to the stream. This function is not responsible + for adding it. + + This function will obtain a repository lock to ensure a consistent view of + the store is captured. It therefore may raise LockError. + """ + entries = [] + total_bytes = 0 + # Get consistent snapshot of repo, lock during scan. + lock = repo.lock() + try: + repo.ui.debug('scanning\n') + for name, ename, size in _walkstreamfiles(repo): + if size: + entries.append((name, size)) + total_bytes += size + finally: + lock.release() + + repo.ui.debug('%d files, %d bytes to transfer\n' % + (len(entries), total_bytes)) + + svfs = repo.svfs + oldaudit = svfs.mustaudit + debugflag = repo.ui.debugflag + svfs.mustaudit = False + + def emitrevlogdata(): + try: + for name, size in entries: + if debugflag: + repo.ui.debug('sending %s (%d bytes)\n' % (name, size)) + # partially encode name over the wire for backwards compat + yield '%s\0%d\n' % (store.encodedir(name), size) + if size <= 65536: + fp = svfs(name) + try: + data = fp.read(size) + finally: + fp.close() + yield data + else: + for chunk in util.filechunkiter(svfs(name), limit=size): + yield chunk + finally: + svfs.mustaudit = oldaudit + + return len(entries), total_bytes, emitrevlogdata() + +def generatev1wireproto(repo): + """Emit content for version 1 of streaming clone suitable for the wire. + + This is the data output from ``generatev1()`` with a header line + indicating file count and byte size. + """ + filecount, bytecount, it = generatev1(repo) + yield '%d %d\n' % (filecount, bytecount) + for chunk in it: + yield chunk + +def consumev1(repo, fp, filecount, bytecount): + """Apply the contents from version 1 of a streaming clone file handle. + + This takes the output from "streamout" and applies it to the specified + repository. + + Like "streamout," the status line added by the wire protocol is not handled + by this function. + """ + lock = repo.lock() + try: + 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) + start = time.time() + + tr = repo.transaction(_('clone')) + try: + for i in xrange(filecount): + # XXX doesn't support '\n' or '\r' in filenames + l = fp.readline() + try: + name, size = l.split('\0', 1) + size = int(size) + except (ValueError, TypeError): + raise error.ResponseError( + _('unexpected response from remote server:'), l) + if repo.ui.debugflag: + repo.ui.debug('adding %s (%s)\n' % + (name, util.bytecount(size))) + # for backwards compat, name was partially encoded + ofp = repo.svfs(store.decodedir(name), 'w') + for chunk in util.filechunkiter(fp, limit=size): + handled_bytes += len(chunk) + repo.ui.progress(_('clone'), handled_bytes, total=bytecount) + ofp.write(chunk) + ofp.close() + tr.close() + finally: + tr.release() + + # Writing straight to files circumvented the inmemory caches + repo.invalidate() + + elapsed = time.time() - start + if elapsed <= 0: + elapsed = 0.001 + repo.ui.progress(_('clone'), None) + repo.ui.status(_('transferred %s in %.1f seconds (%s/sec)\n') % + (util.bytecount(bytecount), elapsed, + util.bytecount(bytecount / elapsed))) + finally: + lock.release()
--- a/mercurial/strutil.py Mon Oct 05 10:43:16 2015 -0600 +++ b/mercurial/strutil.py Wed Oct 07 13:44:48 2015 -0500 @@ -5,6 +5,8 @@ # 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 + def findall(haystack, needle, start=0, end=None): if end is None: end = len(haystack)
--- a/mercurial/subrepo.py Mon Oct 05 10:43:16 2015 -0600 +++ b/mercurial/subrepo.py Wed Oct 07 13:44:48 2015 -0500 @@ -5,15 +5,34 @@ # 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 copy -import errno, os, re, posixpath, sys +import errno +import os +import posixpath +import re +import stat +import subprocess +import sys +import tarfile import xml.dom.minidom -import stat, subprocess, tarfile -from i18n import _ -import config, util, node, error, cmdutil, scmutil, match as matchmod -import phases -import pathutil -import exchange + + +from .i18n import _ +from . import ( + cmdutil, + config, + error, + exchange, + match as matchmod, + node, + pathutil, + phases, + scmutil, + util, +) + hg = None propertycache = util.propertycache @@ -328,7 +347,7 @@ # so we manually delay the circular imports to not break # scripts that don't use our demand-loading global hg - import hg as h + from . import hg as h hg = h pathutil.pathauditor(ctx.repo().root)(path) @@ -346,7 +365,7 @@ # so we manually delay the circular imports to not break # scripts that don't use our demand-loading global hg - import hg as h + from . import hg as h hg = h pathutil.pathauditor(ctx.repo().root)(path)
--- a/mercurial/tagmerge.py Mon Oct 05 10:43:16 2015 -0600 +++ b/mercurial/tagmerge.py Wed Oct 07 13:44:48 2015 -0500 @@ -71,11 +71,20 @@ # - put blocks whose nodes come all from p2 first # - write the tag blocks in the sorted order -import tags as tagsmod -import util -from node import nullid, hex -from i18n import _ +from __future__ import absolute_import + import operator + +from .i18n import _ +from .node import ( + hex, + nullid, +) +from .import ( + tags as tagsmod, + util, +) + hexnullid = hex(nullid) def readtagsformerge(ui, repo, lines, fn='', keeplinenums=False):
--- a/mercurial/tags.py Mon Oct 05 10:43:16 2015 -0600 +++ b/mercurial/tags.py Wed Oct 07 13:44:48 2015 -0500 @@ -10,15 +10,27 @@ # Eventually, it could take care of updating (adding/removing/moving) # tags too. -from node import nullid, bin, hex, short -from i18n import _ -import util -import encoding -import error -from array import array +from __future__ import absolute_import + +import array import errno import time +from .i18n import _ +from .node import ( + bin, + hex, + nullid, + short, +) +from . import ( + encoding, + error, + util, +) + +array = array.array + # Tags computation can be expensive and caches exist to make it fast in # the common case. # @@ -263,7 +275,7 @@ If the cache is not up to date, the caller is responsible for reading tag info from each returned head. (See findglobaltags().) ''' - import scmutil # avoid cycle + from . import scmutil # avoid cycle try: cachefile = repo.vfs(_filename(repo), 'r')
--- a/mercurial/templatefilters.py Mon Oct 05 10:43:16 2015 -0600 +++ b/mercurial/templatefilters.py Wed Oct 07 13:44:48 2015 -0500 @@ -5,10 +5,21 @@ # This software may be used and distributed according to the terms of the # GNU General Public License version 2 or any later version. -import cgi, re, os, time, urllib -import encoding, node, util -import hbisect -import templatekw +from __future__ import absolute_import + +import cgi +import os +import re +import time +import urllib + +from . import ( + encoding, + hbisect, + node, + templatekw, + util, +) def addbreaks(text): """:addbreaks: Any text. Add an XHTML "<br />" tag before the end of @@ -70,12 +81,6 @@ """:count: List or text. Returns the length as an integer.""" return len(i) -def datefilter(text): - """:date: Date. Returns a date in a Unix date format, including the - timezone: "Mon Sep 04 15:13:13 2006 0700". - """ - return util.datestr(text) - def domain(author): """:domain: Any text. Finds the first string that looks like an email address, and extracts just the domain component. Example: ``User @@ -230,10 +235,6 @@ s = s.replace(k, v) return ''.join(_uescape(c) for c in s) -def localdate(text): - """:localdate: Date. Converts a date to local date.""" - return (util.parsedate(text)[0], util.makedate()[1]) - def lower(text): """:lower: Any text. Converts the text to lowercase.""" return encoding.lower(text) @@ -337,10 +338,6 @@ return "" return str(thing) -def strip(text): - """:strip: Any text. Strips all leading and trailing whitespace.""" - return text.strip() - def stripdir(text): """:stripdir: Treat the text as path and strip a directory level, if possible. For example, "foo" and "foo/bar" becomes "foo". @@ -390,7 +387,6 @@ "age": age, "basename": basename, "count": count, - "date": datefilter, "domain": domain, "email": email, "escape": escape, @@ -403,7 +399,6 @@ "isodatesec": isodatesec, "json": json, "jsonescape": jsonescape, - "localdate": localdate, "lower": lower, "nonempty": nonempty, "obfuscate": obfuscate, @@ -418,7 +413,6 @@ "splitlines": splitlines, "stringescape": stringescape, "stringify": stringify, - "strip": strip, "stripdir": stripdir, "tabindent": tabindent, "upper": upper,
--- a/mercurial/templatekw.py Mon Oct 05 10:43:16 2015 -0600 +++ b/mercurial/templatekw.py Wed Oct 07 13:44:48 2015 -0500 @@ -5,9 +5,16 @@ # This software may be used and distributed according to the terms of the # GNU General Public License version 2 or any later version. -from node import hex -import patch, scmutil, util, error -import hbisect +from __future__ import absolute_import + +from .node import hex +from . import ( + error, + hbisect, + patch, + scmutil, + util, +) # This helper class allows us to handle both: # "{files}" (legacy command-line-specific list hack) and @@ -115,14 +122,21 @@ revcache['files'] = repo.status(ctx.p1(), ctx)[:3] return revcache['files'] -def getlatesttags(repo, ctx, cache): +def getlatesttags(repo, ctx, cache, pattern=None): '''return date, distance and name for the latest tag of rev''' - if 'latesttags' not in cache: + cachename = 'latesttags' + if pattern is not None: + cachename += '-' + pattern + match = util.stringmatcher(pattern)[2] + else: + match = util.always + + if cachename not in cache: # Cache mapping from rev to a tuple with tag date, tag # distance and tag name - cache['latesttags'] = {-1: (0, 0, ['null'])} - latesttags = cache['latesttags'] + cache[cachename] = {-1: (0, 0, ['null'])} + latesttags = cache[cachename] rev = ctx.rev() todo = [rev] @@ -132,7 +146,8 @@ continue ctx = repo[rev] tags = [t for t in ctx.tags() - if (repo.tagtype(t) and repo.tagtype(t) != 'local')] + if (repo.tagtype(t) and repo.tagtype(t) != 'local' + and match(t))] if tags: latesttags[rev] = ctx.date()[0], 0, [t for t in sorted(tags)] continue @@ -198,7 +213,7 @@ def showbranches(**args): """:branches: List of strings. The name of the branch on which the changeset was committed. Will be empty if the branch name was - default. + default. (DEPRECATED) """ branch = args['ctx'].branch() if branch != 'default': @@ -329,11 +344,27 @@ """:latesttag: List of strings. The global tags on the most recent globally tagged ancestor of this changeset. """ + return showlatesttags(None, **args) + +def showlatesttags(pattern, **args): + """helper method for the latesttag keyword and function""" repo, ctx = args['repo'], args['ctx'] cache = args['cache'] - latesttags = getlatesttags(repo, ctx, cache)[2] + latesttags = getlatesttags(repo, ctx, cache, pattern) - return showlist('latesttag', latesttags, separator=':', **args) + # latesttag[0] is an implementation detail for sorting csets on different + # branches in a stable manner- it is the date the tagged cset was created, + # not the date the tag was created. Therefore it isn't made visible here. + makemap = lambda v: { + 'changes': _showchangessincetag, + 'distance': latesttags[1], + 'latesttag': v, # BC with {latesttag % '{latesttag}'} + 'tag': v + } + + tags = latesttags[2] + f = _showlist('latesttag', tags, separator=':', **args) + return _hybrid(f, tags, makemap, lambda x: x['latesttag']) def showlatesttagdistance(repo, ctx, templ, cache, **args): """:latesttagdistance: Integer. Longest path to the latest tag.""" @@ -342,15 +373,20 @@ def showchangessincelatesttag(repo, ctx, templ, cache, **args): """:changessincelatesttag: Integer. All ancestors not in the latest tag.""" latesttag = getlatesttags(repo, ctx, cache)[2][0] + + return _showchangessincetag(repo, ctx, tag=latesttag, **args) + +def _showchangessincetag(repo, ctx, **args): offset = 0 revs = [ctx.rev()] + tag = args['tag'] # The only() revset doesn't currently support wdir() if ctx.rev() is None: offset = 1 revs = [p.rev() for p in ctx.parents()] - return len(repo.revs('only(%ld, %s)', revs, latesttag)) + offset + return len(repo.revs('only(%ld, %s)', revs, tag)) + offset def showmanifest(**args): repo, ctx, templ = args['repo'], args['ctx'], args['templ'] @@ -390,6 +426,18 @@ parent, all digits are 0.""" return ctx.p2().hex() +def showparents(**args): + """:parents: List of strings. The parents of the changeset in "rev:node" + format. If the changeset has only one "natural" parent (the predecessor + revision) nothing is shown.""" + repo = args['repo'] + ctx = args['ctx'] + parents = [[('rev', p.rev()), + ('node', p.hex()), + ('phase', p.phasestr())] + for p in scmutil.meaningfulparents(repo, ctx)] + return showlist('parent', parents, **args) + def showphase(repo, ctx, templ, **args): """:phase: String. The changeset phase name.""" return ctx.phasestr() @@ -402,6 +450,14 @@ """:rev: Integer. The repository-local changeset revision number.""" return scmutil.intrev(ctx.rev()) +def showrevslist(name, revs, **args): + """helper to generate a list of revisions in which a mapped template will + be evaluated""" + repo = args['ctx'].repo() + f = _showlist(name, revs, **args) + return _hybrid(f, revs, + lambda x: {name: x, 'ctx': repo[x], 'revcache': {}}) + def showsubrepos(**args): """:subrepos: List of strings. Updated subrepositories in the changeset.""" ctx = args['ctx'] @@ -470,6 +526,7 @@ 'p1node': showp1node, 'p2rev': showp2rev, 'p2node': showp2node, + 'parents': showparents, 'phase': showphase, 'phaseidx': showphaseidx, 'rev': showrev, @@ -477,17 +534,5 @@ 'tags': showtags, } -def _showparents(**args): - """:parents: List of strings. The parents of the changeset in "rev:node" - format. If the changeset has only one "natural" parent (the predecessor - revision) nothing is shown.""" - pass - -dockeywords = { - 'parents': _showparents, -} -dockeywords.update(keywords) -del dockeywords['branches'] - # tell hggettext to extract docstrings from these functions: -i18nfunctions = dockeywords.values() +i18nfunctions = keywords.values()
--- a/mercurial/templater.py Mon Oct 05 10:43:16 2015 -0600 +++ b/mercurial/templater.py Wed Oct 07 13:44:48 2015 -0500 @@ -5,12 +5,23 @@ # This software may be used and distributed according to the terms of the # GNU General Public License version 2 or any later version. -from i18n import _ -import os, re -import util, config, templatefilters, templatekw, parser, error -import revset as revsetmod +from __future__ import absolute_import + +import os +import re import types -import minirst + +from .i18n import _ +from . import ( + config, + error, + minirst, + parser, + revset as revsetmod, + templatefilters, + templatekw, + util, +) # template parsing @@ -94,11 +105,8 @@ pos += 4 # skip over double escaped characters continue if program.startswith(quote, pos, end): - try: - # interpret as if it were a part of an outer string - data = program[s:pos].decode('string-escape') - except ValueError: # unbalanced escapes - raise error.ParseError(_("syntax error"), s) + # interpret as if it were a part of an outer string + data = parser.unescapestr(program[s:pos]) if token == 'template': data = _parsetemplate(data, 0, len(data))[0] yield (token, data, s) @@ -147,19 +155,18 @@ n = min((tmpl.find(c, pos, stop) for c in sepchars), key=lambda n: (n < 0, n)) if n < 0: - parsed.append(('string', tmpl[pos:stop].decode('string-escape'))) + parsed.append(('string', parser.unescapestr(tmpl[pos:stop]))) pos = stop break c = tmpl[n] bs = (n - pos) - len(tmpl[pos:n].rstrip('\\')) if bs % 2 == 1: # escaped (e.g. '\{', '\\\{', but not '\\{') - parsed.append(('string', - tmpl[pos:n - 1].decode('string-escape') + c)) + parsed.append(('string', parser.unescapestr(tmpl[pos:n - 1]) + c)) pos = n + 1 continue if n > pos: - parsed.append(('string', tmpl[pos:n].decode('string-escape'))) + parsed.append(('string', parser.unescapestr(tmpl[pos:n]))) if c == quote: return parsed, n + 1 @@ -194,12 +201,6 @@ return getlist(x[1]) + [x[2]] return [x] -def getfilter(exp, context): - f = getsymbol(exp) - if f not in context._filters: - raise error.ParseError(_("unknown function '%s'") % f) - return context._filters[f] - def gettemplate(exp, context): if exp[0] == 'template': return [compileexp(e, context, methods) for e in exp[1]] @@ -210,6 +211,15 @@ return context._load(exp[1]) raise error.ParseError(_("expected template specifier")) +def evalfuncarg(context, mapping, arg): + func, data = arg + # func() may return string, generator of strings or arbitrary object such + # as date tuple, but filter does not want generator. + thing = func(context, mapping, data) + if isinstance(thing, types.GeneratorType): + thing = stringify(thing) + return thing + def runinteger(context, mapping, data): return int(data) @@ -242,24 +252,26 @@ yield func(context, mapping, data) def buildfilter(exp, context): - func, data = compileexp(exp[1], context, methods) - filt = getfilter(exp[2], context) - return (runfilter, (func, data, filt)) + arg = compileexp(exp[1], context, methods) + n = getsymbol(exp[2]) + if n in context._filters: + filt = context._filters[n] + return (runfilter, (arg, filt)) + if n in funcs: + f = funcs[n] + return (f, [arg]) + raise error.ParseError(_("unknown function '%s'") % n) def runfilter(context, mapping, data): - func, data, filt = data - # func() may return string, generator of strings or arbitrary object such - # as date tuple, but filter does not want generator. - thing = func(context, mapping, data) - if isinstance(thing, types.GeneratorType): - thing = stringify(thing) + arg, filt = data + thing = evalfuncarg(context, mapping, arg) try: return filt(thing) except (ValueError, AttributeError, TypeError): - if isinstance(data, tuple): - dt = data[1] + if isinstance(arg[1], tuple): + dt = arg[1][1] else: - dt = data + dt = arg[1] raise util.Abort(_("template filter '%s' is not compatible with " "keyword '%s'") % (filt.func_name, dt)) @@ -297,12 +309,13 @@ if len(args) != 1: raise error.ParseError(_("filter %s expects one argument") % n) f = context._filters[n] - return (runfilter, (args[0][0], args[0][1], f)) + return (runfilter, (args[0], f)) raise error.ParseError(_("unknown function '%s'") % n) def date(context, mapping, args): """:date(date[, fmt]): Format a date. See :hg:`help dates` for formatting - strings.""" + strings. The default is a Unix date format, including the timezone: + "Mon Sep 04 15:13:13 2006 0700".""" if not (1 <= len(args) <= 2): # i18n: "date" is a keyword raise error.ParseError(_("date expects one or two arguments")) @@ -410,7 +423,7 @@ def get(context, mapping, args): """:get(dict, key): Get an attribute/key from an object. Some keywords are complex types. This function allows you to obtain the value of an - attribute on these type.""" + attribute on these types.""" if len(args) != 2: # i18n: "get" is a keyword raise error.ParseError(_("get() expects two arguments")) @@ -499,6 +512,47 @@ # ignore args[0] (the label string) since this is supposed to be a a no-op yield args[1][0](context, mapping, args[1][1]) +def latesttag(context, mapping, args): + """:latesttag([pattern]): The global tags matching the given pattern on the + most recent globally tagged ancestor of this changeset.""" + if len(args) > 1: + # i18n: "latesttag" is a keyword + raise error.ParseError(_("latesttag expects at most one argument")) + + pattern = None + if len(args) == 1: + pattern = stringify(args[0][0](context, mapping, args[0][1])) + + return templatekw.showlatesttags(pattern, **mapping) + +def localdate(context, mapping, args): + """:localdate(date[, tz]): Converts a date to the specified timezone. + The default is local date.""" + if not (1 <= len(args) <= 2): + # i18n: "localdate" is a keyword + raise error.ParseError(_("localdate expects one or two arguments")) + + date = evalfuncarg(context, mapping, args[0]) + try: + date = util.parsedate(date) + except AttributeError: # not str nor date tuple + # i18n: "localdate" is a keyword + raise error.ParseError(_("localdate expects a date information")) + if len(args) >= 2: + tzoffset = None + tz = evalfuncarg(context, mapping, args[1]) + if isinstance(tz, str): + tzoffset = util.parsetimezone(tz) + if tzoffset is None: + try: + tzoffset = int(tz) + except (TypeError, ValueError): + # i18n: "localdate" is a keyword + raise error.ParseError(_("localdate expects a timezone")) + else: + tzoffset = util.makedate()[1] + return (date[0], tzoffset) + def revset(context, mapping, args): """:revset(query[, formatargs...]): Execute a revision set query. See :hg:`help revset`.""" @@ -527,7 +581,7 @@ revs = list([str(r) for r in revs]) revsetcache[raw] = revs - return templatekw.showlist("revision", revs, **mapping) + return templatekw.showrevslist("revision", revs, **mapping) def rstdoc(context, mapping, args): """:rstdoc(text, style): Format ReStructuredText.""" @@ -593,7 +647,8 @@ return shortest def strip(context, mapping, args): - """:strip(text[, chars]): Strip characters from a string.""" + """:strip(text[, chars]): Strip characters from a string. By default, + strips all leading and trailing whitespace.""" if not (1 <= len(args) <= 2): # i18n: "strip" is a keyword raise error.ParseError(_("strip expects one or two arguments")) @@ -614,7 +669,16 @@ pat = stringify(args[0][0](context, mapping, args[0][1])) rpl = stringify(args[1][0](context, mapping, args[1][1])) src = stringify(args[2][0](context, mapping, args[2][1])) - yield re.sub(pat, rpl, src) + try: + patre = re.compile(pat) + except re.error: + # i18n: "sub" is a keyword + raise error.ParseError(_("sub got an invalid pattern: %s") % pat) + try: + yield patre.sub(rpl, src) + except re.error: + # i18n: "sub" is a keyword + raise error.ParseError(_("sub got an invalid replacement: %s") % rpl) def startswith(context, mapping, args): """:startswith(pattern, text): Returns the value from the "text" argument @@ -682,6 +746,8 @@ "indent": indent, "join": join, "label": label, + "latesttag": latesttag, + "localdate": localdate, "pad": pad, "revset": revset, "rstdoc": rstdoc, @@ -740,9 +806,13 @@ filter uses function to transform value. syntax is {key|filter1|filter2|...}.''' - def __init__(self, loader, filters={}, defaults={}): + def __init__(self, loader, filters=None, defaults=None): self._loader = loader + if filters is None: + filters = {} self._filters = filters + if defaults is None: + defaults = {} self._defaults = defaults self._cache = {} @@ -777,12 +847,18 @@ class templater(object): - def __init__(self, mapfile, filters={}, defaults={}, cache={}, + def __init__(self, mapfile, filters=None, defaults=None, cache=None, minchunk=1024, maxchunk=65536): '''set up template engine. mapfile is name of file to read map definitions from. filters is dict of functions. each transforms a value into another. defaults is dict of default map definitions.''' + if filters is None: + filters = {} + if defaults is None: + defaults = {} + if cache is None: + cache = {} self.mapfile = mapfile or 'template' self.cache = cache.copy() self.map = {}
--- a/mercurial/templates/atom/error.tmpl Mon Oct 05 10:43:16 2015 -0600 +++ b/mercurial/templates/atom/error.tmpl Wed Oct 07 13:44:48 2015 -0500 @@ -7,7 +7,7 @@ <updated>1970-01-01T00:00:00+00:00</updated> <entry> <title>Error</title> - <id>http://mercurial.selenic.com/#error</id> + <id>https://mercurial-scm.org/#error</id> <author> <name>mercurial</name> </author>
--- a/mercurial/templates/coal/header.tmpl Mon Oct 05 10:43:16 2015 -0600 +++ b/mercurial/templates/coal/header.tmpl Wed Oct 07 13:44:48 2015 -0500 @@ -3,5 +3,6 @@ <head> <link rel="icon" href="{staticurl|urlescape}hgicon.png" type="image/png" /> <meta name="robots" content="index, nofollow" /> -<link rel="stylesheet" href="{staticurl|urlescape}style-coal.css" type="text/css" /> +<link rel="stylesheet" href="{staticurl|urlescape}style-paper.css" type="text/css" /> +<link rel="stylesheet" href="{staticurl|urlescape}style-extra-coal.css" type="text/css" /> <script type="text/javascript" src="{staticurl|urlescape}mercurial.js"></script>
--- a/mercurial/templates/coal/map Mon Oct 05 10:43:16 2015 -0600 +++ b/mercurial/templates/coal/map Wed Oct 07 13:44:48 2015 -0500 @@ -1,7 +1,5 @@ -default = 'shortlog' +%include paper/map -mimetype = 'text/html; charset={encoding}' -header = header.tmpl footer = ../paper/footer.tmpl search = ../paper/search.tmpl @@ -13,23 +11,6 @@ help = ../paper/help.tmpl helptopics = ../paper/helptopics.tmpl -helpentry = ' - <tr><td> - <a href="{url|urlescape}help/{topic|escape}{sessionvars%urlparameter}"> - {topic|escape} - </a> - </td><td> - {summary|escape} - </td></tr>' - -naventry = '<a href="{url|urlescape}log/{node|short}{sessionvars%urlparameter}">{label|escape}</a> ' -navshortentry = '<a href="{url|urlescape}shortlog/{node|short}{sessionvars%urlparameter}">{label|escape}</a> ' -navgraphentry = '<a href="{url|urlescape}graph/{node|short}{sessionvars%urlparameter}">{label|escape}</a> ' -filenaventry = '<a href="{url|urlescape}log/{node|short}/{file|urlescape}{sessionvars%urlparameter}">{label|escape}</a> ' -filedifflink = '<a href="{url|urlescape}diff/{node|short}/{file|urlescape}{sessionvars%urlparameter}">{file|escape}</a> ' -filenodelink = '<a href="{url|urlescape}file/{node|short}/{file|urlescape}{sessionvars%urlparameter}">{file|escape}</a> ' -filenolink = '{file|escape} ' -fileellipses = '...' diffstatlink = ../paper/diffstat.tmpl diffstatnolink = ../paper/diffstat.tmpl changelogentry = ../paper/shortlogentry.tmpl @@ -37,212 +18,17 @@ changeset = ../paper/changeset.tmpl manifest = ../paper/manifest.tmpl -nav = '{before%naventry} {after%naventry}' -navshort = '{before%navshortentry}{after%navshortentry}' -navgraph = '{before%navgraphentry}{after%navgraphentry}' -filenav = '{before%filenaventry}{after%filenaventry}' - -direntry = ' - <tr class="fileline"> - <td class="name"> - <a href="{url|urlescape}file/{symrev}{path|urlescape}{sessionvars%urlparameter}"> - <img src="{staticurl|urlescape}coal-folder.png" alt="dir."/> {basename|escape}/ - </a> - <a href="{url|urlescape}file/{symrev}{path|urlescape}/{emptydirs|urlescape}{sessionvars%urlparameter}"> - {emptydirs|escape} - </a> - </td> - <td class="size"></td> - <td class="permissions">drwxr-xr-x</td> - </tr>' - -fileentry = ' - <tr class="fileline"> - <td class="filename"> - <a href="{url|urlescape}file/{symrev}/{file|urlescape}{sessionvars%urlparameter}"> - <img src="{staticurl|urlescape}coal-file.png" alt="file"/> {basename|escape} - </a> - </td> - <td class="size">{size}</td> - <td class="permissions">{permissions|permissions}</td> - </tr>' - filerevision = ../paper/filerevision.tmpl fileannotate = ../paper/fileannotate.tmpl filediff = ../paper/filediff.tmpl filecomparison = ../paper/filecomparison.tmpl filelog = ../paper/filelog.tmpl -fileline = ' - <div class="source"><a href="#{lineid}" id="{lineid}">{linenumber}</a> {line|escape}</div>' filelogentry = ../paper/filelogentry.tmpl -annotateline = ' - <tr> - <td class="annotate"> - <a href="{url|urlescape}annotate/{node|short}/{file|urlescape}{sessionvars%urlparameter}#{targetline}" - title="{node|short}: {desc|escape|firstline}">{author|user}@{rev}</a> - </td> - <td class="source"><a href="#{lineid}" id="{lineid}">{linenumber}</a> {line|escape}</td> - </tr>' - -diffblock = '<div class="source bottomline"><pre>{lines}</pre></div>' -difflineplus = '<a href="#{lineid}" id="{lineid}">{linenumber}</a> <span class="plusline">{line|escape}</span>' -difflineminus = '<a href="#{lineid}" id="{lineid}">{linenumber}</a> <span class="minusline">{line|escape}</span>' -difflineat = '<a href="#{lineid}" id="{lineid}">{linenumber}</a> <span class="atline">{line|escape}</span>' -diffline = '<a href="#{lineid}" id="{lineid}">{linenumber}</a> {line|escape}' - -comparisonblock =' - <tbody class="block"> - {lines} - </tbody>' -comparisonline = ' - <tr> - <td class="source {type}"><a href="#{lineid}" id="{lineid}">{leftlinenumber}</a> {leftline|escape}</td> - <td class="source {type}"><a href="#{lineid}" id="{lineid}">{rightlinenumber}</a> {rightline|escape}</td> - </tr>' - -changelogparent = ' - <tr> - <th class="parent">parent {rev}:</th> - <td class="parent"><a href="{url|urlescape}rev/{node|short}{sessionvars%urlparameter}">{node|short}</a></td> - </tr>' - -changesetparent = '<a href="{url|urlescape}rev/{node|short}{sessionvars%urlparameter}">{node|short}</a> ' - -changesetparentdiff = ' - {changesetparent} - {ifeq(node, basenode, '(current diff)', '({difffrom})')}' - -difffrom = '<a href="{url|urlescape}rev/{node|short}:{originalnode|short}{sessionvars%urlparameter}">diff</a>' - -filerevparent = '<a href="{url|urlescape}file/{node|short}/{file|urlescape}{sessionvars%urlparameter}">{rename%filerename}{node|short}</a> ' -filerevchild = '<a href="{url|urlescape}file/{node|short}/{file|urlescape}{sessionvars%urlparameter}">{node|short}</a> ' +tags = ../paper/tags.tmpl +bookmarks = ../paper/bookmarks.tmpl +branches = ../paper/branches.tmpl -filerename = '{file|escape}@' -filelogrename = ' - <span class="base"> - base - <a href="{url|urlescape}file/{node|short}/{file|urlescape}{sessionvars%urlparameter}"> - {file|escape}@{node|short} - </a> - </span>' -fileannotateparent = ' - <tr> - <td class="metatag">parent:</td> - <td> - <a href="{url|urlescape}annotate/{node|short}/{file|urlescape}{sessionvars%urlparameter}"> - {rename%filerename}{node|short} - </a> - </td> - </tr>' -changesetchild = ' <a href="{url|urlescape}rev/{node|short}{sessionvars%urlparameter}">{node|short}</a>' -changelogchild = ' - <tr> - <th class="child">child</th> - <td class="child"> - <a href="{url|urlescape}rev/{node|short}{sessionvars%urlparameter}"> - {node|short} - </a> - </td> - </tr>' -fileannotatechild = ' - <tr> - <td class="metatag">child:</td> - <td> - <a href="{url|urlescape}annotate/{node|short}/{file|urlescape}{sessionvars%urlparameter}"> - {node|short} - </a> - </td> - </tr>' -tags = ../paper/tags.tmpl -tagentry = ' - <tr class="tagEntry"> - <td> - <a href="{url|urlescape}rev/{tag|revescape}{sessionvars%urlparameter}"> - {tag|escape} - </a> - </td> - <td class="node"> - <a href="{url|urlescape}rev/{node|short}{sessionvars%urlparameter}"> - {node|short} - </a> - </td> - </tr>' -bookmarks = ../paper/bookmarks.tmpl -bookmarkentry = ' - <tr class="tagEntry"> - <td> - <a href="{url|urlescape}rev/{bookmark|revescape}{sessionvars%urlparameter}"> - {bookmark|escape} - </a> - </td> - <td class="node"> - <a href="{url|urlescape}rev/{node|short}{sessionvars%urlparameter}"> - {node|short} - </a> - </td> - </tr>' -branches = ../paper/branches.tmpl -branchentry = ' - <tr class="tagEntry"> - <td> - <a href="{url|urlescape}shortlog/{branch|revescape}{sessionvars%urlparameter}" class="{status}"> - {branch|escape} - </a> - </td> - <td class="node"> - <a href="{url|urlescape}shortlog/{node|short}{sessionvars%urlparameter}" class="{status}"> - {node|short} - </a> - </td> - </tr>' -changelogtag = '<span class="tag">{name|escape}</span> ' -changesettag = '<span class="tag">{tag|escape}</span> ' -changesetbookmark = '<span class="tag">{bookmark|escape}</span> ' -changelogbranchhead = '<span class="branchhead">{name|escape}</span> ' -changelogbranchname = '<span class="branchname">{name|escape}</span> ' - -filediffparent = ' - <tr> - <th class="parent">parent {rev}:</th> - <td class="parent"><a href="{url|urlescape}rev/{node|short}{sessionvars%urlparameter}">{node|short}</a></td> - </tr>' -filelogparent = ' - <tr> - <th>parent {rev}:</th> - <td><a href="{url|urlescape}file/{node|short}/{file|urlescape}{sessionvars%urlparameter}">{node|short}</a></td> - </tr>' -filediffchild = ' - <tr> - <th class="child">child {rev}:</th> - <td class="child"><a href="{url|urlescape}rev/{node|short}{sessionvars%urlparameter}">{node|short}</a> - </td> - </tr>' -filelogchild = ' - <tr> - <th>child {rev}:</th> - <td><a href="{url|urlescape}file/{node|short}/{file|urlescape}{sessionvars%urlparameter}">{node|short}</a></td> - </tr>' - -indexentry = ' - <tr> - <td><a href="{url|urlescape}{sessionvars%urlparameter}">{name|escape}</a></td> - <td>{description}</td> - <td>{contact|obfuscate}</td> - <td class="age">{lastchange|rfc822date}</td> - <td class="indexlinks">{archives%indexarchiveentry}</td> - </tr>\n' -indexarchiveentry = '<a href="{url|urlescape}archive/{node|short}{extension|urlescape}"> ↓{type|escape}</a>' index = ../paper/index.tmpl -archiveentry = ' - <li> - <a href="{url|urlescape}archive/{symrev}{extension|urlescape}{ifeq(path,'/','',path|urlescape)}">{type|escape}</a> - </li>' notfound = ../paper/notfound.tmpl error = ../paper/error.tmpl -urlparameter = '{separator}{name}={value|urlescape}' -hiddenformentry = '<input type="hidden" name="{name}" value="{value|escape}" />' -breadcrumb = '> <a href="{url|urlescape}">{name|escape}</a> ' - -searchhint = 'Find changesets by keywords (author, files, the commit message), revision - number or hash, or <a href="{url|urlescape}help/revsets">revset expression</a>.'
--- a/mercurial/templates/gitweb/changeset.tmpl Mon Oct 05 10:43:16 2015 -0600 +++ b/mercurial/templates/gitweb/changeset.tmpl Wed Oct 07 13:44:48 2015 -0500 @@ -52,6 +52,6 @@ {files} </table></div> -<div class="page_body">{diff}</div> +<div class="page_body diffblocks">{diff}</div> {footer}
--- a/mercurial/templates/gitweb/filerevision.tmpl Mon Oct 05 10:43:16 2015 -0600 +++ b/mercurial/templates/gitweb/filerevision.tmpl Wed Oct 07 13:44:48 2015 -0500 @@ -64,7 +64,7 @@ </div> <div class="page_body"> -{text%fileline} +<pre class="sourcelines stripes">{text%fileline}</pre> </div> {footer}
--- a/mercurial/templates/gitweb/map Mon Oct 05 10:43:16 2015 -0600 +++ b/mercurial/templates/gitweb/map Wed Oct 07 13:44:48 2015 -0500 @@ -93,31 +93,33 @@ filecomparison = filecomparison.tmpl filelog = filelog.tmpl fileline = ' - <div style="font-family:monospace" class="parity{parity}"> - <pre><a class="linenr" href="#{lineid}" id="{lineid}">{linenumber}</a> {line|escape}</pre> - </div>' + <span id="{lineid}">{strip(line|escape, '\r\n')}</span><a href="#{lineid}"></a>' annotateline = ' - <tr style="font-family:monospace" class="parity{parity}"> + <tr id="{lineid}" style="font-family:monospace" class="parity{parity}"> <td class="linenr" style="text-align: right;"> <a href="{url|urlescape}annotate/{node|short}/{file|urlescape}{sessionvars%urlparameter}#l{targetline}" title="{node|short}: {desc|escape|firstline}">{author|user}@{rev}</a> </td> - <td><pre><a class="linenr" href="#{lineid}" id="{lineid}">{linenumber}</a></pre></td> + <td><pre><a class="linenr" href="#{lineid}">{linenumber}</a></pre></td> <td><pre>{line|escape}</pre></td> </tr>' -difflineplus = '<span class="difflineplus"><a class="linenr" href="#{lineid}" id="{lineid}">{linenumber}</a> {line|escape}</span>' -difflineminus = '<span class="difflineminus"><a class="linenr" href="#{lineid}" id="{lineid}">{linenumber}</a> {line|escape}</span>' -difflineat = '<span class="difflineat"><a class="linenr" href="#{lineid}" id="{lineid}">{linenumber}</a> {line|escape}</span>' -diffline = '<a class="linenr" href="#{lineid}" id="{lineid}">{linenumber}</a> {line|escape}' +difflineplus = ' + <span id="{lineid}" class="difflineplus">{strip(line|escape, '\r\n')}</span><a href="#{lineid}"></a>' +difflineminus = ' + <span id="{lineid}" class="difflineminus">{strip(line|escape, '\r\n')}</span><a href="#{lineid}"></a>' +difflineat = ' + <span id="{lineid}" class="difflineat">{strip(line|escape, '\r\n')}</span><a href="#{lineid}"></a>' +diffline = ' + <span id="{lineid}">{strip(line|escape, '\r\n')}</span><a href="#{lineid}"></a>' comparisonblock =' <tbody class="block"> {lines} </tbody>' comparisonline = ' - <tr style="font-family:monospace"> - <td class="{type}"><pre><a class="linenr" href="#{lineid}" id="{lineid}">{leftlinenumber}</a> {leftline|escape}</pre></td> - <td class="{type}"><pre><a class="linenr" href="#{lineid}" id="{lineid}">{rightlinenumber}</a> {rightline|escape}</pre></td> + <tr id="{lineid}" style="font-family:monospace"> + <td class="{type}"><pre><a class="linenr" href="#{lineid}">{leftlinenumber}</a> {leftline|escape}</pre></td> + <td class="{type}"><pre><a class="linenr" href="#{lineid}">{rightlinenumber}</a> {rightline|escape}</pre></td> </tr>' changelogparent = ' @@ -193,7 +195,7 @@ tagentry = ' <tr class="parity{parity}"> <td class="age"><i class="age">{date|rfc822date}</i></td> - <td><a class="list" href="{url|urlescape}rev/{node|short}{sessionvars%urlparameter}"><b>{tag|escape}</b></a></td> + <td><a class="list" href="{url|urlescape}rev/{tag|revescape}{sessionvars%urlparameter}"><b>{tag|escape}</b></a></td> <td class="link"> <a href="{url|urlescape}rev/{node|short}{sessionvars%urlparameter}">changeset</a> | <a href="{url|urlescape}log/{node|short}{sessionvars%urlparameter}">changelog</a> | @@ -204,7 +206,7 @@ bookmarkentry = ' <tr class="parity{parity}"> <td class="age"><i class="age">{date|rfc822date}</i></td> - <td><a class="list" href="{url|urlescape}rev/{node|short}{sessionvars%urlparameter}"><b>{bookmark|escape}</b></a></td> + <td><a class="list" href="{url|urlescape}rev/{bookmark|revescape}{sessionvars%urlparameter}"><b>{bookmark|escape}</b></a></td> <td class="link"> <a href="{url|urlescape}rev/{node|short}{sessionvars%urlparameter}">changeset</a> | <a href="{url|urlescape}log/{node|short}{sessionvars%urlparameter}">changelog</a> | @@ -215,15 +217,14 @@ branchentry = ' <tr class="parity{parity}"> <td class="age"><i class="age">{date|rfc822date}</i></td> - <td><a class="list" href="{url|urlescape}shortlog/{node|short}{sessionvars%urlparameter}"><b>{node|short}</b></a></td> - <td class="{status}">{branch|escape}</td> + <td class="{status}"><a class="list" href="{url|urlescape}shortlog/{branch|revescape}{sessionvars%urlparameter}"><b>{branch|escape}</b></a></td> <td class="link"> <a href="{url|urlescape}changeset/{node|short}{sessionvars%urlparameter}">changeset</a> | <a href="{url|urlescape}log/{node|short}{sessionvars%urlparameter}">changelog</a> | <a href="{url|urlescape}file/{node|short}{sessionvars%urlparameter}">files</a> </td> </tr>' -diffblock = '<pre>{lines}</pre>' +diffblock = '<div class="diffblock"><pre class="sourcelines">{lines}</pre></div>' filediffparent = ' <tr> <td>parent {rev}</td> @@ -290,6 +291,7 @@ filelogentry = ' <tr class="parity{parity}"> <td class="age"><i class="age">{date|rfc822date}</i></td> + <td><i>{author|person}</i></td> <td> <a class="list" href="{url|urlescape}rev/{node|short}{sessionvars%urlparameter}"> <b>{desc|strip|firstline|escape|nonempty}</b>
--- a/mercurial/templates/map-cmdline.xml Mon Oct 05 10:43:16 2015 -0600 +++ b/mercurial/templates/map-cmdline.xml Wed Oct 07 13:44:48 2015 -0500 @@ -1,5 +1,5 @@ -header = '<?xml version="1.0"?>\n<log>\n' -footer = '</log>\n' +docheader = '<?xml version="1.0"?>\n<log>\n' +docfooter = '</log>\n' changeset = '<logentry revision="{rev}" node="{node}">\n{branches}{bookmarks}{tags}{parents}<author email="{author|email|xmlescape}">{author|person|xmlescape}</author>\n<date>{date|rfc3339date}</date>\n<msg xml:space="preserve">{desc|xmlescape}</msg>\n</logentry>\n' changeset_verbose = '<logentry revision="{rev}" node="{node}">\n{branches}{bookmarks}{tags}{parents}<author email="{author|email|xmlescape}">{author|person|xmlescape}</author>\n<date>{date|rfc3339date}</date>\n<msg xml:space="preserve">{desc|xmlescape}</msg>\n<paths>\n{file_adds}{file_dels}{file_mods}</paths>\n{file_copies}</logentry>\n'
--- a/mercurial/templates/monoblue/changeset.tmpl Mon Oct 05 10:43:16 2015 -0600 +++ b/mercurial/templates/monoblue/changeset.tmpl Wed Oct 07 13:44:48 2015 -0500 @@ -59,7 +59,7 @@ {files} </table> - <div class="diff"> + <div class="diff diffblocks"> {diff} </div>
--- a/mercurial/templates/monoblue/filediff.tmpl Mon Oct 05 10:43:16 2015 -0600 +++ b/mercurial/templates/monoblue/filediff.tmpl Wed Oct 07 13:44:48 2015 -0500 @@ -50,7 +50,7 @@ {child%filediffchild} </dl> - <div class="diff"> + <div class="diff diffblocks"> {diff} </div>
--- a/mercurial/templates/monoblue/filerevision.tmpl Mon Oct 05 10:43:16 2015 -0600 +++ b/mercurial/templates/monoblue/filerevision.tmpl Wed Oct 07 13:44:48 2015 -0500 @@ -60,7 +60,7 @@ <p class="description">{desc|strip|escape|websub|addbreaks|nonempty}</p> <div class="source"> - {text%fileline} + <pre class="sourcelines stripes">{text%fileline}</pre> </div> {footer}
--- a/mercurial/templates/monoblue/footer.tmpl Mon Oct 05 10:43:16 2015 -0600 +++ b/mercurial/templates/monoblue/footer.tmpl Wed Oct 07 13:44:48 2015 -0500 @@ -12,11 +12,6 @@ <p><a href="{logourl}" title="Mercurial"><img src="{staticurl|urlescape}{logoimg}" width=75 height=90 border=0 alt="mercurial" /></a></p> </div> - <div id="corner-top-left"></div> - <div id="corner-top-right"></div> - <div id="corner-bottom-left"></div> - <div id="corner-bottom-right"></div> - </div> </body>
--- a/mercurial/templates/monoblue/index.tmpl Mon Oct 05 10:43:16 2015 -0600 +++ b/mercurial/templates/monoblue/index.tmpl Wed Oct 07 13:44:48 2015 -0500 @@ -29,11 +29,6 @@ <p><a href="{logourl}" title="Mercurial"><img src="{staticurl|urlescape}{logoimg}" width=75 height=90 border=0 alt="mercurial"></a></p> </div> - <div id="corner-top-left"></div> - <div id="corner-top-right"></div> - <div id="corner-bottom-left"></div> - <div id="corner-bottom-right"></div> - </div> <script type="text/javascript">process_dates()</script> </body>
--- a/mercurial/templates/monoblue/map Mon Oct 05 10:43:16 2015 -0600 +++ b/mercurial/templates/monoblue/map Wed Oct 07 13:44:48 2015 -0500 @@ -89,33 +89,35 @@ filecomparison = filecomparison.tmpl filelog = filelog.tmpl fileline = ' - <div style="font-family:monospace" class="parity{parity}"> - <pre><a class="linenr" href="#{lineid}" id="{lineid}">{linenumber}</a> {line|escape}</pre> - </div>' + <span id="{lineid}">{strip(line|escape, '\r\n')}</span><a href="#{lineid}"></a>' annotateline = ' - <tr class="parity{parity}"> + <tr id="{lineid}" class="parity{parity}"> <td class="linenr"> <a href="{url|urlescape}annotate/{node|short}/{file|urlescape}{sessionvars%urlparameter}#l{targetline}" title="{node|short}: {desc|escape|firstline}">{author|user}@{rev}</a> </td> <td class="lineno"> - <a href="#{lineid}" id="{lineid}">{linenumber}</a> + <a href="#{lineid}">{linenumber}</a> </td> <td class="source">{line|escape}</td> </tr>' -difflineplus = '<span class="difflineplus"><a class="linenr" href="#{lineid}" id="{lineid}">{linenumber}</a> {line|escape}</span>' -difflineminus = '<span class="difflineminus"><a class="linenr" href="#{lineid}" id="{lineid}">{linenumber}</a> {line|escape}</span>' -difflineat = '<span class="difflineat"><a class="linenr" href="#{lineid}" id="{lineid}">{linenumber}</a> {line|escape}</span>' -diffline = '<span><a class="linenr" href="#{lineid}" id="{lineid}">{linenumber}</a> {line|escape}</span>' +difflineplus = ' + <span id="{lineid}" class="difflineplus">{strip(line|escape, '\r\n')}</span><a href="#{lineid}"></a>' +difflineminus = ' + <span id="{lineid}" class="difflineminus">{strip(line|escape, '\r\n')}</span><a href="#{lineid}"></a>' +difflineat = ' + <span id="{lineid}" class="difflineat">{strip(line|escape, '\r\n')}</span><a href="#{lineid}"></a>' +diffline = ' + <span id="{lineid}">{strip(line|escape, '\r\n')}</span><a href="#{lineid}"></a>' comparisonblock =' <tbody class="block"> {lines} </tbody>' comparisonline = ' - <tr> - <td class="source {type}"><a class="linenr" href="#{lineid}" id="{lineid}">{leftlinenumber}</a> {leftline|escape}</td> - <td class="source {type}"><a class="linenr" href="#{lineid}" id="{lineid}">{rightlinenumber}</a> {rightline|escape}</td> + <tr id="{lineid}"> + <td class="source {type}"><a class="linenr" href="#{lineid}">{leftlinenumber}</a> {leftline|escape}</td> + <td class="source {type}"><a class="linenr" href="#{lineid}">{rightlinenumber}</a> {rightline|escape}</td> </tr>' changesetlink = '<a href="{url|urlescape}rev/{node|short}{sessionvars%urlparameter}">{node|short}</a>' @@ -171,7 +173,7 @@ tagentry = ' <tr class="parity{parity}"> <td class="nowrap age">{date|rfc822date}</td> - <td><a href="{url|urlescape}rev/{node|short}{sessionvars%urlparameter}">{tag|escape}</a></td> + <td><a href="{url|urlescape}rev/{tag|revescape}{sessionvars%urlparameter}">{tag|escape}</a></td> <td class="nowrap"> <a href="{url|urlescape}rev/{node|short}{sessionvars%urlparameter}">changeset</a> | <a href="{url|urlescape}log/{node|short}{sessionvars%urlparameter}">changelog</a> | @@ -182,7 +184,7 @@ bookmarkentry = ' <tr class="parity{parity}"> <td class="nowrap age">{date|rfc822date}</td> - <td><a href="{url|urlescape}rev/{node|short}{sessionvars%urlparameter}">{bookmark|escape}</a></td> + <td><a href="{url|urlescape}rev/{bookmark|revescape}{sessionvars%urlparameter}">{bookmark|escape}</a></td> <td class="nowrap"> <a href="{url|urlescape}rev/{node|short}{sessionvars%urlparameter}">changeset</a> | <a href="{url|urlescape}log/{node|short}{sessionvars%urlparameter}">changelog</a> | @@ -193,15 +195,14 @@ branchentry = ' <tr class="parity{parity}"> <td class="nowrap age">{date|rfc822date}</td> - <td><a href="{url|urlescape}shortlog/{node|short}{sessionvars%urlparameter}">{node|short}</a></td> - <td class="{status}">{branch|escape}</td> + <td class="{status}"><a href="{url|urlescape}shortlog/{branch|revescape}{sessionvars%urlparameter}">{branch|escape}</a></td> <td class="nowrap"> <a href="{url|urlescape}rev/{node|short}{sessionvars%urlparameter}">changeset</a> | <a href="{url|urlescape}log/{node|short}{sessionvars%urlparameter}">changelog</a> | <a href="{url|urlescape}file/{node|short}{sessionvars%urlparameter}">files</a> </td> </tr>' -diffblock = '<pre>{lines}</pre>' +diffblock = '<div class="diffblock"><pre class="sourcelines">{lines}</pre></div>' filediffparent = ' <dt>parent {rev}</dt> <dd><a href="{url|urlescape}diff/{node|short}/{file|urlescape}{sessionvars%urlparameter}">{node|short}</a></dd>' @@ -247,6 +248,7 @@ filelogentry = ' <tr class="parity{parity}"> <td class="nowrap age">{date|rfc822date}</td> + <td>{author|person}</td> <td> <a href="{url|urlescape}rev/{node|short}{sessionvars%urlparameter}"> {desc|strip|firstline|escape|nonempty} @@ -254,7 +256,9 @@ </a> </td> <td class="nowrap"> - <a href="{url|urlescape}file/{node|short}/{file|urlescape}{sessionvars%urlparameter}">file</a> | <a href="{url|urlescape}diff/{node|short}/{file|urlescape}{sessionvars%urlparameter}">diff</a> | <a href="{url|urlescape}annotate/{node|short}/{file|urlescape}{sessionvars%urlparameter}">annotate</a> + <a href="{url|urlescape}file/{node|short}/{file|urlescape}{sessionvars%urlparameter}">file</a> | + <a href="{url|urlescape}diff/{node|short}/{file|urlescape}{sessionvars%urlparameter}">diff</a> | + <a href="{url|urlescape}annotate/{node|short}/{file|urlescape}{sessionvars%urlparameter}">annotate</a> {rename%filelogrename} </td> </tr>'
--- a/mercurial/templates/monoblue/summary.tmpl Mon Oct 05 10:43:16 2015 -0600 +++ b/mercurial/templates/monoblue/summary.tmpl Wed Oct 07 13:44:48 2015 -0500 @@ -26,6 +26,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{sessionvars%urlparameter}">files</a></li> + {archives%archiveentry} <li><a href="{url|urlescape}help{sessionvars%urlparameter}">help</a></li> </ul> </div>
--- a/mercurial/templates/paper/filerevision.tmpl Mon Oct 05 10:43:16 2015 -0600 +++ b/mercurial/templates/paper/filerevision.tmpl Wed Oct 07 13:44:48 2015 -0500 @@ -71,8 +71,7 @@ <div class="overflow"> <div class="sourcefirst linewraptoggle">line wrap: <a class="linewraplink" href="javascript:toggleLinewrap()">on</a></div> <div class="sourcefirst"> line source</div> -<pre class="sourcelines stripes4 wrap">{text%fileline}</pre> -<div class="sourcelast"></div> +<pre class="sourcelines stripes4 wrap bottomline">{text%fileline}</pre> </div> </div> </div>
--- a/mercurial/templates/rss/error.tmpl Mon Oct 05 10:43:16 2015 -0600 +++ b/mercurial/templates/rss/error.tmpl Wed Oct 07 13:44:48 2015 -0500 @@ -4,7 +4,7 @@ <item> <title>Error</title> <description>{error|escape}</description> - <guid>http://mercurial.selenic.com/#error</guid> + <guid>https://mercurial-scm.org/#error</guid> </item> </channel> </rss>
--- a/mercurial/templates/static/style-coal.css Mon Oct 05 10:43:16 2015 -0600 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,368 +0,0 @@ -body { - margin: 0; - padding: 0; - background: black url(background.png) repeat-x; - font-family: sans-serif; -} - -.container { - padding-right: 150px; -} - -.main { - position: relative; - background: white; - padding: 2em; - border-right: 15px solid black; - border-bottom: 15px solid black; -} - -#.main { - width: 98%; -} - -.overflow { - width: 100%; - overflow: auto; -} - -.menu { - background: #999; - padding: 10px; - width: 75px; - margin: 0; - font-size: 80%; - text-align: left; - position: fixed; - top: 27px; - left: auto; - right: 27px; -} - -#.menu { - position: absolute !important; - top:expression(eval(document.body.scrollTop + 27)); -} - -.menu ul { - list-style: none; - padding: 0; - margin: 10px 0 0 0; -} - -.menu li { - margin-bottom: 3px; - padding: 2px 4px; - background: white; - color: black; - font-weight: normal; -} - -.menu li.active { - background: black; - color: white; -} - -.menu img { - width: 75px; - height: 90px; - border: 0; -} - -.menu a { color: black; display: block; } - -.search { - position: absolute; - top: .7em; - right: 2em; -} - -form.search div#hint { - display: none; - position: absolute; - top: 40px; - right: 0px; - width: 190px; - padding: 5px; - background: #ffc; - font-size: 70%; - border: 1px solid yellow; - -moz-border-radius: 5px; /* this works only in camino/firefox */ - -webkit-border-radius: 5px; /* this is just for Safari */ -} - -form.search:hover div#hint { display: block; } - -a { text-decoration:none; } -.age { white-space:nowrap; } -.date { white-space:nowrap; } -.indexlinks { white-space:nowrap; } -.parity0, -.stripes4 > :nth-child(4n+1), -.stripes2 > :nth-child(2n+1) { background-color: #f0f0f0; } -.parity1, -.stripes4 > :nth-child(4n+3), -.stripes2 > :nth-child(2n+2) { background-color: white; } -.plusline { color: green; } -.minusline { color: #dc143c; } /* crimson */ -.atline { color: purple; } - -.diffstat-file { - white-space: nowrap; - font-size: 90%; -} -.diffstat-total { - white-space: nowrap; - font-size: 90%; -} -.diffstat-graph { - width: 100%; -} -.diffstat-add { - background-color: green; - float: left; -} -.diffstat-remove { - background-color: red; - float: left; -} - -.navigate { - text-align: right; - font-size: 60%; - margin: 1em 0; -} - -.tag { - color: #999; - font-size: 70%; - font-weight: normal; - margin-left: .5em; - vertical-align: baseline; -} - -.branchhead { - color: #000; - font-size: 80%; - font-weight: normal; - margin-left: .5em; - vertical-align: baseline; -} - -ul#graphnodes .branchhead { - font-size: 75%; -} - -.branchname { - color: #000; - font-size: 60%; - font-weight: normal; - margin-left: .5em; - vertical-align: baseline; -} - -h3 .branchname { - font-size: 80%; -} - -/* Common */ -pre { margin: 0; } - -h2 { font-size: 120%; border-bottom: 1px solid #999; } -h2 a { color: #000; } -h3 { - margin-top: -.7em; - font-size: 100%; -} - -/* log and tags tables */ -.bigtable { - border-bottom: 1px solid #999; - border-collapse: collapse; - font-size: 90%; - width: 100%; - font-weight: normal; - text-align: left; -} - -.bigtable td { - vertical-align: top; -} - -.bigtable th { - padding: 1px 4px; - border-bottom: 1px solid #999; -} -.bigtable tr { border: none; } -.bigtable .age { width: 6em; } -.bigtable .author { width: 15em; } -.bigtable .description { } -.bigtable .description .base { font-size: 70%; float: right; line-height: 1.66; } -.bigtable .node { width: 5em; font-family: monospace;} -.bigtable .lineno { width: 2em; text-align: right;} -.bigtable .lineno a { color: #999; font-size: smaller; font-family: monospace;} -.bigtable .permissions { width: 8em; text-align: left;} -.bigtable .size { width: 5em; text-align: right; } -.bigtable .annotate { text-align: right; } -.bigtable td.annotate { font-size: smaller; } -.bigtable td.source { font-size: inherit; } - -.source, .sourcefirst, .sourcelast { - font-family: monospace; - white-space: pre; - padding: 1px 4px; - font-size: 90%; -} -.sourcefirst { border-bottom: 1px solid #999; font-weight: bold; } -.sourcelast { border-top: 1px solid #999; } -.source a { color: #999; font-size: smaller; font-family: monospace;} -.bottomline { border-bottom: 1px solid #999; } - -.sourcelines > div { - display: inline-block; - width: 100%; - padding: 1px 0px; - counter-increment: lineno; -} - -.fileline { font-family: monospace; } -.fileline img { border: 0; } - -.tagEntry .closed { color: #99f; } - -/* Changeset entry */ -#changesetEntry { - border-collapse: collapse; - font-size: 90%; - width: 100%; - margin-bottom: 1em; -} - -#changesetEntry th { - padding: 1px 4px; - width: 4em; - text-align: right; - font-weight: normal; - color: #999; - margin-right: .5em; - vertical-align: top; -} - -div.description { - border-left: 3px solid #999; - margin: 1em 0 1em 0; - padding: .3em; - white-space: pre; - font-family: monospace; -} - -/* Graph */ -div#wrapper { - position: relative; - border-top: 1px solid black; - border-bottom: 1px solid black; - margin: 0; - padding: 0; -} - -canvas { - position: absolute; - z-index: 5; - top: -0.7em; - margin: 0; -} - -ul#graphnodes { - position: absolute; - z-index: 10; - top: -1.0em; - list-style: none inside none; - padding: 0; -} - -ul#nodebgs { - list-style: none inside none; - padding: 0; - margin: 0; - top: -0.7em; -} - -ul#graphnodes li, ul#nodebgs li { - height: 39px; -} - -ul#graphnodes li .info { - display: block; - font-size: 70%; - position: relative; - top: -3px; -} - -/* Comparison */ -.legend { - padding: 1.5% 0 1.5% 0; -} - -.legendinfo { - border: 1px solid #999; - font-size: 80%; - text-align: center; - padding: 0.5%; -} - -.equal { - background-color: #ffffff; -} - -.delete { - background-color: #faa; - color: #333; -} - -.insert { - background-color: #ffa; -} - -.replace { - background-color: #e8e8e8; -} - -.header { - text-align: center; -} - -.block { - border-top: 1px solid #999; -} - -.breadcrumb { - color: gray; -} - -.breadcrumb a { - color: blue; -} - -.scroll-loading { - -webkit-animation: change_color 1s linear 0s infinite alternate; - -moz-animation: change_color 1s linear 0s infinite alternate; - -o-animation: change_color 1s linear 0s infinite alternate; - animation: change_color 1s linear 0s infinite alternate; -} - -@-webkit-keyframes change_color { - from { background-color: #A0CEFF; } to { } -} -@-moz-keyframes change_color { - from { background-color: #A0CEFF; } to { } -} -@-o-keyframes change_color { - from { background-color: #A0CEFF; } to { } -} -@keyframes change_color { - from { background-color: #A0CEFF; } to { } -} - -.scroll-loading-error { - background-color: #FFCCCC !important; -}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/mercurial/templates/static/style-extra-coal.css Wed Oct 07 13:44:48 2015 -0500 @@ -0,0 +1,46 @@ +body { + background: black url('background.png') repeat-x; +} + +.container { + padding-left: 0; + padding-right: 150px; +} + +.main { + padding: 2em; + border-right: 15px solid black; + border-bottom: 15px solid black; +} + +.menu { + background: #999; + padding: 10px; + width: 75px; + position: fixed; + top: 27px; + left: auto; + right: 27px; +} + +.menu ul { + border-left: 0; +} + +.menu li.active { + font-weight: normal; + background: black; + color: white; +} + +.menu li.active a { + color: white; +} + +h3 { + margin-top: -.7em; +} + +div.description { + border-left-width: 3px; +}
--- a/mercurial/templates/static/style-gitweb.css Mon Oct 05 10:43:16 2015 -0600 +++ b/mercurial/templates/static/style-gitweb.css Wed Oct 07 13:44:48 2015 -0500 @@ -29,9 +29,9 @@ a.list:hover { text-decoration:underline; color:#880000; } table { padding:8px 4px; } th { padding:2px 5px; font-size:12px; text-align:left; } -tr.light:hover, .parity0:hover { background-color:#edece6; } -tr.dark, .parity1 { background-color:#f6f6f0; } -tr.dark:hover, .parity1:hover { background-color:#edece6; } +tr.light:hover, .parity0:hover, pre.sourcelines.stripes > :nth-child(4n+1):hover { background-color:#edece6; } +tr.dark, .parity1, pre.sourcelines.stripes > :nth-child(4n+3) { background-color:#f6f6f0; } +tr.dark:hover, .parity1:hover, pre.sourcelines.stripes > :nth-child(4n+3):hover { background-color:#edece6; } td { padding:2px 5px; font-size:12px; vertical-align:top; } td.closed { background-color: #99f; } td.link { padding:2px 5px; font-family:sans-serif; font-size:10px; } @@ -87,6 +87,43 @@ span.difflineplus { color:#008800; } span.difflineminus { color:#cc0000; } span.difflineat { color:#990099; } +div.diffblocks { counter-reset: lineno; } +div.diffblock { counter-increment: lineno; } +pre.sourcelines { position: relative; counter-reset: lineno; } +pre.sourcelines > span { + display: inline-block; + box-sizing: border-box; + width: 100%; + padding: 0 0 0 5em; + counter-increment: lineno; + vertical-align: top; +} +pre.sourcelines > span:before { + -moz-user-select: -moz-none; + -khtml-user-select: none; + -webkit-user-select: none; + -ms-user-select: none; + user-select: none; + display: inline-block; + margin-left: -5em; + width: 4em; + color: #999; + text-align: right; + content: counters(lineno,"."); + float: left; +} +pre.sourcelines > a { + display: inline-block; + position: absolute; + left: 0px; + width: 4em; + height: 1em; +} +tr:target td, +pre.sourcelines > span:target, +pre.sourcelines.stripes > span:target { + background-color: #bfdfff; +} /* Graph */ div#wrapper { @@ -194,3 +231,7 @@ .scroll-loading-error { background-color: #FFCCCC !important; } + +#doc { + margin: 0 8px; +}
--- a/mercurial/templates/static/style-monoblue.css Mon Oct 05 10:43:16 2015 -0600 +++ b/mercurial/templates/static/style-monoblue.css Wed Oct 07 13:44:48 2015 -0500 @@ -255,34 +255,63 @@ font-family: monospace; white-space: pre; font-size: 1.2em; - padding: 3px 0; } +div.diffblocks { counter-reset: lineno; } +div.diffblock { counter-increment: lineno; } span.difflineplus { color:#008800; } span.difflineminus { color:#cc0000; } span.difflineat { color:#990099; } +pre.sourcelines { position: relative; counter-reset: lineno; } +pre.sourcelines > span { + display: inline-block; + box-sizing: border-box; + width: 100%; + padding: 0 0 0 5em; + font-size: 1.2em; + counter-increment: lineno; + vertical-align: top; +} +div.source > pre.sourcelines > span { + padding: 1px 1px 1px 5em; +} +pre.sourcelines > span:before { + -moz-user-select: -moz-none; + -khtml-user-select: none; + -webkit-user-select: none; + -ms-user-select: none; + user-select: none; + display: inline-block; + margin-left: -5em; + width: 4em; + color: #999; + text-align: right; + content: counters(lineno,"."); + float: left; +} +pre.sourcelines > a { + display: inline-block; + position: absolute; + left: 0px; + width: 4em; + height: 1em; +} +pre.sourcelines.stripes > :nth-child(4n+1) { background-color: #F1F6F7; } +pre.sourcelines.stripes > :nth-child(4n+3) { background-color: #FFFFFF; } +pre.sourcelines.stripes > :nth-child(4n+1):hover, +pre.sourcelines.stripes > :nth-child(4n+3):hover { background-color: #D5E1E6; } +tr:target td, +pre.sourcelines > span:target, +pre.sourcelines.stripes > span:target { + background-color: #bfdfff; +} + td.source { white-space: pre; - font-family: monospace; margin: 10px 30px 0; font-size: 1.2em; font-family: monospace; } - div.source div.parity0, - div.source div.parity1 { - padding: 1px; - font-size: 1.2em; - } - div.source div.parity0 { - background: #F1F6F7; - } - div.source div.parity1 { - background: #FFFFFF; - } -div.parity0:hover, -div.parity1:hover { - background: #D5E1E6; -} .linenr { color: #999; text-align: right; @@ -311,44 +340,6 @@ div#powered-by a:hover { text-decoration: underline; } -/* -div#monoblue-corner-top-left { - position: absolute; - top: 0; - left: 0; - width: 10px; - height: 10px; - background: url(./monoblue-corner.png) top left no-repeat !important; - background: none; -} -div#monoblue-corner-top-right { - position: absolute; - top: 0; - right: 0; - width: 10px; - height: 10px; - background: url(./monoblue-corner.png) top right no-repeat !important; - background: none; -} -div#monoblue-corner-bottom-left { - position: absolute; - bottom: 0; - left: 0; - width: 10px; - height: 10px; - background: url(./monoblue-corner.png) bottom left no-repeat !important; - background: none; -} -div#monoblue-corner-bottom-right { - position: absolute; - bottom: 0; - right: 0; - width: 10px; - height: 10px; - background: url(./monoblue-corner.png) bottom right no-repeat !important; - background: none; -} -*/ /** end of common settings **/ /** summary **/ @@ -553,3 +544,7 @@ .scroll-loading-error { background-color: #FFCCCC !important; } + +#doc { + margin: 0 30px; +}
--- a/mercurial/templates/static/style-paper.css Mon Oct 05 10:43:16 2015 -0600 +++ b/mercurial/templates/static/style-paper.css Wed Oct 07 13:44:48 2015 -0500 @@ -209,14 +209,13 @@ .bigtable td.annotate { font-size: smaller; } .bigtable td.source { font-size: inherit; } -.source, .sourcefirst, .sourcelast { +.source, .sourcefirst { font-family: monospace; white-space: pre; padding: 1px 4px; font-size: 90%; } .sourcefirst { border-bottom: 1px solid #999; font-weight: bold; } -.sourcelast { border-top: 1px solid #999; } .source a { color: #999; font-size: smaller; font-family: monospace;} .bottomline { border-bottom: 1px solid #999; }
--- a/mercurial/transaction.py Mon Oct 05 10:43:16 2015 -0600 +++ b/mercurial/transaction.py Wed Oct 07 13:44:48 2015 -0500 @@ -11,9 +11,15 @@ # This software may be used and distributed according to the terms of the # GNU General Public License version 2 or any later version. -from i18n import _ +from __future__ import absolute_import + import errno -import error, util + +from .i18n import _ +from . import ( + error, + util, +) version = 2
--- a/mercurial/treediscovery.py Mon Oct 05 10:43:16 2015 -0600 +++ b/mercurial/treediscovery.py Wed Oct 07 13:44:48 2015 -0500 @@ -5,10 +5,19 @@ # 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 collections -from node import nullid, short -from i18n import _ -import util, error + +from .i18n import _ +from .node import ( + nullid, + short, +) +from . import ( + error, + util, +) def findcommonincoming(repo, remote, heads=None, force=False): """Return a tuple (common, fetch, heads) used to identify the common
--- a/mercurial/ui.py Mon Oct 05 10:43:16 2015 -0600 +++ b/mercurial/ui.py Wed Oct 07 13:44:48 2015 -0500 @@ -5,11 +5,28 @@ # 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 errno +import getpass import inspect -from i18n import _ -import errno, getpass, os, socket, sys, tempfile, traceback -import config, scmutil, util, error, formatter, progress -from node import hex +import os +import socket +import sys +import tempfile +import traceback + +from .i18n import _ +from .node import hex + +from . import ( + config, + error, + formatter, + progress, + scmutil, + util, +) samplehgrcs = { 'user': @@ -533,12 +550,21 @@ def expandpath(self, loc, default=None): """Return repository location relative to cwd or from [paths]""" - if util.hasscheme(loc) or os.path.isdir(os.path.join(loc, '.hg')): - return loc + try: + p = self.paths.getpath(loc) + if p: + return p.rawloc + except error.RepoError: + pass - p = self.paths.getpath(loc, default=default) - if p: - return p.loc + if default: + try: + p = self.paths.getpath(default) + if p: + return p.rawloc + except error.RepoError: + pass + return loc @util.propertycache @@ -808,7 +834,9 @@ if self.debugflag: opts['label'] = opts.get('label', '') + ' ui.debug' self.write(*msg, **opts) - def edit(self, text, user, extra={}, editform=None): + def edit(self, text, user, extra=None, editform=None): + if extra is None: + extra = {} (fd, name) = tempfile.mkstemp(prefix="hg-editor-", suffix=".txt", text=True) try: @@ -840,7 +868,7 @@ return t - def system(self, cmd, environ={}, cwd=None, onerr=None, errprefix=None): + def system(self, cmd, environ=None, cwd=None, onerr=None, errprefix=None): '''execute shell command with appropriate output stream. command output will be redirected if fout is not stdout. ''' @@ -943,9 +971,12 @@ service should be a readily-identifiable subsystem, which will allow filtering. - message should be a newline-terminated string to log. + + *msg should be a newline-terminated format string to log, and + then any values to %-format into that format string. + + **opts currently has no defined meanings. ''' - pass def label(self, msg, label): '''style msg based on supplied label @@ -963,7 +994,7 @@ """issue a developer warning message""" msg = 'devel-warn: ' + msg if self.tracebackflag: - util.debugstacktrace(msg, 2) + util.debugstacktrace(msg, 2, self.ferr, self.fout) else: curframe = inspect.currentframe() calframe = inspect.getouterframes(curframe, 2) @@ -982,37 +1013,102 @@ # No location is the same as not existing. if not loc: continue + + # TODO ignore default-push once all consumers stop referencing it + # since it is handled specifically below. + self[name] = path(name, rawloc=loc) + # Handle default-push, which is a one-off that defines the push URL for + # the "default" path. + defaultpush = ui.config('paths', 'default-push') + if defaultpush and 'default' in self: + self['default']._pushloc = defaultpush + def getpath(self, name, default=None): - """Return a ``path`` for the specified name, falling back to a default. + """Return a ``path`` from a string, falling back to a default. + + ``name`` can be a named path or locations. Locations are filesystem + paths or URIs. - Returns the first of ``name`` or ``default`` that is present, or None - if neither is present. + Returns None if ``name`` is not a registered path, a URI, or a local + path to a repo. """ + # Only fall back to default if no path was requested. + if name is None: + if default: + try: + return self[default] + except KeyError: + return None + else: + return None + + # Most likely empty string. + # This may need to raise in the future. + if not name: + return None + try: return self[name] except KeyError: - if default is not None: - try: - return self[default] - except KeyError: - pass + # Try to resolve as a local path or URI. + try: + return path(None, rawloc=name) + except ValueError: + raise error.RepoError(_('repository %s does not exist') % + name) - return None + assert False class path(object): """Represents an individual path and its configuration.""" - def __init__(self, name, rawloc=None): + def __init__(self, name, rawloc=None, pushloc=None): """Construct a path from its config options. ``name`` is the symbolic name of the path. ``rawloc`` is the raw location, as defined in the config. + ``pushloc`` is the raw locations pushes should be made to. + + If ``name`` is not defined, we require that the location be a) a local + filesystem path with a .hg directory or b) a URL. If not, + ``ValueError`` is raised. """ + if not rawloc: + raise ValueError('rawloc must be defined') + + # Locations may define branches via syntax <base>#<branch>. + u = util.url(rawloc) + branch = None + if u.fragment: + branch = u.fragment + u.fragment = None + + self.url = u + self.branch = branch + self.name = name - # We'll do more intelligent things with rawloc in the future. - self.loc = rawloc + self.rawloc = rawloc + self.loc = str(u) + self._pushloc = pushloc + + # When given a raw location but not a symbolic name, validate the + # location is valid. + if not name and not u.scheme and not self._isvalidlocalpath(self.loc): + raise ValueError('location is not a URL or path to a local ' + 'repo: %s' % rawloc) + + def _isvalidlocalpath(self, path): + """Returns True if the given path is a potentially valid repository. + This is its own function so that extensions can change the definition of + 'valid' in this case (like when pulling from a git repo into a hg + one).""" + return os.path.isdir(os.path.join(path, '.hg')) + + @property + def pushloc(self): + return self._pushloc or self.loc # we instantiate one globally shared progress bar to avoid # competing progress bars when multiple UI objects get created
--- a/mercurial/unionrepo.py Mon Oct 05 10:43:16 2015 -0600 +++ b/mercurial/unionrepo.py Wed Oct 07 13:44:48 2015 -0500 @@ -11,11 +11,25 @@ allowing operations like diff and log with revsets. """ -from node import nullid -from i18n import _ +from __future__ import absolute_import + import os -import util, mdiff, cmdutil, scmutil -import localrepo, changelog, manifest, filelog, revlog, pathutil + +from .i18n import _ +from .node import nullid + +from . import ( + changelog, + cmdutil, + filelog, + localrepo, + manifest, + mdiff, + pathutil, + revlog, + scmutil, + util, +) class unionrevlog(revlog.revlog): def __init__(self, opener, indexfile, revlog2, linkmapper): @@ -35,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 if linkmapper is None: # link is to same revlog assert linkrev == rev2 # we never link back @@ -43,6 +57,9 @@ else: # rev must be mapped from repo2 cl to unified cl by linkmapper link = linkmapper(linkrev) + if linkmapper is not None: # link is to same revlog + base = linkmapper(base) + if node in self.nodemap: # this happens for the common revlog revisions self.bundlerevs.add(self.nodemap[node]) @@ -51,7 +68,7 @@ p1node = self.revlog2.node(p1rev) p2node = self.revlog2.node(p2rev) - e = (None, None, None, None, + e = (None, None, None, base, link, self.rev(p1node), self.rev(p2node), node) self.index.insert(-1, e) self.nodemap[node] = n
--- a/mercurial/url.py Mon Oct 05 10:43:16 2015 -0600 +++ b/mercurial/url.py Wed Oct 07 13:44:48 2015 -0500 @@ -7,10 +7,23 @@ # This software may be used and distributed according to the terms of the # GNU General Public License version 2 or any later version. -import urllib, urllib2, httplib, os, socket, cStringIO, base64 -from i18n import _ -import keepalive, util, sslutil -import httpconnection as httpconnectionmod +from __future__ import absolute_import + +import base64 +import cStringIO +import httplib +import os +import socket +import urllib +import urllib2 + +from .i18n import _ +from . import ( + httpconnection as httpconnectionmod, + keepalive, + sslutil, + util, +) class passwordmgr(urllib2.HTTPPasswordMgrWithDefaultRealm): def __init__(self, ui):
--- a/mercurial/util.py Mon Oct 05 10:43:16 2015 -0600 +++ b/mercurial/util.py Wed Oct 07 13:44:48 2015 -0500 @@ -19,8 +19,11 @@ import errno, shutil, sys, tempfile, traceback import re as remod import os, time, datetime, calendar, textwrap, signal, collections +import stat import imp, socket, urllib import gc +import bz2 +import zlib if os.name == 'nt': import windows as platform @@ -728,7 +731,11 @@ global _hgexecutable _hgexecutable = path -def system(cmd, environ={}, cwd=None, onerr=None, errprefix=None, out=None): +def _isstdout(f): + fileno = getattr(f, 'fileno', None) + return fileno and fileno() == sys.__stdout__.fileno() + +def system(cmd, environ=None, cwd=None, onerr=None, errprefix=None, out=None): '''enhanced shell command execution. run with environment maybe modified, maybe in different dir. @@ -737,6 +744,8 @@ if out is specified, it is assumed to be a file-like object that has a write() method. stdout and stderr will be redirected to out.''' + if environ is None: + environ = {} try: sys.stdout.flush() except Exception: @@ -761,7 +770,7 @@ env = dict(os.environ) env.update((k, py2shell(v)) for k, v in environ.iteritems()) env['HG'] = hgexecutable() - if out is None or out == sys.__stdout__: + if out is None or _isstdout(out): rc = subprocess.call(cmd, shell=True, close_fds=closefds, env=env, cwd=cwd) else: @@ -944,6 +953,20 @@ except AttributeError: return os.stat(fp.name) +def statmtimesec(st): + """Get mtime as integer of seconds + + 'int(st.st_mtime)' cannot be used because st.st_mtime is computed as + 'sec + 1e-9 * nsec' and double-precision floating-point type is too narrow + to represent nanoseconds. If 'nsec' is close to 1 sec, 'int(st.st_mtime)' + can be 'sec + 1'. (issue4836) + """ + try: + return st[stat.ST_MTIME] + except TypeError: + # osutil.stat doesn't allow index access and its st_mtime is int + return st.st_mtime + # File system features def checkcase(path): @@ -1278,16 +1301,20 @@ yield chunk self.iter = splitbig(in_iter) self._queue = collections.deque() + self._chunkoffset = 0 def read(self, l=None): """Read L bytes of data from the iterator of chunks of data. Returns less than L bytes if the iterator runs dry. If size parameter is omitted, read everything""" + if l is None: + return ''.join(self.iter) + left = l buf = [] queue = self._queue - while left is None or left > 0: + while left > 0: # refill the queue if not queue: target = 2**18 @@ -1299,14 +1326,40 @@ if not queue: break - chunk = queue.popleft() - if left is not None: - left -= len(chunk) - if left is not None and left < 0: - queue.appendleft(chunk[left:]) - buf.append(chunk[:left]) + # The easy way to do this would be to queue.popleft(), modify the + # chunk (if necessary), then queue.appendleft(). However, for cases + # where we read partial chunk content, this incurs 2 dequeue + # mutations and creates a new str for the remaining chunk in the + # queue. Our code below avoids this overhead. + + chunk = queue[0] + chunkl = len(chunk) + offset = self._chunkoffset + + # Use full chunk. + if offset == 0 and left >= chunkl: + left -= chunkl + queue.popleft() + buf.append(chunk) + # self._chunkoffset remains at 0. + continue + + chunkremaining = chunkl - offset + + # Use all of unconsumed part of chunk. + if left >= chunkremaining: + left -= chunkremaining + queue.popleft() + # offset == 0 is enabled by block above, so this won't merely + # copy via ``chunk[0:]``. + buf.append(chunk[offset:]) + self._chunkoffset = 0 + + # Partial chunk needed. else: - buf.append(chunk) + buf.append(chunk[offset:offset + left]) + self._chunkoffset += left + left -= chunkremaining return ''.join(buf) @@ -1371,22 +1424,22 @@ """turn (timestamp, tzoff) tuple into iso 8631 date.""" return datestr(date, format='%Y-%m-%d') +def parsetimezone(tz): + """parse a timezone string and return an offset integer""" + if tz[0] in "+-" and len(tz) == 5 and tz[1:].isdigit(): + sign = (tz[0] == "+") and 1 or -1 + hours = int(tz[1:3]) + minutes = int(tz[3:5]) + return -sign * (hours * 60 + minutes) * 60 + if tz == "GMT" or tz == "UTC": + return 0 + return None + def strdate(string, format, defaults=[]): """parse a localized time string and return a (unixtime, offset) tuple. if the string cannot be parsed, ValueError is raised.""" - def timezone(string): - tz = string.split()[-1] - if tz[0] in "+-" and len(tz) == 5 and tz[1:].isdigit(): - sign = (tz[0] == "+") and 1 or -1 - hours = int(tz[1:3]) - minutes = int(tz[3:5]) - return -sign * (hours * 60 + minutes) * 60 - if tz == "GMT" or tz == "UTC": - return 0 - return None - # NOTE: unixtime = localunixtime + offset - offset, date = timezone(string), string + offset, date = parsetimezone(string.split()[-1]), string if offset is not None: date = " ".join(string.split()[:-1]) @@ -1412,7 +1465,7 @@ unixtime = localunixtime + offset return unixtime, offset -def parsedate(date, formats=None, bias={}): +def parsedate(date, formats=None, bias=None): """parse a localized date/time and return a (unixtime, offset) tuple. The date may be a "unixtime offset" string or in one of the specified @@ -1432,6 +1485,8 @@ >>> tz == strtz True """ + if bias is None: + bias = {} if not date: return 0, 0 if isinstance(date, tuple) and len(date) == 2: @@ -1565,6 +1620,45 @@ start, stop = lower(date), upper(date) return lambda x: x >= start and x <= stop +def stringmatcher(pattern): + """ + accepts a string, possibly starting with 're:' or 'literal:' prefix. + returns the matcher name, pattern, and matcher function. + missing or unknown prefixes are treated as literal matches. + + helper for tests: + >>> def test(pattern, *tests): + ... kind, pattern, matcher = stringmatcher(pattern) + ... return (kind, pattern, [bool(matcher(t)) for t in tests]) + + exact matching (no prefix): + >>> test('abcdefg', 'abc', 'def', 'abcdefg') + ('literal', 'abcdefg', [False, False, True]) + + regex matching ('re:' prefix) + >>> test('re:a.+b', 'nomatch', 'fooadef', 'fooadefbar') + ('re', 'a.+b', [False, False, True]) + + force exact matches ('literal:' prefix) + >>> test('literal:re:foobar', 'foobar', 're:foobar') + ('literal', 're:foobar', [False, True]) + + unknown prefixes are ignored and treated as literals + >>> test('foo:bar', 'foo', 'bar', 'foo:bar') + ('literal', 'foo:bar', [False, False, True]) + """ + if pattern.startswith('re:'): + pattern = pattern[3:] + try: + regex = remod.compile(pattern) + except remod.error as e: + raise error.ParseError(_('invalid regular expression: %s') + % e) + return 're', pattern, regex.search + elif pattern.startswith('literal:'): + pattern = pattern[8:] + return 'literal', pattern, pattern.__eq__ + def shortuser(user): """Return a short representation of a user name or email address.""" f = user.find('@') @@ -1667,7 +1761,7 @@ elif not cur_line: cur_line.append(reversed_chunks.pop()) - # this overriding code is imported from TextWrapper of python 2.6 + # this overriding code is imported from TextWrapper of Python 2.6 # to calculate columns of string by 'encoding.ucolwidth()' def _wrap_chunks(self, chunks): colwidth = encoding.ucolwidth @@ -2260,7 +2354,7 @@ class hooks(object): '''A collection of hook functions that can be used to extend a - function's behaviour. Hooks are called in lexicographic order, + function's behavior. Hooks are called in lexicographic order, based on the names of their sources.''' def __init__(self): @@ -2338,5 +2432,46 @@ yield path[:pos] pos = path.rfind('/', 0, pos) +# compression utility + +class nocompress(object): + def compress(self, x): + return x + def flush(self): + return "" + +compressors = { + None: nocompress, + # lambda to prevent early import + 'BZ': lambda: bz2.BZ2Compressor(), + 'GZ': lambda: zlib.compressobj(), + } +# also support the old form by courtesies +compressors['UN'] = compressors[None] + +def _makedecompressor(decompcls): + def generator(f): + d = decompcls() + for chunk in filechunkiter(f): + yield d.decompress(chunk) + def func(fh): + return chunkbuffer(generator(fh)) + return func + +def _bz2(): + d = bz2.BZ2Decompressor() + # Bzip2 stream start with BZ, but we stripped it. + # we put it back for good measure. + d.decompress('BZ') + return d + +decompressors = {None: lambda fh: fh, + '_truncatedBZ': _makedecompressor(_bz2), + 'BZ': _makedecompressor(lambda: bz2.BZ2Decompressor()), + 'GZ': _makedecompressor(lambda: zlib.decompressobj()), + } +# also support the old form by courtesies +decompressors['UN'] = decompressors[None] + # convenient shortcut dst = debugstacktrace
--- a/mercurial/verify.py Mon Oct 05 10:43:16 2015 -0600 +++ b/mercurial/verify.py Wed Oct 07 13:44:48 2015 -0500 @@ -5,10 +5,21 @@ # This software may be used and distributed according to the terms of the # GNU General Public License version 2 or any later version. -from node import nullid, short -from i18n import _ +from __future__ import absolute_import + import os -import revlog, util, error + +from .i18n import _ +from .node import ( + nullid, + short, +) + +from . import ( + error, + revlog, + util, +) def verify(repo): lock = repo.lock()
--- a/mercurial/win32.py Mon Oct 05 10:43:16 2015 -0600 +++ b/mercurial/win32.py Wed Oct 07 13:44:48 2015 -0500 @@ -5,7 +5,14 @@ # This software may be used and distributed according to the terms of the # GNU General Public License version 2 or any later version. -import ctypes, errno, msvcrt, os, subprocess, random +from __future__ import absolute_import + +import ctypes +import errno +import msvcrt +import os +import random +import subprocess _kernel32 = ctypes.windll.kernel32 _advapi32 = ctypes.windll.advapi32
--- a/mercurial/windows.py Mon Oct 05 10:43:16 2015 -0600 +++ b/mercurial/windows.py Wed Oct 07 13:44:48 2015 -0500 @@ -27,6 +27,74 @@ umask = 0o022 +class mixedfilemodewrapper(object): + """Wraps a file handle when it is opened in read/write mode. + + fopen() and fdopen() on Windows have a specific-to-Windows requirement + that files opened with mode r+, w+, or a+ make a call to a file positioning + function when switching between reads and writes. Without this extra call, + Python will raise a not very intuitive "IOError: [Errno 0] Error." + + This class wraps posixfile instances when the file is opened in read/write + mode and automatically adds checks or inserts appropriate file positioning + calls when necessary. + """ + OPNONE = 0 + OPREAD = 1 + OPWRITE = 2 + + def __init__(self, fp): + object.__setattr__(self, '_fp', fp) + object.__setattr__(self, '_lastop', 0) + + def __getattr__(self, name): + return getattr(self._fp, name) + + def __setattr__(self, name, value): + return self._fp.__setattr__(name, value) + + def _noopseek(self): + self._fp.seek(0, os.SEEK_CUR) + + def seek(self, *args, **kwargs): + object.__setattr__(self, '_lastop', self.OPNONE) + return self._fp.seek(*args, **kwargs) + + def write(self, d): + if self._lastop == self.OPREAD: + self._noopseek() + + object.__setattr__(self, '_lastop', self.OPWRITE) + return self._fp.write(d) + + def writelines(self, *args, **kwargs): + if self._lastop == self.OPREAD: + self._noopeseek() + + object.__setattr__(self, '_lastop', self.OPWRITE) + return self._fp.writelines(*args, **kwargs) + + def read(self, *args, **kwargs): + if self._lastop == self.OPWRITE: + self._noopseek() + + object.__setattr__(self, '_lastop', self.OPREAD) + return self._fp.read(*args, **kwargs) + + def readline(self, *args, **kwargs): + if self._lastop == self.OPWRITE: + self._noopseek() + + object.__setattr__(self, '_lastop', self.OPREAD) + return self._fp.readline(*args, **kwargs) + + def readlines(self, *args, **kwargs): + if self._lastop == self.OPWRITE: + self._noopseek() + + object.__setattr__(self, '_lastop', self.OPREAD) + return self._fp.readlines(*args, **kwargs) + def posixfile(name, mode='r', buffering=-1): '''Open a file with even more POSIX-like semantics''' try: @@ -37,6 +105,9 @@ if 'a' in mode: fp.seek(0, os.SEEK_END) + if '+' in mode: + return mixedfilemodewrapper(fp) + return fp except WindowsError as err: # convert to a friendlier exception
--- a/mercurial/wireproto.py Mon Oct 05 10:43:16 2015 -0600 +++ b/mercurial/wireproto.py Wed Oct 07 13:44:48 2015 -0500 @@ -5,12 +5,30 @@ # This software may be used and distributed according to the terms of the # GNU General Public License version 2 or any later version. -import urllib, tempfile, os, sys -from i18n import _ -from node import bin, hex -import changegroup as changegroupmod, bundle2, pushkey as pushkeymod -import peer, error, encoding, util, exchange +from __future__ import absolute_import + +import os +import sys +import tempfile +import urllib +from .i18n import _ +from .node import ( + bin, + hex, +) + +from . import ( + bundle2, + changegroup as changegroupmod, + encoding, + error, + exchange, + peer, + pushkey as pushkeymod, + streamclone, + util, +) class abstractserverproto(object): """abstract class that summarizes the protocol API @@ -58,48 +76,12 @@ Some protocols may have compressed the contents.""" raise NotImplementedError() -# abstract batching support - -class future(object): - '''placeholder for a value to be set later''' - def set(self, value): - if util.safehasattr(self, 'value'): - raise error.RepoError("future is already set") - self.value = value - -class batcher(object): - '''base class for batches of commands submittable in a single request - - All methods invoked on instances of this class are simply queued and - return a a future for the result. Once you call submit(), all the queued - calls are performed and the results set in their respective futures. - ''' - def __init__(self): - self.calls = [] - def __getattr__(self, name): - def call(*args, **opts): - resref = future() - self.calls.append((name, args, opts, resref,)) - return resref - return call - def submit(self): - pass - -class localbatch(batcher): - '''performs the queued calls directly''' - def __init__(self, local): - batcher.__init__(self) - self.local = local - def submit(self): - for name, args, opts, resref in self.calls: - resref.set(getattr(self.local, name)(*args, **opts)) - -class remotebatch(batcher): +class remotebatch(peer.batcher): '''batches the queued calls; uses as few roundtrips as possible''' def __init__(self, remote): '''remote must support _submitbatch(encbatch) and _submitone(op, encargs)''' - batcher.__init__(self) + peer.batcher.__init__(self) self.remote = remote def submit(self): req, rsp = [], [] @@ -128,41 +110,10 @@ encresref.set(encres) resref.set(batchable.next()) -def batchable(f): - '''annotation for batchable methods - - Such methods must implement a coroutine as follows: - - @batchable - def sample(self, one, two=None): - # Handle locally computable results first: - if not one: - yield "a local result", None - # Build list of encoded arguments suitable for your wire protocol: - encargs = [('one', encode(one),), ('two', encode(two),)] - # Create future for injection of encoded result: - encresref = future() - # Return encoded arguments and future: - yield encargs, encresref - # Assuming the future to be filled with the result from the batched - # request now. Decode it: - yield decode(encresref.value) - - The decorator returns a function which wraps this coroutine as a plain - method, but adds the original method as an attribute called "batchable", - which is used by remotebatch to split the call into separate encoding and - decoding phases. - ''' - def plain(*args, **opts): - batchable = f(*args, **opts) - encargsorres, encresref = batchable.next() - if not encresref: - return encargsorres # a local result in this case - self = args[0] - encresref.set(self._submitone(f.func_name, encargsorres)) - return batchable.next() - setattr(plain, 'batchable', f) - return plain +# Forward a couple of names from peer to make wireproto interactions +# slightly more sensible. +batchable = peer.batchable +future = peer.future # list of nodes encoding / decoding @@ -216,7 +167,10 @@ class wirepeer(peer.peerrepository): def batch(self): - return remotebatch(self) + if self.capable('batch'): + return remotebatch(self) + else: + return peer.localbatch(self) def _submitbatch(self, req): cmds = [] for op, argsdict in req: @@ -610,7 +564,7 @@ """ # copy to prevent modification of the global list caps = list(wireprotocaps) - if _allowstream(repo.ui): + if streamclone.allowservergeneration(repo.ui): if repo.ui.configbool('server', 'preferuncompressed', False): caps.append('stream-preferred') requiredformats = repo.requirements & repo.supportedformats @@ -747,16 +701,13 @@ encoding.tolocal(old), new) return '%s\n' % int(r) -def _allowstream(ui): - return ui.configbool('server', 'uncompressed', True, untrusted=True) - @wireprotocommand('stream_out') def stream(repo, proto): '''If the server supports streaming clone, it advertises the "stream" capability with a value representing the version and flags of the repo it is serving. Client checks to see if it understands the format. ''' - if not _allowstream(repo.ui): + if not streamclone.allowservergeneration(repo.ui): return '1\n' def getstream(it): @@ -767,7 +718,7 @@ try: # LockError may be raised before the first result is yielded. Don't # emit output until we're sure we got the lock successfully. - it = exchange.generatestreamclone(repo) + it = streamclone.generatev1wireproto(repo) return streamres(getstream(it)) except error.LockError: return '2\n'
--- a/mercurial/worker.py Mon Oct 05 10:43:16 2015 -0600 +++ b/mercurial/worker.py Wed Oct 07 13:44:48 2015 -0500 @@ -5,30 +5,24 @@ # This software may be used and distributed according to the terms of the # GNU General Public License version 2 or any later version. -from i18n import _ -import errno, os, signal, sys, threading -import util +from __future__ import absolute_import + +import errno +import multiprocessing +import os +import signal +import sys +import threading + +from .i18n import _ +from . import util def countcpus(): '''try to count the number of CPUs on the system''' - - # posix try: - n = int(os.sysconf('SC_NPROCESSORS_ONLN')) - if n > 0: - return n - except (AttributeError, ValueError): - pass - - # windows - try: - n = int(os.environ['NUMBER_OF_PROCESSORS']) - if n > 0: - return n - except (KeyError, ValueError): - pass - - return 1 + return multiprocessing.cpu_count() + except NotImplementedError: + return 1 def _numworkers(ui): s = ui.config('worker', 'numcpus')
--- a/setup.py Mon Oct 05 10:43:16 2015 -0600 +++ b/setup.py Wed Oct 07 13:44:48 2015 -0500 @@ -602,8 +602,8 @@ version=setupversion, author='Matt Mackall and many others', author_email='mercurial@selenic.com', - url='http://mercurial.selenic.com/', - download_url='http://mercurial.selenic.com/release/', + url='https://mercurial-scm.org/', + download_url='https://mercurial-scm.org/release/', description=('Fast scalable distributed SCM (revision control, version ' 'control) system'), long_description=('Mercurial is a distributed SCM tool written in Python.'
--- a/tests/README Mon Oct 05 10:43:16 2015 -0600 +++ b/tests/README Wed Oct 07 13:44:48 2015 -0500 @@ -3,5 +3,5 @@ cd tests/ python run-tests.py -See http://mercurial.selenic.com/wiki/WritingTests for +See https://mercurial-scm.org/wiki/WritingTests for more information on writing tests.
--- a/tests/bzr-definitions Mon Oct 05 10:43:16 2015 -0600 +++ b/tests/bzr-definitions Wed Oct 07 13:44:48 2015 -0500 @@ -1,7 +1,5 @@ # this file holds the definitions that are used in various bzr tests -"$TESTDIR/hghave" bzr || exit 80 - TERM=dumb; export TERM echo '[extensions]' >> $HGRCPATH echo 'convert = ' >> $HGRCPATH
--- a/tests/filterpyflakes.py Mon Oct 05 10:43:16 2015 -0600 +++ b/tests/filterpyflakes.py Wed Oct 07 13:44:48 2015 -0500 @@ -2,7 +2,7 @@ # Filter output by pyflakes to control which warnings we check -import sys, re, os +import sys, re def makekey(typeandline): """ @@ -42,7 +42,7 @@ else: continue # no pattern matched, next line fn = line.split(':', 1)[0] - f = open(os.path.join(os.path.dirname(os.path.dirname(__file__)), fn)) + f = open(fn) data = f.read() f.close() if 'no-' 'check-code' in data:
--- a/tests/hghave Mon Oct 05 10:43:16 2015 -0600 +++ b/tests/hghave Wed Oct 07 13:44:48 2015 -0500 @@ -30,10 +30,8 @@ help="test available features") parser.add_option("--list-features", action="store_true", help="list available features") -parser.add_option("-q", "--quiet", action="store_true", - help="check features silently") -def _loadaddon(quiet): +def _loadaddon(): if 'TESTDIR' in os.environ: # loading from '.' isn't needed, because `hghave` should be # running at TESTTMP in this case @@ -48,15 +46,14 @@ try: import hghaveaddon except BaseException, inst: - if not quiet: - sys.stderr.write('failed to import hghaveaddon.py from %r: %s\n' - % (path, inst)) + sys.stderr.write('failed to import hghaveaddon.py from %r: %s\n' + % (path, inst)) sys.exit(2) sys.path.pop(0) if __name__ == '__main__': options, args = parser.parse_args() - _loadaddon(options.quiet) + _loadaddon() if options.list_features: list_features() sys.exit(0) @@ -64,36 +61,4 @@ if options.test_features: sys.exit(test_features()) - quiet = options.quiet - - failures = 0 - - def error(msg): - global failures - if not quiet: - sys.stderr.write(msg + '\n') - failures += 1 - - for feature in args: - negate = feature.startswith('no-') - if negate: - feature = feature[3:] - - if feature not in checks: - error('skipped: unknown feature: ' + feature) - sys.exit(2) - - check, desc = checks[feature] - try: - available = check() - except Exception, e: - error('hghave check failed: ' + feature) - continue - - if not negate and not available: - error('skipped: missing feature: ' + desc) - elif negate and available: - error('skipped: system supports %s' % desc) - - if failures != 0: - sys.exit(1) + hghave.require(args)
--- a/tests/hghave.py Mon Oct 05 10:43:16 2015 -0600 +++ b/tests/hghave.py Wed Oct 07 13:44:48 2015 -0500 @@ -1,6 +1,9 @@ -import os, stat +import errno +import os import re import socket +import stat +import subprocess import sys import tempfile @@ -17,19 +20,68 @@ return func return decorator +def checkfeatures(features): + result = { + 'error': [], + 'missing': [], + 'skipped': [], + } + + for feature in features: + negate = feature.startswith('no-') + if negate: + feature = feature[3:] + + if feature not in checks: + result['missing'].append(feature) + continue + + check, desc = checks[feature] + try: + available = check() + except Exception: + result['error'].append('hghave check failed: %s' % feature) + continue + + if not negate and not available: + result['skipped'].append('missing feature: %s' % desc) + elif negate and available: + result['skipped'].append('system supports %s' % desc) + + return result + +def require(features): + """Require that features are available, exiting if not.""" + result = checkfeatures(features) + + for missing in result['missing']: + sys.stderr.write('skipped: unknown feature: %s\n' % missing) + for msg in result['skipped']: + sys.stderr.write('skipped: %s\n' % msg) + for msg in result['error']: + sys.stderr.write('%s\n' % msg) + + if result['missing']: + sys.exit(2) + + if result['skipped'] or result['error']: + sys.exit(1) + def matchoutput(cmd, regexp, ignorestatus=False): """Return True if cmd executes successfully and its output is matched by the supplied regular expression. """ r = re.compile(regexp) - fh = os.popen(cmd) - s = fh.read() try: - ret = fh.close() - except IOError: - # Happen in Windows test environment - ret = 1 - return (ignorestatus or ret is None) and r.search(s) + p = subprocess.Popen( + cmd, shell=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT) + except OSError as e: + if e.errno != errno.ENOENT: + raise + ret = -1 + ret = p.wait() + s = p.stdout.read() + return (ignorestatus or not ret) and r.search(s) @check("baz", "GNU Arch baz client") def has_baz(): @@ -367,6 +419,33 @@ def has_osx(): return sys.platform == 'darwin' +@check("docker", "docker support") +def has_docker(): + pat = r'A self-sufficient runtime for linux containers\.' + if matchoutput('docker --help', pat): + if 'linux' not in sys.platform: + # TODO: in theory we should be able to test docker-based + # package creation on non-linux using boot2docker, but in + # practice that requires extra coordination to make sure + # $TESTTEMP is going to be visible at the same path to the + # boot2docker VM. If we figure out how to verify that, we + # can use the following instead of just saying False: + # return 'DOCKER_HOST' in os.environ + return False + + return True + return False + +@check("debhelper", "debian packaging tools") +def has_debhelper(): + dpkg = matchoutput('dpkg --version', + "Debian `dpkg' package management program") + dh = matchoutput('dh --help', + 'dh is a part of debhelper.', ignorestatus=True) + dh_py2 = matchoutput('dh_python2 --help', + 'other supported Python versions') + return dpkg and dh and dh_py2 + @check("absimport", "absolute_import in __future__") def has_absimport(): import __future__ @@ -380,3 +459,7 @@ @check("pure", "running with pure Python code") def has_pure(): return os.environ.get("HGTEST_RUN_TESTS_PURE") == "--pure" + +@check("slow", "allow slow tests") +def has_slow(): + return os.environ.get('HGTEST_SLOW') == 'slow'
--- a/tests/run-tests.py Mon Oct 05 10:43:16 2015 -0600 +++ b/tests/run-tests.py Wed Oct 07 13:44:48 2015 -0500 @@ -35,6 +35,8 @@ # ./run-tests.py -j2 -c --local test-s* # unsupported (and broken) # 9) parallel, custom tmp dir: # ./run-tests.py -j2 --tmpdir /tmp/myhgtests +# 10) parallel, pure, tests that call run-tests: +# ./run-tests.py --pure `grep -l run-tests.py *.t` # # (You could use any subset of the tests: test-s* happens to match # enough that it's worth doing parallel runs, few enough that it @@ -259,6 +261,8 @@ help='run tests in random order') parser.add_option('--profile-runner', action='store_true', help='run statprof on run-tests') + parser.add_option('--allow-slow-tests', action='store_true', + help='allow extremely slow tests') for option, (envvar, default) in defaults.items(): defaults[option] = type(default)(os.environ.get(envvar, default)) @@ -661,7 +665,10 @@ killdaemons(entry) self._daemonpids = [] - if not self._keeptmpdir: + if self._keeptmpdir: + log('\nKeeping testtmp dir: %s\nKeeping threadtmp dir: %s' % + (self._testtmp, self._threadtmp)) + else: shutil.rmtree(self._testtmp, True) shutil.rmtree(self._threadtmp, True) @@ -1835,6 +1842,11 @@ if self.options.pure: os.environ["HGTEST_RUN_TESTS_PURE"] = "--pure" + if self.options.allow_slow_tests: + os.environ["HGTEST_SLOW"] = "slow" + elif 'HGTEST_SLOW' in os.environ: + del os.environ['HGTEST_SLOW'] + self._coveragefile = os.path.join(self._testdir, b'.coverage') vlog("# Using TESTDIR", self._testdir) @@ -2078,7 +2090,11 @@ vlog("# Running", cmd) if os.system(cmd) == 0: if not self.options.verbose: - os.remove(installerrs) + try: + os.remove(installerrs) + except OSError as e: + if e.errno != errno.ENOENT: + raise else: f = open(installerrs, 'rb') for line in f:
--- a/tests/test-backout.t Mon Oct 05 10:43:16 2015 -0600 +++ b/tests/test-backout.t Wed Oct 07 13:44:48 2015 -0500 @@ -593,6 +593,23 @@ use 'hg resolve' to retry unresolved file merges [1] $ hg status + $ hg debugmergestate + * version 2 records + local: b71750c4b0fdf719734971e3ef90dbeab5919a2d + other: a30dd8addae3ce71b8667868478542bc417439e6 + file: foo (state "u", hash 0beec7b5ea3f0fdbc95d0dd47f3c5bc275da8a33) + local path: foo (flags "") + ancestor path: foo (node f89532f44c247a0e993d63e3a734dd781ab04708) + other path: foo (node f50039b486d6fa1a90ae51778388cad161f425ee) + $ mv .hg/merge/state2 .hg/merge/state2-moved + $ hg debugmergestate + * version 1 records + local: b71750c4b0fdf719734971e3ef90dbeab5919a2d + file: foo (state "u", hash 0beec7b5ea3f0fdbc95d0dd47f3c5bc275da8a33) + local path: foo (flags "") + ancestor path: foo (node f89532f44c247a0e993d63e3a734dd781ab04708) + other path: foo (node not stored in v1 format) + $ mv .hg/merge/state2-moved .hg/merge/state2 $ hg resolve -l # still unresolved U foo $ hg summary
--- a/tests/test-bad-extension.t Mon Oct 05 10:43:16 2015 -0600 +++ b/tests/test-bad-extension.t Wed Oct 07 13:44:48 2015 -0500 @@ -9,31 +9,26 @@ > badext2 = > EOF - $ hg -q help help + $ hg -q help help 2>&1 |grep extension *** failed to import extension badext from $TESTTMP/badext.py: bit bucket overflow *** failed to import extension badext2: No module named badext2 - hg help [-ec] [TOPIC] - - show help for a given topic or a help overview show traceback - $ hg -q help help --traceback 2>&1 | grep -v '^ ' + $ hg -q help help --traceback 2>&1 | egrep ' extension|^Exception|Traceback|ImportError' *** failed to import extension badext from $TESTTMP/badext.py: bit bucket overflow Traceback (most recent call last): Exception: bit bucket overflow *** failed to import extension badext2: No module named badext2 Traceback (most recent call last): ImportError: No module named badext2 - hg help [-ec] [TOPIC] - - show help for a given topic or a help overview show traceback for ImportError of hgext.name if debug is set (note that --debug option isn't applied yet when loading extensions) - $ hg help help --traceback --config ui.debug=True 2>&1 \ - > | grep -v '^ ' | head -n10 + $ (hg -q help help --traceback --config ui.debug=True 2>&1) \ + > | grep -v '^ ' \ + > | egrep 'extension..[^p]|^Exception|Traceback|ImportError|not import' *** failed to import extension badext from $TESTTMP/badext.py: bit bucket overflow Traceback (most recent call last): Exception: bit bucket overflow @@ -43,4 +38,3 @@ *** failed to import extension badext2: No module named badext2 Traceback (most recent call last): ImportError: No module named badext2 - hg help [-ec] [TOPIC]
--- a/tests/test-batching.py Mon Oct 05 10:43:16 2015 -0600 +++ b/tests/test-batching.py Wed Oct 07 13:44:48 2015 -0500 @@ -5,7 +5,8 @@ # This software may be used and distributed according to the terms of the # GNU General Public License version 2 or any later version. -from mercurial.wireproto import localbatch, remotebatch, batchable, future +from mercurial.peer import localbatch, batchable, future +from mercurial.wireproto import remotebatch # equivalent of repo.repository class thing(object):
--- a/tests/test-blackbox.t Mon Oct 05 10:43:16 2015 -0600 +++ b/tests/test-blackbox.t Wed Oct 07 13:44:48 2015 -0500 @@ -13,8 +13,8 @@ $ echo a > a $ hg add a $ hg blackbox - 1970/01/01 00:00:00 bob> add a - 1970/01/01 00:00:00 bob> add a exited 0 after * seconds (glob) + 1970/01/01 00:00:00 bob (*)> add a (glob) + 1970/01/01 00:00:00 bob (*)> add a exited 0 after * seconds (glob) incoming change tracking @@ -44,11 +44,11 @@ added 1 changesets with 1 changes to 1 files (run 'hg update' to get a working copy) $ hg blackbox -l 5 - 1970/01/01 00:00:00 bob> pull - 1970/01/01 00:00:00 bob> updated served branch cache in ?.???? seconds (glob) - 1970/01/01 00:00:00 bob> wrote served branch cache with 1 labels and 2 nodes - 1970/01/01 00:00:00 bob> 1 incoming changes - new heads: d02f48003e62 - 1970/01/01 00:00:00 bob> pull exited 0 after * seconds (glob) + 1970/01/01 00:00:00 bob (*)> pull (glob) + 1970/01/01 00:00:00 bob (*)> updated served branch cache in ?.???? seconds (glob) + 1970/01/01 00:00:00 bob (*)> wrote served branch cache with 1 labels and 2 nodes (glob) + 1970/01/01 00:00:00 bob (*)> 1 incoming changes - new heads: d02f48003e62 (glob) + 1970/01/01 00:00:00 bob (*)> pull exited 0 after * seconds (glob) we must not cause a failure if we cannot write to the log @@ -106,11 +106,11 @@ 0 files updated, 0 files merged, 1 files removed, 0 files unresolved saved backup bundle to $TESTTMP/blackboxtest2/.hg/strip-backup/*-backup.hg (glob) $ hg blackbox -l 5 - 1970/01/01 00:00:00 bob> strip tip - 1970/01/01 00:00:00 bob> saved backup bundle to $TESTTMP/blackboxtest2/.hg/strip-backup/*-backup.hg (glob) - 1970/01/01 00:00:00 bob> updated base branch cache in ?.???? seconds (glob) - 1970/01/01 00:00:00 bob> wrote base branch cache with 1 labels and 2 nodes - 1970/01/01 00:00:00 bob> strip tip exited 0 after * seconds (glob) + 1970/01/01 00:00:00 bob (*)> strip tip (glob) + 1970/01/01 00:00:00 bob (*)> saved backup bundle to $TESTTMP/blackboxtest2/.hg/strip-backup/*-backup.hg (glob) + 1970/01/01 00:00:00 bob (*)> updated base branch cache in ?.???? seconds (glob) + 1970/01/01 00:00:00 bob (*)> wrote base branch cache with 1 labels and 2 nodes (glob) + 1970/01/01 00:00:00 bob (*)> strip tip exited 0 after * seconds (glob) extension and python hooks - use the eol extension for a pythonhook @@ -122,11 +122,11 @@ 1 files updated, 0 files merged, 0 files removed, 0 files unresolved hooked $ hg blackbox -l 5 - 1970/01/01 00:00:00 bob> update - 1970/01/01 00:00:00 bob> writing .hg/cache/tags2-visible with 0 tags - 1970/01/01 00:00:00 bob> pythonhook-preupdate: hgext.eol.preupdate finished in * seconds (glob) - 1970/01/01 00:00:00 bob> exthook-update: echo hooked finished in * seconds (glob) - 1970/01/01 00:00:00 bob> update exited 0 after * seconds (glob) + 1970/01/01 00:00:00 bob (*)> update (glob) + 1970/01/01 00:00:00 bob (*)> writing .hg/cache/tags2-visible with 0 tags (glob) + 1970/01/01 00:00:00 bob (*)> pythonhook-preupdate: hgext.eol.preupdate finished in * seconds (glob) + 1970/01/01 00:00:00 bob (*)> exthook-update: echo hooked finished in * seconds (glob) + 1970/01/01 00:00:00 bob (*)> update exited 0 after * seconds (glob) log rotation
--- a/tests/test-bookmarks.t Mon Oct 05 10:43:16 2015 -0600 +++ b/tests/test-bookmarks.t Wed Oct 07 13:44:48 2015 -0500 @@ -511,10 +511,10 @@ test clone with update to a bookmark - $ hg clone -u Z . cloned-bookmarks-update + $ hg clone -u Z . ../cloned-bookmarks-update updating to branch default 2 files updated, 0 files merged, 0 files removed, 0 files unresolved - $ hg -R cloned-bookmarks-update bookmarks + $ hg -R ../cloned-bookmarks-update bookmarks X2 1:925d80f479bb Y 2:db815d6d32e6 * Z 2:db815d6d32e6 @@ -569,10 +569,40 @@ $ hg bookmark -r3 Y moving bookmark 'Y' forward from db815d6d32e6 - $ hg -R cloned-bookmarks-update update Y + $ cp -r ../cloned-bookmarks-update ../cloned-bookmarks-manual-update + +(manual version) + + $ hg -R ../cloned-bookmarks-manual-update update Y 0 files updated, 0 files merged, 0 files removed, 0 files unresolved (activating bookmark Y) - $ hg -R cloned-bookmarks-update pull --update . + $ hg -R ../cloned-bookmarks-manual-update pull . + pulling from . + searching for changes + adding changesets + adding manifests + adding file changes + added 2 changesets with 2 changes to 2 files (+1 heads) + updating bookmark Y + updating bookmark Z + (run 'hg heads' to see heads, 'hg merge' to merge) + +(# tests strange but with --date crashing when bookmark have to move) + + $ hg -R ../cloned-bookmarks-manual-update update -d 1986 + abort: revision matching date not found + [255] + $ hg -R ../cloned-bookmarks-manual-update update + updating to active bookmark Y + 1 files updated, 0 files merged, 0 files removed, 0 files unresolved + (activating bookmark Y) + +(all in one version) + + $ hg -R ../cloned-bookmarks-update update Y + 0 files updated, 0 files merged, 0 files removed, 0 files unresolved + (activating bookmark Y) + $ hg -R ../cloned-bookmarks-update pull --update . pulling from . searching for changes adding changesets
--- a/tests/test-bundle.t Mon Oct 05 10:43:16 2015 -0600 +++ b/tests/test-bundle.t Wed Oct 07 13:44:48 2015 -0500 @@ -437,7 +437,7 @@ abort: empty destination path is not valid [255] -test for http://mercurial.selenic.com/bts/issue216 +test for https://bz.mercurial-scm.org/216 Unbundle incremental bundles into fresh empty in one go @@ -551,7 +551,7 @@ $ cd .. -test for http://mercurial.selenic.com/bts/issue1144 +test for https://bz.mercurial-scm.org/1144 test that verify bundle does not traceback
--- a/tests/test-bundle2-format.t Mon Oct 05 10:43:16 2015 -0600 +++ b/tests/test-bundle2-format.t Wed Oct 07 13:44:48 2015 -0500 @@ -78,7 +78,9 @@ > ('', 'reply', False, 'produce a reply bundle'), > ('', 'pushrace', False, 'includes a check:head part with unknown nodes'), > ('', 'genraise', False, 'includes a part that raise an exception during generation'), - > ('r', 'rev', [], 'includes those changeset in the bundle'),], + > ('', 'timeout', False, 'emulate a timeout during bundle generation'), + > ('r', 'rev', [], 'includes those changeset in the bundle'), + > ('', 'compress', '', 'compress the stream'),], > '[OUTPUTFILE]') > def cmdbundle2(ui, repo, path=None, **opts): > """write a bundle2 container on standard output""" @@ -90,6 +92,9 @@ > except ValueError, exc: > raise util.Abort('%s' % exc) > + > if opts['compress']: + > bundler.setcompression(opts['compress']) + > > if opts['reply']: > capsstring = 'ping-pong\nelephants=babar,celeste\ncity%3D%21=celeste%2Cville' > bundler.newpart('replycaps', data=capsstring) @@ -143,6 +148,18 @@ > else: > file = open(path, 'wb') > + > if opts['timeout']: + > bundler.newpart('test:song', data=ELEPHANTSSONG, mandatory=False) + > for idx, junk in enumerate(bundler.getchunks()): + > ui.write('%d chunk\n' % idx) + > if idx > 4: + > # This throws a GeneratorExit inside the generator, which + > # can cause problems if the exception-recovery code is + > # too zealous. It's important for this test that the break + > # occur while we're in the middle of a part. + > break + > ui.write('fake timeout complete.\n') + > return > try: > for chunk in bundler.getchunks(): > file.write(chunk) @@ -236,8 +253,29 @@ Test bundling - $ hg bundle2 - HG20\x00\x00\x00\x00\x00\x00\x00\x00 (no-eol) (esc) + $ hg bundle2 | f --hexdump + + 0000: 48 47 32 30 00 00 00 00 00 00 00 00 |HG20........| + +Test timeouts during bundling + $ hg bundle2 --timeout --debug --config devel.bundle2.debug=yes + bundle2-output-bundle: "HG20", 1 parts total + bundle2-output: start emission of HG20 stream + 0 chunk + bundle2-output: bundle parameter: + 1 chunk + bundle2-output: start of parts + bundle2-output: bundle part: "test:song" + bundle2-output-part: "test:song" (advisory) 178 bytes payload + bundle2-output: part 0: "test:song" + bundle2-output: header chunk size: 16 + 2 chunk + 3 chunk + bundle2-output: payload chunk size: 178 + 4 chunk + 5 chunk + bundle2-generatorexit + fake timeout complete. Test unbundling @@ -266,8 +304,10 @@ Test generation simple option - $ hg bundle2 --param 'caution' - HG20\x00\x00\x00\x07caution\x00\x00\x00\x00 (no-eol) (esc) + $ hg bundle2 --param 'caution' | f --hexdump + + 0000: 48 47 32 30 00 00 00 07 63 61 75 74 69 6f 6e 00 |HG20....caution.| + 0010: 00 00 00 |...| Test unbundling @@ -278,8 +318,10 @@ Test generation multiple option - $ hg bundle2 --param 'caution' --param 'meal' - HG20\x00\x00\x00\x0ccaution meal\x00\x00\x00\x00 (no-eol) (esc) + $ hg bundle2 --param 'caution' --param 'meal' | f --hexdump + + 0000: 48 47 32 30 00 00 00 0c 63 61 75 74 69 6f 6e 20 |HG20....caution | + 0010: 6d 65 61 6c 00 00 00 00 |meal....| Test unbundling @@ -294,8 +336,11 @@ Test generation - $ hg bundle2 --param 'caution' --param 'meal=vegan' --param 'elephants' - HG20\x00\x00\x00\x1ccaution meal=vegan elephants\x00\x00\x00\x00 (no-eol) (esc) + $ hg bundle2 --param 'caution' --param 'meal=vegan' --param 'elephants' | f --hexdump + + 0000: 48 47 32 30 00 00 00 1c 63 61 75 74 69 6f 6e 20 |HG20....caution | + 0010: 6d 65 61 6c 3d 76 65 67 61 6e 20 65 6c 65 70 68 |meal=vegan eleph| + 0020: 61 6e 74 73 00 00 00 00 |ants....| Test unbundling @@ -312,8 +357,12 @@ Test generation - $ hg bundle2 --param 'e|! 7/=babar%#==tutu' --param simple - HG20\x00\x00\x00)e%7C%21%207/=babar%25%23%3D%3Dtutu simple\x00\x00\x00\x00 (no-eol) (esc) + $ hg bundle2 --param 'e|! 7/=babar%#==tutu' --param simple | f --hexdump + + 0000: 48 47 32 30 00 00 00 29 65 25 37 43 25 32 31 25 |HG20...)e%7C%21%| + 0010: 32 30 37 2f 3d 62 61 62 61 72 25 32 35 25 32 33 |207/=babar%25%23| + 0020: 25 33 44 25 33 44 74 75 74 75 20 73 69 6d 70 6c |%3D%3Dtutu simpl| + 0030: 65 00 00 00 00 |e....| Test unbundling @@ -345,8 +394,12 @@ file content is ok - $ cat ../out.hg2 - HG20\x00\x00\x00)e%7C%21%207/=babar%25%23%3D%3Dtutu simple\x00\x00\x00\x00 (no-eol) (esc) + $ f --hexdump ../out.hg2 + ../out.hg2: + 0000: 48 47 32 30 00 00 00 29 65 25 37 43 25 32 31 25 |HG20...)e%7C%21%| + 0010: 32 30 37 2f 3d 62 61 62 61 72 25 32 35 25 32 33 |207/=babar%25%23| + 0020: 25 33 44 25 33 44 74 75 74 75 20 73 69 6d 70 6c |%3D%3Dtutu simpl| + 0030: 65 00 00 00 00 |e....| unbundling debug @@ -428,12 +481,34 @@ bundle2-output: closing payload chunk bundle2-output: end of bundle - $ cat ../parts.hg2 - HG20\x00\x00\x00\x00\x00\x00\x00\x11 (esc) - test:empty\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x11 (esc) - test:empty\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x10 test:song\x00\x00\x00\x02\x00\x00\x00\x00\x00\xb2Patali Dirapata, Cromda Cromda Ripalo, Pata Pata, Ko Ko Ko (esc) - Bokoro Dipoulito, Rondi Rondi Pepino, Pata Pata, Ko Ko Ko - Emana Karassoli, Loucra Loucra Ponponto, Pata Pata, Ko Ko Ko.\x00\x00\x00\x00\x00\x00\x00\x16\x0ftest:debugreply\x00\x00\x00\x03\x00\x00\x00\x00\x00\x00\x00\x00\x00+ test:math\x00\x00\x00\x04\x02\x01\x02\x04\x01\x04\x07\x03pi3.14e2.72cookingraw\x00\x00\x00\x0242\x00\x00\x00\x00\x00\x00\x00\x1d test:song\x00\x00\x00\x05\x01\x00\x0b\x00randomparam\x00\x00\x00\x00\x00\x00\x00\x10 test:ping\x00\x00\x00\x06\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00 (no-eol) (esc) + $ f --hexdump ../parts.hg2 + ../parts.hg2: + 0000: 48 47 32 30 00 00 00 00 00 00 00 11 0a 74 65 73 |HG20.........tes| + 0010: 74 3a 65 6d 70 74 79 00 00 00 00 00 00 00 00 00 |t:empty.........| + 0020: 00 00 00 00 11 0a 74 65 73 74 3a 65 6d 70 74 79 |......test:empty| + 0030: 00 00 00 01 00 00 00 00 00 00 00 00 00 10 09 74 |...............t| + 0040: 65 73 74 3a 73 6f 6e 67 00 00 00 02 00 00 00 00 |est:song........| + 0050: 00 b2 50 61 74 61 6c 69 20 44 69 72 61 70 61 74 |..Patali Dirapat| + 0060: 61 2c 20 43 72 6f 6d 64 61 20 43 72 6f 6d 64 61 |a, Cromda Cromda| + 0070: 20 52 69 70 61 6c 6f 2c 20 50 61 74 61 20 50 61 | Ripalo, Pata Pa| + 0080: 74 61 2c 20 4b 6f 20 4b 6f 20 4b 6f 0a 42 6f 6b |ta, Ko Ko Ko.Bok| + 0090: 6f 72 6f 20 44 69 70 6f 75 6c 69 74 6f 2c 20 52 |oro Dipoulito, R| + 00a0: 6f 6e 64 69 20 52 6f 6e 64 69 20 50 65 70 69 6e |ondi Rondi Pepin| + 00b0: 6f 2c 20 50 61 74 61 20 50 61 74 61 2c 20 4b 6f |o, Pata Pata, Ko| + 00c0: 20 4b 6f 20 4b 6f 0a 45 6d 61 6e 61 20 4b 61 72 | Ko Ko.Emana Kar| + 00d0: 61 73 73 6f 6c 69 2c 20 4c 6f 75 63 72 61 20 4c |assoli, Loucra L| + 00e0: 6f 75 63 72 61 20 50 6f 6e 70 6f 6e 74 6f 2c 20 |oucra Ponponto, | + 00f0: 50 61 74 61 20 50 61 74 61 2c 20 4b 6f 20 4b 6f |Pata Pata, Ko Ko| + 0100: 20 4b 6f 2e 00 00 00 00 00 00 00 16 0f 74 65 73 | Ko..........tes| + 0110: 74 3a 64 65 62 75 67 72 65 70 6c 79 00 00 00 03 |t:debugreply....| + 0120: 00 00 00 00 00 00 00 00 00 2b 09 74 65 73 74 3a |.........+.test:| + 0130: 6d 61 74 68 00 00 00 04 02 01 02 04 01 04 07 03 |math............| + 0140: 70 69 33 2e 31 34 65 32 2e 37 32 63 6f 6f 6b 69 |pi3.14e2.72cooki| + 0150: 6e 67 72 61 77 00 00 00 02 34 32 00 00 00 00 00 |ngraw....42.....| + 0160: 00 00 1d 09 74 65 73 74 3a 73 6f 6e 67 00 00 00 |....test:song...| + 0170: 05 01 00 0b 00 72 61 6e 64 6f 6d 70 61 72 61 6d |.....randomparam| + 0180: 00 00 00 00 00 00 00 10 09 74 65 73 74 3a 70 69 |.........test:pi| + 0190: 6e 67 00 00 00 06 00 00 00 00 00 00 00 00 00 00 |ng..............| $ hg statbundle2 < ../parts.hg2 @@ -654,21 +729,49 @@ The reply is a bundle - $ cat ../reply.hg2 - HG20\x00\x00\x00\x00\x00\x00\x00\x1b\x06output\x00\x00\x00\x00\x00\x01\x0b\x01in-reply-to3\x00\x00\x00\xd9The choir starts singing: (esc) - Patali Dirapata, Cromda Cromda Ripalo, Pata Pata, Ko Ko Ko - Bokoro Dipoulito, Rondi Rondi Pepino, Pata Pata, Ko Ko Ko - Emana Karassoli, Loucra Loucra Ponponto, Pata Pata, Ko Ko Ko. - \x00\x00\x00\x00\x00\x00\x00\x1b\x06output\x00\x00\x00\x01\x00\x01\x0b\x01in-reply-to4\x00\x00\x00\xc9debugreply: capabilities: (esc) - debugreply: 'city=!' - debugreply: 'celeste,ville' - debugreply: 'elephants' - debugreply: 'babar' - debugreply: 'celeste' - debugreply: 'ping-pong' - \x00\x00\x00\x00\x00\x00\x00\x1e test:pong\x00\x00\x00\x02\x01\x00\x0b\x01in-reply-to7\x00\x00\x00\x00\x00\x00\x00\x1b\x06output\x00\x00\x00\x03\x00\x01\x0b\x01in-reply-to7\x00\x00\x00=received ping request (id 7) (esc) - replying to ping request (id 7) - \x00\x00\x00\x00\x00\x00\x00\x00 (no-eol) (esc) + $ f --hexdump ../reply.hg2 + ../reply.hg2: + 0000: 48 47 32 30 00 00 00 00 00 00 00 1b 06 6f 75 74 |HG20.........out| + 0010: 70 75 74 00 00 00 00 00 01 0b 01 69 6e 2d 72 65 |put........in-re| + 0020: 70 6c 79 2d 74 6f 33 00 00 00 d9 54 68 65 20 63 |ply-to3....The c| + 0030: 68 6f 69 72 20 73 74 61 72 74 73 20 73 69 6e 67 |hoir starts sing| + 0040: 69 6e 67 3a 0a 20 20 20 20 50 61 74 61 6c 69 20 |ing:. Patali | + 0050: 44 69 72 61 70 61 74 61 2c 20 43 72 6f 6d 64 61 |Dirapata, Cromda| + 0060: 20 43 72 6f 6d 64 61 20 52 69 70 61 6c 6f 2c 20 | Cromda Ripalo, | + 0070: 50 61 74 61 20 50 61 74 61 2c 20 4b 6f 20 4b 6f |Pata Pata, Ko Ko| + 0080: 20 4b 6f 0a 20 20 20 20 42 6f 6b 6f 72 6f 20 44 | Ko. Bokoro D| + 0090: 69 70 6f 75 6c 69 74 6f 2c 20 52 6f 6e 64 69 20 |ipoulito, Rondi | + 00a0: 52 6f 6e 64 69 20 50 65 70 69 6e 6f 2c 20 50 61 |Rondi Pepino, Pa| + 00b0: 74 61 20 50 61 74 61 2c 20 4b 6f 20 4b 6f 20 4b |ta Pata, Ko Ko K| + 00c0: 6f 0a 20 20 20 20 45 6d 61 6e 61 20 4b 61 72 61 |o. Emana Kara| + 00d0: 73 73 6f 6c 69 2c 20 4c 6f 75 63 72 61 20 4c 6f |ssoli, Loucra Lo| + 00e0: 75 63 72 61 20 50 6f 6e 70 6f 6e 74 6f 2c 20 50 |ucra Ponponto, P| + 00f0: 61 74 61 20 50 61 74 61 2c 20 4b 6f 20 4b 6f 20 |ata Pata, Ko Ko | + 0100: 4b 6f 2e 0a 00 00 00 00 00 00 00 1b 06 6f 75 74 |Ko...........out| + 0110: 70 75 74 00 00 00 01 00 01 0b 01 69 6e 2d 72 65 |put........in-re| + 0120: 70 6c 79 2d 74 6f 34 00 00 00 c9 64 65 62 75 67 |ply-to4....debug| + 0130: 72 65 70 6c 79 3a 20 63 61 70 61 62 69 6c 69 74 |reply: capabilit| + 0140: 69 65 73 3a 0a 64 65 62 75 67 72 65 70 6c 79 3a |ies:.debugreply:| + 0150: 20 20 20 20 20 27 63 69 74 79 3d 21 27 0a 64 65 | 'city=!'.de| + 0160: 62 75 67 72 65 70 6c 79 3a 20 20 20 20 20 20 20 |bugreply: | + 0170: 20 20 27 63 65 6c 65 73 74 65 2c 76 69 6c 6c 65 | 'celeste,ville| + 0180: 27 0a 64 65 62 75 67 72 65 70 6c 79 3a 20 20 20 |'.debugreply: | + 0190: 20 20 27 65 6c 65 70 68 61 6e 74 73 27 0a 64 65 | 'elephants'.de| + 01a0: 62 75 67 72 65 70 6c 79 3a 20 20 20 20 20 20 20 |bugreply: | + 01b0: 20 20 27 62 61 62 61 72 27 0a 64 65 62 75 67 72 | 'babar'.debugr| + 01c0: 65 70 6c 79 3a 20 20 20 20 20 20 20 20 20 27 63 |eply: 'c| + 01d0: 65 6c 65 73 74 65 27 0a 64 65 62 75 67 72 65 70 |eleste'.debugrep| + 01e0: 6c 79 3a 20 20 20 20 20 27 70 69 6e 67 2d 70 6f |ly: 'ping-po| + 01f0: 6e 67 27 0a 00 00 00 00 00 00 00 1e 09 74 65 73 |ng'..........tes| + 0200: 74 3a 70 6f 6e 67 00 00 00 02 01 00 0b 01 69 6e |t:pong........in| + 0210: 2d 72 65 70 6c 79 2d 74 6f 37 00 00 00 00 00 00 |-reply-to7......| + 0220: 00 1b 06 6f 75 74 70 75 74 00 00 00 03 00 01 0b |...output.......| + 0230: 01 69 6e 2d 72 65 70 6c 79 2d 74 6f 37 00 00 00 |.in-reply-to7...| + 0240: 3d 72 65 63 65 69 76 65 64 20 70 69 6e 67 20 72 |=received ping r| + 0250: 65 71 75 65 73 74 20 28 69 64 20 37 29 0a 72 65 |equest (id 7).re| + 0260: 70 6c 79 69 6e 67 20 74 6f 20 70 69 6e 67 20 72 |plying to ping r| + 0270: 65 71 75 65 73 74 20 28 69 64 20 37 29 0a 00 00 |equest (id 7)...| + 0280: 00 00 00 00 00 00 |......| The reply is valid @@ -779,29 +882,108 @@ bundle2-output: closing payload chunk bundle2-output: end of bundle - $ cat ../rev.hg2 - HG20\x00\x00\x00\x00\x00\x00\x00\x12\x0bchangegroup\x00\x00\x00\x00\x00\x00\x00\x00\x06\x13\x00\x00\x00\xa42\xafv\x86\xd4\x03\xcfE\xb5\xd9_-p\xce\xbe\xa5\x87\xac\x80j_\xdd\xd9\x89W\xc8\xa5JMCm\xfe\x1d\xa9\xd8\x7f!\xa1\xb9{\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x002\xafv\x86\xd4\x03\xcfE\xb5\xd9_-p\xce\xbe\xa5\x87\xac\x80j\x00\x00\x00\x00\x00\x00\x00)\x00\x00\x00)6e1f4c47ecb533ffd0c8e52cdc88afb6cd39e20c (esc) - \x00\x00\x00f\x00\x00\x00h\x00\x00\x00\x02D (esc) - \x00\x00\x00i\x00\x00\x00j\x00\x00\x00\x01D\x00\x00\x00\xa4\x95 \xee\xa7\x81\xbc\xca\x16\xc1\xe1Z\xcc\x0b\xa1C5\xa0\xe8\xe5\xba\xcd\x01\x0b\x8c\xd9\x98\xf3\x98\x1aZ\x81\x15\xf9O\x8d\xa4\xabP`\x89\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x95 \xee\xa7\x81\xbc\xca\x16\xc1\xe1Z\xcc\x0b\xa1C5\xa0\xe8\xe5\xba\x00\x00\x00\x00\x00\x00\x00)\x00\x00\x00)4dece9c826f69490507b98c6383a3009b295837d (esc) - \x00\x00\x00f\x00\x00\x00h\x00\x00\x00\x02E (esc) - \x00\x00\x00i\x00\x00\x00j\x00\x00\x00\x01E\x00\x00\x00\xa2\xee\xa17Fy\x9a\x9e\x0b\xfd\x88\xf2\x9d<.\x9d\xc98\x9fRO$\xb68|\x8c\x8c\xae7\x17\x88\x80\xf3\xfa\x95\xde\xd3\xcb\x1c\xf7\x85\x95 \xee\xa7\x81\xbc\xca\x16\xc1\xe1Z\xcc\x0b\xa1C5\xa0\xe8\xe5\xba\xee\xa17Fy\x9a\x9e\x0b\xfd\x88\xf2\x9d<.\x9d\xc98\x9fRO\x00\x00\x00\x00\x00\x00\x00)\x00\x00\x00)365b93d57fdf4814e2b5911d6bacff2b12014441 (esc) - \x00\x00\x00f\x00\x00\x00h\x00\x00\x00\x00\x00\x00\x00i\x00\x00\x00j\x00\x00\x00\x01G\x00\x00\x00\xa4\x02\xdeB\x19n\xbe\xe4.\xf2\x84\xb6x (esc) - \x87\xcd\xc9n\x8e\xaa\xb6$\xb68|\x8c\x8c\xae7\x17\x88\x80\xf3\xfa\x95\xde\xd3\xcb\x1c\xf7\x85\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\xdeB\x19n\xbe\xe4.\xf2\x84\xb6x (esc) - \x87\xcd\xc9n\x8e\xaa\xb6\x00\x00\x00\x00\x00\x00\x00)\x00\x00\x00)8bee48edc7318541fc0013ee41b089276a8c24bf (esc) - \x00\x00\x00f\x00\x00\x00f\x00\x00\x00\x02H (esc) - \x00\x00\x00g\x00\x00\x00h\x00\x00\x00\x01H\x00\x00\x00\x00\x00\x00\x00\x8bn\x1fLG\xec\xb53\xff\xd0\xc8\xe5,\xdc\x88\xaf\xb6\xcd9\xe2\x0cf\xa5\xa0\x18\x17\xfd\xf5#\x9c'8\x02\xb5\xb7a\x8d\x05\x1c\x89\xe4\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x002\xafv\x86\xd4\x03\xcfE\xb5\xd9_-p\xce\xbe\xa5\x87\xac\x80j\x00\x00\x00\x81\x00\x00\x00\x81\x00\x00\x00+D\x00c3f1ca2924c16a19b0656a84900e504e5b0aec2d (esc) - \x00\x00\x00\x8bM\xec\xe9\xc8&\xf6\x94\x90P{\x98\xc68:0 \xb2\x95\x83}\x00}\x8c\x9d\x88\x84\x13%\xf5\xc6\xb0cq\xb3[N\x8a+\x1a\x83\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x95 \xee\xa7\x81\xbc\xca\x16\xc1\xe1Z\xcc\x0b\xa1C5\xa0\xe8\xe5\xba\x00\x00\x00+\x00\x00\x00\xac\x00\x00\x00+E\x009c6fd0350a6c0d0c49d4a9c5017cf07043f54e58 (esc) - \x00\x00\x00\x8b6[\x93\xd5\x7f\xdfH\x14\xe2\xb5\x91\x1dk\xac\xff+\x12\x01DA(\xa5\x84\xc6^\xf1!\xf8\x9e\xb6j\xb7\xd0\xbc\x15=\x80\x99\xe7\xceM\xec\xe9\xc8&\xf6\x94\x90P{\x98\xc68:0 \xb2\x95\x83}\xee\xa17Fy\x9a\x9e\x0b\xfd\x88\xf2\x9d<.\x9d\xc98\x9fRO\x00\x00\x00V\x00\x00\x00V\x00\x00\x00+F\x0022bfcfd62a21a3287edbd4d656218d0f525ed76a (esc) - \x00\x00\x00\x97\x8b\xeeH\xed\xc71\x85A\xfc\x00\x13\xeeA\xb0\x89'j\x8c$\xbf(\xa5\x84\xc6^\xf1!\xf8\x9e\xb6j\xb7\xd0\xbc\x15=\x80\x99\xe7\xce\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\xdeB\x19n\xbe\xe4.\xf2\x84\xb6x (esc) - \x87\xcd\xc9n\x8e\xaa\xb6\x00\x00\x00+\x00\x00\x00V\x00\x00\x00\x00\x00\x00\x00\x81\x00\x00\x00\x81\x00\x00\x00+H\x008500189e74a9e0475e822093bc7db0d631aeb0b4 (esc) - \x00\x00\x00\x00\x00\x00\x00\x05D\x00\x00\x00b\xc3\xf1\xca)$\xc1j\x19\xb0ej\x84\x90\x0ePN[ (esc) - \xec-\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x002\xafv\x86\xd4\x03\xcfE\xb5\xd9_-p\xce\xbe\xa5\x87\xac\x80j\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02D (esc) - \x00\x00\x00\x00\x00\x00\x00\x05E\x00\x00\x00b\x9co\xd05 (esc) - l\r (no-eol) (esc) - \x0cI\xd4\xa9\xc5\x01|\xf0pC\xf5NX\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x95 \xee\xa7\x81\xbc\xca\x16\xc1\xe1Z\xcc\x0b\xa1C5\xa0\xe8\xe5\xba\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02E (esc) - \x00\x00\x00\x00\x00\x00\x00\x05H\x00\x00\x00b\x85\x00\x18\x9et\xa9\xe0G^\x82 \x93\xbc}\xb0\xd61\xae\xb0\xb4\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\xdeB\x19n\xbe\xe4.\xf2\x84\xb6x (esc) - \x87\xcd\xc9n\x8e\xaa\xb6\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02H (esc) - \x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00 (no-eol) (esc) + $ f --hexdump ../rev.hg2 + ../rev.hg2: + 0000: 48 47 32 30 00 00 00 00 00 00 00 12 0b 63 68 61 |HG20.........cha| + 0010: 6e 67 65 67 72 6f 75 70 00 00 00 00 00 00 00 00 |ngegroup........| + 0020: 06 13 00 00 00 a4 32 af 76 86 d4 03 cf 45 b5 d9 |......2.v....E..| + 0030: 5f 2d 70 ce be a5 87 ac 80 6a 5f dd d9 89 57 c8 |_-p......j_...W.| + 0040: a5 4a 4d 43 6d fe 1d a9 d8 7f 21 a1 b9 7b 00 00 |.JMCm.....!..{..| + 0050: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................| + 0060: 00 00 32 af 76 86 d4 03 cf 45 b5 d9 5f 2d 70 ce |..2.v....E.._-p.| + 0070: be a5 87 ac 80 6a 00 00 00 00 00 00 00 29 00 00 |.....j.......)..| + 0080: 00 29 36 65 31 66 34 63 34 37 65 63 62 35 33 33 |.)6e1f4c47ecb533| + 0090: 66 66 64 30 63 38 65 35 32 63 64 63 38 38 61 66 |ffd0c8e52cdc88af| + 00a0: 62 36 63 64 33 39 65 32 30 63 0a 00 00 00 66 00 |b6cd39e20c....f.| + 00b0: 00 00 68 00 00 00 02 44 0a 00 00 00 69 00 00 00 |..h....D....i...| + 00c0: 6a 00 00 00 01 44 00 00 00 a4 95 20 ee a7 81 bc |j....D..... ....| + 00d0: ca 16 c1 e1 5a cc 0b a1 43 35 a0 e8 e5 ba cd 01 |....Z...C5......| + 00e0: 0b 8c d9 98 f3 98 1a 5a 81 15 f9 4f 8d a4 ab 50 |.......Z...O...P| + 00f0: 60 89 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |`...............| + 0100: 00 00 00 00 00 00 95 20 ee a7 81 bc ca 16 c1 e1 |....... ........| + 0110: 5a cc 0b a1 43 35 a0 e8 e5 ba 00 00 00 00 00 00 |Z...C5..........| + 0120: 00 29 00 00 00 29 34 64 65 63 65 39 63 38 32 36 |.)...)4dece9c826| + 0130: 66 36 39 34 39 30 35 30 37 62 39 38 63 36 33 38 |f69490507b98c638| + 0140: 33 61 33 30 30 39 62 32 39 35 38 33 37 64 0a 00 |3a3009b295837d..| + 0150: 00 00 66 00 00 00 68 00 00 00 02 45 0a 00 00 00 |..f...h....E....| + 0160: 69 00 00 00 6a 00 00 00 01 45 00 00 00 a2 ee a1 |i...j....E......| + 0170: 37 46 79 9a 9e 0b fd 88 f2 9d 3c 2e 9d c9 38 9f |7Fy.......<...8.| + 0180: 52 4f 24 b6 38 7c 8c 8c ae 37 17 88 80 f3 fa 95 |RO$.8|...7......| + 0190: de d3 cb 1c f7 85 95 20 ee a7 81 bc ca 16 c1 e1 |....... ........| + 01a0: 5a cc 0b a1 43 35 a0 e8 e5 ba ee a1 37 46 79 9a |Z...C5......7Fy.| + 01b0: 9e 0b fd 88 f2 9d 3c 2e 9d c9 38 9f 52 4f 00 00 |......<...8.RO..| + 01c0: 00 00 00 00 00 29 00 00 00 29 33 36 35 62 39 33 |.....)...)365b93| + 01d0: 64 35 37 66 64 66 34 38 31 34 65 32 62 35 39 31 |d57fdf4814e2b591| + 01e0: 31 64 36 62 61 63 66 66 32 62 31 32 30 31 34 34 |1d6bacff2b120144| + 01f0: 34 31 0a 00 00 00 66 00 00 00 68 00 00 00 00 00 |41....f...h.....| + 0200: 00 00 69 00 00 00 6a 00 00 00 01 47 00 00 00 a4 |..i...j....G....| + 0210: 02 de 42 19 6e be e4 2e f2 84 b6 78 0a 87 cd c9 |..B.n......x....| + 0220: 6e 8e aa b6 24 b6 38 7c 8c 8c ae 37 17 88 80 f3 |n...$.8|...7....| + 0230: fa 95 de d3 cb 1c f7 85 00 00 00 00 00 00 00 00 |................| + 0240: 00 00 00 00 00 00 00 00 00 00 00 00 02 de 42 19 |..............B.| + 0250: 6e be e4 2e f2 84 b6 78 0a 87 cd c9 6e 8e aa b6 |n......x....n...| + 0260: 00 00 00 00 00 00 00 29 00 00 00 29 38 62 65 65 |.......)...)8bee| + 0270: 34 38 65 64 63 37 33 31 38 35 34 31 66 63 30 30 |48edc7318541fc00| + 0280: 31 33 65 65 34 31 62 30 38 39 32 37 36 61 38 63 |13ee41b089276a8c| + 0290: 32 34 62 66 0a 00 00 00 66 00 00 00 66 00 00 00 |24bf....f...f...| + 02a0: 02 48 0a 00 00 00 67 00 00 00 68 00 00 00 01 48 |.H....g...h....H| + 02b0: 00 00 00 00 00 00 00 8b 6e 1f 4c 47 ec b5 33 ff |........n.LG..3.| + 02c0: d0 c8 e5 2c dc 88 af b6 cd 39 e2 0c 66 a5 a0 18 |...,.....9..f...| + 02d0: 17 fd f5 23 9c 27 38 02 b5 b7 61 8d 05 1c 89 e4 |...#.'8...a.....| + 02e0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................| + 02f0: 00 00 00 00 32 af 76 86 d4 03 cf 45 b5 d9 5f 2d |....2.v....E.._-| + 0300: 70 ce be a5 87 ac 80 6a 00 00 00 81 00 00 00 81 |p......j........| + 0310: 00 00 00 2b 44 00 63 33 66 31 63 61 32 39 32 34 |...+D.c3f1ca2924| + 0320: 63 31 36 61 31 39 62 30 36 35 36 61 38 34 39 30 |c16a19b0656a8490| + 0330: 30 65 35 30 34 65 35 62 30 61 65 63 32 64 0a 00 |0e504e5b0aec2d..| + 0340: 00 00 8b 4d ec e9 c8 26 f6 94 90 50 7b 98 c6 38 |...M...&...P{..8| + 0350: 3a 30 09 b2 95 83 7d 00 7d 8c 9d 88 84 13 25 f5 |:0....}.}.....%.| + 0360: c6 b0 63 71 b3 5b 4e 8a 2b 1a 83 00 00 00 00 00 |..cq.[N.+.......| + 0370: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 95 |................| + 0380: 20 ee a7 81 bc ca 16 c1 e1 5a cc 0b a1 43 35 a0 | ........Z...C5.| + 0390: e8 e5 ba 00 00 00 2b 00 00 00 ac 00 00 00 2b 45 |......+.......+E| + 03a0: 00 39 63 36 66 64 30 33 35 30 61 36 63 30 64 30 |.9c6fd0350a6c0d0| + 03b0: 63 34 39 64 34 61 39 63 35 30 31 37 63 66 30 37 |c49d4a9c5017cf07| + 03c0: 30 34 33 66 35 34 65 35 38 0a 00 00 00 8b 36 5b |043f54e58.....6[| + 03d0: 93 d5 7f df 48 14 e2 b5 91 1d 6b ac ff 2b 12 01 |....H.....k..+..| + 03e0: 44 41 28 a5 84 c6 5e f1 21 f8 9e b6 6a b7 d0 bc |DA(...^.!...j...| + 03f0: 15 3d 80 99 e7 ce 4d ec e9 c8 26 f6 94 90 50 7b |.=....M...&...P{| + 0400: 98 c6 38 3a 30 09 b2 95 83 7d ee a1 37 46 79 9a |..8:0....}..7Fy.| + 0410: 9e 0b fd 88 f2 9d 3c 2e 9d c9 38 9f 52 4f 00 00 |......<...8.RO..| + 0420: 00 56 00 00 00 56 00 00 00 2b 46 00 32 32 62 66 |.V...V...+F.22bf| + 0430: 63 66 64 36 32 61 32 31 61 33 32 38 37 65 64 62 |cfd62a21a3287edb| + 0440: 64 34 64 36 35 36 32 31 38 64 30 66 35 32 35 65 |d4d656218d0f525e| + 0450: 64 37 36 61 0a 00 00 00 97 8b ee 48 ed c7 31 85 |d76a.......H..1.| + 0460: 41 fc 00 13 ee 41 b0 89 27 6a 8c 24 bf 28 a5 84 |A....A..'j.$.(..| + 0470: c6 5e f1 21 f8 9e b6 6a b7 d0 bc 15 3d 80 99 e7 |.^.!...j....=...| + 0480: ce 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................| + 0490: 00 00 00 00 00 02 de 42 19 6e be e4 2e f2 84 b6 |.......B.n......| + 04a0: 78 0a 87 cd c9 6e 8e aa b6 00 00 00 2b 00 00 00 |x....n......+...| + 04b0: 56 00 00 00 00 00 00 00 81 00 00 00 81 00 00 00 |V...............| + 04c0: 2b 48 00 38 35 30 30 31 38 39 65 37 34 61 39 65 |+H.8500189e74a9e| + 04d0: 30 34 37 35 65 38 32 32 30 39 33 62 63 37 64 62 |0475e822093bc7db| + 04e0: 30 64 36 33 31 61 65 62 30 62 34 0a 00 00 00 00 |0d631aeb0b4.....| + 04f0: 00 00 00 05 44 00 00 00 62 c3 f1 ca 29 24 c1 6a |....D...b...)$.j| + 0500: 19 b0 65 6a 84 90 0e 50 4e 5b 0a ec 2d 00 00 00 |..ej...PN[..-...| + 0510: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................| + 0520: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................| + 0530: 00 00 00 00 00 32 af 76 86 d4 03 cf 45 b5 d9 5f |.....2.v....E.._| + 0540: 2d 70 ce be a5 87 ac 80 6a 00 00 00 00 00 00 00 |-p......j.......| + 0550: 00 00 00 00 02 44 0a 00 00 00 00 00 00 00 05 45 |.....D.........E| + 0560: 00 00 00 62 9c 6f d0 35 0a 6c 0d 0c 49 d4 a9 c5 |...b.o.5.l..I...| + 0570: 01 7c f0 70 43 f5 4e 58 00 00 00 00 00 00 00 00 |.|.pC.NX........| + 0580: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................| + 0590: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................| + 05a0: 95 20 ee a7 81 bc ca 16 c1 e1 5a cc 0b a1 43 35 |. ........Z...C5| + 05b0: a0 e8 e5 ba 00 00 00 00 00 00 00 00 00 00 00 02 |................| + 05c0: 45 0a 00 00 00 00 00 00 00 05 48 00 00 00 62 85 |E.........H...b.| + 05d0: 00 18 9e 74 a9 e0 47 5e 82 20 93 bc 7d b0 d6 31 |...t..G^. ..}..1| + 05e0: ae b0 b4 00 00 00 00 00 00 00 00 00 00 00 00 00 |................| + 05f0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................| + 0600: 00 00 00 00 00 00 00 00 00 00 00 02 de 42 19 6e |.............B.n| + 0610: be e4 2e f2 84 b6 78 0a 87 cd c9 6e 8e aa b6 00 |......x....n....| + 0620: 00 00 00 00 00 00 00 00 00 00 02 48 0a 00 00 00 |...........H....| + 0630: 00 00 00 00 00 00 00 00 00 00 00 00 00 |.............| $ hg debugbundle ../rev.hg2 Stream params: {} @@ -823,12 +1005,21 @@ 0 unread bytes addchangegroup return: 1 - $ cat ../rev-reply.hg2 - HG20\x00\x00\x00\x00\x00\x00\x00/\x11reply:changegroup\x00\x00\x00\x00\x00\x02\x0b\x01\x06\x01in-reply-to1return1\x00\x00\x00\x00\x00\x00\x00\x1b\x06output\x00\x00\x00\x01\x00\x01\x0b\x01in-reply-to1\x00\x00\x00dadding changesets (esc) - adding manifests - adding file changes - added 0 changesets with 0 changes to 3 files - \x00\x00\x00\x00\x00\x00\x00\x00 (no-eol) (esc) + $ f --hexdump ../rev-reply.hg2 + ../rev-reply.hg2: + 0000: 48 47 32 30 00 00 00 00 00 00 00 2f 11 72 65 70 |HG20......./.rep| + 0010: 6c 79 3a 63 68 61 6e 67 65 67 72 6f 75 70 00 00 |ly:changegroup..| + 0020: 00 00 00 02 0b 01 06 01 69 6e 2d 72 65 70 6c 79 |........in-reply| + 0030: 2d 74 6f 31 72 65 74 75 72 6e 31 00 00 00 00 00 |-to1return1.....| + 0040: 00 00 1b 06 6f 75 74 70 75 74 00 00 00 01 00 01 |....output......| + 0050: 0b 01 69 6e 2d 72 65 70 6c 79 2d 74 6f 31 00 00 |..in-reply-to1..| + 0060: 00 64 61 64 64 69 6e 67 20 63 68 61 6e 67 65 73 |.dadding changes| + 0070: 65 74 73 0a 61 64 64 69 6e 67 20 6d 61 6e 69 66 |ets.adding manif| + 0080: 65 73 74 73 0a 61 64 64 69 6e 67 20 66 69 6c 65 |ests.adding file| + 0090: 20 63 68 61 6e 67 65 73 0a 61 64 64 65 64 20 30 | changes.added 0| + 00a0: 20 63 68 61 6e 67 65 73 65 74 73 20 77 69 74 68 | changesets with| + 00b0: 20 30 20 63 68 61 6e 67 65 73 20 74 6f 20 33 20 | 0 changes to 3 | + 00c0: 66 69 6c 65 73 0a 00 00 00 00 00 00 00 00 |files.........| Check handling of exception during generation. ---------------------------------------------- @@ -839,9 +1030,16 @@ Should still be a valid bundle - $ cat ../genfailed.hg2 - HG20\x00\x00\x00\x00\x00\x00\x00\r (no-eol) (esc) - \x06output\x00\x00\x00\x00\x00\x00\xff\xff\xff\xff\x00\x00\x00H\x0berror:abort\x00\x00\x00\x00\x01\x00\x07-messageunexpected error: Someone set up us the bomb!\x00\x00\x00\x00\x00\x00\x00\x00 (no-eol) (esc) + $ f --hexdump ../genfailed.hg2 + ../genfailed.hg2: + 0000: 48 47 32 30 00 00 00 00 00 00 00 0d 06 6f 75 74 |HG20.........out| + 0010: 70 75 74 00 00 00 00 00 00 ff ff ff ff 00 00 00 |put.............| + 0020: 48 0b 65 72 72 6f 72 3a 61 62 6f 72 74 00 00 00 |H.error:abort...| + 0030: 00 01 00 07 2d 6d 65 73 73 61 67 65 75 6e 65 78 |....-messageunex| + 0040: 70 65 63 74 65 64 20 65 72 72 6f 72 3a 20 53 6f |pected error: So| + 0050: 6d 65 6f 6e 65 20 73 65 74 20 75 70 20 75 73 20 |meone set up us | + 0060: 74 68 65 20 62 6f 6d 62 21 00 00 00 00 00 00 00 |the bomb!.......| + 0070: 00 |.| And its handling on the other size raise a clean exception @@ -850,5 +1048,180 @@ abort: unexpected error: Someone set up us the bomb! [255] +Test compression +================ + +Simple case where it just work: GZ +---------------------------------- + + $ hg bundle2 --compress GZ --rev '8+7+5+4' ../rev.hg2.bz + $ f --hexdump ../rev.hg2.bz + ../rev.hg2.bz: + 0000: 48 47 32 30 00 00 00 0e 43 6f 6d 70 72 65 73 73 |HG20....Compress| + 0010: 69 6f 6e 3d 47 5a 78 9c 95 94 7d 68 95 55 1c c7 |ion=GZx...}h.U..| + 0020: 9f 3b 31 e8 ce fa c3 65 be a0 a4 b4 52 b9 29 e7 |.;1....e....R.).| + 0030: f5 79 ce 89 fa 63 ed 5e 77 8b 9c c3 3f 2a 1c 68 |.y...c.^w...?*.h| + 0040: cf 79 9b dd 6a ae b0 28 74 b8 e5 96 5b bb 86 61 |.y..j..(t...[..a| + 0050: a3 15 6e 3a 71 c8 6a e8 a5 da 95 64 28 22 ce 69 |..n:q.j....d(".i| + 0060: cd 06 59 34 28 2b 51 2a 58 c3 17 56 2a 9a 9d 67 |..Y4(+Q*X..V*..g| + 0070: dc c6 35 9e c4 1d f8 9e 87 f3 9c f3 3b bf 0f bf |..5.........;...| + 0080: 97 e3 38 ce f4 42 b9 d6 af ae d2 55 af ae 7b ad |..8..B.....U..{.| + 0090: c6 c9 8d bb 8a ec b4 07 ed 7f fd ed d3 53 be 4e |.............S.N| + 00a0: f4 0e af 59 52 73 ea 50 d7 96 9e ba d4 9a 1f 87 |...YRs.P........| + 00b0: 9b 9f 1d e8 7a 6a 79 e9 cb 7f cf eb fe 7e d3 82 |....zjy......~..| + 00c0: ce 2f 36 38 21 23 cc 36 b7 b5 38 90 ab a1 21 92 |./68!#.6..8...!.| + 00d0: 78 5a 0a 8a b1 31 0a 48 a6 29 92 4a 32 e6 1b e1 |xZ...1.H.).J2...| + 00e0: 4a 85 b9 46 40 46 ed 61 63 b5 d6 aa 20 1e ac 5e |J..F@F.ac... ..^| + 00f0: b0 0a ae 8a c4 03 c6 d6 f9 a3 7b eb fb 4e de 7f |..........{..N..| + 0100: e4 97 55 5f 15 76 96 d2 5d bf 9d 3f 38 18 29 4c |..U_.v..]..?8.)L| + 0110: 0f b7 5d 6e 9b b3 aa 7e c6 d5 15 5b f7 7c 52 f1 |..]n...~...[.|R.| + 0120: 7c 73 18 63 98 6d 3e 23 51 5a 6a 2e 19 72 8d cb ||s.c.m>#QZj..r..| + 0130: 09 07 14 78 82 33 e9 62 86 7d 0c 00 17 88 53 86 |...x.3.b.}....S.| + 0140: 3d 75 0b 63 e2 16 c6 84 9d 76 8f 76 7a cb de fc |=u.c.....v.vz...| + 0150: a8 a3 f0 46 d3 a5 f6 c7 96 b6 9f 60 3b 57 ae 28 |...F.......`;W.(| + 0160: ce b2 8d e9 f4 3e 6f 66 53 dd e5 6b ad 67 be f9 |.....>ofS..k.g..| + 0170: 72 ee 5f 8d 61 3c 61 b6 f9 8c d8 a5 82 63 45 3d |r._.a<a......cE=| + 0180: a3 0c 61 90 68 24 28 87 50 b9 c2 97 c6 20 01 11 |..a.h$(.P.... ..| + 0190: 80 84 10 98 cf e8 e4 13 96 05 51 2c 38 f3 c4 ec |..........Q,8...| + 01a0: ea 43 e7 96 5e 6a c8 be 11 dd 32 78 a2 fa dd 8f |.C..^j....2x....| + 01b0: b3 61 84 61 51 0c b3 cd 27 64 42 6b c2 b4 92 1e |.a.aQ...'dBk....| + 01c0: 86 8c 12 68 24 00 10 db 7f 50 00 c6 91 e7 fa 4c |...h$....P.....L| + 01d0: 22 22 cc bf 84 81 0a 92 c1 aa 2a c7 1b 49 e6 ee |""........*..I..| + 01e0: 6b a9 7e e0 e9 b2 91 5e 7c 73 68 e0 fc 23 3f 34 |k.~....^|sh..#?4| + 01f0: ed cf 0e f2 b3 d3 4c d7 ae 59 33 6f 8c 3d b8 63 |......L..Y3o.=.c| + 0200: 21 2b e8 3d e0 6f 9d 3a b7 f9 dc 24 2a b2 3e a7 |!+.=.o.:...$*.>.| + 0210: 58 dc 91 d8 40 e9 23 8e 88 84 ae 0f b9 00 2e b5 |X...@.#.........| + 0220: 74 36 f3 40 53 40 34 15 c0 d7 12 8d e7 bb 65 f9 |t6.@S@4.......e.| + 0230: c8 ef 03 0f ff f9 fe b6 8a 0d 6d fd ec 51 70 f7 |..........m..Qp.| + 0240: a7 ad 9b 6b 9d da 74 7b 53 43 d1 43 63 fd 19 f9 |...k..t{SC.Cc...| + 0250: ca 67 95 e5 ef c4 e6 6c 9e 44 e1 c5 ac 7a 82 6f |.g.....l.D...z.o| + 0260: c2 e1 d2 b5 2d 81 29 f0 5d 09 6c 6f 10 ae 88 cf |....-.).].lo....| + 0270: 25 05 d0 93 06 78 80 60 43 2d 10 1b 47 71 2b b7 |%....x.`C-..Gq+.| + 0280: 7f bb e9 a7 e4 7d 67 7b df 9b f7 62 cf cd d8 f4 |.....}g{...b....| + 0290: 48 bc 64 51 57 43 ff ea 8b 0b ae 74 64 53 07 86 |H.dQWC.....tdS..| + 02a0: fa 66 3c 5e f7 e1 af a7 c2 90 ff a7 be 9e c9 29 |.f<^...........)| + 02b0: b6 cc 41 48 18 69 94 8b 7c 04 7d 8c 98 a7 95 50 |..AH.i..|.}....P| + 02c0: 44 d9 d0 20 c8 14 30 14 51 ad 6c 16 03 94 0f 5a |D.. ..0.Q.l....Z| + 02d0: 46 93 7f 1c 87 8d 25 d7 9d a2 d1 92 4c f3 c2 54 |F.....%.....L..T| + 02e0: ba f8 70 18 ca 24 0a 29 96 43 71 f2 93 95 74 18 |..p..$.).Cq...t.| + 02f0: b5 65 c4 b8 f6 6c 5c 34 20 1e d5 0c 21 c0 b1 90 |.e...l\4 ...!...| + 0300: 9e 12 40 b9 18 fa 5a 00 41 a2 39 d3 a9 c1 73 21 |..@...Z.A.9...s!| + 0310: 8e 5e 3c b9 b8 f8 48 6a 76 46 a7 1a b6 dd 5b 51 |.^<...HjvF....[Q| + 0320: 5e 19 1d 59 12 c6 32 89 02 9a c0 8f 4f b8 0a ba |^..Y..2.....O...| + 0330: 5e ec 58 37 44 a3 2f dd 33 ed c9 d3 dd c7 22 1b |^.X7D./.3.....".| + 0340: 2f d4 94 8e 95 3f 77 a7 ae 6e f3 32 8d bb 4a 4c |/....?w..n.2..JL| + 0350: b8 0a 5a 43 34 3a b3 3a d6 77 ff 5c b6 fa ad f9 |..ZC4:.:.w.\....| + 0360: db fb 6a 33 df c1 7d 99 cf ef d4 d5 6d da 77 7c |..j3..}.....m.w|| + 0370: 3b 19 fd af c5 3f f1 60 c3 17 |;....?.`..| + $ hg debugbundle ../rev.hg2.bz + Stream params: {'Compression': 'GZ'} + changegroup -- '{}' + 32af7686d403cf45b5d95f2d70cebea587ac806a + 9520eea781bcca16c1e15acc0ba14335a0e8e5ba + eea13746799a9e0bfd88f29d3c2e9dc9389f524f + 02de42196ebee42ef284b6780a87cdc96e8eaab6 + $ hg unbundle ../rev.hg2.bz + adding changesets + adding manifests + adding file changes + added 0 changesets with 0 changes to 3 files +Simple case where it just work: BZ +---------------------------------- + + $ hg bundle2 --compress BZ --rev '8+7+5+4' ../rev.hg2.bz + $ f --hexdump ../rev.hg2.bz + ../rev.hg2.bz: + 0000: 48 47 32 30 00 00 00 0e 43 6f 6d 70 72 65 73 73 |HG20....Compress| + 0010: 69 6f 6e 3d 42 5a 42 5a 68 39 31 41 59 26 53 59 |ion=BZBZh91AY&SY| + 0020: a3 4b 18 3d 00 00 1a 7f ff ff bf 5f f6 ef ef 7f |.K.=......._....| + 0030: f6 3f f7 d1 d9 ff ff f7 6e ff ff 6e f7 f6 bd df |.?......n..n....| + 0040: b5 ab ff cf 67 f6 e7 7b f7 c0 02 d7 33 82 8b 51 |....g..{....3..Q| + 0050: 04 a5 53 d5 3d 27 a0 99 18 4d 0d 34 00 d1 a1 e8 |..S.='...M.4....| + 0060: 80 c8 7a 87 a9 a3 43 6a 3d 46 86 26 80 34 3d 40 |..z...Cj=F.&.4=@| + 0070: c8 c9 b5 34 f4 8f 48 0f 51 ea 34 34 fd 4d aa 19 |...4..H.Q.44.M..| + 0080: 03 40 0c 08 da 86 43 d4 f5 0f 42 1e a0 f3 54 33 |.@....C...B...T3| + 0090: 54 d3 13 4d 03 40 32 00 00 32 03 26 80 0d 00 0d |T..M.@2..2.&....| + 00a0: 00 68 c8 c8 03 20 32 30 98 8c 80 00 00 03 4d 00 |.h... 20......M.| + 00b0: c8 00 00 0d 00 00 22 99 a1 34 c2 64 a6 d5 34 1a |......"..4.d..4.| + 00c0: 00 00 06 86 83 4d 07 a8 d1 a0 68 01 a0 00 00 00 |.....M....h.....| + 00d0: 00 0d 06 80 00 00 00 0d 00 03 40 00 00 04 a4 a1 |..........@.....| + 00e0: 4d a9 89 89 b4 9a 32 0c 43 46 86 87 a9 8d 41 9a |M.....2.CF....A.| + 00f0: 98 46 9a 0d 31 32 1a 34 0d 0c 8d a2 0c 98 4d 06 |.F..12.4......M.| + 0100: 8c 40 c2 60 8d 0d 0c 20 c9 89 fa a0 d0 d3 21 a1 |.@.`... ......!.| + 0110: ea 34 d3 68 9e a6 d1 74 05 33 cb 66 96 93 28 64 |.4.h...t.3.f..(d| + 0120: 40 91 22 ac 55 9b ea 40 7b 38 94 e2 f8 06 00 cb |@.".U..@{8......| + 0130: 28 02 00 4d ab 40 24 10 43 18 cf 64 b4 06 83 0c |(..M.@$.C..d....| + 0140: 34 6c b4 a3 d4 0a 0a e4 a8 5c 4e 23 c0 c9 7a 31 |4l.......\N#..z1| + 0150: 97 87 77 7a 64 88 80 8e 60 97 20 93 0f 8e eb c4 |..wzd...`. .....| + 0160: 62 a4 44 a3 52 20 b2 99 a9 2e e1 d7 29 4a 54 ac |b.D.R ......)JT.| + 0170: 44 7a bb cc 04 3d e0 aa bd 6a 33 5e 9b a2 57 36 |Dz...=...j3^..W6| + 0180: fa cb 45 bb 6d 3e c1 d9 d9 f5 83 69 8a d0 e0 e2 |..E.m>.....i....| + 0190: e7 ae 90 55 24 da 3f ab 78 c0 4c b4 56 a3 9e a4 |...U$.?.x.L.V...| + 01a0: af 9c 65 74 86 ec 6d dc 62 dc 33 ca c8 50 dd 9d |..et..m.b.3..P..| + 01b0: 98 8e 9e 59 20 f3 f0 42 91 4a 09 f5 75 8d 3d a5 |...Y ..B.J..u.=.| + 01c0: a5 15 cb 8d 10 63 b0 c2 2e b2 81 f7 c1 76 0e 53 |.....c.......v.S| + 01d0: 6c 0e 46 73 b5 ae 67 f9 4c 0b 45 6b a8 32 2a 2f |l.Fs..g.L.Ek.2*/| + 01e0: a2 54 a4 44 05 20 a1 38 d1 a4 c6 09 a8 2b 08 99 |.T.D. .8.....+..| + 01f0: a4 14 ae 8d a3 e3 aa 34 27 d8 44 ca c3 5d 21 8b |.......4'.D..]!.| + 0200: 1a 1e 97 29 71 2b 09 4a 4a 55 55 94 58 65 b2 bc |...)q+.JJUU.Xe..| + 0210: f3 a5 90 26 36 76 67 7a 51 98 d6 8a 4a 99 50 b5 |...&6vgzQ...J.P.| + 0220: 99 8f 94 21 17 a9 8b f3 ad 4c 33 d4 2e 40 c8 0c |...!.....L3..@..| + 0230: 3b 90 53 39 db 48 02 34 83 48 d6 b3 99 13 d2 58 |;.S9.H.4.H.....X| + 0240: 65 8e 71 ac a9 06 95 f2 c4 8e b4 08 6b d3 0c ae |e.q.........k...| + 0250: d9 90 56 71 43 a7 a2 62 16 3e 50 63 d3 57 3c 2d |..VqC..b.>Pc.W<-| + 0260: 9f 0f 34 05 08 d8 a6 4b 59 31 54 66 3a 45 0c 8a |..4....KY1Tf:E..| + 0270: c7 90 3a f0 6a 83 1b f5 ca fb 80 2b 50 06 fb 51 |..:.j......+P..Q| + 0280: 7e a6 a4 d4 81 44 82 21 54 00 5b 1a 30 83 62 a3 |~....D.!T.[.0.b.| + 0290: 18 b6 24 19 1e 45 df 4d 5c db a6 af 5b ac 90 fa |..$..E.M\...[...| + 02a0: 3e ed f9 ec 4c ba 36 ee d8 60 20 a7 c7 3b cb d1 |>...L.6..` ..;..| + 02b0: 90 43 7d 27 16 50 5d ad f4 14 07 0b 90 5c cc 6b |.C}'.P]......\.k| + 02c0: 8d 3f a6 88 f4 34 37 a8 cf 14 63 36 19 f7 3e 28 |.?...47...c6..>(| + 02d0: de 99 e8 16 a4 9d 0d 40 a1 a7 24 52 14 a6 72 62 |.......@..$R..rb| + 02e0: 59 5a ca 2d e5 51 90 78 88 d9 c6 c7 21 d0 f7 46 |YZ.-.Q.x....!..F| + 02f0: b2 04 46 44 4e 20 9c 12 b1 03 4e 25 e0 a9 0c 58 |..FDN ....N%...X| + 0300: 5b 1d 3c 93 20 01 51 de a9 1c 69 23 32 46 14 b4 |[.<. .Q...i#2F..| + 0310: 90 db 17 98 98 50 03 90 29 aa 40 b0 13 d8 43 d2 |.....P..).@...C.| + 0320: 5f c5 9d eb f3 f2 ad 41 e8 7a a9 ed a1 58 84 a6 |_......A.z...X..| + 0330: 42 bf d6 fc 24 82 c1 20 32 26 4a 15 a6 1d 29 7f |B...$.. 2&J...).| + 0340: 7e f4 3d 07 bc 62 9a 5b ec 44 3d 72 1d 41 8b 5c |~.=..b.[.D=r.A.\| + 0350: 80 de 0e 62 9a 2e f8 83 00 d5 07 a0 9c c6 74 98 |...b..........t.| + 0360: 11 b2 5e a9 38 02 03 ee fd 86 5c f4 86 b3 ae da |..^.8.....\.....| + 0370: 05 94 01 c5 c6 ea 18 e6 ba 2a ba b3 04 5c 96 89 |.........*...\..| + 0380: 72 63 5b 10 11 f6 67 34 98 cb e4 c0 4e fa e6 99 |rc[...g4....N...| + 0390: 19 6e 50 e8 26 8d 0c 17 e0 be ef e1 8e 02 6f 32 |.nP.&.........o2| + 03a0: 82 dc 26 f8 a1 08 f3 8a 0d f3 c4 75 00 48 73 b8 |..&........u.Hs.| + 03b0: be 3b 0d 7f d0 fd c7 78 96 ec e0 03 80 68 4d 8d |.;.....x.....hM.| + 03c0: 43 8c d7 68 58 f9 50 f0 18 cb 21 58 1b 60 cd 1f |C..hX.P...!X.`..| + 03d0: 84 36 2e 16 1f 0a f7 4e 8f eb df 01 2d c2 79 0b |.6.....N....-.y.| + 03e0: f7 24 ea 0d e8 59 86 51 6e 1c 30 a3 ad 2f ee 8c |.$...Y.Qn.0../..| + 03f0: 90 c8 84 d5 e8 34 c1 95 b2 c9 f6 4d 87 1c 7d 19 |.....4.....M..}.| + 0400: d6 41 58 56 7a e0 6c ba 10 c7 e8 33 39 36 96 e7 |.AXVz.l....396..| + 0410: d2 f9 59 9a 08 95 48 38 e7 0b b7 0a 24 67 c4 39 |..Y...H8....$g.9| + 0420: 8b 43 88 57 9c 01 f5 61 b5 e1 27 41 7e af 83 fe |.C.W...a..'A~...| + 0430: 2e e4 8a 70 a1 21 46 96 30 7a |...p.!F.0z| + $ hg debugbundle ../rev.hg2.bz + Stream params: {'Compression': 'BZ'} + changegroup -- '{}' + 32af7686d403cf45b5d95f2d70cebea587ac806a + 9520eea781bcca16c1e15acc0ba14335a0e8e5ba + eea13746799a9e0bfd88f29d3c2e9dc9389f524f + 02de42196ebee42ef284b6780a87cdc96e8eaab6 + $ hg unbundle ../rev.hg2.bz + adding changesets + adding manifests + adding file changes + added 0 changesets with 0 changes to 3 files + +unknown compression while unbundling +----------------------------- + + $ hg bundle2 --param Compression=FooBarUnknown --rev '8+7+5+4' ../rev.hg2.bz + $ cat ../rev.hg2.bz | hg statbundle2 + abort: unknown parameters: Stream Parameter - Compression='FooBarUnknown' + [255] + $ hg unbundle ../rev.hg2.bz + abort: ../rev.hg2.bz: unknown bundle feature, Stream Parameter - Compression='FooBarUnknown' + (see https://mercurial-scm.org/wiki/BundleFeature for more information) + [255] $ cd ..
--- a/tests/test-clone-r.t Mon Oct 05 10:43:16 2015 -0600 +++ b/tests/test-clone-r.t Wed Oct 07 13:44:48 2015 -0500 @@ -218,3 +218,26 @@ 4 files, 9 changesets, 7 total revisions $ cd .. + $ hg clone test test-9 + updating to branch default + 1 files updated, 0 files merged, 0 files removed, 0 files unresolved + $ cd test-9 + $ hg branch foobar + marked working directory as branch foobar + (branches are permanent and global, did you want a bookmark?) + $ echo file2 >> file2 + $ hg add file2 + $ hg commit -m "changeset9" + $ echo file3 >> file3 + $ hg add file3 + $ hg commit -m "changeset10" + $ cd .. + $ hg clone -r 9 -u foobar test-9 test-10 + adding changesets + adding manifests + adding file changes + added 6 changesets with 6 changes to 3 files + updating to branch foobar + 2 files updated, 0 files merged, 0 files removed, 0 files unresolved + +
--- a/tests/test-clone.t Mon Oct 05 10:43:16 2015 -0600 +++ b/tests/test-clone.t Wed Oct 07 13:44:48 2015 -0500 @@ -1020,7 +1020,7 @@ $ hg -R a id -r 0 acb14030fe0a $ hg id -R remote -r 0 - abort: there is no Mercurial repository here (.hg not found) + abort: repository remote not found! [255] $ hg --config share.pool=share -q clone -e "python \"$TESTDIR/dummyssh\"" a ssh://user@dummy/remote $ hg -R remote id -r 0
--- a/tests/test-command-template.t Mon Oct 05 10:43:16 2015 -0600 +++ b/tests/test-command-template.t Wed Oct 07 13:44:48 2015 -0500 @@ -343,6 +343,11 @@ 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> @@ -2495,10 +2500,14 @@ abort: template filter 'escape' is not compatible with keyword 'date' [255] + $ 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' - abort: template filter 'datefilter' is not compatible with keyword 'author' + hg: parse error: date expects a date information [255] Error in nested template: @@ -2681,6 +2690,32 @@ 1: t1+0 0: null+1 + $ hg log --template "{rev}: {latesttag % '{tag}+{distance},{changes} '}\n" + 10: t5+5,5 + 9: t5+4,4 + 8: t5+3,3 + 7: t5+2,2 + 6: t5+1,1 + 5: t5+0,0 + 4: at3+1,1 t3+1,1 + 3: at3+0,0 t3+0,0 + 2: t2+0,0 + 1: t1+0,0 + 0: null+1,1 + + $ hg log --template "{rev}: {latesttag('re:^t[13]$') % '{tag}, C: {changes}, D: {distance}'}\n" + 10: t3, C: 8, D: 7 + 9: t3, C: 7, D: 6 + 8: t3, C: 6, D: 5 + 7: t3, C: 5, D: 4 + 6: t3, C: 4, D: 3 + 5: t3, C: 3, D: 2 + 4: t3, C: 1, D: 1 + 3: t3, C: 0, D: 0 + 2: t1, C: 1, D: 1 + 1: t1, C: 0, D: 0 + 0: null, C: 1, D: 1 + $ cd .. @@ -2727,6 +2762,13 @@ $ 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' @@ -2925,10 +2967,10 @@ hg: parse error at 21: unterminated string [255] $ hg log -r 2 -T '{if(rev, \"\\"")}\n' - hg: parse error at 11: syntax error + hg: parse error: trailing \ in string [255] $ hg log -r 2 -T '{if(rev, r\"\\"")}\n' - hg: parse error at 12: syntax error + hg: parse error: trailing \ in string [255] $ cd .. @@ -3105,6 +3147,25 @@ hg: parse error: get() expects a dict as first argument [255] +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, "+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 @@ -3117,6 +3178,8 @@ e777603221 bcc7ff960b f7769ec2ab + $ hg log --template '{node|shortest}\n' -l1 + e777 Test pad function @@ -3197,6 +3260,23 @@ $ hg log --template '{revset("TIP"|lower)}\n' -l1 2 + a list template is evaluated for each item of revset + + $ hg log -T '{rev} p: {revset("p1(%s)", rev) % "{rev}:{node|short}"}\n' + 2 p: 1:bcc7ff960b8e + 1 p: 0:f7769ec2ab97 + 0 p: + + 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 + Test active bookmark templating $ hg book foo @@ -3390,3 +3470,12 @@ $ 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 + [255]
--- a/tests/test-commandserver.t Mon Oct 05 10:43:16 2015 -0600 +++ b/tests/test-commandserver.t Wed Oct 07 13:44:48 2015 -0500 @@ -322,7 +322,6 @@ ... runcommand(server, ['phase', '-r', '.']) *** runcommand phase -r . -p no phases changed - [1] *** runcommand commit -Am. *** runcommand rollback repository tip rolled back to revision 3 (undo commit) @@ -379,7 +378,10 @@ ... runcommand(server, ['log', '-qr', 'draft()']) ... # create draft commits by another process ... for i in xrange(5, 7): - ... os.system('echo a >> a') + ... f = open('a', 'ab') + ... f.seek(0, os.SEEK_END) + ... f.write('a\n') + ... f.close() ... os.system('hg commit -Aqm%d' % i) ... # new commits should be listed as draft revisions ... runcommand(server, ['log', '-qr', 'draft()']) @@ -458,6 +460,30 @@ *** runcommand branches default 1:731265503d86 +in-memory cache must be reloaded if transaction is aborted. otherwise +changelog and manifest would have invalid node: + + $ echo a >> a + >>> from hgclient import readchannel, runcommand, check + >>> @check + ... def txabort(server): + ... readchannel(server) + ... runcommand(server, ['commit', '--config', 'hooks.pretxncommit=false', + ... '-mfoo']) + ... runcommand(server, ['verify']) + *** runcommand commit --config hooks.pretxncommit=false -mfoo + transaction abort! + rollback completed + abort: pretxncommit hook exited with status 1 + [255] + *** runcommand verify + checking changesets + checking manifests + crosschecking files in changesets and manifests + checking files + 1 files, 2 changesets, 2 total revisions + $ hg revert --no-backup -aq + $ cat >> .hg/hgrc << EOF > [experimental] > evolution=createmarkers @@ -632,6 +658,15 @@ 000000000000 tip +don't fall back to cwd if invalid -R path is specified (issue4805): + + $ cd repo + $ hg serve --cmdserver pipe -R ../nonexistent + abort: repository ../nonexistent not found! + [255] + $ cd .. + + unix domain socket: $ cd repo
--- a/tests/test-commit.t Mon Oct 05 10:43:16 2015 -0600 +++ b/tests/test-commit.t Wed Oct 07 13:44:48 2015 -0500 @@ -117,7 +117,7 @@ $ echo fake >> .hg/requires $ hg commit -m bla abort: repository requires features unknown to this Mercurial: fake! - (see http://mercurial.selenic.com/wiki/MissingRequirement for more information) + (see https://mercurial-scm.org/wiki/MissingRequirement for more information) [255] $ cd ..
--- a/tests/test-completion.t Mon Oct 05 10:43:16 2015 -0600 +++ b/tests/test-completion.t Wed Oct 07 13:44:48 2015 -0500 @@ -80,6 +80,7 @@ debugdate debugdirstate debugdiscovery + debugextensions debugfileset debugfsinfo debuggetbundle @@ -90,6 +91,7 @@ debugknown debuglabelcomplete debuglocks + debugmergestate debugnamecomplete debugobsolete debugpathcomplete @@ -239,6 +241,7 @@ debugdate: extended debugdirstate: nodates, datesort debugdiscovery: old, nonheads, ssh, remotecmd, insecure + debugextensions: template debugfileset: rev debugfsinfo: debuggetbundle: head, common, type @@ -249,12 +252,13 @@ debugknown: debuglabelcomplete: debuglocks: force-lock, force-wlock + debugmergestate: debugnamecomplete: debugobsolete: flags, record-parents, rev, date, user debugpathcomplete: full, normal, added, removed debugpushkey: debugpvec: - debugrebuilddirstate: rev + debugrebuilddirstate: rev, minimal debugrebuildfncache: debugrename: rev debugrevlog: changelog, manifest, dir, dump
--- a/tests/test-conflict.t Mon Oct 05 10:43:16 2015 -0600 +++ b/tests/test-conflict.t Wed Oct 07 13:44:48 2015 -0500 @@ -232,3 +232,65 @@ 5 >>>>>>> other Hop we are done. + +Add some unconflicting changes on each head, to make sure we really +are merging, unlike :local and :other + + $ hg up -C + 2 files updated, 0 files merged, 0 files removed, 0 files unresolved + $ printf "\n\nEnd of file\n" >> a + $ hg ci -m "Add some stuff at the end" + $ hg up -r 1 + 1 files updated, 0 files merged, 1 files removed, 0 files unresolved + $ printf "Start of file\n\n\n" > tmp + $ cat a >> tmp + $ mv tmp a + $ hg ci -m "Add some stuff at the beginning" + +Now test :merge-other and :merge-local + + $ hg merge + merging a + warning: conflicts during merge. + merging a incomplete! (edit conflicts, then use 'hg resolve --mark') + 1 files updated, 0 files merged, 0 files removed, 1 files unresolved + use 'hg resolve' to retry unresolved file merges or 'hg update -C .' to abandon + [1] + $ hg resolve --tool :merge-other a + merging a + (no more unresolved files) + $ cat a + Start of file + + + Small Mathematical Series. + 1 + 2 + 3 + 6 + 8 + Hop we are done. + + + End of file + + $ hg up -C + 1 files updated, 0 files merged, 1 files removed, 0 files unresolved + $ hg merge --tool :merge-local + merging a + 2 files updated, 0 files merged, 0 files removed, 0 files unresolved + (branch merge, don't forget to commit) + $ cat a + Start of file + + + Small Mathematical Series. + 1 + 2 + 3 + 4 + 5 + Hop we are done. + + + End of file
--- a/tests/test-convert-bzr-114.t Mon Oct 05 10:43:16 2015 -0600 +++ b/tests/test-convert-bzr-114.t Wed Oct 07 13:44:48 2015 -0500 @@ -1,4 +1,4 @@ -#require bzr114 +#require bzr bzr114 $ . "$TESTDIR/bzr-definitions"
--- a/tests/test-convert-bzr-directories.t Mon Oct 05 10:43:16 2015 -0600 +++ b/tests/test-convert-bzr-directories.t Wed Oct 07 13:44:48 2015 -0500 @@ -1,3 +1,4 @@ +#require bzr $ . "$TESTDIR/bzr-definitions"
--- a/tests/test-convert-bzr-ghosts.t Mon Oct 05 10:43:16 2015 -0600 +++ b/tests/test-convert-bzr-ghosts.t Wed Oct 07 13:44:48 2015 -0500 @@ -1,3 +1,4 @@ +#require bzr $ . "$TESTDIR/bzr-definitions" $ cat > ghostcreator.py <<EOF
--- a/tests/test-convert-bzr-merges.t Mon Oct 05 10:43:16 2015 -0600 +++ b/tests/test-convert-bzr-merges.t Wed Oct 07 13:44:48 2015 -0500 @@ -1,3 +1,5 @@ +#require bzr + N.B. bzr 1.13 has a bug that breaks this test. If you see this test fail, check your bzr version. Upgrading to bzr 1.13.1 should fix it.
--- a/tests/test-convert-bzr-treeroot.t Mon Oct 05 10:43:16 2015 -0600 +++ b/tests/test-convert-bzr-treeroot.t Wed Oct 07 13:44:48 2015 -0500 @@ -1,3 +1,4 @@ +#require bzr $ . "$TESTDIR/bzr-definitions" $ cat > treeset.py <<EOF
--- a/tests/test-convert-bzr.t Mon Oct 05 10:43:16 2015 -0600 +++ b/tests/test-convert-bzr.t Wed Oct 07 13:44:48 2015 -0500 @@ -1,3 +1,5 @@ +#require bzr + $ . "$TESTDIR/bzr-definitions" create and rename on the same file in the same step
--- a/tests/test-convert-cvs-branch.t Mon Oct 05 10:43:16 2015 -0600 +++ b/tests/test-convert-cvs-branch.t Wed Oct 07 13:44:48 2015 -0500 @@ -1,7 +1,7 @@ #require cvs -This is http://mercurial.selenic.com/bts/issue1148 -and http://mercurial.selenic.com/bts/issue1447 +This is https://bz.mercurial-scm.org/1148 +and https://bz.mercurial-scm.org/1447 $ cvscall() > {
--- a/tests/test-convert-filemap.t Mon Oct 05 10:43:16 2015 -0600 +++ b/tests/test-convert-filemap.t Wed Oct 07 13:44:48 2015 -0500 @@ -671,3 +671,73 @@ |/ o 0:c334dc3be0da@default "add" files: a + $ cd .. + +test converting merges into a repo that contains other files + + $ hg init merge-test1 + $ cd merge-test1 + $ touch a && hg commit -Aqm 'add a' + $ echo a > a && hg commit -Aqm 'edit a' + $ hg up -q 0 + $ touch b && hg commit -Aqm 'add b' + $ hg merge -q 1 && hg commit -qm 'merge a & b' + + $ cd .. + $ hg init merge-test2 + $ cd merge-test2 + $ mkdir converted + $ touch converted/a toberemoved && hg commit -Aqm 'add converted/a & toberemoved' + $ touch x && rm toberemoved && hg commit -Aqm 'add x & remove tobremoved' + $ cd .. + $ hg log -G -T '{shortest(node)} {desc}' -R merge-test1 + @ 1191 merge a & b + |\ + | o 9077 add b + | | + o | d19f edit a + |/ + o ac82 add a + + $ hg log -G -T '{shortest(node)} {desc}' -R merge-test2 + @ 150e add x & remove tobremoved + | + o bbac add converted/a & toberemoved + +- Build a shamap where the target converted/a is in on top of an unrelated +- change to 'x'. This simulates using convert to merge several repositories +- together. + $ cat >> merge-test2/.hg/shamap <<EOF + > $(hg -R merge-test1 log -r 0 -T '{node}') $(hg -R merge-test2 log -r 0 -T '{node}') + > $(hg -R merge-test1 log -r 1 -T '{node}') $(hg -R merge-test2 log -r 1 -T '{node}') + > EOF + $ cat >> merge-test-filemap <<EOF + > rename . converted/ + > EOF + $ hg convert --filemap merge-test-filemap merge-test1 merge-test2 --traceback + scanning source... + sorting... + converting... + 1 add b + 0 merge a & b + $ hg -R merge-test2 manifest -r tip + converted/a + converted/b + x + $ hg -R merge-test2 log -G -T '{shortest(node)} {desc}\n{files % "- {file}\n"}\n' + o 6eaa merge a & b + |\ - converted/a + | | - toberemoved + | | + | o 2995 add b + | | - converted/b + | | + @ | 150e add x & remove tobremoved + |/ - toberemoved + | - x + | + o bbac add converted/a & toberemoved + - converted/a + - toberemoved + +
--- a/tests/test-convert-git.t Mon Oct 05 10:43:16 2015 -0600 +++ b/tests/test-convert-git.t Wed Oct 07 13:44:48 2015 -0500 @@ -652,6 +652,12 @@ $ hg -R git-repo6-hg tip -T "{file_dels}\n" .hgsub .hgsubstate +skip submodules in the conversion + + $ hg convert -q git-repo6 no-submodules --config convert.git.skipsubmodules=True + $ hg -R no-submodules manifest --all + .gitmodules-renamed + convert using a different remote prefix $ git init git-repo7 Initialized empty Git repository in $TESTTMP/git-repo7/.git/ @@ -678,6 +684,28 @@ master 0:03bf38caa4c6 origin/master 0:03bf38caa4c6 +Run convert when the remote branches have changed +(there was an old bug where the local convert read branches from the server) + + $ cd git-repo7 + $ echo a >> a + $ git commit -am "move master forward" + [master 0c81947] move master forward + Author: nottest <test@example.org> + 1 file changed, 1 insertion(+) + $ cd .. + $ rm -rf hg-repo7 + $ hg convert --config convert.git.remoteprefix=origin git-repo7-client hg-repo7 + initializing destination hg-repo7 repository + scanning source... + sorting... + converting... + 0 commit a + updating bookmarks + $ hg -R hg-repo7 bookmarks + master 0:03bf38caa4c6 + origin/master 0:03bf38caa4c6 + damaged git repository tests: In case the hard-coded hashes change, the following commands can be used to list the hashes and their corresponding types in the repository:
--- a/tests/test-convert-hg-startrev.t Mon Oct 05 10:43:16 2015 -0600 +++ b/tests/test-convert-hg-startrev.t Wed Oct 07 13:44:48 2015 -0500 @@ -201,4 +201,23 @@ | o 0 "0: add a b f" files: a b f - $ cd .. +Convert from specified revs + + $ hg convert --rev 3 --rev 2 source multiplerevs + initializing destination multiplerevs repository + scanning source... + sorting... + converting... + 3 0: add a b f + 2 1: add c, move f to d + 1 2: copy e from a, change b + 0 3: change a + $ glog multiplerevs + o 3 "3: change a" files: a + | + | o 2 "2: copy e from a, change b" files: b e + | | + | o 1 "1: add c, move f to d" files: c d f + |/ + o 0 "0: add a b f" files: a b f +
--- a/tests/test-convert.t Mon Oct 05 10:43:16 2015 -0600 +++ b/tests/test-convert.t Wed Oct 07 13:44:48 2015 -0500 @@ -265,6 +265,9 @@ remote refs are converted as bookmarks with "convert.git.remoteprefix" as a prefix followed by a /. The default is 'remote'. + convert.git.skipsubmodules + does not convert root level .gitmodules files or files with + 160000 mode indicating a submodule. Default is False. Perforce Source ###############
--- a/tests/test-copy-move-merge.t Mon Oct 05 10:43:16 2015 -0600 +++ b/tests/test-copy-move-merge.t Wed Oct 07 13:44:48 2015 -0500 @@ -59,4 +59,107 @@ 1 2 +Test disabling copy tracing + +- first verify copy metadata was kept + + $ hg up -qC 2 + $ hg rebase --keep -d 1 -b 2 --config extensions.rebase= + rebasing 2:add3f11052fa "other" (tip) + merging b and a to b + merging c and a to c + + $ cat b + 0 + 1 + 2 + +- next verify copy metadata is lost when disabled + + $ hg strip -r . --config extensions.strip= + 2 files updated, 0 files merged, 0 files removed, 0 files unresolved + saved backup bundle to $TESTTMP/t/.hg/strip-backup/550bd84c0cd3-fc575957-backup.hg (glob) + $ hg up -qC 2 + $ hg rebase --keep -d 1 -b 2 --config extensions.rebase= --config experimental.disablecopytrace=True + rebasing 2:add3f11052fa "other" (tip) + remote changed a which local deleted + use (c)hanged version or leave (d)eleted? c + + $ cat b + 1 + 2 + $ cd .. + +Verify disabling copy tracing still keeps copies from rebase source + + $ hg init copydisable + $ cd copydisable + $ touch a + $ hg ci -Aqm 'add a' + $ touch b + $ hg ci -Aqm 'add b, c' + $ hg cp b x + $ echo x >> x + $ hg ci -qm 'copy b->x' + $ hg up -q 1 + $ touch z + $ hg ci -Aqm 'add z' + $ hg log -G -T '{rev} {desc}\n' + @ 3 add z + | + | o 2 copy b->x + |/ + o 1 add b, c + | + o 0 add a + + $ hg rebase -d . -b 2 --config extensions.rebase= --config experimental.disablecopytrace=True + rebasing 2:6adcf8c12e7d "copy b->x" + saved backup bundle to $TESTTMP/copydisable/.hg/strip-backup/6adcf8c12e7d-ce4b3e75-backup.hg (glob) + $ hg up -q 3 + $ hg log -f x -T '{rev} {desc}\n' + 3 copy b->x + 1 add b, c + + $ cd ../ + +Verify we duplicate existing copies, instead of detecting them + + $ hg init copydisable3 + $ cd copydisable3 + $ touch a + $ hg ci -Aqm 'add a' + $ hg cp a b + $ hg ci -Aqm 'copy a->b' + $ hg mv b c + $ hg ci -Aqm 'move b->c' + $ hg up -q 0 + $ hg cp a b + $ echo b >> b + $ hg ci -Aqm 'copy a->b (2)' + $ hg log -G -T '{rev} {desc}\n' + @ 3 copy a->b (2) + | + | o 2 move b->c + | | + | o 1 copy a->b + |/ + o 0 add a + + $ hg rebase -d 2 -s 3 --config extensions.rebase= --config experimental.disablecopytrace=True + rebasing 3:47e1a9e6273b "copy a->b (2)" (tip) + saved backup bundle to $TESTTMP/copydisable3/.hg/strip-backup/47e1a9e6273b-2d099c59-backup.hg (glob) + + $ hg log -G -f b + @ changeset: 3:76024fb4b05b + | tag: tip + | user: test + | date: Thu Jan 01 00:00:00 1970 +0000 + | summary: copy a->b (2) + | + o changeset: 0:ac82d8b1f7c4 + user: test + date: Thu Jan 01 00:00:00 1970 +0000 + summary: add a +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tests/test-debian-packages.t Wed Oct 07 13:44:48 2015 -0500 @@ -0,0 +1,20 @@ +#require test-repo slow debhelper + +Ensure debuild doesn't run the testsuite, as that could get silly. + $ DEB_BUILD_OPTIONS=nocheck + $ export DEB_BUILD_OPTIONS + $ OUTPUTDIR=`pwd` + $ export OUTPUTDIR + + $ cd "$TESTDIR"/.. + $ make deb > $OUTPUTDIR/build.log 2>&1 + $ cd $OUTPUTDIR + $ ls *.deb + mercurial-common_*.deb (glob) + mercurial_*.deb (glob) +main deb should have .so but no .py + $ dpkg --contents mercurial_*.deb | egrep '(localrepo|parsers)' + * ./usr/lib/python2.7/dist-packages/mercurial/parsers*.so (glob) +mercurial-common should have py but no .so or pyc + $ dpkg --contents mercurial-common_*.deb | egrep '(localrepo|parsers)' + * ./usr/lib/python2.7/dist-packages/mercurial/localrepo.py (glob)
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tests/test-debugextensions.t Wed Oct 07 13:44:48 2015 -0500 @@ -0,0 +1,83 @@ + $ hg debugextensions + + $ debugpath=`pwd`/extwithoutinfos.py + + $ cat > extwithoutinfos.py <<EOF + > EOF + + $ cat >> $HGRCPATH <<EOF + > [extensions] + > color= + > histedit= + > patchbomb= + > rebase= + > mq= + > ext1 = $debugpath + > EOF + + $ hg debugextensions + color + ext1 (untested!) + histedit + mq + patchbomb + rebase + + $ hg debugextensions -v + color + location: */hgext/color.pyc (glob) + tested with: internal + ext1 + location: */extwithoutinfos.pyc (glob) + histedit + location: */hgext/histedit.pyc (glob) + tested with: internal + mq + location: */hgext/mq.pyc (glob) + tested with: internal + patchbomb + location: */hgext/patchbomb.pyc (glob) + tested with: internal + rebase + location: */hgext/rebase.pyc (glob) + tested with: internal + + $ hg debugextensions -Tjson | sed 's|\\\\|/|g' + [ + { + "buglink": "", + "name": "color", + "source": "*/hgext/color.pyc", (glob) + "testedwith": "internal" + }, + { + "buglink": "", + "name": "ext1", + "source": "*/extwithoutinfos.pyc", (glob) + "testedwith": "" + }, + { + "buglink": "", + "name": "histedit", + "source": "*/hgext/histedit.pyc", (glob) + "testedwith": "internal" + }, + { + "buglink": "", + "name": "mq", + "source": "*/hgext/mq.pyc", (glob) + "testedwith": "internal" + }, + { + "buglink": "", + "name": "patchbomb", + "source": "*/hgext/patchbomb.pyc", (glob) + "testedwith": "internal" + }, + { + "buglink": "", + "name": "rebase", + "source": "*/hgext/rebase.pyc", (glob) + "testedwith": "internal" + } + ]
--- a/tests/test-default-push.t Mon Oct 05 10:43:16 2015 -0600 +++ b/tests/test-default-push.t Wed Oct 07 13:44:48 2015 -0500 @@ -18,7 +18,6 @@ Push should provide a hint when both 'default' and 'default-push' not set: $ cd c $ hg push --config paths.default= - pushing to default-push abort: default repository not configured! (see the "path" section in "hg help config") [255] @@ -46,3 +45,9 @@ adding manifests adding file changes added 1 changesets with 1 changes to 1 files + +Pushing to a path that isn't defined should not fall back to default + + $ hg --cwd b push doesnotexist + abort: repository doesnotexist does not exist! + [255]
--- a/tests/test-diff-change.t Mon Oct 05 10:43:16 2015 -0600 +++ b/tests/test-diff-change.t Wed Oct 07 13:44:48 2015 -0500 @@ -29,15 +29,59 @@ -first +second -Test dumb revspecs (issue3474) + $ cd .. + +Test dumb revspecs: top-level "x:y", "x:", ":y" and ":" ranges should be handled +as pairs even if x == y, but not for "f(x:y)" nor "x::y" (issue3474, issue4774) + + $ hg clone -q a dumbspec + $ cd dumbspec + $ echo "wdir" > file.txt $ hg diff -r 2:2 + $ hg diff -r 2:. + $ hg diff -r 2: + $ hg diff -r :0 + $ hg diff -r '2:first(2:2)' + $ hg diff -r 'first(2:2)' --nodates + diff -r bf5ff72eb7e0 file.txt + --- a/file.txt + +++ b/file.txt + @@ -1,1 +1,1 @@ + -third + +wdir + $ hg diff -r 2::2 --nodates + diff -r bf5ff72eb7e0 file.txt + --- a/file.txt + +++ b/file.txt + @@ -1,1 +1,1 @@ + -third + +wdir $ hg diff -r "2 and 1" abort: empty revision range [255] + $ cd .. + + $ hg clone -qr0 a dumbspec-rev0 + $ cd dumbspec-rev0 + $ echo "wdir" > file.txt + + $ hg diff -r : + $ hg diff -r 'first(:)' --nodates + diff -r 4bb65dda5db4 file.txt + --- a/file.txt + +++ b/file.txt + @@ -1,1 +1,1 @@ + -first + +wdir + + $ cd .. + Testing diff --change when merge: + $ cd a + $ for i in 1 2 3 4 5 6 7 8 9 10; do > echo $i >> file.txt > done
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tests/test-docker-packaging.t Wed Oct 07 13:44:48 2015 -0500 @@ -0,0 +1,27 @@ +#require test-repo slow docker + +Ensure debuild doesn't run the testsuite, as that could get silly. + $ DEB_BUILD_OPTIONS=nocheck + $ export DEB_BUILD_OPTIONS + $ OUTPUTDIR=`pwd` + $ export OUTPUTDIR + + $ cd "$TESTDIR"/.. + $ make docker-debian-jessie > $OUTPUTDIR/build.log 2>&1 + $ cd $OUTPUTDIR + $ ls *.deb + mercurial-common_*.deb (glob) + mercurial_*.deb (glob) + +We check debian package contents with portable tools so that when +we're on non-debian machines we can still test the packages that are +built using docker. + +main deb should have .so but no .py + $ ar x mercurial_*.deb + $ tar tf data.tar* | egrep '(localrepo|parsers)' + ./usr/lib/python2.7/dist-packages/mercurial/parsers*.so (glob) +mercurial-common should have .py but no .so or .pyc + $ ar x mercurial-common_*.deb + $ tar tf data.tar* | egrep '(localrepo|parsers)' + ./usr/lib/python2.7/dist-packages/mercurial/localrepo.py
--- a/tests/test-eol-tag.t Mon Oct 05 10:43:16 2015 -0600 +++ b/tests/test-eol-tag.t Wed Oct 07 13:44:48 2015 -0500 @@ -1,4 +1,4 @@ -http://mercurial.selenic.com/bts/issue2493 +https://bz.mercurial-scm.org/2493 Testing tagging with the EOL extension
--- a/tests/test-eolfilename.t Mon Oct 05 10:43:16 2015 -0600 +++ b/tests/test-eolfilename.t Wed Oct 07 13:44:48 2015 -0500 @@ -1,6 +1,6 @@ #require eol-in-paths -http://mercurial.selenic.com/bts/issue352 +https://bz.mercurial-scm.org/352 test issue352 @@ -51,7 +51,7 @@ abort: '\n' and '\r' disallowed in filenames: 'quick\rfox' [255] -http://mercurial.selenic.com/bts/issue2036 +https://bz.mercurial-scm.org/2036 $ cd ..
--- a/tests/test-extdiff.t Mon Oct 05 10:43:16 2015 -0600 +++ b/tests/test-extdiff.t Wed Oct 07 13:44:48 2015 -0500 @@ -46,6 +46,7 @@ -o --option OPT [+] pass option to comparison program -r --rev REV [+] revision -c --change REV change made by revision + --patch compare patches for two revisions -I --include PATTERN [+] include names matching the given patterns -X --exclude PATTERN [+] exclude names matching the given patterns -S --subrepos recurse into subrepositories
--- a/tests/test-extension.t Mon Oct 05 10:43:16 2015 -0600 +++ b/tests/test-extension.t Wed Oct 07 13:44:48 2015 -0500 @@ -115,8 +115,6 @@ 3) bar extsetup 4) foo reposetup 4) bar reposetup - 4) foo reposetup - 4) bar reposetup $ echo 'foo = !' >> $HGRCPATH $ echo 'bar = !' >> $HGRCPATH @@ -289,17 +287,23 @@ $ echo "debugextension = $debugpath" >> $HGRCPATH $ hg help debugextension - debugextension extension - only debugcommands + hg debugextensions + + show information about active extensions - no commands defined + options: + + (some details hidden, use --verbose to show complete help) $ hg --verbose help debugextension - debugextension extension - only debugcommands + hg debugextensions + + show information about active extensions - list of commands: + options: - foo yet another foo command + -T --template TEMPLATE display with template (EXPERIMENTAL) global options ([+] can be repeated): @@ -328,12 +332,13 @@ $ hg --debug help debugextension - debugextension extension - only debugcommands + hg debugextensions + + show information about active extensions - list of commands: + options: - debugfoobar yet another debug command - foo yet another foo command + -T --template TEMPLATE display with template (EXPERIMENTAL) global options ([+] can be repeated): @@ -392,6 +397,7 @@ -o --option OPT [+] pass option to comparison program -r --rev REV [+] revision -c --change REV change made by revision + --patch compare patches for two revisions -I --include PATTERN [+] include names matching the given patterns -X --exclude PATTERN [+] exclude names matching the given patterns -S --subrepos recurse into subrepositories @@ -546,20 +552,7 @@ Issue811: Problem loading extensions twice (by site and by user) - $ debugpath=`pwd`/debugissue811.py - $ cat > debugissue811.py <<EOF - > '''show all loaded extensions - > ''' - > from mercurial import cmdutil, commands, extensions - > cmdtable = {} - > command = cmdutil.command(cmdtable) - > @command('debugextensions', [], 'hg debugextensions', norepo=True) - > def debugextensions(ui): - > "yet another debug command" - > ui.write("%s\n" % '\n'.join([x for x, y in extensions.extensions()])) - > EOF $ cat <<EOF >> $HGRCPATH - > debugissue811 = $debugpath > mq = > strip = > hgext.mq = @@ -570,9 +563,8 @@ (note that mq force load strip, also checking it's not loaded twice) $ hg debugextensions - debugissue811 + mq strip - mq For extensions, which name matches one of its commands, help message should ask '-v -e' to get list of built-in aliases @@ -944,6 +936,15 @@ ** Mercurial Distributed SCM (version 1.9.3) ** Extensions loaded: throw, older +Ability to point to a different point + $ hg --config extensions.throw=throw.py --config extensions.older=older.py \ + > --config ui.supportcontact='Your Local Goat Lenders' throw 2>&1 | egrep '^\*\*' + ** unknown exception encountered, please report by visiting + ** Your Local Goat Lenders + ** Python * (glob) + ** Mercurial Distributed SCM (*) (glob) + ** Extensions loaded: throw, older + Declare the version as supporting this hg version, show regular bts link: $ hgver=`$PYTHON -c 'from mercurial import util; print util.version().split("+")[0]'` $ echo 'testedwith = """'"$hgver"'"""' >> throw.py @@ -953,7 +954,7 @@ $ rm -f throw.pyc throw.pyo $ hg --config extensions.throw=throw.py throw 2>&1 | egrep '^\*\*' ** unknown exception encountered, please report by visiting - ** http://mercurial.selenic.com/wiki/BugTracker + ** https://mercurial-scm.org/wiki/BugTracker ** Python * (glob) ** Mercurial Distributed SCM (*) (glob) ** Extensions loaded: throw @@ -964,7 +965,7 @@ $ rm -f throw.pyc throw.pyo $ hg --config extensions.throw=throw.py throw 2>&1 | egrep '^\*\*' ** unknown exception encountered, please report by visiting - ** http://mercurial.selenic.com/wiki/BugTracker + ** https://mercurial-scm.org/wiki/BugTracker ** Python * (glob) ** Mercurial Distributed SCM (*) (glob) ** Extensions loaded: throw @@ -974,7 +975,7 @@ $ rm -f throw.pyc throw.pyo $ hg version -v Mercurial Distributed SCM (version *) (glob) - (see http://mercurial.selenic.com for more information) + (see https://mercurial-scm.org for more information) Copyright (C) 2005-* Matt Mackall and others (glob) This is free software; see the source for copying conditions. There is NO @@ -985,7 +986,7 @@ $ hg version -v --config extensions.throw=throw.py Mercurial Distributed SCM (version *) (glob) - (see http://mercurial.selenic.com for more information) + (see https://mercurial-scm.org for more information) Copyright (C) 2005-* Matt Mackall and others (glob) This is free software; see the source for copying conditions. There is NO @@ -998,7 +999,7 @@ $ rm -f throw.pyc throw.pyo $ hg version -v --config extensions.throw=throw.py Mercurial Distributed SCM (version *) (glob) - (see http://mercurial.selenic.com for more information) + (see https://mercurial-scm.org for more information) Copyright (C) 2005-* Matt Mackall and others (glob) This is free software; see the source for copying conditions. There is NO
--- a/tests/test-filecache.py Mon Oct 05 10:43:16 2015 -0600 +++ b/tests/test-filecache.py Wed Oct 07 13:44:48 2015 -0500 @@ -130,7 +130,7 @@ util.cachestat.__init__ = originit def test_filecache_synced(): - # test old behaviour that caused filecached properties to go out of sync + # test old behavior that caused filecached properties to go out of sync os.system('hg init && echo a >> a && hg ci -qAm.') repo = hg.repository(ui.ui()) # first rollback clears the filecache, but changelog to stays in __dict__
--- a/tests/test-filelog.py Mon Oct 05 10:43:16 2015 -0600 +++ b/tests/test-filelog.py Wed Oct 07 13:44:48 2015 -0500 @@ -1,6 +1,6 @@ #!/usr/bin/env python """ -Tests the behaviour of filelog w.r.t. data starting with '\1\n' +Tests the behavior of filelog w.r.t. data starting with '\1\n' """ from mercurial import ui, hg from mercurial.node import nullid, hex
--- a/tests/test-fileset.t Mon Oct 05 10:43:16 2015 -0600 +++ b/tests/test-fileset.t Wed Oct 07 13:44:48 2015 -0500 @@ -49,6 +49,9 @@ $ fileset 'a* - a1' a2 $ fileset 'a_b' + $ fileset '"\xy"' + hg: parse error: invalid \x escape + [255] Test files status
--- a/tests/test-fncache.t Mon Oct 05 10:43:16 2015 -0600 +++ b/tests/test-fncache.t Wed Oct 07 13:44:48 2015 -0500 @@ -205,10 +205,10 @@ > from mercurial import commands, util > from mercurial.extensions import wrapfunction > - > def lockexception(orig, vfs, lockname, wait, releasefn, acquirefn, desc): + > def lockexception(orig, vfs, lockname, wait, releasefn, *args, **kwargs): > def releasewrap(): > raise util.Abort("forced lock failure") - > return orig(vfs, lockname, wait, releasewrap, acquirefn, desc) + > return orig(vfs, lockname, wait, releasewrap, *args, **kwargs) > > def reposetup(ui, repo): > wrapfunction(repo, '_lock', lockexception)
--- a/tests/test-generaldelta.t Mon Oct 05 10:43:16 2015 -0600 +++ b/tests/test-generaldelta.t Wed Oct 07 13:44:48 2015 -0500 @@ -69,3 +69,46 @@ rev offset length base linkrev nodeid p1 p2 0 0 3 0 1 1406e7411862 000000000000 000000000000 + $ cd .. + +Test format.aggressivemergedeltas + + $ hg init --config format.generaldelta=1 aggressive + $ cd aggressive + $ touch a b c d e + $ hg commit -Aqm side1 + $ hg up -q null + $ touch x y + $ hg commit -Aqm side2 + +- Verify non-aggressive merge uses p1 (commit 1) as delta parent + $ hg merge -q 0 + $ hg commit -q -m merge + $ hg debugindex -m + rev offset length delta linkrev nodeid p1 p2 + 0 0 59 -1 0 8dde941edb6e 000000000000 000000000000 + 1 59 59 -1 1 315c023f341d 000000000000 000000000000 + 2 118 65 1 2 2ab389a983eb 315c023f341d 8dde941edb6e + + $ 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 debugindex -m + rev offset length delta linkrev nodeid p1 p2 + 0 0 59 -1 0 8dde941edb6e 000000000000 000000000000 + 1 59 59 -1 1 315c023f341d 000000000000 000000000000 + 2 118 62 0 2 2ab389a983eb 315c023f341d 8dde941edb6e + +Test that strip bundle use bundle2 + $ hg --config extensions.strip= strip . + 0 files updated, 0 files merged, 5 files removed, 0 files unresolved + saved backup bundle to $TESTTMP/aggressive/.hg/strip-backup/1c5d4dc9a8b8-6c68e60c-backup.hg (glob) + $ hg debugbundle .hg/strip-backup/* + Stream params: {'Compression': 'BZ'} + changegroup -- "{'version': '02'}" + 1c5d4dc9a8b8d6e1750966d343e94db665e7a1e9 + + $ cd ..
--- a/tests/test-graft.t Mon Oct 05 10:43:16 2015 -0600 +++ b/tests/test-graft.t Wed Oct 07 13:44:48 2015 -0500 @@ -332,6 +332,54 @@ skipping already grafted revision 7:ef0ef43d49e7 (was grafted from 2:5c095ad7e90f) [255] + $ hg extdiff --config extensions.extdiff= --patch -r 2 -r 13 + --- */hg-5c095ad7e90f.patch * +0000 (glob) + +++ */hg-7a4785234d87.patch * +0000 (glob) + @@ -1,18 +1,18 @@ + # HG changeset patch + -# User test + +# User foo + # Date 0 0 + # Thu Jan 01 00:00:00 1970 +0000 + -# Node ID 5c095ad7e90f871700f02dd1fa5012cb4498a2d4 + -# Parent 5d205f8b35b66bc36375c9534ffd3237730e8f04 + +# Node ID 7a4785234d87ec1aa420ed6b11afe40fa73e12a9 + +# Parent b592ea63bb0c19a6c5c44685ee29a2284f9f1b8f + 2 + + -diff -r 5d205f8b35b6 -r 5c095ad7e90f a + +diff -r b592ea63bb0c -r 7a4785234d87 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 @@ + --b + -diff -r 5d205f8b35b6 -r 5c095ad7e90f b + +-a + +diff -r b592ea63bb0c -r 7a4785234d87 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 + ++a + [1] + + $ hg extdiff --config extensions.extdiff= --patch -r 2 -r 13 -X . + --- */hg-5c095ad7e90f.patch * +0000 (glob) + +++ */hg-7a4785234d87.patch * +0000 (glob) + @@ -1,8 +1,8 @@ + # HG changeset patch + -# User test + +# User foo + # Date 0 0 + # Thu Jan 01 00:00:00 1970 +0000 + -# Node ID 5c095ad7e90f871700f02dd1fa5012cb4498a2d4 + -# Parent 5d205f8b35b66bc36375c9534ffd3237730e8f04 + +# Node ID 7a4785234d87ec1aa420ed6b11afe40fa73e12a9 + +# Parent b592ea63bb0c19a6c5c44685ee29a2284f9f1b8f + 2 + + [1] + Disallow grafting already grafted csets with the same origin onto each other $ hg up -q 13 $ hg graft 2
--- a/tests/test-help.t Mon Oct 05 10:43:16 2015 -0600 +++ b/tests/test-help.t Wed Oct 07 13:44:48 2015 -0500 @@ -278,6 +278,11 @@ transplant command to transplant changesets from another branch win32mbcs allow the use of MBCS paths with problematic encodings zeroconf discover and advertise repositories on the local network + +Verify that extension keywords appear in help templates + + $ hg help --config extensions.transplant= templating|grep transplant > /dev/null + Test short command list with verbose option $ hg -v help shortlist @@ -411,7 +416,7 @@ $ hg add -h --version Mercurial Distributed SCM (version *) (glob) - (see http://mercurial.selenic.com for more information) + (see https://mercurial-scm.org for more information) Copyright (C) 2005-2015 Matt Mackall and others This is free software; see the source for copying conditions. There is NO @@ -457,7 +462,7 @@ manifest, and tracked files, as well as the integrity of their crosslinks and indices. - Please see http://mercurial.selenic.com/wiki/RepositoryCorruption for more + Please see https://mercurial-scm.org/wiki/RepositoryCorruption for more information about recovery from corruption of the repository. Returns 0 on success, 1 if errors are encountered. @@ -616,6 +621,23 @@ [255] +Make sure that we don't run afoul of the help system thinking that +this is a section and erroring out weirdly. + + $ hg .log + hg: unknown command '.log' + (did you mean one of log?) + [255] + + $ hg log. + hg: unknown command 'log.' + (did you mean one of log?) + [255] + $ hg pu.lh + hg: unknown command 'pu.lh' + (did you mean one of pull, push?) + [255] + $ cat > helpext.py <<EOF > import os > from mercurial import cmdutil, commands @@ -629,8 +651,8 @@ > ('', 'newline', '', 'line1\nline2')], > 'hg nohelp', > norepo=True) - > @command('debugoptDEP', [('', 'dopt', None, 'option is DEPRECATED')]) - > @command('debugoptEXP', [('', 'eopt', None, 'option is EXPERIMENTAL')]) + > @command('debugoptDEP', [('', 'dopt', None, 'option is (DEPRECATED)')]) + > @command('debugoptEXP', [('', 'eopt', None, 'option is (EXPERIMENTAL)')]) > def nohelp(ui, *args, **kwargs): > pass > @@ -775,6 +797,8 @@ show the contents of the current dirstate debugdiscovery runs the changeset discovery protocol in isolation + debugextensions + show information about active extensions debugfileset parse and apply a fileset specification debugfsinfo show information detected about current filesystem debuggetbundle @@ -786,6 +810,8 @@ debuginstall test Mercurial installation debugknown test whether node ids are known to a repo debuglocks show or modify state of locks + debugmergestate + print merge state debugnamecomplete complete "names" - tags, open branch names, bookmark names debugobsolete @@ -849,9 +875,9 @@ test deprecated and experimental options is shown with -v $ hg help -v debugoptDEP | grep dopt - --dopt option is DEPRECATED + --dopt option is (DEPRECATED) $ hg help -v debugoptEXP | grep eopt - --eopt option is EXPERIMENTAL + --eopt option is (EXPERIMENTAL) #if gettext test deprecated option is hidden with translation with untranslated description @@ -912,6 +938,47 @@ working directory is checked out, it is equivalent to null. If an uncommitted merge is in progress, "." is the revision of the first parent. +Test repeated config section name + + $ hg help config.host + "http_proxy.host" + Host name and (optional) port of the proxy server, for example + "myproxy:8000". + + "smtp.host" + Host name of mail server, e.g. "mail.example.com". + +Unrelated trailing paragraphs shouldn't be included + + $ hg help config.extramsg | grep '^$' + + +Test capitalized section name + + $ hg help scripting.HGPLAIN > /dev/null + +Help subsection: + + $ hg help config.charsets |grep "Email example:" > /dev/null + [1] + +Show nested definitions +("profiling.type"[break]"ls"[break]"stat"[break]) + + $ hg help config.type | egrep '^$'|wc -l + \s*3 (re) + +Last item in help config.*: + + $ hg help config.`hg help config|grep '^ "'| \ + > tail -1|sed 's![ "]*!!g'`| \ + > grep "hg help -c config" > /dev/null + [1] + +note to use help -c for general hg help config: + + $ hg help config |grep "hg help -c config" > /dev/null + Test templating help $ hg help templating | egrep '(desc|diffstat|firstline|nonempty) ' @@ -920,12 +987,18 @@ firstline Any text. Returns the first line of text. nonempty Any text. Returns '(none)' if the string is empty. +Test deprecated items + + $ hg help -v templating | grep currentbookmark + currentbookmark + $ hg help templating | (grep currentbookmark || true) + Test help hooks $ cat > helphook1.py <<EOF > from mercurial import help > - > def rewrite(topic, doc): + > def rewrite(ui, topic, doc): > return doc + '\nhelphook1\n' > > def extsetup(ui): @@ -934,7 +1007,7 @@ $ cat > helphook2.py <<EOF > from mercurial import help > - > def rewrite(topic, doc): + > def rewrite(ui, topic, doc): > return doc + '\nhelphook2\n' > > def extsetup(ui): @@ -947,6 +1020,28 @@ helphook1 helphook2 +Test -e / -c / -k combinations + + $ hg help -c progress + abort: no such help topic: progress + (try "hg help --keyword progress") + [255] + $ hg help -e progress |head -1 + progress extension - show progress bars for some actions (DEPRECATED) + $ hg help -c -k dates |egrep '^(Topics|Extensions|Commands):' + Commands: + $ hg help -e -k a |egrep '^(Topics|Extensions|Commands):' + Extensions: + $ hg help -e -c -k date |egrep '^(Topics|Extensions|Commands):' + Extensions: + Commands: + $ hg help -c commit > /dev/null + $ hg help -e -c commit > /dev/null + $ hg help -e commit > /dev/null + abort: no such help topic: commit + (try "hg help --keyword commit") + [255] + Test keyword search help $ cat > prefixedname.py <<EOF @@ -1023,7 +1118,7 @@ > def extsetup(ui): > help.helptable.append((["topic-containing-verbose"], > "This is the topic to test omit indicating.", - > lambda : testtopic)) + > lambda ui: testtopic)) > EOF $ echo '[extensions]' >> $HGRCPATH $ echo "addverboseitems = `pwd`/addverboseitems.py" >> $HGRCPATH @@ -1095,8 +1190,7 @@ "default" Directory or URL to use when pulling if no source is specified. - Default is set to repository from which the current repository was - cloned. + (default: repository from which the current repository was cloned) "default-push" Optional. Directory or URL to use when pushing if no destination is @@ -1186,6 +1280,14 @@ partially merged file. Markers will have two sections, one for each side of merge. + ":merge-local" + Like :merge, but resolve all conflicts non-interactively in favor of the + local changes. + + ":merge-other" + Like :merge, but resolve all conflicts non-interactively in favor of the + other changes. + ":merge3" Uses the internal non-interactive simple merge algorithm for merging files. It will fail if there are any conflicts and leave markers in the @@ -1202,6 +1304,11 @@ ":tagmerge" Uses the internal tag merge algorithm (experimental). + ":union" + Uses the internal non-interactive simple merge algorithm for merging + files. It will use both left and right sides for conflict regions. No + markers are inserted. + Internal tools are always available and do not require a GUI but will by default not handle symlinks or binary files. @@ -1277,7 +1384,7 @@ <div class="container"> <div class="menu"> <div class="logo"> - <a href="http://mercurial.selenic.com/"> + <a href="https://mercurial-scm.org/"> <img src="/static/hglogo.png" alt="mercurial" /></a> </div> <ul> @@ -1835,7 +1942,7 @@ <div class="container"> <div class="menu"> <div class="logo"> - <a href="http://mercurial.selenic.com/"> + <a href="https://mercurial-scm.org/"> <img src="/static/hglogo.png" alt="mercurial" /></a> </div> <ul> @@ -1995,7 +2102,7 @@ <div class="container"> <div class="menu"> <div class="logo"> - <a href="http://mercurial.selenic.com/"> + <a href="https://mercurial-scm.org/"> <img src="/static/hglogo.png" alt="mercurial" /></a> </div> <ul> @@ -2188,7 +2295,7 @@ <div class="container"> <div class="menu"> <div class="logo"> - <a href="http://mercurial.selenic.com/"> + <a href="https://mercurial-scm.org/"> <img src="/static/hglogo.png" alt="mercurial" /></a> </div> <ul>
--- a/tests/test-hghave.t Mon Oct 05 10:43:16 2015 -0600 +++ b/tests/test-hghave.t Wed Oct 07 13:44:48 2015 -0500 @@ -18,7 +18,7 @@ > $ echo foo > foo > EOF - $ run-tests.py test-hghaveaddon.t + $ run-tests.py $HGTEST_RUN_TESTS_PURE test-hghaveaddon.t . # Ran 1 tests, 0 skipped, 0 warned, 0 failed.
--- a/tests/test-hgrc.t Mon Oct 05 10:43:16 2015 -0600 +++ b/tests/test-hgrc.t Wed Oct 07 13:44:48 2015 -0500 @@ -69,7 +69,7 @@ $ echo '%include $FAKEPATH/no-such-file' > $HGRC $ hg version Mercurial Distributed SCM (version *) (glob) - (see http://mercurial.selenic.com for more information) + (see https://mercurial-scm.org for more information) Copyright (C) 2005-2015 Matt Mackall and others This is free software; see the source for copying conditions. There is NO
--- a/tests/test-hgweb-commands.t Mon Oct 05 10:43:16 2015 -0600 +++ b/tests/test-hgweb-commands.t Wed Oct 07 13:44:48 2015 -0500 @@ -705,7 +705,7 @@ <div class="container"> <div class="menu"> <div class="logo"> - <a href="http://mercurial.selenic.com/"> + <a href="https://mercurial-scm.org/"> <img src="/static/hglogo.png" alt="mercurial" /></a> </div> <ul> @@ -841,7 +841,7 @@ <div class="container"> <div class="menu"> <div class="logo"> - <a href="http://mercurial.selenic.com/"> + <a href="https://mercurial-scm.org/"> <img src="/static/hglogo.png" alt="mercurial" /></a> </div> <ul> @@ -991,7 +991,7 @@ <div class="container"> <div class="menu"> <div class="logo"> - <a href="http://mercurial.selenic.com/"> + <a href="https://mercurial-scm.org/"> <img src="/static/hglogo.png" width=75 height=90 border=0 alt="mercurial"></a> </div> <ul> @@ -1269,7 +1269,7 @@ <div class="container"> <div class="menu"> <div class="logo"> - <a href="http://mercurial.selenic.com/"> + <a href="https://mercurial-scm.org/"> <img src="/static/hglogo.png" alt="mercurial" /></a> </div> <ul> @@ -1335,9 +1335,8 @@ <div class="overflow"> <div class="sourcefirst linewraptoggle">line wrap: <a class="linewraplink" href="javascript:toggleLinewrap()">on</a></div> <div class="sourcefirst"> line source</div> - <pre class="sourcelines stripes4 wrap"> + <pre class="sourcelines stripes4 wrap bottomline"> <span id="l1">foo</span><a href="#l1"></a></pre> - <div class="sourcelast"></div> </div> </div> </div> @@ -1396,7 +1395,7 @@ <div class="container"> <div class="menu"> <div class="logo"> - <a href="http://mercurial.selenic.com/"> + <a href="https://mercurial-scm.org/"> <img src="/static/hglogo.png" alt="mercurial" /></a> </div> <ul> @@ -1462,9 +1461,8 @@ <div class="overflow"> <div class="sourcefirst linewraptoggle">line wrap: <a class="linewraplink" href="javascript:toggleLinewrap()">on</a></div> <div class="sourcefirst"> line source</div> - <pre class="sourcelines stripes4 wrap"> + <pre class="sourcelines stripes4 wrap bottomline"> <span id="l1">another</span><a href="#l1"></a></pre> - <div class="sourcelast"></div> </div> </div> </div> @@ -1516,7 +1514,7 @@ <body> <div class="page_header"> - <a href="http://mercurial.selenic.com/" title="Mercurial" style="float: right;">Mercurial</a> + <a href="https://mercurial-scm.org/" title="Mercurial" style="float: right;">Mercurial</a> <a href="/">Mercurial</a> / summary <form action="/log"> <input type="hidden" name="style" value="gitweb" /> @@ -1613,7 +1611,7 @@ <tr class="parity0"> <td class="age"><i class="age">Thu, 01 Jan 1970 00:00:00 +0000</i></td> - <td><a class="list" href="/rev/2ef0ac749a14?style=gitweb"><b>1.0</b></a></td> + <td><a class="list" href="/rev/1.0?style=gitweb"><b>1.0</b></a></td> <td class="link"> <a href="/rev/2ef0ac749a14?style=gitweb">changeset</a> | <a href="/log/2ef0ac749a14?style=gitweb">changelog</a> | @@ -1628,7 +1626,7 @@ <tr class="parity0"> <td class="age"><i class="age">Thu, 01 Jan 1970 00:00:00 +0000</i></td> - <td><a class="list" href="/rev/2ef0ac749a14?style=gitweb"><b>anotherthing</b></a></td> + <td><a class="list" href="/rev/anotherthing?style=gitweb"><b>anotherthing</b></a></td> <td class="link"> <a href="/rev/2ef0ac749a14?style=gitweb">changeset</a> | <a href="/log/2ef0ac749a14?style=gitweb">changelog</a> | @@ -1637,7 +1635,7 @@ </tr> <tr class="parity1"> <td class="age"><i class="age">Thu, 01 Jan 1970 00:00:00 +0000</i></td> - <td><a class="list" href="/rev/cad8025a2e87?style=gitweb"><b>something</b></a></td> + <td><a class="list" href="/rev/something?style=gitweb"><b>something</b></a></td> <td class="link"> <a href="/rev/cad8025a2e87?style=gitweb">changeset</a> | <a href="/log/cad8025a2e87?style=gitweb">changelog</a> | @@ -1652,8 +1650,7 @@ <tr class="parity0"> <td class="age"><i class="age">Thu, 01 Jan 1970 00:00:00 +0000</i></td> - <td><a class="list" href="/shortlog/cad8025a2e87?style=gitweb"><b>cad8025a2e87</b></a></td> - <td class="">unstable</td> + <td class="open"><a class="list" href="/shortlog/unstable?style=gitweb"><b>unstable</b></a></td> <td class="link"> <a href="/changeset/cad8025a2e87?style=gitweb">changeset</a> | <a href="/log/cad8025a2e87?style=gitweb">changelog</a> | @@ -1662,8 +1659,7 @@ </tr> <tr class="parity1"> <td class="age"><i class="age">Thu, 01 Jan 1970 00:00:00 +0000</i></td> - <td><a class="list" href="/shortlog/1d22e65f027e?style=gitweb"><b>1d22e65f027e</b></a></td> - <td class="">stable</td> + <td class="inactive"><a class="list" href="/shortlog/stable?style=gitweb"><b>stable</b></a></td> <td class="link"> <a href="/changeset/1d22e65f027e?style=gitweb">changeset</a> | <a href="/log/1d22e65f027e?style=gitweb">changelog</a> | @@ -1672,8 +1668,7 @@ </tr> <tr class="parity0"> <td class="age"><i class="age">Thu, 01 Jan 1970 00:00:00 +0000</i></td> - <td><a class="list" href="/shortlog/a4f92ed23982?style=gitweb"><b>a4f92ed23982</b></a></td> - <td class="">default</td> + <td class="inactive"><a class="list" href="/shortlog/default?style=gitweb"><b>default</b></a></td> <td class="link"> <a href="/changeset/a4f92ed23982?style=gitweb">changeset</a> | <a href="/log/a4f92ed23982?style=gitweb">changelog</a> | @@ -1719,7 +1714,7 @@ <body> <div class="page_header"> - <a href="http://mercurial.selenic.com/" title="Mercurial" style="float: right;">Mercurial</a> + <a href="https://mercurial-scm.org/" title="Mercurial" style="float: right;">Mercurial</a> <a href="/">Mercurial</a> / graph </div> @@ -2052,6 +2047,35 @@ top: -1px; } +Stop and restart the server at the directory different from the repository +root. Even in such case, file patterns should be resolved relative to the +repository root. (issue4568) + + $ killdaemons.py + $ hg serve --config server.preferuncompressed=True -n test \ + > -p $HGPORT -d --pid-file=`pwd`/hg.pid -E `pwd`/errors.log \ + > --cwd .. -R `pwd` + $ cat hg.pid >> $DAEMON_PIDS + + $ get-with-headers.py 127.0.0.1:$HGPORT 'log?rev=adds("foo")&style=raw' + 200 Script output follows + + + # HG changesets search + # Node ID cad8025a2e87f88c06259790adfa15acb4080123 + # Query "adds("foo")" + # Mode revset expression search + + changeset: 2ef0ac749a14e4f57a5a822464a0902c6f7f448f + revision: 0 + user: test + date: Thu, 01 Jan 1970 00:00:00 +0000 + summary: base + tag: 1.0 + bookmark: anotherthing + + + Stop and restart with HGENCODING=cp932 and preferuncompressed $ killdaemons.py
--- a/tests/test-hgweb-descend-empties.t Mon Oct 05 10:43:16 2015 -0600 +++ b/tests/test-hgweb-descend-empties.t Wed Oct 07 13:44:48 2015 -0500 @@ -47,7 +47,7 @@ <div class="container"> <div class="menu"> <div class="logo"> - <a href="http://mercurial.selenic.com/"> + <a href="https://mercurial-scm.org/"> <img src="/static/hglogo.png" alt="mercurial" /></a> </div> <ul> @@ -156,7 +156,8 @@ <head> <link rel="icon" href="/static/hgicon.png" type="image/png" /> <meta name="robots" content="index, nofollow" /> - <link rel="stylesheet" href="/static/style-coal.css" type="text/css" /> + <link rel="stylesheet" href="/static/style-paper.css" type="text/css" /> + <link rel="stylesheet" href="/static/style-extra-coal.css" type="text/css" /> <script type="text/javascript" src="/static/mercurial.js"></script> <title>test: c9f45f7a1659 /</title> @@ -166,7 +167,7 @@ <div class="container"> <div class="menu"> <div class="logo"> - <a href="http://mercurial.selenic.com/"> + <a href="https://mercurial-scm.org/"> <img src="/static/hglogo.png" alt="mercurial" /></a> </div> <ul> @@ -370,14 +371,9 @@ </div> <div id="powered-by"> - <p><a href="http://mercurial.selenic.com/" title="Mercurial"><img src="/static/hglogo.png" width=75 height=90 border=0 alt="mercurial" /></a></p> + <p><a href="https://mercurial-scm.org/" title="Mercurial"><img src="/static/hglogo.png" width=75 height=90 border=0 alt="mercurial" /></a></p> </div> - <div id="corner-top-left"></div> - <div id="corner-top-right"></div> - <div id="corner-bottom-left"></div> - <div id="corner-bottom-right"></div> - </div> </body> @@ -407,7 +403,7 @@ <body> <div class="page_header"> - <a href="http://mercurial.selenic.com/" title="Mercurial" style="float: right;">Mercurial</a> + <a href="https://mercurial-scm.org/" title="Mercurial" style="float: right;">Mercurial</a> <a href="/">Mercurial</a> / files </div> @@ -558,7 +554,7 @@ <script type="text/javascript">process_dates()</script> <div class="logo"> - <a href="http://mercurial.selenic.com/"> + <a href="https://mercurial-scm.org/"> <img src="/static/hglogo.png" width=75 height=90 border=0 alt="mercurial"></a> </div>
--- a/tests/test-hgweb-diffs.t Mon Oct 05 10:43:16 2015 -0600 +++ b/tests/test-hgweb-diffs.t Wed Oct 07 13:44:48 2015 -0500 @@ -53,7 +53,7 @@ <div class="container"> <div class="menu"> <div class="logo"> - <a href="http://mercurial.selenic.com/"> + <a href="https://mercurial-scm.org/"> <img src="/static/hglogo.png" alt="mercurial" /></a> </div> <ul> @@ -222,7 +222,7 @@ <div class="container"> <div class="menu"> <div class="logo"> - <a href="http://mercurial.selenic.com/"> + <a href="https://mercurial-scm.org/"> <img src="/static/hglogo.png" alt="mercurial" /></a> </div> <ul> @@ -331,7 +331,7 @@ <div class="container"> <div class="menu"> <div class="logo"> - <a href="http://mercurial.selenic.com/"> + <a href="https://mercurial-scm.org/"> <img src="/static/hglogo.png" alt="mercurial" /></a> </div> <ul> @@ -504,7 +504,7 @@ <div class="container"> <div class="menu"> <div class="logo"> - <a href="http://mercurial.selenic.com/"> + <a href="https://mercurial-scm.org/"> <img src="/static/hglogo.png" alt="mercurial" /></a> </div> <ul> @@ -610,7 +610,7 @@ <div class="container"> <div class="menu"> <div class="logo"> - <a href="http://mercurial.selenic.com/"> + <a href="https://mercurial-scm.org/"> <img src="/static/hglogo.png" alt="mercurial" /></a> </div> <ul> @@ -740,7 +740,7 @@ <div class="container"> <div class="menu"> <div class="logo"> - <a href="http://mercurial.selenic.com/"> + <a href="https://mercurial-scm.org/"> <img src="/static/hglogo.png" alt="mercurial" /></a> </div> <ul> @@ -872,7 +872,7 @@ <div class="container"> <div class="menu"> <div class="logo"> - <a href="http://mercurial.selenic.com/"> + <a href="https://mercurial-scm.org/"> <img src="/static/hglogo.png" alt="mercurial" /></a> </div> <ul> @@ -1010,7 +1010,7 @@ <div class="container"> <div class="menu"> <div class="logo"> - <a href="http://mercurial.selenic.com/"> + <a href="https://mercurial-scm.org/"> <img src="/static/hglogo.png" alt="mercurial" /></a> </div> <ul>
--- a/tests/test-hgweb-empty.t Mon Oct 05 10:43:16 2015 -0600 +++ b/tests/test-hgweb-empty.t Wed Oct 07 13:44:48 2015 -0500 @@ -28,7 +28,7 @@ <div class="container"> <div class="menu"> <div class="logo"> - <a href="http://mercurial.selenic.com/"> + <a href="https://mercurial-scm.org/"> <img src="/static/hglogo.png" alt="mercurial" /></a> </div> <ul> @@ -139,7 +139,7 @@ <div class="container"> <div class="menu"> <div class="logo"> - <a href="http://mercurial.selenic.com/"> + <a href="https://mercurial-scm.org/"> <img src="/static/hglogo.png" alt="mercurial" /></a> </div> <ul> @@ -249,7 +249,7 @@ <div class="container"> <div class="menu"> <div class="logo"> - <a href="http://mercurial.selenic.com/"> + <a href="https://mercurial-scm.org/"> <img src="/static/hglogo.png" alt="mercurial" /></a> </div> <ul> @@ -398,7 +398,7 @@ <div class="container"> <div class="menu"> <div class="logo"> - <a href="http://mercurial.selenic.com/"> + <a href="https://mercurial-scm.org/"> <img src="/static/hglogo.png" alt="mercurial" /></a> </div> <ul>
--- a/tests/test-hgweb-filelog.t Mon Oct 05 10:43:16 2015 -0600 +++ b/tests/test-hgweb-filelog.t Wed Oct 07 13:44:48 2015 -0500 @@ -153,7 +153,7 @@ <div class="container"> <div class="menu"> <div class="logo"> - <a href="http://mercurial.selenic.com/"> + <a href="https://mercurial-scm.org/"> <img src="/static/hglogo.png" alt="mercurial" /></a> </div> <ul> @@ -270,7 +270,7 @@ <div class="container"> <div class="menu"> <div class="logo"> - <a href="http://mercurial.selenic.com/"> + <a href="https://mercurial-scm.org/"> <img src="/static/hglogo.png" alt="mercurial" /></a> </div> <ul> @@ -387,7 +387,7 @@ <div class="container"> <div class="menu"> <div class="logo"> - <a href="http://mercurial.selenic.com/"> + <a href="https://mercurial-scm.org/"> <img src="/static/hglogo.png" alt="mercurial" /></a> </div> <ul> @@ -496,7 +496,7 @@ <div class="container"> <div class="menu"> <div class="logo"> - <a href="http://mercurial.selenic.com/"> + <a href="https://mercurial-scm.org/"> <img src="/static/hglogo.png" alt="mercurial" /></a> </div> <ul> @@ -601,7 +601,7 @@ <div class="container"> <div class="menu"> <div class="logo"> - <a href="http://mercurial.selenic.com/"> + <a href="https://mercurial-scm.org/"> <img src="/static/hglogo.png" width=75 height=90 border=0 alt="mercurial" /></a> </div> <ul> @@ -748,7 +748,7 @@ <script type="text/javascript">process_dates()</script> <div class="logo"> - <a href="http://mercurial.selenic.com/"> + <a href="https://mercurial-scm.org/"> <img src="/static/hglogo.png" width=75 height=90 border=0 alt="mercurial"></a> </div>
--- a/tests/test-hgweb-non-interactive.t Mon Oct 05 10:43:16 2015 -0600 +++ b/tests/test-hgweb-non-interactive.t Wed Oct 07 13:44:48 2015 -0500 @@ -58,13 +58,15 @@ > } > > i = hgweb('.') - > i(env, startrsp) + > for c in i(env, startrsp): + > pass > print '---- ERRORS' > print errors.getvalue() > print '---- OS.ENVIRON wsgi variables' > print sorted([x for x in os.environ if x.startswith('wsgi')]) > print '---- request.ENVIRON wsgi variables' - > print sorted([x for x in i.repo.ui.environ if x.startswith('wsgi')]) + > with i._obtainrepo() as repo: + > print sorted([x for x in repo.ui.environ if x.startswith('wsgi')]) > EOF $ python request.py ---- STATUS
--- a/tests/test-hgweb-removed.t Mon Oct 05 10:43:16 2015 -0600 +++ b/tests/test-hgweb-removed.t Wed Oct 07 13:44:48 2015 -0500 @@ -34,7 +34,7 @@ <div class="container"> <div class="menu"> <div class="logo"> - <a href="http://mercurial.selenic.com/"> + <a href="https://mercurial-scm.org/"> <img src="/static/hglogo.png" alt="mercurial" /></a> </div> <ul> @@ -158,7 +158,7 @@ <div class="container"> <div class="menu"> <div class="logo"> - <a href="http://mercurial.selenic.com/"> + <a href="https://mercurial-scm.org/"> <img src="/static/hglogo.png" alt="mercurial" /></a> </div> <ul>
--- a/tests/test-hgweb-symrev.t Mon Oct 05 10:43:16 2015 -0600 +++ b/tests/test-hgweb-symrev.t Wed Oct 07 13:44:48 2015 -0500 @@ -376,8 +376,8 @@ annotate foo @ 1:<a href="/rev/a7c1559b7bba?style=coal">a7c1559b7bba</a> <td class="author"><a href="/file/43c799df6e75/foo?style=coal">43c799df6e75</a> </td> <td class="author"><a href="/file/9d8c40cba617/foo?style=coal">9d8c40cba617</a> </td> - <a href="/annotate/43c799df6e75/foo?style=coal#1" - <a href="/annotate/a7c1559b7bba/foo?style=coal#2" + <a href="/annotate/43c799df6e75/foo?style=coal#l1" + <a href="/annotate/a7c1559b7bba/foo?style=coal#l2" $ "$TESTDIR/get-with-headers.py" 127.0.0.1:$HGPORT 'diff/xyzzy/foo?style=coal' | egrep $REVLINKS <li><a href="/shortlog/xyzzy?style=coal">log</a></li> @@ -422,11 +422,11 @@ <a class="list" href="/rev/43c799df6e75?style=gitweb"> <a href="/rev/43c799df6e75?style=gitweb">changeset</a> | <a href="/file/43c799df6e75?style=gitweb">files</a> - <td><a class="list" href="/rev/a7c1559b7bba?style=gitweb"><b>xyzzy</b></a></td> + <td><a class="list" href="/rev/xyzzy?style=gitweb"><b>xyzzy</b></a></td> <a href="/rev/a7c1559b7bba?style=gitweb">changeset</a> | <a href="/log/a7c1559b7bba?style=gitweb">changelog</a> | <a href="/file/a7c1559b7bba?style=gitweb">files</a> - <td><a class="list" href="/shortlog/9d8c40cba617?style=gitweb"><b>9d8c40cba617</b></a></td> + <td class="open"><a class="list" href="/shortlog/default?style=gitweb"><b>default</b></a></td> <a href="/changeset/9d8c40cba617?style=gitweb">changeset</a> | <a href="/log/9d8c40cba617?style=gitweb">changelog</a> | <a href="/file/9d8c40cba617?style=gitweb">files</a> @@ -469,19 +469,19 @@ | <a href="/graph/43c799df6e75?style=gitweb">(0)</a> <a href="/graph/tip?style=gitweb">tip</a> $ "$TESTDIR/get-with-headers.py" 127.0.0.1:$HGPORT 'tags?style=gitweb' | egrep $REVLINKS - <td><a class="list" href="/rev/9d8c40cba617?style=gitweb"><b>tip</b></a></td> + <td><a class="list" href="/rev/tip?style=gitweb"><b>tip</b></a></td> <a href="/rev/9d8c40cba617?style=gitweb">changeset</a> | <a href="/log/9d8c40cba617?style=gitweb">changelog</a> | <a href="/file/9d8c40cba617?style=gitweb">files</a> $ "$TESTDIR/get-with-headers.py" 127.0.0.1:$HGPORT 'bookmarks?style=gitweb' | egrep $REVLINKS - <td><a class="list" href="/rev/a7c1559b7bba?style=gitweb"><b>xyzzy</b></a></td> + <td><a class="list" href="/rev/xyzzy?style=gitweb"><b>xyzzy</b></a></td> <a href="/rev/a7c1559b7bba?style=gitweb">changeset</a> | <a href="/log/a7c1559b7bba?style=gitweb">changelog</a> | <a href="/file/a7c1559b7bba?style=gitweb">files</a> $ "$TESTDIR/get-with-headers.py" 127.0.0.1:$HGPORT 'branches?style=gitweb' | egrep $REVLINKS - <td><a class="list" href="/shortlog/9d8c40cba617?style=gitweb"><b>9d8c40cba617</b></a></td> + <td class="open"><a class="list" href="/shortlog/default?style=gitweb"><b>default</b></a></td> <a href="/changeset/9d8c40cba617?style=gitweb">changeset</a> | <a href="/log/9d8c40cba617?style=gitweb">changelog</a> | <a href="/file/9d8c40cba617?style=gitweb">files</a> @@ -639,6 +639,7 @@ (De)referencing symbolic revisions (monoblue) $ "$TESTDIR/get-with-headers.py" 127.0.0.1:$HGPORT 'summary?style=monoblue' | egrep $REVLINKS + <li><a href="/archive/tip.zip">zip</a></li> <a href="/rev/9d8c40cba617?style=monoblue"> <a href="/rev/9d8c40cba617?style=monoblue">changeset</a> | <a href="/file/9d8c40cba617?style=monoblue">files</a> @@ -648,11 +649,11 @@ <a href="/rev/43c799df6e75?style=monoblue"> <a href="/rev/43c799df6e75?style=monoblue">changeset</a> | <a href="/file/43c799df6e75?style=monoblue">files</a> - <td><a href="/rev/a7c1559b7bba?style=monoblue">xyzzy</a></td> + <td><a href="/rev/xyzzy?style=monoblue">xyzzy</a></td> <a href="/rev/a7c1559b7bba?style=monoblue">changeset</a> | <a href="/log/a7c1559b7bba?style=monoblue">changelog</a> | <a href="/file/a7c1559b7bba?style=monoblue">files</a> - <td><a href="/shortlog/9d8c40cba617?style=monoblue">9d8c40cba617</a></td> + <td class="open"><a href="/shortlog/default?style=monoblue">default</a></td> <a href="/rev/9d8c40cba617?style=monoblue">changeset</a> | <a href="/log/9d8c40cba617?style=monoblue">changelog</a> | <a href="/file/9d8c40cba617?style=monoblue">files</a> @@ -688,19 +689,19 @@ | <a href="/graph/43c799df6e75?style=monoblue">(0)</a> <a href="/graph/tip?style=monoblue">tip</a> $ "$TESTDIR/get-with-headers.py" 127.0.0.1:$HGPORT 'tags?style=monoblue' | egrep $REVLINKS - <td><a href="/rev/9d8c40cba617?style=monoblue">tip</a></td> + <td><a href="/rev/tip?style=monoblue">tip</a></td> <a href="/rev/9d8c40cba617?style=monoblue">changeset</a> | <a href="/log/9d8c40cba617?style=monoblue">changelog</a> | <a href="/file/9d8c40cba617?style=monoblue">files</a> $ "$TESTDIR/get-with-headers.py" 127.0.0.1:$HGPORT 'bookmarks?style=monoblue' | egrep $REVLINKS - <td><a href="/rev/a7c1559b7bba?style=monoblue">xyzzy</a></td> + <td><a href="/rev/xyzzy?style=monoblue">xyzzy</a></td> <a href="/rev/a7c1559b7bba?style=monoblue">changeset</a> | <a href="/log/a7c1559b7bba?style=monoblue">changelog</a> | <a href="/file/a7c1559b7bba?style=monoblue">files</a> $ "$TESTDIR/get-with-headers.py" 127.0.0.1:$HGPORT 'branches?style=monoblue' | egrep $REVLINKS - <td><a href="/shortlog/9d8c40cba617?style=monoblue">9d8c40cba617</a></td> + <td class="open"><a href="/shortlog/default?style=monoblue">default</a></td> <a href="/rev/9d8c40cba617?style=monoblue">changeset</a> | <a href="/log/9d8c40cba617?style=monoblue">changelog</a> | <a href="/file/9d8c40cba617?style=monoblue">files</a> @@ -800,9 +801,13 @@ <li><a href="/comparison/xyzzy/foo?style=monoblue">comparison</a></li> <li><a href="/rss-log/tip/foo">rss</a></li> <a href="/rev/a7c1559b7bba?style=monoblue"> - <a href="/file/a7c1559b7bba/foo?style=monoblue">file</a> | <a href="/diff/a7c1559b7bba/foo?style=monoblue">diff</a> | <a href="/annotate/a7c1559b7bba/foo?style=monoblue">annotate</a> + <a href="/file/a7c1559b7bba/foo?style=monoblue">file</a> | + <a href="/diff/a7c1559b7bba/foo?style=monoblue">diff</a> | + <a href="/annotate/a7c1559b7bba/foo?style=monoblue">annotate</a> <a href="/rev/43c799df6e75?style=monoblue"> - <a href="/file/43c799df6e75/foo?style=monoblue">file</a> | <a href="/diff/43c799df6e75/foo?style=monoblue">diff</a> | <a href="/annotate/43c799df6e75/foo?style=monoblue">annotate</a> + <a href="/file/43c799df6e75/foo?style=monoblue">file</a> | + <a href="/diff/43c799df6e75/foo?style=monoblue">diff</a> | + <a href="/annotate/43c799df6e75/foo?style=monoblue">annotate</a> <a href="/log/43c799df6e75/foo?style=monoblue">(0)</a><a href="/log/tip/foo?style=monoblue">tip</a> $ "$TESTDIR/get-with-headers.py" 127.0.0.1:$HGPORT 'annotate/xyzzy/foo?style=monoblue' | egrep $REVLINKS
--- a/tests/test-hgweb.t Mon Oct 05 10:43:16 2015 -0600 +++ b/tests/test-hgweb.t Wed Oct 07 13:44:48 2015 -0500 @@ -61,7 +61,7 @@ <div class="container"> <div class="menu"> <div class="logo"> - <a href="http://mercurial.selenic.com/"> + <a href="https://mercurial-scm.org/"> <img src="/static/hglogo.png" width=75 height=90 border=0 alt="mercurial" /></a> </div> <ul> @@ -169,7 +169,7 @@ <div class="container"> <div class="menu"> <div class="logo"> - <a href="http://mercurial.selenic.com/"> + <a href="https://mercurial-scm.org/"> <img src="/static/hglogo.png" width=75 height=90 border=0 alt="mercurial" /></a> </div> <ul> @@ -241,7 +241,7 @@ <div class="container"> <div class="menu"> <div class="logo"> - <a href="http://mercurial.selenic.com/"> + <a href="https://mercurial-scm.org/"> <img src="/static/hglogo.png" alt="mercurial" /></a> </div> <ul> @@ -340,7 +340,7 @@ $ get-with-headers.py --twice localhost:$HGPORT 'static/style-gitweb.css' - date etag server 200 Script output follows - content-length: 5372 + content-length: 6379 content-type: text/css body { font-family: sans-serif; font-size: 12px; border:solid #d9d8d1; border-width:1px; margin:10px; } @@ -374,9 +374,9 @@ a.list:hover { text-decoration:underline; color:#880000; } table { padding:8px 4px; } th { padding:2px 5px; font-size:12px; text-align:left; } - tr.light:hover, .parity0:hover { background-color:#edece6; } - tr.dark, .parity1 { background-color:#f6f6f0; } - tr.dark:hover, .parity1:hover { background-color:#edece6; } + tr.light:hover, .parity0:hover, pre.sourcelines.stripes > :nth-child(4n+1):hover { background-color:#edece6; } + tr.dark, .parity1, pre.sourcelines.stripes > :nth-child(4n+3) { background-color:#f6f6f0; } + tr.dark:hover, .parity1:hover, pre.sourcelines.stripes > :nth-child(4n+3):hover { background-color:#edece6; } td { padding:2px 5px; font-size:12px; vertical-align:top; } td.closed { background-color: #99f; } td.link { padding:2px 5px; font-family:sans-serif; font-size:10px; } @@ -432,6 +432,43 @@ span.difflineplus { color:#008800; } span.difflineminus { color:#cc0000; } span.difflineat { color:#990099; } + div.diffblocks { counter-reset: lineno; } + div.diffblock { counter-increment: lineno; } + pre.sourcelines { position: relative; counter-reset: lineno; } + pre.sourcelines > span { + display: inline-block; + box-sizing: border-box; + width: 100%; + padding: 0 0 0 5em; + counter-increment: lineno; + vertical-align: top; + } + pre.sourcelines > span:before { + -moz-user-select: -moz-none; + -khtml-user-select: none; + -webkit-user-select: none; + -ms-user-select: none; + user-select: none; + display: inline-block; + margin-left: -5em; + width: 4em; + color: #999; + text-align: right; + content: counters(lineno,"."); + float: left; + } + pre.sourcelines > a { + display: inline-block; + position: absolute; + left: 0px; + width: 4em; + height: 1em; + } + tr:target td, + pre.sourcelines > span:target, + pre.sourcelines.stripes > span:target { + background-color: #bfdfff; + } /* Graph */ div#wrapper { @@ -539,6 +576,10 @@ .scroll-loading-error { background-color: #FFCCCC !important; } + + #doc { + margin: 0 8px; + } 304 Not Modified
--- a/tests/test-hgwebdir.t Mon Oct 05 10:43:16 2015 -0600 +++ b/tests/test-hgwebdir.t Wed Oct 07 13:44:48 2015 -0500 @@ -201,7 +201,7 @@ <div class="container"> <div class="menu"> - <a href="http://mercurial.selenic.com/"> + <a href="https://mercurial-scm.org/"> <img src="/static/hglogo.png" width=75 height=90 border=0 alt="mercurial" /></a> </div> <div class="main"> @@ -701,7 +701,7 @@ <div class="container"> <div class="menu"> - <a href="http://mercurial.selenic.com/"> + <a href="https://mercurial-scm.org/"> <img src="/static/hglogo.png" width=75 height=90 border=0 alt="mercurial" /></a> </div> <div class="main"> @@ -1152,7 +1152,7 @@ <div class="container"> <div class="menu"> - <a href="http://mercurial.selenic.com/"> + <a href="https://mercurial-scm.org/"> <img src="/static/hglogo.png" width=75 height=90 border=0 alt="mercurial" /></a> </div> <div class="main"> @@ -1245,6 +1245,67 @@ $ get-with-headers.py localhost:$HGPORT2 'a/rss-log' | grep '<guid' <guid isPermaLink="true">http://hg.example.com:8080/foo/a/rev/8580ff50825a</guid> +Path refreshing works as expected + + $ killdaemons.py + $ mkdir $root/refreshtest + $ hg init $root/refreshtest/a + $ cat > paths.conf << EOF + > [paths] + > / = $root/refreshtest/* + > EOF + $ hg serve -p $HGPORT1 -d --pid-file hg.pid --webdir-conf paths.conf + $ cat hg.pid >> $DAEMON_PIDS + + $ get-with-headers.py localhost:$HGPORT1 '?style=raw' + 200 Script output follows + + + /a/ + + +By default refreshing occurs every 20s and a new repo won't be listed +immediately. + + $ hg init $root/refreshtest/b + $ get-with-headers.py localhost:$HGPORT1 '?style=raw' + 200 Script output follows + + + /a/ + + +Restart the server with no refresh interval. New repo should appear +immediately. + + $ killdaemons.py + $ cat > paths.conf << EOF + > [web] + > refreshinterval = -1 + > [paths] + > / = $root/refreshtest/* + > EOF + $ hg serve -p $HGPORT1 -d --pid-file hg.pid --webdir-conf paths.conf + $ cat hg.pid >> $DAEMON_PIDS + + $ get-with-headers.py localhost:$HGPORT1 '?style=raw' + 200 Script output follows + + + /a/ + /b/ + + + $ hg init $root/refreshtest/c + $ get-with-headers.py localhost:$HGPORT1 '?style=raw' + 200 Script output follows + + + /a/ + /b/ + /c/ + + paths errors 1 $ cat error-paths-1.log
--- a/tests/test-highlight.t Mon Oct 05 10:43:16 2015 -0600 +++ b/tests/test-highlight.t Wed Oct 07 13:44:48 2015 -0500 @@ -5,6 +5,7 @@ > highlight = > [web] > pygments_style = friendly + > highlightfiles = **.py and size('<100KB') > EOF $ hg init test $ cd test @@ -76,7 +77,7 @@ <div class="container"> <div class="menu"> <div class="logo"> - <a href="http://mercurial.selenic.com/"> + <a href="https://mercurial-scm.org/"> <img src="/static/hglogo.png" alt="mercurial" /></a> </div> <ul> @@ -142,7 +143,7 @@ <div class="overflow"> <div class="sourcefirst linewraptoggle">line wrap: <a class="linewraplink" href="javascript:toggleLinewrap()">on</a></div> <div class="sourcefirst"> line source</div> - <pre class="sourcelines stripes4 wrap"> + <pre class="sourcelines stripes4 wrap bottomline"> <span id="l1"><span class="c">#!/usr/bin/env python</span></span><a href="#l1"></a> <span id="l2"></span><a href="#l2"></a> <span id="l3"><span class="sd">"""Fun with generators. Corresponding Haskell implementation:</span></span><a href="#l3"></a> @@ -176,7 +177,6 @@ <span id="l31"> <span class="n">p</span> <span class="o">=</span> <span class="n">primes</span><span class="p">()</span></span><a href="#l31"></a> <span id="l32"> <span class="kn">print</span> <span class="s">"The first </span><span class="si">%d</span><span class="s"> primes: </span><span class="si">%s</span><span class="s">"</span> <span class="o">%</span> <span class="p">(</span><span class="n">n</span><span class="p">,</span> <span class="nb">list</span><span class="p">(</span><span class="n">islice</span><span class="p">(</span><span class="n">p</span><span class="p">,</span> <span class="n">n</span><span class="p">)))</span></span><a href="#l32"></a> <span id="l33"></span><a href="#l33"></a></pre> - <div class="sourcelast"></div> </div> </div> </div> @@ -210,7 +210,7 @@ <div class="container"> <div class="menu"> <div class="logo"> - <a href="http://mercurial.selenic.com/"> + <a href="https://mercurial-scm.org/"> <img src="/static/hglogo.png" alt="mercurial" /></a> </div> <ul> @@ -591,6 +591,28 @@ errors encountered $ cat errors.log + $ killdaemons.py + +only highlight C source files + + $ cat > .hg/hgrc <<EOF + > [web] + > highlightfiles = **.c + > EOF + +hg serve again + + $ hg serve -p $HGPORT -d -n test --pid-file=hg.pid -A access.log -E errors.log + $ cat hg.pid >> $DAEMON_PIDS + +test that fileset in highlightfiles works and primes.py is not highlighted + + $ get-with-headers.py localhost:$HGPORT 'file/tip/primes.py' | grep 'id="l11"' + <span id="l11">def primes():</span><a href="#l11"></a> + +errors encountered + + $ cat errors.log $ cd .. $ hg init eucjp $ cd eucjp
--- a/tests/test-histedit-arguments.t Mon Oct 05 10:43:16 2015 -0600 +++ b/tests/test-histedit-arguments.t Wed Oct 07 13:44:48 2015 -0500 @@ -69,7 +69,7 @@ # f, fold = use commit, but combine it with the one above # r, roll = like fold, but discard this commit's description # d, drop = remove commit from history - # m, mess = edit message without changing commit content + # m, mess = edit commit message without changing commit content # 0 files updated, 0 files merged, 0 files removed, 0 files unresolved @@ -292,7 +292,7 @@ # f, fold = use commit, but combine it with the one above # r, roll = like fold, but discard this commit's description # d, drop = remove commit from history - # m, mess = edit message without changing commit content + # m, mess = edit commit message without changing commit content # 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
--- a/tests/test-histedit-bookmark-motion.t Mon Oct 05 10:43:16 2015 -0600 +++ b/tests/test-histedit-bookmark-motion.t Wed Oct 07 13:44:48 2015 -0500 @@ -75,7 +75,7 @@ # f, fold = use commit, but combine it with the one above # r, roll = like fold, but discard this commit's description # d, drop = remove commit from history - # m, mess = edit message without changing commit content + # m, mess = edit commit message without changing commit content # 0 files updated, 0 files merged, 0 files removed, 0 files unresolved $ hg histedit 1 --commands - --verbose << EOF | grep histedit @@ -136,7 +136,7 @@ # f, fold = use commit, but combine it with the one above # r, roll = like fold, but discard this commit's description # d, drop = remove commit from history - # m, mess = edit message without changing commit content + # m, mess = edit commit message without changing commit content # 0 files updated, 0 files merged, 0 files removed, 0 files unresolved $ hg histedit 1 --commands - --verbose << EOF | grep histedit
--- a/tests/test-histedit-commute.t Mon Oct 05 10:43:16 2015 -0600 +++ b/tests/test-histedit-commute.t Wed Oct 07 13:44:48 2015 -0500 @@ -69,7 +69,7 @@ # f, fold = use commit, but combine it with the one above # r, roll = like fold, but discard this commit's description # d, drop = remove commit from history - # m, mess = edit message without changing commit content + # m, mess = edit commit message without changing commit content # 0 files updated, 0 files merged, 0 files removed, 0 files unresolved @@ -347,7 +347,7 @@ # f, fold = use commit, but combine it with the one above # r, roll = like fold, but discard this commit's description # d, drop = remove commit from history - # m, mess = edit message without changing commit content + # m, mess = edit commit message without changing commit content # 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
--- a/tests/test-histedit-fold.t Mon Oct 05 10:43:16 2015 -0600 +++ b/tests/test-histedit-fold.t Wed Oct 07 13:44:48 2015 -0500 @@ -509,4 +509,64 @@ $ hg add amended.txt $ hg ci -q --config extensions.largefiles= --amend -I amended.txt +Test that folding multiple changes in a row doesn't show multiple +editors. + + $ echo foo >> foo + $ hg add foo + $ hg ci -m foo1 + $ echo foo >> foo + $ hg ci -m foo2 + $ echo foo >> foo + $ hg ci -m foo3 + $ hg logt + 4:21679ff7675c foo3 + 3:b7389cc4d66e foo2 + 2:0e01aeef5fa8 foo1 + 1:578c7455730c a + 0:79b99e9c8e49 b + $ cat > $TESTTMP/editor.sh <<EOF + > echo ran editor >> $TESTTMP/editorlog.txt + > cat \$1 >> $TESTTMP/editorlog.txt + > echo END >> $TESTTMP/editorlog.txt + > echo merged foos > \$1 + > EOF + $ HGEDITOR="sh $TESTTMP/editor.sh" hg histedit 1 --commands - 2>&1 <<EOF | fixbundle + > pick 578c7455730c 1 a + > pick 0e01aeef5fa8 2 foo1 + > fold b7389cc4d66e 3 foo2 + > fold 21679ff7675c 4 foo3 + > EOF + 1 files updated, 0 files merged, 0 files removed, 0 files unresolved + reverting foo + 0 files updated, 0 files merged, 1 files removed, 0 files unresolved + 1 files updated, 0 files merged, 0 files removed, 0 files unresolved + 0 files updated, 0 files merged, 0 files removed, 0 files unresolved + merging foo + 0 files updated, 0 files merged, 1 files removed, 0 files unresolved + 1 files updated, 0 files merged, 0 files removed, 0 files unresolved + 0 files updated, 0 files merged, 0 files removed, 0 files unresolved + $ hg logt + 2:e8bedbda72c1 merged foos + 1:578c7455730c a + 0:79b99e9c8e49 b +Editor should have run only once + $ cat $TESTTMP/editorlog.txt + ran editor + foo1 + *** + foo2 + *** + foo3 + + + + HG: Enter commit message. Lines beginning with 'HG:' are removed. + HG: Leave message empty to abort commit. + HG: -- + HG: user: test + HG: branch 'default' + HG: added foo + END + $ cd ..
--- a/tests/test-histedit-obsolete.t Mon Oct 05 10:43:16 2015 -0600 +++ b/tests/test-histedit-obsolete.t Wed Oct 07 13:44:48 2015 -0500 @@ -54,7 +54,7 @@ # f, fold = use commit, but combine it with the one above # r, roll = like fold, but discard this commit's description # d, drop = remove commit from history - # m, mess = edit message without changing commit content + # m, mess = edit commit message without changing commit content # 0 files updated, 0 files merged, 0 files removed, 0 files unresolved $ hg histedit 1 --commands - --verbose <<EOF | grep histedit
--- a/tests/test-histedit-outgoing.t Mon Oct 05 10:43:16 2015 -0600 +++ b/tests/test-histedit-outgoing.t Wed Oct 07 13:44:48 2015 -0500 @@ -51,7 +51,7 @@ # f, fold = use commit, but combine it with the one above # r, roll = like fold, but discard this commit's description # d, drop = remove commit from history - # m, mess = edit message without changing commit content + # m, mess = edit commit message without changing commit content # 0 files updated, 0 files merged, 0 files removed, 0 files unresolved $ cd .. @@ -83,7 +83,7 @@ # f, fold = use commit, but combine it with the one above # r, roll = like fold, but discard this commit's description # d, drop = remove commit from history - # m, mess = edit message without changing commit content + # m, mess = edit commit message without changing commit content # 0 files updated, 0 files merged, 0 files removed, 0 files unresolved $ cd .. @@ -107,7 +107,7 @@ # f, fold = use commit, but combine it with the one above # r, roll = like fold, but discard this commit's description # d, drop = remove commit from history - # m, mess = edit message without changing commit content + # m, mess = edit commit message without changing commit content # 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
--- a/tests/test-i18n.t Mon Oct 05 10:43:16 2015 -0600 +++ b/tests/test-i18n.t Wed Oct 07 13:44:48 2015 -0500 @@ -1,8 +1,6 @@ -#require gettext - (Translations are optional) -#if no-outer-repo +#if gettext no-outer-repo Test that translations are compiled and installed correctly. @@ -27,6 +25,8 @@ #endif +#if gettext + Test keyword search in translated help text: $ HGENCODING=UTF-8 LANGUAGE=de hg help -k blättern @@ -38,6 +38,8 @@ pager Verwendet einen externen Pager zum Bl\xc3\xa4ttern in der Ausgabe von Befehlen (esc) +#endif + Check Mercurial specific translation problems in each *.po files, and tool itself by doctest
--- a/tests/test-identify.t Mon Oct 05 10:43:16 2015 -0600 +++ b/tests/test-identify.t Wed Oct 07 13:44:48 2015 -0500 @@ -120,13 +120,13 @@ $ echo fake >> .hg/requires $ hg id abort: repository requires features unknown to this Mercurial: fake! - (see http://mercurial.selenic.com/wiki/MissingRequirement for more information) + (see https://mercurial-scm.org/wiki/MissingRequirement for more information) [255] $ cd .. #if no-outer-repo $ hg id test abort: repository requires features unknown to this Mercurial: fake! - (see http://mercurial.selenic.com/wiki/MissingRequirement for more information) + (see https://mercurial-scm.org/wiki/MissingRequirement for more information) [255] #endif
--- a/tests/test-issue1089.t Mon Oct 05 10:43:16 2015 -0600 +++ b/tests/test-issue1089.t Wed Oct 07 13:44:48 2015 -0500 @@ -1,4 +1,4 @@ -http://mercurial.selenic.com/bts/issue1089 +https://bz.mercurial-scm.org/1089 $ hg init $ mkdir a
--- a/tests/test-issue1175.t Mon Oct 05 10:43:16 2015 -0600 +++ b/tests/test-issue1175.t Wed Oct 07 13:44:48 2015 -0500 @@ -1,4 +1,4 @@ -http://mercurial.selenic.com/bts/issue1175 +https://bz.mercurial-scm.org/1175 $ hg init $ touch a
--- a/tests/test-issue1306.t Mon Oct 05 10:43:16 2015 -0600 +++ b/tests/test-issue1306.t Wed Oct 07 13:44:48 2015 -0500 @@ -1,4 +1,4 @@ -http://mercurial.selenic.com/bts/issue1306 +https://bz.mercurial-scm.org/1306 Initialize remote repo with branches:
--- a/tests/test-issue1438.t Mon Oct 05 10:43:16 2015 -0600 +++ b/tests/test-issue1438.t Wed Oct 07 13:44:48 2015 -0500 @@ -1,6 +1,6 @@ #require symlink -http://mercurial.selenic.com/bts/issue1438 +https://bz.mercurial-scm.org/1438 $ hg init
--- a/tests/test-issue1502.t Mon Oct 05 10:43:16 2015 -0600 +++ b/tests/test-issue1502.t Wed Oct 07 13:44:48 2015 -0500 @@ -1,4 +1,4 @@ -http://mercurial.selenic.com/bts/issue1502 +https://bz.mercurial-scm.org/1502 Initialize repository
--- a/tests/test-issue1877.t Mon Oct 05 10:43:16 2015 -0600 +++ b/tests/test-issue1877.t Wed Oct 07 13:44:48 2015 -0500 @@ -1,4 +1,4 @@ -http://mercurial.selenic.com/bts/issue1877 +https://bz.mercurial-scm.org/1877 $ hg init a $ cd a
--- a/tests/test-issue2137.t Mon Oct 05 10:43:16 2015 -0600 +++ b/tests/test-issue2137.t Wed Oct 07 13:44:48 2015 -0500 @@ -1,4 +1,4 @@ -http://mercurial.selenic.com/bts/issue2137 +https://bz.mercurial-scm.org/2137 Setup:
--- a/tests/test-issue522.t Mon Oct 05 10:43:16 2015 -0600 +++ b/tests/test-issue522.t Wed Oct 07 13:44:48 2015 -0500 @@ -1,4 +1,4 @@ -http://mercurial.selenic.com/bts/issue522 +https://bz.mercurial-scm.org/522 In the merge below, the file "foo" has the same contents in both parents, but if we look at the file-level history, we'll notice that
--- a/tests/test-issue612.t Mon Oct 05 10:43:16 2015 -0600 +++ b/tests/test-issue612.t Wed Oct 07 13:44:48 2015 -0500 @@ -1,4 +1,4 @@ -http://mercurial.selenic.com/bts/issue612 +https://bz.mercurial-scm.org/612 $ hg init $ mkdir src
--- a/tests/test-issue619.t Mon Oct 05 10:43:16 2015 -0600 +++ b/tests/test-issue619.t Wed Oct 07 13:44:48 2015 -0500 @@ -1,4 +1,4 @@ -http://mercurial.selenic.com/bts/issue619 +https://bz.mercurial-scm.org/619 $ hg init $ echo a > a
--- a/tests/test-issue660.t Mon Oct 05 10:43:16 2015 -0600 +++ b/tests/test-issue660.t Wed Oct 07 13:44:48 2015 -0500 @@ -1,5 +1,5 @@ -http://mercurial.selenic.com/bts/issue660 and: -http://mercurial.selenic.com/bts/issue322 +https://bz.mercurial-scm.org/660 and: +https://bz.mercurial-scm.org/322 $ hg init $ echo a > a
--- a/tests/test-issue672.t Mon Oct 05 10:43:16 2015 -0600 +++ b/tests/test-issue672.t Wed Oct 07 13:44:48 2015 -0500 @@ -1,4 +1,4 @@ -http://mercurial.selenic.com/bts/issue672 +https://bz.mercurial-scm.org/672 # 0-2-4 # \ \ \
--- a/tests/test-issue842.t Mon Oct 05 10:43:16 2015 -0600 +++ b/tests/test-issue842.t Wed Oct 07 13:44:48 2015 -0500 @@ -1,4 +1,4 @@ -http://mercurial.selenic.com/bts/issue842 +https://bz.mercurial-scm.org/842 $ hg init $ echo foo > a
--- a/tests/test-largefiles-misc.t Mon Oct 05 10:43:16 2015 -0600 +++ b/tests/test-largefiles-misc.t Wed Oct 07 13:44:48 2015 -0500 @@ -934,7 +934,7 @@ $TESTTMP/individualenabling/enabledlocally (glob) $ hg -R notenabledlocally root abort: repository requires features unknown to this Mercurial: largefiles! - (see http://mercurial.selenic.com/wiki/MissingRequirement for more information) + (see https://mercurial-scm.org/wiki/MissingRequirement for more information) [255] $ hg init push-dst @@ -951,7 +951,7 @@ $ hg clone enabledlocally clone-dst abort: repository requires features unknown to this Mercurial: largefiles! - (see http://mercurial.selenic.com/wiki/MissingRequirement for more information) + (see https://mercurial-scm.org/wiki/MissingRequirement for more information) [255] $ test -d clone-dst [1]
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tests/test-lock.py Wed Oct 07 13:44:48 2015 -0500 @@ -0,0 +1,271 @@ +from __future__ import absolute_import + +import copy +import os +import silenttestrunner +import tempfile +import types +import unittest + +from mercurial import ( + error, + lock, + scmutil, +) + +testlockname = 'testlock' + +# work around http://bugs.python.org/issue1515 +if types.MethodType not in copy._deepcopy_dispatch: + def _deepcopy_method(x, memo): + return type(x)(x.im_func, copy.deepcopy(x.im_self, memo), x.im_class) + copy._deepcopy_dispatch[types.MethodType] = _deepcopy_method + +class lockwrapper(lock.lock): + def __init__(self, pidoffset, *args, **kwargs): + # lock.lock.__init__() calls lock(), so the pidoffset assignment needs + # to be earlier + self._pidoffset = pidoffset + super(lockwrapper, self).__init__(*args, **kwargs) + def _getpid(self): + return os.getpid() + self._pidoffset + +class teststate(object): + def __init__(self, testcase, dir, pidoffset=0): + self._testcase = testcase + self._acquirecalled = False + self._releasecalled = False + self._postreleasecalled = False + self.vfs = scmutil.vfs(dir, audit=False) + self._pidoffset = pidoffset + + def makelock(self, *args, **kwargs): + l = lockwrapper(self._pidoffset, self.vfs, testlockname, + releasefn=self.releasefn, acquirefn=self.acquirefn, + *args, **kwargs) + l.postrelease.append(self.postreleasefn) + return l + + def acquirefn(self): + self._acquirecalled = True + + def releasefn(self): + self._releasecalled = True + + def postreleasefn(self): + self._postreleasecalled = True + + def assertacquirecalled(self, called): + self._testcase.assertEqual( + self._acquirecalled, called, + 'expected acquire to be %s but was actually %s' % ( + self._tocalled(called), + self._tocalled(self._acquirecalled), + )) + + def resetacquirefn(self): + self._acquirecalled = False + + def assertreleasecalled(self, called): + self._testcase.assertEqual( + self._releasecalled, called, + 'expected release to be %s but was actually %s' % ( + self._tocalled(called), + self._tocalled(self._releasecalled), + )) + + def assertpostreleasecalled(self, called): + self._testcase.assertEqual( + self._postreleasecalled, called, + 'expected postrelease to be %s but was actually %s' % ( + self._tocalled(called), + self._tocalled(self._postreleasecalled), + )) + + def assertlockexists(self, exists): + actual = self.vfs.lexists(testlockname) + self._testcase.assertEqual( + actual, exists, + 'expected lock to %s but actually did %s' % ( + self._toexists(exists), + self._toexists(actual), + )) + + def _tocalled(self, called): + if called: + return 'called' + else: + return 'not called' + + def _toexists(self, exists): + if exists: + return 'exist' + else: + return 'not exist' + +class testlock(unittest.TestCase): + def testlock(self): + state = teststate(self, tempfile.mkdtemp(dir=os.getcwd())) + lock = state.makelock() + state.assertacquirecalled(True) + lock.release() + state.assertreleasecalled(True) + state.assertpostreleasecalled(True) + state.assertlockexists(False) + + def testrecursivelock(self): + state = teststate(self, tempfile.mkdtemp(dir=os.getcwd())) + lock = state.makelock() + state.assertacquirecalled(True) + + state.resetacquirefn() + lock.lock() + # recursive lock should not call acquirefn again + state.assertacquirecalled(False) + + lock.release() # brings lock refcount down from 2 to 1 + state.assertreleasecalled(False) + state.assertpostreleasecalled(False) + state.assertlockexists(True) + + lock.release() # releases the lock + state.assertreleasecalled(True) + state.assertpostreleasecalled(True) + state.assertlockexists(False) + + def testlockfork(self): + state = teststate(self, tempfile.mkdtemp(dir=os.getcwd())) + lock = state.makelock() + state.assertacquirecalled(True) + + # fake a fork + forklock = copy.deepcopy(lock) + forklock._pidoffset = 1 + forklock.release() + state.assertreleasecalled(False) + state.assertpostreleasecalled(False) + state.assertlockexists(True) + + # release the actual lock + lock.release() + state.assertreleasecalled(True) + state.assertpostreleasecalled(True) + state.assertlockexists(False) + + def testinheritlock(self): + d = tempfile.mkdtemp(dir=os.getcwd()) + parentstate = teststate(self, d) + parentlock = parentstate.makelock() + parentstate.assertacquirecalled(True) + + # set up lock inheritance + with parentlock.inherit() as lockname: + parentstate.assertreleasecalled(True) + parentstate.assertpostreleasecalled(False) + parentstate.assertlockexists(True) + + childstate = teststate(self, d, pidoffset=1) + childlock = childstate.makelock(parentlock=lockname) + childstate.assertacquirecalled(True) + + childlock.release() + childstate.assertreleasecalled(True) + childstate.assertpostreleasecalled(False) + childstate.assertlockexists(True) + + parentstate.resetacquirefn() + + parentstate.assertacquirecalled(True) + + parentlock.release() + parentstate.assertreleasecalled(True) + parentstate.assertpostreleasecalled(True) + parentstate.assertlockexists(False) + + def testmultilock(self): + d = tempfile.mkdtemp(dir=os.getcwd()) + state0 = teststate(self, d) + lock0 = state0.makelock() + state0.assertacquirecalled(True) + + with lock0.inherit() as lock0name: + state0.assertreleasecalled(True) + state0.assertpostreleasecalled(False) + state0.assertlockexists(True) + + state1 = teststate(self, d, pidoffset=1) + lock1 = state1.makelock(parentlock=lock0name) + state1.assertacquirecalled(True) + + # from within lock1, acquire another lock + with lock1.inherit() as lock1name: + # since the file on disk is lock0's this should have the same + # name + self.assertEqual(lock0name, lock1name) + + state2 = teststate(self, d, pidoffset=2) + lock2 = state2.makelock(parentlock=lock1name) + state2.assertacquirecalled(True) + + lock2.release() + state2.assertreleasecalled(True) + state2.assertpostreleasecalled(False) + state2.assertlockexists(True) + + state1.resetacquirefn() + + state1.assertacquirecalled(True) + + lock1.release() + state1.assertreleasecalled(True) + state1.assertpostreleasecalled(False) + state1.assertlockexists(True) + + lock0.release() + + def testinheritlockfork(self): + d = tempfile.mkdtemp(dir=os.getcwd()) + parentstate = teststate(self, d) + parentlock = parentstate.makelock() + parentstate.assertacquirecalled(True) + + # set up lock inheritance + with parentlock.inherit() as lockname: + childstate = teststate(self, d, pidoffset=1) + childlock = childstate.makelock(parentlock=lockname) + childstate.assertacquirecalled(True) + + # fork the child lock + forkchildlock = copy.deepcopy(childlock) + forkchildlock._pidoffset += 1 + forkchildlock.release() + childstate.assertreleasecalled(False) + childstate.assertpostreleasecalled(False) + childstate.assertlockexists(True) + + # release the child lock + childlock.release() + childstate.assertreleasecalled(True) + childstate.assertpostreleasecalled(False) + childstate.assertlockexists(True) + + parentlock.release() + + def testinheritcheck(self): + d = tempfile.mkdtemp(dir=os.getcwd()) + state = teststate(self, d) + def check(): + raise error.LockInheritanceContractViolation('check failed') + lock = state.makelock(inheritchecker=check) + state.assertacquirecalled(True) + + def tryinherit(): + with lock.inherit(): + pass + + self.assertRaises(error.LockInheritanceContractViolation, tryinherit) + + lock.release() + +if __name__ == '__main__': + silenttestrunner.main(__name__)
--- a/tests/test-log.t Mon Oct 05 10:43:16 2015 -0600 +++ b/tests/test-log.t Wed Oct 07 13:44:48 2015 -0500 @@ -620,6 +620,21 @@ $ hg up -C 1 1 files updated, 0 files merged, 0 files removed, 0 files unresolved $ echo b1 > b1 + +log -r "follow('set:clean()')" + + $ hg log -r "follow('set:clean()')" + changeset: 0:67e992f2c4f3 + user: test + date: Thu Jan 01 00:00:01 1970 +0000 + summary: base + + changeset: 1:3d5bf5654eda + user: test + date: Thu Jan 01 00:00:01 1970 +0000 + summary: r1 + + $ hg ci -Amb1 -d '1 0' adding b1 created new head @@ -646,7 +661,26 @@ summary: base +log -r follow('glob:b*') + $ hg log -r "follow('glob:b*')" + changeset: 0:67e992f2c4f3 + user: test + date: Thu Jan 01 00:00:01 1970 +0000 + summary: base + + changeset: 1:3d5bf5654eda + user: test + date: Thu Jan 01 00:00:01 1970 +0000 + summary: r1 + + changeset: 3:e62f78d544b4 + tag: tip + parent: 1:3d5bf5654eda + user: test + date: Thu Jan 01 00:00:01 1970 +0000 + summary: b1 + log -f -r '1 + 4' $ hg up -C 0 @@ -673,6 +707,16 @@ date: Thu Jan 01 00:00:01 1970 +0000 summary: base +log -r "follow('set:grep(b2)')" + + $ hg log -r "follow('set:grep(b2)')" + changeset: 4:ddb82e70d1a1 + tag: tip + parent: 0:67e992f2c4f3 + user: test + date: Thu Jan 01 00:00:01 1970 +0000 + summary: b2 + log -f -r null $ hg log -f -r null
--- a/tests/test-merge-internal-tools-pattern.t Mon Oct 05 10:43:16 2015 -0600 +++ b/tests/test-merge-internal-tools-pattern.t Wed Oct 07 13:44:48 2015 -0500 @@ -1,5 +1,6 @@ -Make sure that the internal merge tools (internal:fail, internal:local, and -internal:other) are used when matched by a merge-pattern in hgrc +Make sure that the internal merge tools (internal:fail, internal:local, +internal:union and internal:other) are used when matched by a +merge-pattern in hgrc Make sure HGMERGE doesn't interfere with the test: @@ -110,3 +111,31 @@ $ hg stat M f +Merge using internal:union tool: + + $ hg update -C 2 + 1 files updated, 0 files merged, 0 files removed, 0 files unresolved + + $ echo "line 4a" >>f + $ hg ci -Am "Adding fourth line (commit 4)" + $ hg update 2 + 1 files updated, 0 files merged, 0 files removed, 0 files unresolved + + $ echo "line 4b" >>f + $ hg ci -Am "Adding fourth line v2 (commit 5)" + created new head + + $ echo "[merge-patterns]" > .hg/hgrc + $ echo "* = internal:union" >> .hg/hgrc + + $ hg merge 3 + merging f + 1 files updated, 0 files merged, 0 files removed, 0 files unresolved + (branch merge, don't forget to commit) + + $ cat f + line 1 + line 2 + third line + line 4b + line 4a
--- a/tests/test-merge-tools.t Mon Oct 05 10:43:16 2015 -0600 +++ b/tests/test-merge-tools.t Wed Oct 07 13:44:48 2015 -0500 @@ -65,7 +65,7 @@ override $PATH to ensure hgmerge not visible; use $PYTHON in case we're running from a devel copy, not a temp installation - $ PATH="$BINDIR" $PYTHON "$BINDIR"/hg merge -r 2 + $ PATH="$BINDIR:/usr/sbin" $PYTHON "$BINDIR"/hg merge -r 2 merging f warning: conflicts during merge. merging f incomplete! (edit conflicts, then use 'hg resolve --mark') @@ -111,7 +111,7 @@ $ echo "echo fail" > false $ hg up -qC 1 - $ PATH="`pwd`:$BINDIR" $PYTHON "$BINDIR"/hg merge -r 2 + $ PATH="`pwd`:$BINDIR:/usr/sbin" $PYTHON "$BINDIR"/hg merge -r 2 merging f warning: conflicts during merge. merging f incomplete! (edit conflicts, then use 'hg resolve --mark') @@ -126,7 +126,7 @@ $ mkdir false $ hg up -qC 1 - $ PATH="`pwd`:$BINDIR" $PYTHON "$BINDIR"/hg merge -r 2 + $ PATH="`pwd`:$BINDIR:/usr/sbin" $PYTHON "$BINDIR"/hg merge -r 2 merging f warning: conflicts during merge. merging f incomplete! (edit conflicts, then use 'hg resolve --mark')
--- a/tests/test-merge9.t Mon Oct 05 10:43:16 2015 -0600 +++ b/tests/test-merge9.t Wed Oct 07 13:44:48 2015 -0500 @@ -77,7 +77,7 @@ resolve all warning $ hg resolve abort: no files or directories specified - (use --all to remerge all files) + (use --all to re-merge all unresolved files) [255] resolve all
--- a/tests/test-module-imports.t Mon Oct 05 10:43:16 2015 -0600 +++ b/tests/test-module-imports.t Wed Oct 07 13:44:48 2015 -0500 @@ -112,22 +112,5 @@ these may expose other cycles. $ hg locate 'mercurial/**.py' 'hgext/**.py' | sed 's-\\-/-g' | python "$import_checker" - - mercurial/dispatch.py mixed imports - stdlib: commands - relative: error, extensions, fancyopts, hg, hook, util - mercurial/fileset.py mixed imports - stdlib: parser - relative: error, merge, util - mercurial/revset.py mixed imports - stdlib: parser - relative: error, hbisect, phases, util - mercurial/templater.py mixed imports - stdlib: parser - relative: config, error, templatefilters, templatekw, util - mercurial/ui.py mixed imports - stdlib: formatter - relative: config, error, progress, scmutil, util - Import cycle: mercurial.cmdutil -> mercurial.context -> mercurial.subrepo -> mercurial.cmdutil Import cycle: hgext.largefiles.basestore -> hgext.largefiles.localstore -> hgext.largefiles.basestore - Import cycle: mercurial.commands -> mercurial.commandserver -> mercurial.dispatch -> mercurial.commands [1]
--- a/tests/test-mq-subrepo-svn.t Mon Oct 05 10:43:16 2015 -0600 +++ b/tests/test-mq-subrepo-svn.t Wed Oct 07 13:44:48 2015 -0500 @@ -27,10 +27,7 @@ #endif $ mkdir -p svn-project-2499/trunk - $ svn import -m 'init project' svn-project-2499 "$SVNREPOURL" - Adding svn-project-2499/trunk (glob) - - Committed revision 1. + $ svn import -qm 'init project' svn-project-2499 "$SVNREPOURL" qnew on repo w/svn subrepo $ mkrepo repo-2499-svn-subrepo
--- a/tests/test-mq.t Mon Oct 05 10:43:16 2015 -0600 +++ b/tests/test-mq.t Wed Oct 07 13:44:48 2015 -0500 @@ -39,7 +39,7 @@ By default, mq will automatically use git patches when required to avoid losing file mode changes, copy records, binary files or empty files creations - or deletions. This behaviour can be configured with: + or deletions. This behavior can be configured with: [mq] git = auto/keep/yes/no
--- a/tests/test-obsolete-tag-cache.t Mon Oct 05 10:43:16 2015 -0600 +++ b/tests/test-obsolete-tag-cache.t Wed Oct 07 13:44:48 2015 -0500 @@ -68,10 +68,10 @@ 55482a6fb4b1881fa8f746fd52cf6f096bb21c89 test1 $ hg blackbox -l 4 - 1970/01/01 00:00:00 bob> tags - 1970/01/01 00:00:00 bob> 2/2 cache hits/lookups in * seconds (glob) - 1970/01/01 00:00:00 bob> writing .hg/cache/tags2-visible with 2 tags - 1970/01/01 00:00:00 bob> tags exited 0 after * seconds (glob) + 1970/01/01 00:00:00 bob (*)> tags (glob) + 1970/01/01 00:00:00 bob (*)> 2/2 cache hits/lookups in * seconds (glob) + 1970/01/01 00:00:00 bob (*)> writing .hg/cache/tags2-visible with 2 tags (glob) + 1970/01/01 00:00:00 bob (*)> tags exited 0 after * seconds (glob) Hiding another changeset should cause the filtered hash to change @@ -87,10 +87,10 @@ 042eb6bfcc4909bad84a1cbf6eb1ddf0ab587d41 head2 $ hg blackbox -l 4 - 1970/01/01 00:00:00 bob> tags - 1970/01/01 00:00:00 bob> 1/1 cache hits/lookups in * seconds (glob) - 1970/01/01 00:00:00 bob> writing .hg/cache/tags2-visible with 1 tags - 1970/01/01 00:00:00 bob> tags exited 0 after * seconds (glob) + 1970/01/01 00:00:00 bob (*)> tags (glob) + 1970/01/01 00:00:00 bob (*)> 1/1 cache hits/lookups in * seconds (glob) + 1970/01/01 00:00:00 bob (*)> writing .hg/cache/tags2-visible with 1 tags (glob) + 1970/01/01 00:00:00 bob (*)> tags exited 0 after * seconds (glob) Resolving tags on an unfiltered repo writes a separate tags cache @@ -107,7 +107,7 @@ d75775ffbc6bca1794d300f5571272879bd280da test2 $ hg blackbox -l 4 - 1970/01/01 00:00:00 bob> --hidden tags - 1970/01/01 00:00:00 bob> 2/2 cache hits/lookups in * seconds (glob) - 1970/01/01 00:00:00 bob> writing .hg/cache/tags2 with 3 tags - 1970/01/01 00:00:00 bob> --hidden tags exited 0 after * seconds (glob) + 1970/01/01 00:00:00 bob (*)> --hidden tags (glob) + 1970/01/01 00:00:00 bob (*)> 2/2 cache hits/lookups in * seconds (glob) + 1970/01/01 00:00:00 bob (*)> writing .hg/cache/tags2 with 3 tags (glob) + 1970/01/01 00:00:00 bob (*)> --hidden tags exited 0 after * seconds (glob)
--- a/tests/test-parseindex.t Mon Oct 05 10:43:16 2015 -0600 +++ b/tests/test-parseindex.t Wed Oct 07 13:44:48 2015 -0500 @@ -60,9 +60,66 @@ $ cd .. -Test corrupted p1/p2 fields that could cause SEGV at parsers.c: +#if no-pure + +Test SEGV caused by bad revision passed to reachableroots() (issue4775): + + $ cd a -#if no-pure + $ python <<EOF + > from mercurial import changelog, scmutil + > cl = changelog.changelog(scmutil.vfs('.hg/store')) + > print 'good heads:' + > for head in [0, len(cl) - 1, -1]: + > print'%s: %r' % (head, cl.reachableroots(0, [head], [0])) + > print 'bad heads:' + > for head in [len(cl), 10000, -2, -10000, None]: + > print '%s:' % head, + > try: + > cl.reachableroots(0, [head], [0]) + > print 'uncaught buffer overflow?' + > except (IndexError, TypeError) as inst: + > 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:' + > for root in [len(cl), 10000, -2, -10000]: + > print '%s: %r' % (root, cl.reachableroots(root, [len(cl) - 1], [root])) + > print 'bad roots:' + > for root in [None]: + > print '%s:' % root, + > try: + > cl.reachableroots(root, [len(cl) - 1], [root]) + > print 'uncaught error?' + > except TypeError as inst: + > print inst + > EOF + good heads: + 0: [0] + 1: [0] + -1: [] + bad heads: + 2: head out of range + 10000: head out of range + -2: head out of range + -10000: head out of range + None: an integer is required + good roots: + 0: [0] + 1: [1] + -1: [-1] + out-of-range roots are ignored: + 2: [] + 10000: [] + -2: [] + -10000: [] + bad roots: + None: an integer is required + + $ cd .. + +Test corrupted p1/p2 fields that could cause SEGV at parsers.c: $ mkdir invalidparent $ cd invalidparent @@ -94,6 +151,8 @@ > cl = changelog.changelog(scmutil.vfs(sys.argv[1])) > n0, n1 = cl.node(0), cl.node(1) > ops = [ + > ('reachableroots', + > lambda: cl.index.reachableroots2(0, [1], [0], False)), > ('compute_phases_map_sets', lambda: cl.computephases([[0], []])), > ('index_headrevs', lambda: cl.headrevs()), > ('find_gca_candidates', lambda: cl.commonancestorsheads(n0, n1)), @@ -109,11 +168,13 @@ > EOF $ python test.py limit/.hg/store + reachableroots: parent out of range compute_phases_map_sets: parent out of range index_headrevs: parent out of range find_gca_candidates: parent out of range find_deepest: parent out of range $ python test.py segv/.hg/store + reachableroots: parent out of range compute_phases_map_sets: parent out of range index_headrevs: parent out of range find_gca_candidates: parent out of range
--- a/tests/test-pathencode.py Mon Oct 05 10:43:16 2015 -0600 +++ b/tests/test-pathencode.py Wed Oct 07 13:44:48 2015 -0500 @@ -2,7 +2,7 @@ # time it is invoked, and tests the encoding of those pathnames. # # It uses a simple probabilistic model to generate valid pathnames -# that have proven likely to expose bugs and divergent behaviour in +# that have proven likely to expose bugs and divergent behavior in # different encoding implementations. from mercurial import store
--- a/tests/test-profile.t Mon Oct 05 10:43:16 2015 -0600 +++ b/tests/test-profile.t Wed Oct 07 13:44:48 2015 -0500 @@ -14,6 +14,9 @@ $ hg --profile --config profiling.output=../out st $ grep CallCount ../out > /dev/null || cat ../out + $ hg --profile --config profiling.output=blackbox --config extensions.blackbox= st + $ grep CallCount .hg/blackbox.log > /dev/null || cat .hg/blackbox.log + $ hg --profile --config profiling.format=text st 2>../out $ grep CallCount ../out > /dev/null || cat ../out
--- a/tests/test-push-http-bundle1.t Mon Oct 05 10:43:16 2015 -0600 +++ b/tests/test-push-http-bundle1.t Wed Oct 07 13:44:48 2015 -0500 @@ -114,6 +114,36 @@ $ hg rollback repository tip rolled back to revision 0 (undo serve) +expect success, pre-d1b16a746db6 server supports the unbundle capability, but +has no parameter + + $ cat <<EOF > notcapable-unbundleparam.py + > from mercurial import extensions, httppeer + > def capable(orig, self, name): + > if name == 'unbundle': + > return True + > return orig(self, name) + > def uisetup(ui): + > extensions.wrapfunction(httppeer.httppeer, 'capable', capable) + > EOF + $ cp $HGRCPATH $HGRCPATH.orig + $ cat <<EOF >> $HGRCPATH + > [extensions] + > notcapable-unbundleparam = `pwd`/notcapable-unbundleparam.py + > EOF + $ req + pushing to http://localhost:$HGPORT/ + searching for changes + remote: adding changesets + remote: adding manifests + remote: adding file changes + remote: added 1 changesets with 1 changes to 1 files + remote: changegroup hook: * (glob) + % serve errors + $ hg rollback + repository tip rolled back to revision 0 (undo serve) + $ mv $HGRCPATH.orig $HGRCPATH + expect push success, phase change failure $ cat > .hg/hgrc <<EOF
--- a/tests/test-rebase-named-branches.t Mon Oct 05 10:43:16 2015 -0600 +++ b/tests/test-rebase-named-branches.t Wed Oct 07 13:44:48 2015 -0500 @@ -97,7 +97,6 @@ $ hg rebase -s dev-one -d 0 --keepbranches rebasing 5:643fc9128048 "dev-one named branch" - note: rebase of 5:643fc9128048 created no changes to commit rebasing 6:24de4aff8e28 "F" rebasing 7:4b988a958030 "G" rebasing 8:31d0e4ba75e6 "H" @@ -105,13 +104,15 @@ saved backup bundle to $TESTTMP/a1/.hg/strip-backup/643fc9128048-c4ee9ef5-backup.hg (glob) $ hg tglog - @ 8: 'dev-two named branch' dev-two + @ 9: 'dev-two named branch' dev-two | - o 7: 'H' + o 8: 'H' | - | o 6: 'G' + | o 7: 'G' |/| - o | 5: 'F' + o | 6: 'F' + | | + o | 5: 'dev-one named branch' dev-one | | | o 4: 'E' |/ @@ -125,20 +126,23 @@ $ hg update 3 3 files updated, 0 files merged, 3 files removed, 0 files unresolved - $ hg branch dev-one + $ hg branch -f dev-one marked working directory as branch dev-one $ hg ci -m 'dev-one named branch' + created new head $ hg tglog - @ 9: 'dev-one named branch' dev-one + @ 10: 'dev-one named branch' dev-one | - | o 8: 'dev-two named branch' dev-two + | o 9: 'dev-two named branch' dev-two + | | + | o 8: 'H' | | - | o 7: 'H' - | | - | | o 6: 'G' + | | o 7: 'G' | |/| - | o | 5: 'F' + | o | 6: 'F' + | | | + | o | 5: 'dev-one named branch' dev-one | | | | | o 4: 'E' | |/ @@ -151,11 +155,13 @@ o 0: 'A' $ hg rebase -b 'max(branch("dev-two"))' -d dev-one --keepbranches - rebasing 5:77854864208c "F" - rebasing 6:63b4f9c788a1 "G" - rebasing 7:87861e68abd3 "H" - rebasing 8:ec00d4e0efca "dev-two named branch" - saved backup bundle to $TESTTMP/a1/.hg/strip-backup/77854864208c-74d59436-backup.hg (glob) + rebasing 5:bc8139ee757c "dev-one named branch" + note: rebase of 5:bc8139ee757c created no changes to commit + rebasing 6:42aa3cf0fa7a "F" + rebasing 7:1a1e6f72ec38 "G" + rebasing 8:904590360559 "H" + rebasing 9:59c2e59309fe "dev-two named branch" + saved backup bundle to $TESTTMP/a1/.hg/strip-backup/bc8139ee757c-f11c1080-backup.hg (glob) $ hg tglog o 9: 'dev-two named branch' dev-two @@ -180,21 +186,22 @@ $ hg rebase -s 'max(branch("dev-one"))' -d 0 --keepbranches rebasing 5:643fc9128048 "dev-one named branch" - note: rebase of 5:643fc9128048 created no changes to commit - rebasing 6:05584c618d45 "F" - rebasing 7:471695f5257d "G" - rebasing 8:8382a539a2df "H" - rebasing 9:11f718458b32 "dev-two named branch" (tip) - saved backup bundle to $TESTTMP/a1/.hg/strip-backup/643fc9128048-177f3c5c-backup.hg (glob) + rebasing 6:679f28760620 "F" + rebasing 7:549f007a9f5f "G" + rebasing 8:12b2bc666e20 "H" + rebasing 9:71325f8bc082 "dev-two named branch" (tip) + saved backup bundle to $TESTTMP/a1/.hg/strip-backup/643fc9128048-6cdd1a52-backup.hg (glob) $ hg tglog - o 8: 'dev-two named branch' dev-two + o 9: 'dev-two named branch' dev-two | - o 7: 'H' + o 8: 'H' | - | o 6: 'G' + | o 7: 'G' |/| - o | 5: 'F' + o | 6: 'F' + | | + @ | 5: 'dev-one named branch' dev-one | | | o 4: 'E' |/ @@ -204,61 +211,66 @@ | | | o 1: 'B' |/ - @ 0: 'A' + o 0: 'A' + $ hg up -r 0 > /dev/null Rebasing descendant onto ancestor across different named branches - $ hg rebase -s 1 -d 8 --keepbranches + $ hg rebase -s 1 -d 9 --keepbranches rebasing 1:42ccdea3bb16 "B" rebasing 2:5fddd98957c8 "C" rebasing 3:32af7686d403 "D" saved backup bundle to $TESTTMP/a1/.hg/strip-backup/42ccdea3bb16-3cb021d3-backup.hg (glob) $ hg tglog - o 8: 'D' + o 9: 'D' + | + o 8: 'C' | - o 7: 'C' + o 7: 'B' | - o 6: 'B' + o 6: 'dev-two named branch' dev-two | - o 5: 'dev-two named branch' dev-two - | - o 4: 'H' + o 5: 'H' | - | o 3: 'G' + | o 4: 'G' |/| - o | 2: 'F' + o | 3: 'F' + | | + o | 2: 'dev-one named branch' dev-one | | | o 1: 'E' |/ @ 0: 'A' - $ hg rebase -s 4 -d 5 + $ hg rebase -s 5 -d 6 abort: source is ancestor of destination [255] - $ hg rebase -s 5 -d 4 - rebasing 5:32d3b0de7f37 "dev-two named branch" - rebasing 6:580fcd9fd48f "B" - rebasing 7:32aba0402ed2 "C" - rebasing 8:e4787b575338 "D" (tip) - saved backup bundle to $TESTTMP/a1/.hg/strip-backup/32d3b0de7f37-c37815ca-backup.hg (glob) + $ hg rebase -s 6 -d 5 + rebasing 6:3944801ae4ea "dev-two named branch" + rebasing 7:3bdb949809d9 "B" + rebasing 8:a0d543090fa4 "C" + rebasing 9:e9f862ce8bad "D" (tip) + saved backup bundle to $TESTTMP/a1/.hg/strip-backup/3944801ae4ea-fb46ed74-backup.hg (glob) $ hg tglog - o 8: 'D' + o 9: 'D' + | + o 8: 'C' | - o 7: 'C' + o 7: 'B' | - o 6: 'B' + o 6: 'dev-two named branch' | - o 5: 'dev-two named branch' - | - o 4: 'H' + o 5: 'H' | - | o 3: 'G' + | o 4: 'G' |/| - o | 2: 'F' + o | 3: 'F' + | | + o | 2: 'dev-one named branch' dev-one | | | o 1: 'E' |/ @@ -272,13 +284,13 @@ $ hg ci -m 'create b' $ hg ci -m 'close b' --close $ hg rebase -b 8 -d b - reopening closed branch head ea9de14a36c6 - rebasing 4:86693275b2ef "H" - rebasing 5:2149726d0970 "dev-two named branch" - rebasing 6:81e55225e95d "B" - rebasing 7:09eda3dc3195 "C" - rebasing 8:31298fc9d159 "D" - saved backup bundle to $TESTTMP/a1/.hg/strip-backup/86693275b2ef-f9fcf4e2-backup.hg (glob) + reopening closed branch head 2b586e70108d + rebasing 5:8e279d293175 "H" + rebasing 6:c57724c84928 "dev-two named branch" + rebasing 7:160b0930ccc6 "B" + rebasing 8:810110211f50 "C" + rebasing 9:e522577ccdbd "D" + saved backup bundle to $TESTTMP/a1/.hg/strip-backup/8e279d293175-b023e27c-backup.hg (glob) $ cd ..
--- a/tests/test-rebase-obsolete.t Mon Oct 05 10:43:16 2015 -0600 +++ b/tests/test-rebase-obsolete.t Wed Oct 07 13:44:48 2015 -0500 @@ -203,10 +203,9 @@ |/ o 0:cd010b8cd998 A - $ hg rebase --source 'desc(B)' --dest 'tip' + $ hg rebase --source 'desc(B)' --dest 'tip' --config experimental.rebaseskipobsolete=True rebasing 8:8877864f1edb "B" - rebasing 9:08483444fef9 "D" - note: rebase of 9:08483444fef9 created no changes to commit + note: not rebasing 9:08483444fef9 "D", already in destination as 11:4596109a6a43 "D" rebasing 10:5ae4c968c6ac "C" $ hg debugobsolete 42ccdea3bb16d28e1848c95fe2e44c000f3f21b1 0 {cd010b8cd998f3981a5a8115f94f8da4ab506089} (*) {'user': 'test'} (glob) @@ -214,7 +213,6 @@ 32af7686d403cf45b5d95f2d70cebea587ac806a 0 {5fddd98957c8a54a4d436dfe1da9d87f21a1b97b} (*) {'user': 'test'} (glob) 08483444fef91d6224f6655ee586a65d263ad34c 4596109a6a4328c398bde3a4a3b6737cfade3003 0 (*) {'user': 'test'} (glob) 8877864f1edb05d0e07dc4ba77b67a80a7b86672 462a34d07e599b87ea08676a449373fe4e2e1347 0 (*) {'user': 'test'} (glob) - 08483444fef91d6224f6655ee586a65d263ad34c 0 {8877864f1edb05d0e07dc4ba77b67a80a7b86672} (*) {'user': 'test'} (glob) 5ae4c968c6aca831df823664e706c9d4aa34473d 98f6af4ee9539e14da4465128f894c274900b6e5 0 (*) {'user': 'test'} (glob) $ hg log --rev 'divergent()' $ hg log -G @@ -540,3 +538,55 @@ |/ o 0:cd010b8cd998 A + $ hg up 14 -C + 0 files updated, 0 files merged, 1 files removed, 0 files unresolved + $ echo "K" > K + $ hg add K + $ hg commit --amend -m "K" + $ echo "L" > L + $ hg add L + $ hg commit -m "L" + $ hg up '.^' + 0 files updated, 0 files merged, 1 files removed, 0 files unresolved + $ echo "M" > M + $ hg add M + $ hg commit --amend -m "M" + $ hg log -G + @ 20:bfaedf8eb73b M + | + | o 18:97219452e4bd L + | | + | x 17:fc37a630c901 K + |/ + | o 15:5ae8a643467b J + | | + | x 14:9ad579b4a5de I + |/ + | o 12:acd174b7ab39 I + | | + | o 11:6c11a6218c97 H + | | + o | 10:b5313c85b22e D + |/ + | o 8:53a6a128b2b7 M + | |\ + | | x 7:02de42196ebe H + | | | + o---+ 6:eea13746799a G + | | | + | | o 5:24b6387c8c8c F + | | | + o---+ 4:9520eea781bc E + / / + x | 3:32af7686d403 D + | | + o | 2:5fddd98957c8 C + | | + o | 1:42ccdea3bb16 B + |/ + o 0:cd010b8cd998 A + + $ hg rebase -s 14 -d 18 --config experimental.rebaseskipobsolete=True + note: not rebasing 14:9ad579b4a5de "I", already in destination as 17:fc37a630c901 "K" + rebasing 15:5ae8a643467b "J" +
--- a/tests/test-rebase-parameters.t Mon Oct 05 10:43:16 2015 -0600 +++ b/tests/test-rebase-parameters.t Wed Oct 07 13:44:48 2015 -0500 @@ -485,17 +485,16 @@ $ hg resolve -m c2 (no more unresolved files) $ hg rebase -c --tool internal:fail - tool option will be ignored rebasing 2:e4e3f3546619 "c2b" (tip) note: rebase of 2:e4e3f3546619 created no changes to commit saved backup bundle to $TESTTMP/b3/.hg/strip-backup/e4e3f3546619-b0841178-backup.hg (glob) $ hg rebase -i - abort: interactive history editing is supported by the 'histedit' extension (see "hg help histedit") + abort: interactive history editing is supported by the 'histedit' extension (see "hg --config extensions.histedit= help -e histedit") [255] $ hg rebase --interactive - abort: interactive history editing is supported by the 'histedit' extension (see "hg help histedit") + abort: interactive history editing is supported by the 'histedit' extension (see "hg --config extensions.histedit= help -e histedit") [255] $ cd ..
--- a/tests/test-rebase-pull.t Mon Oct 05 10:43:16 2015 -0600 +++ b/tests/test-rebase-pull.t Wed Oct 07 13:44:48 2015 -0500 @@ -185,7 +185,7 @@ o 0: 'C1' $ cd ../c - $ hg pull --rebase --config experimental.bundle2-exp=True --config experimental.strip-bundle2-version=02 + $ hg pull --rebase pulling from $TESTTMP/a (glob) searching for changes adding changesets
--- a/tests/test-requires.t Mon Oct 05 10:43:16 2015 -0600 +++ b/tests/test-requires.t Wed Oct 07 13:44:48 2015 -0500 @@ -10,12 +10,12 @@ $ echo indoor-pool > .hg/requires $ hg tip abort: repository requires features unknown to this Mercurial: indoor-pool! - (see http://mercurial.selenic.com/wiki/MissingRequirement for more information) + (see https://mercurial-scm.org/wiki/MissingRequirement for more information) [255] $ echo outdoor-pool >> .hg/requires $ hg tip abort: repository requires features unknown to this Mercurial: indoor-pool outdoor-pool! - (see http://mercurial.selenic.com/wiki/MissingRequirement for more information) + (see https://mercurial-scm.org/wiki/MissingRequirement for more information) [255] $ cd .. @@ -63,7 +63,7 @@ $ hg clone supported clone-dst abort: repository requires features unknown to this Mercurial: featuresetup-test! - (see http://mercurial.selenic.com/wiki/MissingRequirement for more information) + (see https://mercurial-scm.org/wiki/MissingRequirement for more information) [255] $ hg clone --pull supported clone-dst abort: required features are not supported in the destination: featuresetup-test
--- a/tests/test-resolve.t Mon Oct 05 10:43:16 2015 -0600 +++ b/tests/test-resolve.t Wed Oct 07 13:44:48 2015 -0500 @@ -129,7 +129,7 @@ resolve without arguments should suggest --all $ hg resolve abort: no files or directories specified - (use --all to remerge all files) + (use --all to re-merge all unresolved files) [255] resolve --all should re-merge all unresolved files
--- a/tests/test-revset.t Mon Oct 05 10:43:16 2015 -0600 +++ b/tests/test-revset.t Wed Oct 07 13:44:48 2015 -0500 @@ -141,7 +141,7 @@ ('symbol', '3') ('symbol', '6')) * set: - <baseset [3, 5, 6]> + <baseset+ [3, 5, 6]> 3 5 6 @@ -197,11 +197,53 @@ <filteredset <baseset [7]>> 7 - $ try -- '-a-b-c-' # complains - hg: parse error at 7: not a prefix: end - [255] - $ log -a-b-c- # succeeds with fallback + +names that should be caught by fallback mechanism + + $ try -- '-a-b-c-' + ('symbol', '-a-b-c-') + * set: + <baseset [4]> + 4 + $ log -a-b-c- + 4 + $ try '+a+b+c+' + ('symbol', '+a+b+c+') + * set: + <baseset [3]> + 3 + $ try '+a+b+c+:' + (rangepost + ('symbol', '+a+b+c+')) + * set: + <spanset+ 3:9> + 3 4 + 5 + 6 + 7 + 8 + 9 + $ try ':+a+b+c+' + (rangepre + ('symbol', '+a+b+c+')) + * set: + <spanset+ 0:3> + 0 + 1 + 2 + 3 + $ try -- '-a-b-c-:+a+b+c+' + (range + ('symbol', '-a-b-c-') + ('symbol', '+a+b+c+')) + * set: + <spanset- 3:4> + 4 + 3 + $ log '-a-b-c-:+a+b+c+' + 4 + 3 $ try -- -a-b-c--a # complains (minus @@ -311,6 +353,9 @@ $ log 'date(' hg: parse error at 5: not a prefix: end [255] + $ log 'date("\xy")' + hg: parse error: invalid \x escape + [255] $ log 'date(tip)' abort: invalid date: 'tip' [255] @@ -949,7 +994,7 @@ ('symbol', '4'))) * set: <addset - <baseset [5, 3, 1]>, + <baseset- [1, 3, 5]>, <generatorset+>> 5 3 @@ -972,7 +1017,7 @@ * set: <addset+ <generatorset+>, - <baseset [5, 3, 1]>> + <baseset- [1, 3, 5]>> 0 1 2 @@ -1473,10 +1518,16 @@ (single rev) $ hg diff -r 'tip^' -r 'tip^' - $ hg diff -r 'tip^::tip^ or tip^' + $ hg diff -r 'tip^:tip^' (single rev that does not looks like a range) + $ hg diff -r 'tip^::tip^ or tip^' + diff -r d5d0dcbdc4d9 .hgtags + --- /dev/null Thu Jan 01 00:00:00 1970 +0000 + +++ b/.hgtags * (glob) + @@ -0,0 +1,1 @@ + +e0cc66ef77e8b6f711815af4e001a6594fde3ba5 1.0 $ hg diff -r 'tip^ or tip^' diff -r d5d0dcbdc4d9 .hgtags --- /dev/null Thu Jan 01 00:00:00 1970 +0000
--- a/tests/test-run-tests.t Mon Oct 05 10:43:16 2015 -0600 +++ b/tests/test-run-tests.t Wed Oct 07 13:44:48 2015 -0500 @@ -393,6 +393,14 @@ python hash seed: * (glob) [1] +test --tmpdir support + $ run-tests.py --with-hg=`which hg` --tmpdir=$TESTTMP/keep test-success.t + + Keeping testtmp dir: $TESTTMP/keep/child1/test-success.t (glob) + Keeping threadtmp dir: $TESTTMP/keep/child1 (glob) + . + # Ran 1 tests, 0 skipped, 0 warned, 0 failed. + test for --time ================== @@ -580,7 +588,7 @@ > $ echo foo > foo > EOF - $ run-tests.py test-hghave.t + $ run-tests.py $HGTEST_RUN_TESTS_PURE test-hghave.t . # Ran 1 tests, 0 skipped, 0 warned, 0 failed. @@ -599,7 +607,7 @@ > # > # check-code - a style and portability checker for Mercurial > EOF - $ run-tests.py test-runtestdir.t + $ run-tests.py $HGTEST_RUN_TESTS_PURE test-runtestdir.t . # Ran 1 tests, 0 skipped, 0 warned, 0 failed. @@ -616,8 +624,22 @@ > $ custom-command.sh > hello world > EOF - $ run-tests.py test-testdir-path.t + $ run-tests.py $HGTEST_RUN_TESTS_PURE test-testdir-path.t . # Ran 1 tests, 0 skipped, 0 warned, 0 failed. #endif + +test support for --allow-slow-tests + $ cat > test-very-slow-test.t <<EOF + > #require slow + > $ echo pass + > pass + > EOF + $ run-tests.py $HGTEST_RUN_TESTS_PURE test-very-slow-test.t + s + Skipped test-very-slow-test.t: skipped + # Ran 0 tests, 1 skipped, 0 warned, 0 failed. + $ run-tests.py $HGTEST_RUN_TESTS_PURE --allow-slow-tests test-very-slow-test.t + . + # Ran 1 tests, 0 skipped, 0 warned, 0 failed.
--- a/tests/test-ssh-bundle1.t Mon Oct 05 10:43:16 2015 -0600 +++ b/tests/test-ssh-bundle1.t Wed Oct 07 13:44:48 2015 -0500 @@ -43,14 +43,14 @@ repo not found error $ hg clone -e "python \"$TESTDIR/dummyssh\"" ssh://user@dummy/nonexistent local - remote: abort: there is no Mercurial repository here (.hg not found)! + remote: abort: repository nonexistent not found! abort: no suitable response from remote hg! [255] non-existent absolute path $ hg clone -e "python \"$TESTDIR/dummyssh\"" ssh://user@dummy//`pwd`/nonexistent local - remote: abort: there is no Mercurial repository here (.hg not found)! + remote: abort: repository /$TESTTMP/nonexistent not found! abort: no suitable response from remote hg! [255] @@ -128,7 +128,7 @@ $ hg pull -e "python \"$TESTDIR/dummyssh\"" ssh://user@dummy/doesnotexist pulling from ssh://user@dummy/doesnotexist - remote: abort: there is no Mercurial repository here (.hg not found)! + remote: abort: repository doesnotexist not found! abort: no suitable response from remote hg! [255]
--- a/tests/test-ssh.t Mon Oct 05 10:43:16 2015 -0600 +++ b/tests/test-ssh.t Wed Oct 07 13:44:48 2015 -0500 @@ -34,14 +34,14 @@ repo not found error $ hg clone -e "python \"$TESTDIR/dummyssh\"" ssh://user@dummy/nonexistent local - remote: abort: there is no Mercurial repository here (.hg not found)! + remote: abort: repository nonexistent not found! abort: no suitable response from remote hg! [255] non-existent absolute path - $ hg clone -e "python \"$TESTDIR/dummyssh\"" ssh://user@dummy//`pwd`/nonexistent local - remote: abort: there is no Mercurial repository here (.hg not found)! + $ hg clone -e "python \"$TESTDIR/dummyssh\"" ssh://user@dummy/`pwd`/nonexistent local + remote: abort: repository $TESTTMP/nonexistent not found! abort: no suitable response from remote hg! [255] @@ -119,7 +119,7 @@ $ hg pull -e "python \"$TESTDIR/dummyssh\"" ssh://user@dummy/doesnotexist pulling from ssh://user@dummy/doesnotexist - remote: abort: there is no Mercurial repository here (.hg not found)! + remote: abort: repository doesnotexist not found! abort: no suitable response from remote hg! [255] @@ -471,7 +471,7 @@ $ cat dummylog Got arguments 1:user@dummy 2:hg -R nonexistent serve --stdio - Got arguments 1:user@dummy 2:hg -R /$TESTTMP/nonexistent serve --stdio + Got arguments 1:user@dummy 2:hg -R $TESTTMP/nonexistent serve --stdio Got arguments 1:user@dummy 2:hg -R remote serve --stdio Got arguments 1:user@dummy 2:hg -R local-stream serve --stdio Got arguments 1:user@dummy 2:hg -R remote serve --stdio
--- a/tests/test-strip.t Mon Oct 05 10:43:16 2015 -0600 +++ b/tests/test-strip.t Wed Oct 07 13:44:48 2015 -0500 @@ -197,17 +197,8 @@ date: Thu Jan 01 00:00:00 1970 +0000 summary: c - $ hg --config experimental.bundle2-exp=True --config experimental.strip-bundle2-version=INVALID strip 4 - 1 files updated, 0 files merged, 0 files removed, 0 files unresolved - unknown strip-bundle2-version value 'INVALID'; should be one of ['01', '02'] - saved backup bundle to $TESTTMP/test/.hg/strip-backup/264128213d29-0b39d6bf-backup.hg (glob) - $ hg debugbundle .hg/strip-backup/* - 264128213d290d868c54642d13aeaa3675551a78 - $ restore - $ hg up -C 4 - 1 files updated, 0 files merged, 0 files removed, 0 files unresolved - $ hg --config experimental.bundle2-exp=True --config experimental.strip-bundle2-version=02 --traceback strip 4 + $ hg --traceback strip 4 1 files updated, 0 files merged, 0 files removed, 0 files unresolved saved backup bundle to $TESTTMP/test/.hg/strip-backup/264128213d29-0b39d6bf-backup.hg (glob) $ hg parents @@ -217,35 +208,7 @@ summary: b $ hg debugbundle .hg/strip-backup/* - Stream params: {} - changegroup -- "{'version': '02'}" - 264128213d290d868c54642d13aeaa3675551a78 - $ hg incoming .hg/strip-backup/* - comparing with .hg/strip-backup/264128213d29-0b39d6bf-backup.hg - searching for changes - changeset: 4:264128213d29 - tag: tip - parent: 1:ef3a871183d7 - user: test - date: Thu Jan 01 00:00:00 1970 +0000 - summary: c - - $ restore - $ hg up -C 4 - 1 files updated, 0 files merged, 0 files removed, 0 files unresolved - $ hg --config experimental.bundle2-exp=True --config experimental.strip-bundle2-version=02 --traceback strip 4 - 1 files updated, 0 files merged, 0 files removed, 0 files unresolved - saved backup bundle to $TESTTMP/test/.hg/strip-backup/264128213d29-0b39d6bf-backup.hg (glob) - $ hg parents - changeset: 1:ef3a871183d7 - user: test - date: Thu Jan 01 00:00:00 1970 +0000 - summary: b - - $ hg debugbundle .hg/strip-backup/* - Stream params: {} - changegroup -- "{'version': '02'}" - 264128213d290d868c54642d13aeaa3675551a78 + 264128213d290d868c54642d13aeaa3675551a78 $ hg pull .hg/strip-backup/* pulling from .hg/strip-backup/264128213d29-0b39d6bf-backup.hg searching for changes @@ -697,7 +660,7 @@ Test that we only bundle the stripped changesets (issue4736) ------------------------------------------------------------ -initialisation (previous repo is empty anyway) +initialization (previous repo is empty anyway) $ hg init issue4736 $ cd issue4736 @@ -844,7 +807,7 @@ > EOF $ hg strip tip --config extensions.crash=$TESTTMP/crashstrip.py saved backup bundle to $TESTTMP/issue4736/.hg/strip-backup/5c51d8d6557d-70daef06-backup.hg (glob) - strip failed, full bundle stored in '$TESTTMP/issue4736/.hg/strip-backup/5c51d8d6557d-70daef06-backup.hg' + strip failed, full bundle stored in '$TESTTMP/issue4736/.hg/strip-backup/5c51d8d6557d-70daef06-backup.hg' (glob) abort: boom [255]
--- a/tests/test-subrepo-deep-nested-change.t Mon Oct 05 10:43:16 2015 -0600 +++ b/tests/test-subrepo-deep-nested-change.t Wed Oct 07 13:44:48 2015 -0500 @@ -466,7 +466,7 @@ The local repo enables largefiles if a largefiles repo is cloned $ hg showconfig extensions abort: repository requires features unknown to this Mercurial: largefiles! - (see http://mercurial.selenic.com/wiki/MissingRequirement for more information) + (see https://mercurial-scm.org/wiki/MissingRequirement for more information) [255] $ hg --config extensions.largefiles= clone -qU . ../lfclone $ cat ../lfclone/.hg/hgrc
--- a/tests/test-subrepo-svn.t Mon Oct 05 10:43:16 2015 -0600 +++ b/tests/test-subrepo-svn.t Wed Oct 07 13:44:48 2015 -0500 @@ -7,6 +7,10 @@ $ SVNREPOURL=file://`$PYTHON -c "import urllib, sys; sys.stdout.write(urllib.quote(sys.argv[1]))" "$SVNREPOPATH"` #endif + $ filter_svn_output () { + > egrep -v 'Committing|Transmitting|Updating' || true + > } + create subversion repo $ WCROOT="`pwd`/svn-wc" @@ -24,21 +28,12 @@ $ svn add externals A externals A externals/other (glob) - $ svn ci -m 'Add alpha' - Adding externals - Adding externals/other (glob) - Adding src - Adding src/alpha (glob) - Transmitting file data .. - Committed revision 1. + $ svn ci -qm 'Add alpha' $ svn up -q $ echo "externals -r1 $SVNREPOURL/externals" > extdef $ svn propset -F extdef svn:externals src property 'svn:externals' set on 'src' - $ svn ci -m 'Setting externals' - Sending src - - Committed revision 2. + $ svn ci -qm 'Setting externals' $ cd .. create hg repo @@ -98,10 +93,9 @@ commit: 1 modified, 1 subrepos update: (current) phases: 2 draft - $ hg commit --subrepos -m 'Message!' | grep -v Updating + $ hg commit --subrepos -m 'Message!' | filter_svn_output committing subrepository s Sending*s/alpha (glob) - Transmitting file data . Committed revision 3. Fetching external item into '*s/externals'* (glob) @@ -128,9 +122,7 @@ add an unrelated revision in svn and update the subrepo to without bringing any changes. - $ svn mkdir "$SVNREPOURL/unrelated" -m 'create unrelated' - - Committed revision 4. + $ svn mkdir "$SVNREPOURL/unrelated" -qm 'create unrelated' $ svn up -q s $ hg sum parent: 2:* tip (glob) @@ -153,10 +145,7 @@ $ echo xyz >> alpha $ svn propset svn:mime-type 'text/xml' alpha property 'svn:mime-type' set on 'alpha' - $ svn ci -m 'amend a from svn' - Sending *alpha (glob) - Transmitting file data . - Committed revision 5. + $ svn ci -qm 'amend a from svn' $ cd ../../sub/t this commit from hg will fail @@ -429,11 +418,7 @@ $ svn add dir A dir A dir/epsilon.py (glob) - $ svn ci -m 'Add dir/epsilon.py' - Adding *dir (glob) - Adding *dir/epsilon.py (glob) - Transmitting file data . - Committed revision 6. + $ svn ci -qm 'Add dir/epsilon.py' $ cd ../.. $ hg init rebaserepo $ cd rebaserepo @@ -495,41 +480,26 @@ Point to a Subversion branch which has since been deleted and recreated First, create that condition in the repository. - $ hg ci --subrepos -m cleanup | grep -v Updating + $ hg ci --subrepos -m cleanup | filter_svn_output committing subrepository obstruct Sending obstruct/other (glob) - Transmitting file data . Committed revision 7. At revision 7. - $ svn mkdir -m "baseline" $SVNREPOURL/trunk - - Committed revision 8. - $ svn copy -m "initial branch" $SVNREPOURL/trunk $SVNREPOURL/branch - - Committed revision 9. + $ svn mkdir -qm "baseline" $SVNREPOURL/trunk + $ svn copy -qm "initial branch" $SVNREPOURL/trunk $SVNREPOURL/branch $ svn co --quiet "$SVNREPOURL"/branch tempwc $ cd tempwc $ echo "something old" > somethingold $ svn add somethingold A somethingold - $ svn ci -m 'Something old' - Adding somethingold - Transmitting file data . - Committed revision 10. - $ svn rm -m "remove branch" $SVNREPOURL/branch - - Committed revision 11. - $ svn copy -m "recreate branch" $SVNREPOURL/trunk $SVNREPOURL/branch - - Committed revision 12. + $ svn ci -qm 'Something old' + $ svn rm -qm "remove branch" $SVNREPOURL/branch + $ svn copy -qm "recreate branch" $SVNREPOURL/trunk $SVNREPOURL/branch $ svn up -q $ echo "something new" > somethingnew $ svn add somethingnew A somethingnew - $ svn ci -m 'Something new' - Adding somethingnew - Transmitting file data . - Committed revision 13. + $ svn ci -qm 'Something new' $ cd .. $ rm -rf tempwc $ svn co "$SVNREPOURL/branch"@10 recreated @@ -610,15 +580,8 @@ A trunk/subdir (glob) A trunk/subdir/a (glob) A branches - $ svn ci -m addsubdir - Adding branches - Adding trunk/subdir (glob) - Adding trunk/subdir/a (glob) - Transmitting file data . - Committed revision 14. - $ svn cp -m branchtrunk $SVNREPOURL/trunk $SVNREPOURL/branches/somebranch - - Committed revision 15. + $ svn ci -qm addsubdir + $ svn cp -qm branchtrunk $SVNREPOURL/trunk $SVNREPOURL/branches/somebranch $ cd .. $ hg init repo2 @@ -652,14 +615,7 @@ A sub A sub/.hg (glob) A sub/.hg/hgrc (glob) - $ svn ci -m 'add .hg/hgrc to be sanitized at hg update' - Adding .hg - Adding .hg/hgrc (glob) - Adding sub - Adding sub/.hg (glob) - Adding sub/.hg/hgrc (glob) - Transmitting file data .. - Committed revision 16. + $ svn ci -qm 'add .hg/hgrc to be sanitized at hg update' $ svn up -q $ cd .. $ hg commit -S -m 'commit with svn revision including .hg/hgrc'
--- a/tests/test-tag.t Mon Oct 05 10:43:16 2015 -0600 +++ b/tests/test-tag.t Wed Oct 07 13:44:48 2015 -0500 @@ -461,6 +461,13 @@ $ hg log -r '.' -T "{latesttag % '{latesttag}\n'}" t4 t6 + $ hg log -r '.' -T "{latesttag('t4') % 'T: {tag}, C: {changes}, D: {distance}\n'}" + T: t4, C: 2, D: 2 + $ hg log -r '.' -T "{latesttag('re:\d') % 'T: {tag}, C: {changes}, D: {distance}\n'}" + T: t4, C: 2, D: 2 + T: t6, C: 2, D: 2 + $ hg log -r . -T '{join(latesttag(), "*")}\n' + t4*t6 $ hg ci -A -m4 adding f4 $ hg log -r 'wdir()' -T "{changessincelatesttag} changes since {latesttag}\n"
--- a/tests/test-tags.t Mon Oct 05 10:43:16 2015 -0600 +++ b/tests/test-tags.t Wed Oct 07 13:44:48 2015 -0500 @@ -137,11 +137,11 @@ $ hg identify b9154636be93 tip $ hg blackbox -l 5 - 1970/01/01 00:00:00 bob> identify - 1970/01/01 00:00:00 bob> writing 48 bytes to cache/hgtagsfnodes1 - 1970/01/01 00:00:00 bob> 0/1 cache hits/lookups in * seconds (glob) - 1970/01/01 00:00:00 bob> writing .hg/cache/tags2-visible with 1 tags - 1970/01/01 00:00:00 bob> identify exited 0 after ?.?? seconds (glob) + 1970/01/01 00:00:00 bob (*)> identify (glob) + 1970/01/01 00:00:00 bob (*)> writing 48 bytes to cache/hgtagsfnodes1 (glob) + 1970/01/01 00:00:00 bob (*)> 0/1 cache hits/lookups in * seconds (glob) + 1970/01/01 00:00:00 bob (*)> writing .hg/cache/tags2-visible with 1 tags (glob) + 1970/01/01 00:00:00 bob (*)> identify exited 0 after ?.?? seconds (glob) Failure to acquire lock results in no write @@ -150,11 +150,11 @@ $ hg identify b9154636be93 tip $ hg blackbox -l 5 - 1970/01/01 00:00:00 bob> identify - 1970/01/01 00:00:00 bob> not writing .hg/cache/hgtagsfnodes1 because lock cannot be acquired - 1970/01/01 00:00:00 bob> 0/1 cache hits/lookups in * seconds (glob) - 1970/01/01 00:00:00 bob> writing .hg/cache/tags2-visible with 1 tags - 1970/01/01 00:00:00 bob> identify exited 0 after * seconds (glob) + 1970/01/01 00:00:00 bob (*)> identify (glob) + 1970/01/01 00:00:00 bob (*)> not writing .hg/cache/hgtagsfnodes1 because lock cannot be acquired (glob) + 1970/01/01 00:00:00 bob (*)> 0/1 cache hits/lookups in * seconds (glob) + 1970/01/01 00:00:00 bob (*)> writing .hg/cache/tags2-visible with 1 tags (glob) + 1970/01/01 00:00:00 bob (*)> identify exited 0 after * seconds (glob) $ fnodescacheexists no fnodes cache @@ -349,11 +349,11 @@ bar 1:78391a272241 $ hg blackbox -l 5 - 1970/01/01 00:00:00 bob> tags - 1970/01/01 00:00:00 bob> writing 24 bytes to cache/hgtagsfnodes1 - 1970/01/01 00:00:00 bob> 2/3 cache hits/lookups in * seconds (glob) - 1970/01/01 00:00:00 bob> writing .hg/cache/tags2-visible with 1 tags - 1970/01/01 00:00:00 bob> tags exited 0 after * seconds (glob) + 1970/01/01 00:00:00 bob (*)> tags (glob) + 1970/01/01 00:00:00 bob (*)> writing 24 bytes to cache/hgtagsfnodes1 (glob) + 1970/01/01 00:00:00 bob (*)> 2/3 cache hits/lookups in * seconds (glob) + 1970/01/01 00:00:00 bob (*)> writing .hg/cache/tags2-visible with 1 tags (glob) + 1970/01/01 00:00:00 bob (*)> tags exited 0 after * seconds (glob) #if unix-permissions no-root Errors writing to .hgtags fnodes cache are silently ignored @@ -369,11 +369,11 @@ bar 1:78391a272241 $ hg blackbox -l 5 - 1970/01/01 00:00:00 bob> tags - 1970/01/01 00:00:00 bob> couldn't write cache/hgtagsfnodes1: [Errno 13] Permission denied: '$TESTTMP/t2/.hg/cache/hgtagsfnodes1' - 1970/01/01 00:00:00 bob> 2/3 cache hits/lookups in * seconds (glob) - 1970/01/01 00:00:00 bob> writing .hg/cache/tags2-visible with 1 tags - 1970/01/01 00:00:00 bob> tags exited 0 after * seconds (glob) + 1970/01/01 00:00:00 bob (*)> tags (glob) + 1970/01/01 00:00:00 bob (*)> couldn't write cache/hgtagsfnodes1: [Errno 13] Permission denied: '$TESTTMP/t2/.hg/cache/hgtagsfnodes1' (glob) + 1970/01/01 00:00:00 bob (*)> 2/3 cache hits/lookups in * seconds (glob) + 1970/01/01 00:00:00 bob (*)> writing .hg/cache/tags2-visible with 1 tags (glob) + 1970/01/01 00:00:00 bob (*)> tags exited 0 after * seconds (glob) $ chmod a+w .hg/cache/hgtagsfnodes1 @@ -383,11 +383,11 @@ bar 1:78391a272241 $ hg blackbox -l 5 - 1970/01/01 00:00:00 bob> tags - 1970/01/01 00:00:00 bob> writing 24 bytes to cache/hgtagsfnodes1 - 1970/01/01 00:00:00 bob> 2/3 cache hits/lookups in * seconds (glob) - 1970/01/01 00:00:00 bob> writing .hg/cache/tags2-visible with 1 tags - 1970/01/01 00:00:00 bob> tags exited 0 after * seconds (glob) + 1970/01/01 00:00:00 bob (*)> tags (glob) + 1970/01/01 00:00:00 bob (*)> writing 24 bytes to cache/hgtagsfnodes1 (glob) + 1970/01/01 00:00:00 bob (*)> 2/3 cache hits/lookups in * seconds (glob) + 1970/01/01 00:00:00 bob (*)> writing .hg/cache/tags2-visible with 1 tags (glob) + 1970/01/01 00:00:00 bob (*)> tags exited 0 after * seconds (glob) $ f --size .hg/cache/hgtagsfnodes1 .hg/cache/hgtagsfnodes1: size=168 @@ -411,10 +411,10 @@ bar 1:78391a272241 $ hg blackbox -l 4 - 1970/01/01 00:00:00 bob> writing 24 bytes to cache/hgtagsfnodes1 - 1970/01/01 00:00:00 bob> 2/3 cache hits/lookups in * seconds (glob) - 1970/01/01 00:00:00 bob> writing .hg/cache/tags2-visible with 1 tags - 1970/01/01 00:00:00 bob> tags exited 0 after * seconds (glob) + 1970/01/01 00:00:00 bob (*)> writing 24 bytes to cache/hgtagsfnodes1 (glob) + 1970/01/01 00:00:00 bob (*)> 2/3 cache hits/lookups in * seconds (glob) + 1970/01/01 00:00:00 bob (*)> writing .hg/cache/tags2-visible with 1 tags (glob) + 1970/01/01 00:00:00 bob (*)> tags exited 0 after * seconds (glob) $ f --size .hg/cache/hgtagsfnodes1 .hg/cache/hgtagsfnodes1: size=120 @@ -427,11 +427,11 @@ bar 1:78391a272241 $ hg blackbox -l 5 - 1970/01/01 00:00:00 bob> tags - 1970/01/01 00:00:00 bob> writing 24 bytes to cache/hgtagsfnodes1 - 1970/01/01 00:00:00 bob> 2/3 cache hits/lookups in * seconds (glob) - 1970/01/01 00:00:00 bob> writing .hg/cache/tags2-visible with 1 tags - 1970/01/01 00:00:00 bob> tags exited 0 after * seconds (glob) + 1970/01/01 00:00:00 bob (*)> tags (glob) + 1970/01/01 00:00:00 bob (*)> writing 24 bytes to cache/hgtagsfnodes1 (glob) + 1970/01/01 00:00:00 bob (*)> 2/3 cache hits/lookups in * seconds (glob) + 1970/01/01 00:00:00 bob (*)> writing .hg/cache/tags2-visible with 1 tags (glob) + 1970/01/01 00:00:00 bob (*)> tags exited 0 after * seconds (glob) $ f --size .hg/cache/hgtagsfnodes1 .hg/cache/hgtagsfnodes1: size=144
--- a/tests/test-url.py Mon Oct 05 10:43:16 2015 -0600 +++ b/tests/test-url.py Wed Oct 07 13:44:48 2015 -0500 @@ -99,8 +99,8 @@ >>> url('://192.0.2.16:80/') <url path: '://192.0.2.16:80/'> - >>> url('http://mercurial.selenic.com') - <url scheme: 'http', host: 'mercurial.selenic.com'> + >>> url('https://mercurial-scm.org') + <url scheme: 'https', host: 'mercurial-scm.org'> >>> url('/foo') <url path: '/foo'> >>> url('bundle:/foo') @@ -174,7 +174,7 @@ Non-localhost file URL: - >>> u = url('file://mercurial.selenic.com/foo') + >>> 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
--- a/tests/test-win32text.t Mon Oct 05 10:43:16 2015 -0600 +++ b/tests/test-win32text.t Wed Oct 07 13:44:48 2015 -0500 @@ -370,7 +370,7 @@ Trigger deprecation warning: $ hg id -t - win32text is deprecated: http://mercurial.selenic.com/wiki/Win32TextExtension + win32text is deprecated: https://mercurial-scm.org/wiki/Win32TextExtension tip Disable warning:
--- a/tests/test-wireproto.py Mon Oct 05 10:43:16 2015 -0600 +++ b/tests/test-wireproto.py Wed Oct 07 13:44:48 2015 -0500 @@ -12,6 +12,10 @@ class clientpeer(wireproto.wirepeer): def __init__(self, serverrepo): self.serverrepo = serverrepo + + def _capabilities(self): + return ['batch'] + def _call(self, cmd, **args): return wireproto.dispatch(self.serverrepo, proto(args), cmd)