--- a/.hgignore Fri Apr 29 11:04:39 2011 +0200
+++ b/.hgignore Fri Apr 29 11:10:11 2011 +0200
@@ -7,6 +7,7 @@
*.mergebackup
*.o
*.so
+*.dll
*.pyd
*.pyc
*.pyo
--- a/contrib/check-code.py Fri Apr 29 11:04:39 2011 +0200
+++ b/contrib/check-code.py Fri Apr 29 11:10:11 2011 +0200
@@ -42,6 +42,7 @@
testpats = [
+ [
(r'(pushd|popd)', "don't use 'pushd' or 'popd', use 'cd'"),
(r'\W\$?\(\([^\)]*\)\)', "don't use (()) or $(()), use 'expr'"),
(r'^function', "don't use 'function', use old style"),
@@ -66,6 +67,10 @@
(r'^source\b', "don't use 'source', use '.'"),
(r'touch -d', "don't use 'touch -d', use 'touch -t' instead"),
(r'ls\s+[^|-]+\s+-', "options to 'ls' must come before filenames"),
+ (r'[^>]>\s*\$HGRCPATH', "don't overwrite $HGRCPATH, append to it"),
+ ],
+ # warnings
+ []
]
testfilters = [
@@ -76,6 +81,7 @@
uprefix = r"^ \$ "
uprefixc = r"^ > "
utestpats = [
+ [
(r'^(\S| $ ).*(\S\s+|^\s+)\n', "trailing whitespace on non-output"),
(uprefix + r'.*\|\s*sed', "use regex test output patterns instead of sed"),
(uprefix + r'(true|exit 0)', "explicit zero exit unnecessary"),
@@ -84,9 +90,12 @@
"explicit exit code checks unnecessary"),
(uprefix + r'set -e', "don't use set -e"),
(uprefixc + r'( *)\t', "don't use tabs to indent"),
+ ],
+ # warnings
+ []
]
-for p, m in testpats:
+for p, m in testpats[0] + testpats[1]:
if p.startswith('^'):
p = uprefix + p[1:]
else:
@@ -98,6 +107,7 @@
]
pypats = [
+ [
(r'^\s*def\s*\w+\s*\(.*,\s*\(',
"tuple parameter unpacking not available in Python 3+"),
(r'lambda\s*\(.*,.*\)',
@@ -111,7 +121,6 @@
(r'\w[+/*\-<>]\w', "missing whitespace in expression"),
(r'^\s+\w+=\w+[^,)]$', "missing whitespace in assignment"),
(r'.{85}', "line too long"),
- (r'.{81}', "warning: line over 80 characters"),
(r'[^\n]\Z', "no trailing newline"),
(r'(\S\s+|^\s+)\n', "trailing whitespace"),
# (r'^\s+[^_ ][^_. ]+_[^_]+\s*=', "don't use underbars in identifiers"),
@@ -149,11 +158,17 @@
(r'[^+=*!<>&| -](\s=|=\s)[^= ]',
"wrong whitespace around ="),
(r'raise Exception', "don't raise generic exceptions"),
- (r'ui\.(status|progress|write|note|warn)\([\'\"]x',
- "warning: unwrapped ui message"),
(r' is\s+(not\s+)?["\'0-9-]', "object comparison with literal"),
(r' [=!]=\s+(True|False|None)',
"comparison with singleton, use 'is' or 'is not' instead"),
+ ],
+ # warnings
+ [
+ (r'.{81}', "warning: line over 80 characters"),
+ (r'^\s*except:$', "warning: naked except clause"),
+ (r'ui\.(status|progress|write|note|warn)\([\'\"]x',
+ "warning: unwrapped ui message"),
+ ]
]
pyfilters = [
@@ -164,6 +179,7 @@
]
cpats = [
+ [
(r'//', "don't use //-style comments"),
(r'^ ', "don't use spaces to indent"),
(r'\S\t', "don't use tabs except for indent"),
@@ -176,9 +192,13 @@
(r'\([^\)]+\) \w+', "use (int)foo, not (int) foo"),
(r'\S+ (\+\+|--)', "use foo++, not foo ++"),
(r'\w,\w', "missing whitespace after ,"),
- (r'\w[+/*]\w', "missing whitespace in expression"),
+ (r'^[^#]\w[+/*]\w', "missing whitespace in expression"),
(r'^#\s+\w', "use #foo, not # foo"),
(r'[^\n]\Z', "no trailing newline"),
+ (r'^\s*#import\b', "use only #include in standard C code"),
+ ],
+ # warnings
+ []
]
cfilters = [
@@ -255,14 +275,16 @@
break
for p, r in filters:
post = re.sub(p, r, post)
+ if warnings:
+ pats = pats[0] + pats[1]
+ else:
+ pats = pats[0]
# print post # uncomment to show filtered version
z = enumerate(zip(pre.splitlines(), post.splitlines(True)))
for n, l in z:
if "check-code" + "-ignore" in l[0]:
continue
for p, msg in pats:
- if not warnings and msg.startswith("warning"):
- continue
if re.search(p, l[1]):
bd = ""
if blame:
--- a/contrib/hg-ssh Fri Apr 29 11:04:39 2011 +0200
+++ b/contrib/hg-ssh Fri Apr 29 11:10:11 2011 +0200
@@ -16,7 +16,7 @@
(probably together with these other useful options:
no-port-forwarding,no-X11-forwarding,no-agent-forwarding)
-This allows pull/push over ssh to to the repositories given as arguments.
+This allows pull/push over ssh from/to the repositories given as arguments.
If all your repositories are subdirectories of a common directory, you can
allow shorter paths with:
--- a/contrib/python-hook-examples.py Fri Apr 29 11:04:39 2011 +0200
+++ b/contrib/python-hook-examples.py Fri Apr 29 11:10:11 2011 +0200
@@ -13,7 +13,7 @@
if kwargs.get('parent2'):
return
node = kwargs['node']
- first = repo[node].parents()[0].node()
+ first = repo[node].p1().node()
if 'url' in kwargs:
last = repo['tip'].node()
else:
--- a/contrib/shrink-revlog.py Fri Apr 29 11:04:39 2011 +0200
+++ b/contrib/shrink-revlog.py Fri Apr 29 11:10:11 2011 +0200
@@ -102,15 +102,16 @@
ui.status(_('writing revs\n'))
- count = [0]
- def progress(*args):
- ui.progress(_('writing'), count[0], total=len(order))
- count[0] += 1
order = [r1.node(r) for r in order]
# this is a bit ugly, but it works
- lookup = lambda x: "%020d" % r1.linkrev(r1.rev(x))
+ count = [0]
+ def lookup(x):
+ count[0] += 1
+ ui.progress(_('writing'), count[0], total=len(order))
+ return "%020d" % r1.linkrev(r1.rev(x))
+
unlookup = lambda x: int(x, 10)
try:
--- a/contrib/undumprevlog Fri Apr 29 11:04:39 2011 +0200
+++ b/contrib/undumprevlog Fri Apr 29 11:10:11 2011 +0200
@@ -4,12 +4,12 @@
# $ undumprevlog < repo.dump
import sys
-from mercurial import revlog, node, util, transaction
+from mercurial import revlog, node, scmutil, util, transaction
for fp in (sys.stdin, sys.stdout, sys.stderr):
util.set_binary(fp)
-opener = util.opener('.', False)
+opener = scmutil.opener('.', False)
tr = transaction.transaction(sys.stderr.write, opener, "undump.journal")
while 1:
l = sys.stdin.readline()
--- a/contrib/zsh_completion Fri Apr 29 11:04:39 2011 +0200
+++ b/contrib/zsh_completion Fri Apr 29 11:10:11 2011 +0200
@@ -360,8 +360,8 @@
'(--help -h)'{-h,--help}'[display help and exit]'
'--debug[debug mode]'
'--debugger[start debugger]'
- '--encoding[set the charset encoding (default: UTF8)]'
- '--encodingmode[set the charset encoding mode (default: strict)]'
+ '--encoding[set the charset encoding]'
+ '--encodingmode[set the charset encoding mode]'
'--lsprof[print improved command execution profile]'
'--traceback[print traceback on exception]'
'--time[time how long the command takes]'
--- a/doc/hgrc.5.txt Fri Apr 29 11:04:39 2011 +0200
+++ b/doc/hgrc.5.txt Fri Apr 29 11:10:11 2011 +0200
@@ -910,9 +910,16 @@
The conflict resolution program to use during a manual merge.
For more information on merge tools see :hg:`help merge-tools`.
For configuring merge tools see the merge-tools_ section.
-``patch``
- command to use to apply patches. Look for ``gpatch`` or ``patch`` in
- PATH if unset.
+``portablefilenames``
+ Check for portable filenames. Can be ``warn``, ``ignore`` or ``abort``.
+ Default is ``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
+ parts like ``AUX`` or reserved characters like ``:``).
+ If set to ``ignore`` (or ``false``), no warning is printed.
+ If set to ``abort``, the command is aborted.
+ On Windows, this configuration option is ignored and the command aborted.
``quiet``
Reduce the amount of output printed. True or False. Default is False.
``remotecmd``
@@ -1045,6 +1052,8 @@
To disable SSL verification temporarily, specify ``--insecure`` from
command line.
+``cache``
+ Whether to support caching in hgweb. Defaults to True.
``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.
@@ -1085,6 +1094,9 @@
Default is False.
``ipv6``
Whether to use IPv6. Default is False.
+``logourl``
+ Base URL to use for logos. If unset, ``http://mercurial.selenic.com/``
+ will be used.
``name``
Repository name to use in the web interface. Default is current
working directory.
--- a/hgext/bugzilla.py Fri Apr 29 11:04:39 2011 +0200
+++ b/hgext/bugzilla.py Fri Apr 29 11:10:11 2011 +0200
@@ -1,6 +1,7 @@
# bugzilla.py - bugzilla integration for mercurial
#
# Copyright 2006 Vadim Gelfer <vadim.gelfer@gmail.com>
+# Copyright 2011 Jim Hague <jim.hague@acm.org>
#
# This software may be used and distributed according to the terms of the
# GNU General Public License version 2 or any later version.
@@ -8,97 +9,161 @@
'''hooks for integrating with the Bugzilla bug tracker
This hook extension adds comments on bugs in Bugzilla when changesets
-that refer to bugs by Bugzilla ID are seen. The hook does not change
-bug status.
+that refer to bugs by Bugzilla ID are seen. The comment is formatted using
+the Mercurial template mechanism.
+
+The hook does not change bug status.
+
+Three basic modes of access to Bugzilla are provided:
+
+1. Access via the Bugzilla XMLRPC interface. Requires Bugzilla 3.4 or later.
+
+2. Check data via the Bugzilla XMLRPC interface and submit bug change
+ via email to Bugzilla email interface. Requires Bugzilla 3.4 or later.
+
+3. Writing directly to the Bugzilla database. Only Bugzilla installations
+ using MySQL are supported. Requires Python MySQLdb.
-The hook updates the Bugzilla database directly. Only Bugzilla
-installations using MySQL are supported.
+Writing directly to the database is susceptible to schema changes, and
+relies on a Bugzilla contrib script to send out bug change
+notification emails. This script runs as the user running Mercurial,
+must be run on the host with the Bugzilla install, and requires
+permission to read Bugzilla configuration details and the necessary
+MySQL user and password to have full access rights to the Bugzilla
+database. For these reasons this access mode is now considered
+deprecated, and will not be updated for new Bugzilla versions going
+forward.
+
+Access via XMLRPC needs a Bugzilla username and password to be specified
+in the configuration. Comments are added under that username. Since the
+configuration must be readable by all Mercurial users, it is recommended
+that the rights of that user are restricted in Bugzilla to the minimum
+necessary to add comments.
-The hook relies on a Bugzilla script to send bug change notification
-emails. That script changes between Bugzilla versions; the
-'processmail' script used prior to 2.18 is replaced in 2.18 and
-subsequent versions by 'config/sendbugmail.pl'. Note that these will
-be run by Mercurial as the user pushing the change; you will need to
-ensure the Bugzilla install file permissions are set appropriately.
+Access via XMLRPC/email uses XMLRPC to query Bugzilla, but sends
+email to the Bugzilla email interface to submit comments to bugs.
+The From: address in the email is set to the email address of the Mercurial
+user, so the comment appears to come from the Mercurial user. In the event
+that the Mercurial user email is not recognised by Bugzilla as a Bugzilla
+user, the email associated with the Bugzilla username used to log into
+Bugzilla is used instead as the source of the comment.
+
+Configuration items common to all access modes:
+
+bugzilla.version
+ This access type to use. Values recognised are:
+
+ :``xmlrpc``: Bugzilla XMLRPC interface.
+ :``xmlrpc+email``: Bugzilla XMLRPC and email interfaces.
+ :``3.0``: MySQL access, Bugzilla 3.0 and later.
+ :``2.18``: MySQL access, Bugzilla 2.18 and up to but not
+ including 3.0.
+ :``2.16``: MySQL access, Bugzilla 2.16 and up to but not
+ including 2.18.
+
+bugzilla.regexp
+ Regular expression to match bug IDs in changeset commit message.
+ Must contain one "()" group. The default expression matches ``Bug
+ 1234``, ``Bug no. 1234``, ``Bug number 1234``, ``Bugs 1234,5678``,
+ ``Bug 1234 and 5678`` and variations thereof. Matching is case
+ insensitive.
+
+bugzilla.style
+ The style file to use when formatting comments.
+
+bugzilla.template
+ Template to use when formatting comments. Overrides style if
+ specified. In addition to the usual Mercurial keywords, the
+ extension specifies:
-The extension is configured through three different configuration
-sections. These keys are recognized in the [bugzilla] section:
+ :``{bug}``: The Bugzilla bug ID.
+ :``{root}``: The full pathname of the Mercurial repository.
+ :``{webroot}``: Stripped pathname of the Mercurial repository.
+ :``{hgweb}``: Base URL for browsing Mercurial repositories.
+
+ Default ``changeset {node|short} in repo {root} refers to bug
+ {bug}.\\ndetails:\\n\\t{desc|tabindent}``
+
+bugzilla.strip
+ The number of path separator characters to strip from the front of
+ the Mercurial repository path (``{root}`` in templates) to produce
+ ``{webroot}``. For example, a repository with ``{root}``
+ ``/var/local/my-project`` with a strip of 2 gives a value for
+ ``{webroot}`` of ``my-project``. Default 0.
-host
- Hostname of the MySQL server holding the Bugzilla database.
+web.baseurl
+ Base URL for browsing Mercurial repositories. Referenced from
+ templates as ``{hgweb}``.
+
+Configuration items common to XMLRPC+email and MySQL access modes:
+
+bugzilla.usermap
+ Path of file containing Mercurial committer email to Bugzilla user email
+ mappings. If specified, the file should contain one mapping per
+ line::
+
+ committer = Bugzilla user
+
+ See also the ``[usermap]`` section.
+
+The ``[usermap]`` section is used to specify mappings of Mercurial
+committer email to Bugzilla user email. See also ``bugzilla.usermap``.
+Contains entries of the form ``committer = Bugzilla user``.
-db
- Name of the Bugzilla database in MySQL. Default 'bugs'.
+XMLRPC access mode configuration:
+
+bugzilla.bzurl
+ The base URL for the Bugzilla installation.
+ Default ``http://localhost/bugzilla``.
+
+bugzilla.user
+ The username to use to log into Bugzilla via XMLRPC. Default
+ ``bugs``.
+
+bugzilla.password
+ The password for Bugzilla login.
+
+XMLRPC+email access mode uses the XMLRPC access mode configuration items,
+and also:
-user
- Username to use to access MySQL server. Default 'bugs'.
+bugzilla.bzemail
+ The Bugzilla email address.
+
+In addition, the Mercurial email settings must be configured. See the
+documentation in hgrc(5), sections ``[email]`` and ``[smtp]``.
+
+MySQL access mode configuration:
-password
+bugzilla.host
+ Hostname of the MySQL server holding the Bugzilla database.
+ Default ``localhost``.
+
+bugzilla.db
+ Name of the Bugzilla database in MySQL. Default ``bugs``.
+
+bugzilla.user
+ Username to use to access MySQL server. Default ``bugs``.
+
+bugzilla.password
Password to use to access MySQL server.
-timeout
+bugzilla.timeout
Database connection timeout (seconds). Default 5.
-version
- Bugzilla version. Specify '3.0' for Bugzilla versions 3.0 and later,
- '2.18' for Bugzilla versions from 2.18 and '2.16' for versions prior
- to 2.18.
-
-bzuser
+bugzilla.bzuser
Fallback Bugzilla user name to record comments with, if changeset
committer cannot be found as a Bugzilla user.
-bzdir
+bugzilla.bzdir
Bugzilla install directory. Used by default notify. Default
- '/var/www/html/bugzilla'.
-
-notify
- The command to run to get Bugzilla to send bug change notification
- emails. Substitutes from a map with 3 keys, 'bzdir', 'id' (bug id)
- and 'user' (committer bugzilla email). Default depends on version;
- from 2.18 it is "cd %(bzdir)s && perl -T contrib/sendbugmail.pl
- %(id)s %(user)s".
-
-regexp
- Regular expression to match bug IDs in changeset commit message.
- Must contain one "()" group. The default expression matches 'Bug
- 1234', 'Bug no. 1234', 'Bug number 1234', 'Bugs 1234,5678', 'Bug
- 1234 and 5678' and variations thereof. Matching is case insensitive.
-
-style
- The style file to use when formatting comments.
-
-template
- Template to use when formatting comments. Overrides style if
- specified. In addition to the usual Mercurial keywords, the
- extension specifies::
+ ``/var/www/html/bugzilla``.
- {bug} The Bugzilla bug ID.
- {root} The full pathname of the Mercurial repository.
- {webroot} Stripped pathname of the Mercurial repository.
- {hgweb} Base URL for browsing Mercurial repositories.
-
- Default 'changeset {node|short} in repo {root} refers '
- 'to bug {bug}.\\ndetails:\\n\\t{desc|tabindent}'
-
-strip
- The number of slashes to strip from the front of {root} to produce
- {webroot}. Default 0.
-
-usermap
- Path of file containing Mercurial committer ID to Bugzilla user ID
- mappings. If specified, the file should contain one mapping per
- line, "committer"="Bugzilla user". See also the [usermap] section.
-
-The [usermap] section is used to specify mappings of Mercurial
-committer ID to Bugzilla user ID. See also [bugzilla].usermap.
-"committer"="Bugzilla user"
-
-Finally, the [web] section supports one entry:
-
-baseurl
- Base URL for browsing Mercurial repositories. Reference from
- templates as {hgweb}.
+bugzilla.notify
+ The command to run to get Bugzilla to send bug change notification
+ emails. Substitutes from a map with 3 keys, ``bzdir``, ``id`` (bug
+ id) and ``user`` (committer bugzilla email). Default depends on
+ version; from 2.18 it is "cd %(bzdir)s && perl -T
+ contrib/sendbugmail.pl %(id)s %(user)s".
Activating the extension::
@@ -109,11 +174,58 @@
# run bugzilla hook on every change pulled or pushed in here
incoming.bugzilla = python:hgext.bugzilla.hook
-Example configuration:
+Example configurations:
+
+XMLRPC example configuration. This uses the Bugzilla at
+``http://my-project.org/bugzilla``, logging in as user
+``bugmail@my-project.org`` with password ``plugh``. It is used with a
+collection of Mercurial repositories in ``/var/local/hg/repos/``,
+with a web interface at ``http://my-project.org/hg``. ::
+
+ [bugzilla]
+ bzurl=http://my-project.org/bugzilla
+ user=bugmail@my-project.org
+ password=plugh
+ version=xmlrpc
+ template=Changeset {node|short} in {root|basename}.
+ {hgweb}/{webroot}/rev/{node|short}\\n
+ {desc}\\n
+ strip=5
+
+ [web]
+ baseurl=http://my-project.org/hg
-This example configuration is for a collection of Mercurial
-repositories in /var/local/hg/repos/ used with a local Bugzilla 3.2
-installation in /opt/bugzilla-3.2. ::
+XMLRPC+email example configuration. This uses the Bugzilla at
+``http://my-project.org/bugzilla``, logging in as user
+``bugmail@my-project.org`` wityh password ``plugh``. It is used with a
+collection of Mercurial repositories in ``/var/local/hg/repos/``,
+with a web interface at ``http://my-project.org/hg``. Bug comments
+are sent to the Bugzilla email address
+``bugzilla@my-project.org``. ::
+
+ [bugzilla]
+ bzurl=http://my-project.org/bugzilla
+ user=bugmail@my-project.org
+ password=plugh
+ version=xmlrpc
+ bzemail=bugzilla@my-project.org
+ template=Changeset {node|short} in {root|basename}.
+ {hgweb}/{webroot}/rev/{node|short}\\n
+ {desc}\\n
+ strip=5
+
+ [web]
+ baseurl=http://my-project.org/hg
+
+ [usermap]
+ user@emaildomain.com=user.name@bugzilladomain.com
+
+MySQL example configuration. This has a local Bugzilla 3.2 installation
+in ``/opt/bugzilla-3.2``. The MySQL database is on ``localhost``,
+the Bugzilla database name is ``bugs`` and MySQL is
+accessed with MySQL username ``bugs`` password ``XYZZY``. It is used
+with a collection of Mercurial repositories in ``/var/local/hg/repos/``,
+with a web interface at ``http://my-project.org/hg``. ::
[bugzilla]
host=localhost
@@ -127,46 +239,98 @@
strip=5
[web]
- baseurl=http://dev.domain.com/hg
+ baseurl=http://my-project.org/hg
[usermap]
user@emaildomain.com=user.name@bugzilladomain.com
-Commits add a comment to the Bugzilla bug record of the form::
+All the above add a comment to the Bugzilla bug record of the form::
Changeset 3b16791d6642 in repository-name.
- http://dev.domain.com/hg/repository-name/rev/3b16791d6642
+ http://my-project.org/hg/repository-name/rev/3b16791d6642
Changeset commit comment. Bug 1234.
'''
from mercurial.i18n import _
from mercurial.node import short
-from mercurial import cmdutil, templater, util
-import re, time
-
-MySQLdb = None
+from mercurial import cmdutil, mail, templater, util
+import re, time, xmlrpclib
-def buglist(ids):
- return '(' + ','.join(map(str, ids)) + ')'
-
-class bugzilla_2_16(object):
- '''support for bugzilla version 2.16.'''
+class bzaccess(object):
+ '''Base class for access to Bugzilla.'''
def __init__(self, ui):
self.ui = ui
+ usermap = self.ui.config('bugzilla', 'usermap')
+ if usermap:
+ self.ui.readconfig(usermap, sections=['usermap'])
+
+ def map_committer(self, user):
+ '''map name of committer to Bugzilla user name.'''
+ for committer, bzuser in self.ui.configitems('usermap'):
+ if committer.lower() == user.lower():
+ return bzuser
+ return user
+
+ # Methods to be implemented by access classes.
+ def filter_real_bug_ids(self, ids):
+ '''remove bug IDs that do not exist in Bugzilla from set.'''
+ pass
+
+ def filter_cset_known_bug_ids(self, node, ids):
+ '''remove bug IDs where node occurs in comment text from set.'''
+ pass
+
+ def add_comment(self, bugid, text, committer):
+ '''add comment to bug.
+
+ If possible add the comment as being from the committer of
+ the changeset. Otherwise use the default Bugzilla user.
+ '''
+ pass
+
+ def notify(self, ids, committer):
+ '''Force sending of Bugzilla notification emails.'''
+ pass
+
+# Bugzilla via direct access to MySQL database.
+class bzmysql(bzaccess):
+ '''Support for direct MySQL access to Bugzilla.
+
+ The earliest Bugzilla version this is tested with is version 2.16.
+
+ If your Bugzilla is version 3.2 or above, you are strongly
+ recommended to use the XMLRPC access method instead.
+ '''
+
+ @staticmethod
+ def sql_buglist(ids):
+ '''return SQL-friendly list of bug ids'''
+ return '(' + ','.join(map(str, ids)) + ')'
+
+ _MySQLdb = None
+
+ def __init__(self, ui):
+ try:
+ import MySQLdb as mysql
+ bzmysql._MySQLdb = mysql
+ except ImportError, err:
+ raise util.Abort(_('python mysql support not available: %s') % err)
+
+ bzaccess.__init__(self, ui)
+
host = self.ui.config('bugzilla', 'host', 'localhost')
user = self.ui.config('bugzilla', 'user', 'bugs')
passwd = self.ui.config('bugzilla', 'password')
db = self.ui.config('bugzilla', 'db', 'bugs')
timeout = int(self.ui.config('bugzilla', 'timeout', 5))
- usermap = self.ui.config('bugzilla', 'usermap')
- if usermap:
- self.ui.readconfig(usermap, sections=['usermap'])
self.ui.note(_('connecting to %s:%s as %s, password %s\n') %
(host, db, user, '*' * len(passwd)))
- self.conn = MySQLdb.connect(host=host, user=user, passwd=passwd,
- db=db, connect_timeout=timeout)
+ self.conn = bzmysql._MySQLdb.connect(host=host,
+ user=user, passwd=passwd,
+ db=db,
+ connect_timeout=timeout)
self.cursor = self.conn.cursor()
self.longdesc_id = self.get_longdesc_id()
self.user_ids = {}
@@ -177,7 +341,7 @@
self.ui.note(_('query: %s %s\n') % (args, kwargs))
try:
self.cursor.execute(*args, **kwargs)
- except MySQLdb.MySQLError:
+ except bzmysql._MySQLdb.MySQLError:
self.ui.note(_('failed query: %s %s\n') % (args, kwargs))
raise
@@ -190,22 +354,22 @@
return ids[0][0]
def filter_real_bug_ids(self, ids):
- '''filter not-existing bug ids from list.'''
- self.run('select bug_id from bugs where bug_id in %s' % buglist(ids))
- return sorted([c[0] for c in self.cursor.fetchall()])
+ '''filter not-existing bug ids from set.'''
+ self.run('select bug_id from bugs where bug_id in %s' %
+ bzmysql.sql_buglist(ids))
+ return set([c[0] for c in self.cursor.fetchall()])
- def filter_unknown_bug_ids(self, node, ids):
- '''filter bug ids from list that already refer to this changeset.'''
+ def filter_cset_known_bug_ids(self, node, ids):
+ '''filter bug ids that already refer to this changeset from set.'''
self.run('''select bug_id from longdescs where
bug_id in %s and thetext like "%%%s%%"''' %
- (buglist(ids), short(node)))
- unknown = set(ids)
+ (bzmysql.sql_buglist(ids), short(node)))
for (id,) in self.cursor.fetchall():
self.ui.status(_('bug %d already knows about changeset %s\n') %
(id, short(node)))
- unknown.discard(id)
- return sorted(unknown)
+ ids.discard(id)
+ return ids
def notify(self, ids, committer):
'''tell bugzilla to send mail.'''
@@ -251,15 +415,8 @@
self.user_ids[user] = userid
return userid
- def map_committer(self, user):
- '''map name of committer to bugzilla user name.'''
- for committer, bzuser in self.ui.configitems('usermap'):
- if committer.lower() == user.lower():
- return bzuser
- return user
-
def get_bugzilla_user(self, committer):
- '''see if committer is a registered bugzilla user. Return
+ '''See if committer is a registered bugzilla user. Return
bugzilla username and userid if so. If not, return default
bugzilla username and userid.'''
user = self.map_committer(committer)
@@ -292,19 +449,19 @@
(bugid, userid, now, self.longdesc_id))
self.conn.commit()
-class bugzilla_2_18(bugzilla_2_16):
+class bzmysql_2_18(bzmysql):
'''support for bugzilla 2.18 series.'''
def __init__(self, ui):
- bugzilla_2_16.__init__(self, ui)
+ bzmysql.__init__(self, ui)
self.default_notify = \
"cd %(bzdir)s && perl -T contrib/sendbugmail.pl %(id)s %(user)s"
-class bugzilla_3_0(bugzilla_2_18):
+class bzmysql_3_0(bzmysql_2_18):
'''support for bugzilla 3.0 series.'''
def __init__(self, ui):
- bugzilla_2_18.__init__(self, ui)
+ bzmysql_2_18.__init__(self, ui)
def get_longdesc_id(self):
'''get identity of longdesc field'''
@@ -314,13 +471,176 @@
raise util.Abort(_('unknown database schema'))
return ids[0][0]
+# Buzgilla via XMLRPC interface.
+
+class CookieSafeTransport(xmlrpclib.SafeTransport):
+ """A SafeTransport that retains cookies over its lifetime.
+
+ The regular xmlrpclib transports ignore cookies. Which causes
+ a bit of a problem when you need a cookie-based login, as with
+ the Bugzilla XMLRPC interface.
+
+ So this is a SafeTransport which looks for cookies being set
+ in responses and saves them to add to all future requests.
+ It appears a SafeTransport can do both HTTP and HTTPS sessions,
+ which saves us having to do a CookieTransport too.
+ """
+
+ # Inspiration drawn from
+ # http://blog.godson.in/2010/09/how-to-make-python-xmlrpclib-client.html
+ # http://www.itkovian.net/base/transport-class-for-pythons-xml-rpc-lib/
+
+ cookies = []
+ def send_cookies(self, connection):
+ if self.cookies:
+ for cookie in self.cookies:
+ connection.putheader("Cookie", cookie)
+
+ def request(self, host, handler, request_body, verbose=0):
+ self.verbose = verbose
+
+ # issue XML-RPC request
+ h = self.make_connection(host)
+ if verbose:
+ h.set_debuglevel(1)
+
+ self.send_request(h, handler, request_body)
+ self.send_host(h, host)
+ self.send_cookies(h)
+ self.send_user_agent(h)
+ self.send_content(h, request_body)
+
+ # Deal with differences between Python 2.4-2.6 and 2.7.
+ # In the former h is a HTTP(S). In the latter it's a
+ # HTTP(S)Connection. Luckily, the 2.4-2.6 implementation of
+ # HTTP(S) has an underlying HTTP(S)Connection, so extract
+ # that and use it.
+ try:
+ response = h.getresponse()
+ except AttributeError:
+ response = h._conn.getresponse()
+
+ # Add any cookie definitions to our list.
+ for header in response.msg.getallmatchingheaders("Set-Cookie"):
+ val = header.split(": ", 1)[1]
+ cookie = val.split(";", 1)[0]
+ self.cookies.append(cookie)
+
+ if response.status != 200:
+ raise xmlrpclib.ProtocolError(host + handler, response.status,
+ response.reason, response.msg.headers)
+
+ payload = response.read()
+ parser, unmarshaller = self.getparser()
+ parser.feed(payload)
+ parser.close()
+
+ return unmarshaller.close()
+
+class bzxmlrpc(bzaccess):
+ """Support for access to Bugzilla via the Bugzilla XMLRPC API.
+
+ Requires a minimum Bugzilla version 3.4.
+ """
+
+ def __init__(self, ui):
+ bzaccess.__init__(self, ui)
+
+ bzweb = self.ui.config('bugzilla', 'bzurl',
+ 'http://localhost/bugzilla/')
+ bzweb = bzweb.rstrip("/") + "/xmlrpc.cgi"
+
+ user = self.ui.config('bugzilla', 'user', 'bugs')
+ passwd = self.ui.config('bugzilla', 'password')
+
+ self.bzproxy = xmlrpclib.ServerProxy(bzweb, CookieSafeTransport())
+ self.bzproxy.User.login(dict(login=user, password=passwd))
+
+ def get_bug_comments(self, id):
+ """Return a string with all comment text for a bug."""
+ c = self.bzproxy.Bug.comments(dict(ids=[id]))
+ return ''.join([t['text'] for t in c['bugs'][str(id)]['comments']])
+
+ def filter_real_bug_ids(self, ids):
+ res = set()
+ bugs = self.bzproxy.Bug.get(dict(ids=sorted(ids), permissive=True))
+ for bug in bugs['bugs']:
+ res.add(bug['id'])
+ return res
+
+ def filter_cset_known_bug_ids(self, node, ids):
+ for id in sorted(ids):
+ if self.get_bug_comments(id).find(short(node)) != -1:
+ self.ui.status(_('bug %d already knows about changeset %s\n') %
+ (id, short(node)))
+ ids.discard(id)
+ return ids
+
+ def add_comment(self, bugid, text, committer):
+ self.bzproxy.Bug.add_comment(dict(id=bugid, comment=text))
+
+class bzxmlrpcemail(bzxmlrpc):
+ """Read data from Bugzilla via XMLRPC, send updates via email.
+
+ Advantages of sending updates via email:
+ 1. Comments can be added as any user, not just logged in user.
+ 2. Bug statuses and other fields not accessible via XMLRPC can
+ be updated. This is not currently used.
+ """
+
+ def __init__(self, ui):
+ bzxmlrpc.__init__(self, ui)
+
+ self.bzemail = self.ui.config('bugzilla', 'bzemail')
+ if not self.bzemail:
+ raise util.Abort(_("configuration 'bzemail' missing"))
+ mail.validateconfig(self.ui)
+
+ def send_bug_modify_email(self, bugid, commands, comment, committer):
+ '''send modification message to Bugzilla bug via email.
+
+ The message format is documented in the Bugzilla email_in.pl
+ specification. commands is a list of command lines, comment is the
+ comment text.
+
+ To stop users from crafting commit comments with
+ Bugzilla commands, specify the bug ID via the message body, rather
+ than the subject line, and leave a blank line after it.
+ '''
+ user = self.map_committer(committer)
+ matches = self.bzproxy.User.get(dict(match=[user]))
+ if not matches['users']:
+ user = self.ui.config('bugzilla', 'user', 'bugs')
+ matches = self.bzproxy.User.get(dict(match=[user]))
+ if not matches['users']:
+ raise util.Abort(_("default bugzilla user %s email not found") %
+ user)
+ user = matches['users'][0]['email']
+
+ text = "\n".join(commands) + "\n@bug_id = %d\n\n" % bugid + comment
+
+ _charsets = mail._charsets(self.ui)
+ user = mail.addressencode(self.ui, user, _charsets)
+ bzemail = mail.addressencode(self.ui, self.bzemail, _charsets)
+ msg = mail.mimeencode(self.ui, text, _charsets)
+ msg['From'] = user
+ msg['To'] = bzemail
+ msg['Subject'] = mail.headencode(self.ui, "Bug modification", _charsets)
+ sendmail = mail.connect(self.ui)
+ sendmail(user, bzemail, msg.as_string())
+
+ def add_comment(self, bugid, text, committer):
+ self.send_bug_modify_email(bugid, [], text, committer)
+
class bugzilla(object):
# supported versions of bugzilla. different versions have
# different schemas.
_versions = {
- '2.16': bugzilla_2_16,
- '2.18': bugzilla_2_18,
- '3.0': bugzilla_3_0
+ '2.16': bzmysql,
+ '2.18': bzmysql_2_18,
+ '3.0': bzmysql_3_0,
+ 'xmlrpc': bzxmlrpc,
+ 'xmlrpc+email': bzxmlrpcemail
}
_default_bug_re = (r'bugs?\s*,?\s*(?:#|nos?\.?|num(?:ber)?s?)?\s*'
@@ -353,10 +673,12 @@
_split_re = None
def find_bug_ids(self, ctx):
- '''find valid bug ids that are referred to in changeset
- comments and that do not already have references to this
- changeset.'''
+ '''return set of integer bug IDs from commit comment.
+ Extract bug IDs from changeset comments. Filter out any that are
+ not known to Bugzilla, and any that already have a reference to
+ the given changeset in their comments.
+ '''
if bugzilla._bug_re is None:
bugzilla._bug_re = re.compile(
self.ui.config('bugzilla', 'regexp', bugzilla._default_bug_re),
@@ -376,7 +698,7 @@
if ids:
ids = self.filter_real_bug_ids(ids)
if ids:
- ids = self.filter_unknown_bug_ids(ctx.node(), ids)
+ ids = self.filter_cset_known_bug_ids(ctx.node(), ids)
return ids
def update(self, bugid, ctx):
@@ -418,13 +740,6 @@
'''add comment to bugzilla for each changeset that refers to a
bugzilla bug id. only add a comment once per bug, so same change
seen multiple times does not fill bug with duplicate data.'''
- try:
- import MySQLdb as mysql
- global MySQLdb
- MySQLdb = mysql
- except ImportError, err:
- raise util.Abort(_('python mysql support not available: %s') % err)
-
if node is None:
raise util.Abort(_('hook type %s does not pass a changeset id') %
hooktype)
@@ -436,6 +751,6 @@
for id in ids:
bz.update(id, ctx)
bz.notify(ids, util.email(ctx.user()))
- except MySQLdb.MySQLError, err:
- raise util.Abort(_('database error: %s') % err.args[1])
+ except Exception, e:
+ raise util.Abort(_('Bugzilla error: %s') % e)
--- a/hgext/color.py Fri Apr 29 11:04:39 2011 +0200
+++ b/hgext/color.py Fri Apr 29 11:10:11 2011 +0200
@@ -18,14 +18,16 @@
'''colorize output from some commands
-This extension modifies the status and resolve commands to add color to their
-output to reflect file status, the qseries command to add color to reflect
-patch status (applied, unapplied, missing), and to diff-related
-commands to highlight additions, removals, diff headers, and trailing
-whitespace.
+This extension modifies the status and resolve commands to add color
+to their output to reflect file status, the qseries command to add
+color to reflect patch status (applied, unapplied, missing), and to
+diff-related commands to highlight additions, removals, diff headers,
+and trailing whitespace.
Other effects in addition to color, like bold and underlined text, are
-also available. Effects are rendered with the ECMA-48 SGR control
+also available. By default, the terminfo database is used to find the
+terminal codes used to change color and effect. If terminfo is not
+available, then effects are rendered with the ECMA-48 SGR control
function (aka ANSI escape codes).
Default effects may be overridden from your configuration file::
@@ -66,13 +68,35 @@
branches.current = green
branches.inactive = none
-The color extension will try to detect whether to use ANSI codes or
-Win32 console APIs, unless it is made explicit::
+The available effects in terminfo mode are 'blink', 'bold', 'dim',
+'inverse', 'invisible', 'italic', 'standout', and 'underline'; in
+ECMA-48 mode, the options are 'bold', 'inverse', 'italic', and
+'underline'. How each is rendered depends on the terminal emulator.
+Some may not be available for a given terminal type, and will be
+silently ignored.
+
+Because there are only eight standard colors, this module allows you
+to define color names for other color slots which might be available
+for your terminal type, assuming terminfo mode. For instance::
+
+ color.brightblue = 12
+ color.pink = 207
+ color.orange = 202
+
+to set 'brightblue' to color slot 12 (useful for 16 color terminals
+that have brighter colors defined in the upper eight) and, 'pink' and
+'orange' to colors in 256-color xterm's default color cube. These
+defined colors may then be used as any of the pre-defined eight,
+including appending '_background' to set the background to that color.
+
+The color extension will try to detect whether to use terminfo, ANSI
+codes or Win32 console APIs, unless it is made explicit; e.g.::
[color]
mode = ansi
-Any value other than 'ansi', 'win32', or 'auto' will disable color.
+Any value other than 'ansi', 'win32', 'terminfo', or 'auto' will
+disable color.
'''
@@ -90,6 +114,66 @@
'blue_background': 44, 'purple_background': 45,
'cyan_background': 46, 'white_background': 47}
+def _terminfosetup(ui):
+ '''Initialize terminfo data and the terminal if we're in terminfo mode.'''
+
+ global _terminfo_params
+ # If we failed to load curses, we go ahead and return.
+ if not _terminfo_params:
+ return
+ # Otherwise, see what the config file says.
+ mode = ui.config('color', 'mode', 'auto')
+ if mode not in ('auto', 'terminfo'):
+ return
+
+ _terminfo_params.update((key[6:], (False, int(val)))
+ for key, val in ui.configitems('color')
+ if key.startswith('color.'))
+
+ try:
+ curses.setupterm()
+ except curses.error, e:
+ _terminfo_params = {}
+ return
+
+ for key, (b, e) in _terminfo_params.items():
+ if not b:
+ continue
+ if not curses.tigetstr(e):
+ # Most terminals don't support dim, invis, etc, so don't be
+ # noisy and use ui.debug().
+ ui.debug("no terminfo entry for %s\n" % e)
+ del _terminfo_params[key]
+ if not curses.tigetstr('setaf') or not curses.tigetstr('setab'):
+ ui.warn(_("no terminfo entry for setab/setaf: reverting to "
+ "ECMA-48 color\n"))
+ _terminfo_params = {}
+
+try:
+ import curses
+ # Mapping from effect name to terminfo attribute name or color number.
+ # This will also force-load the curses module.
+ _terminfo_params = {'none': (True, 'sgr0'),
+ 'standout': (True, 'smso'),
+ 'underline': (True, 'smul'),
+ 'reverse': (True, 'rev'),
+ 'inverse': (True, 'rev'),
+ 'blink': (True, 'blink'),
+ 'dim': (True, 'dim'),
+ 'bold': (True, 'bold'),
+ 'invisible': (True, 'invis'),
+ 'italic': (True, 'sitm'),
+ 'black': (False, curses.COLOR_BLACK),
+ 'red': (False, curses.COLOR_RED),
+ 'green': (False, curses.COLOR_GREEN),
+ 'yellow': (False, curses.COLOR_YELLOW),
+ 'blue': (False, curses.COLOR_BLUE),
+ 'magenta': (False, curses.COLOR_MAGENTA),
+ 'cyan': (False, curses.COLOR_CYAN),
+ 'white': (False, curses.COLOR_WHITE)}
+except ImportError:
+ _terminfo_params = False
+
_styles = {'grep.match': 'red bold',
'bookmarks.current': 'green',
'branches.active': 'none',
@@ -107,6 +191,7 @@
'diff.trailingwhitespace': 'bold red_background',
'diffstat.deleted': 'red',
'diffstat.inserted': 'green',
+ 'ui.prompt': 'yellow',
'log.changeset': 'yellow',
'resolve.resolved': 'green bold',
'resolve.unresolved': 'red bold',
@@ -120,13 +205,33 @@
'status.unknown': 'magenta bold underline'}
+def _effect_str(effect):
+ '''Helper function for render_effects().'''
+
+ bg = False
+ if effect.endswith('_background'):
+ bg = True
+ effect = effect[:-11]
+ attr, val = _terminfo_params[effect]
+ if attr:
+ return curses.tigetstr(val)
+ elif bg:
+ return curses.tparm(curses.tigetstr('setab'), val)
+ else:
+ return curses.tparm(curses.tigetstr('setaf'), val)
+
def render_effects(text, effects):
'Wrap text in commands to turn on each effect.'
if not text:
return text
- start = [str(_effects[e]) for e in ['none'] + effects.split()]
- start = '\033[' + ';'.join(start) + 'm'
- stop = '\033[' + str(_effects['none']) + 'm'
+ if not _terminfo_params:
+ start = [str(_effects[e]) for e in ['none'] + effects.split()]
+ start = '\033[' + ';'.join(start) + 'm'
+ stop = '\033[' + str(_effects['none']) + 'm'
+ else:
+ start = ''.join(_effect_str(effect)
+ for effect in ['none'] + effects.split())
+ stop = _effect_str('none')
return ''.join([start, text, stop])
def extstyles():
@@ -135,13 +240,15 @@
def configstyles(ui):
for status, cfgeffects in ui.configitems('color'):
- if '.' not in status:
+ if '.' not in status or status.startswith('color.'):
continue
cfgeffects = ui.configlist('color', status)
if cfgeffects:
good = []
for e in cfgeffects:
- if e in _effects:
+ if not _terminfo_params and e in _effects:
+ good.append(e)
+ elif e in _terminfo_params or e[:-11] in _terminfo_params:
good.append(e)
else:
ui.warn(_("ignoring unknown color/effect %r "
@@ -191,6 +298,7 @@
def uisetup(ui):
+ global _terminfo_params
if ui.plain():
return
mode = ui.config('color', 'mode', 'auto')
@@ -199,14 +307,22 @@
# looks line a cmd.exe console, use win32 API or nothing
mode = w32effects and 'win32' or 'none'
else:
- mode = 'ansi'
+ _terminfosetup(ui)
+ if not _terminfo_params:
+ mode = 'ansi'
+ else:
+ mode = 'terminfo'
if mode == 'win32':
if w32effects is None:
# only warn if color.mode is explicitly set to win32
ui.warn(_('warning: failed to set color mode to %s\n') % mode)
return
_effects.update(w32effects)
- elif mode != 'ansi':
+ elif mode == 'ansi':
+ _terminfo_params = {}
+ elif mode == 'terminfo':
+ _terminfosetup(ui)
+ else:
return
def colorcmd(orig, ui_, opts, cmd, cmdfunc):
coloropt = opts['color']
@@ -348,13 +464,15 @@
# Look for ANSI-like codes embedded in text
m = re.match(ansire, text)
- while m:
- for sattr in m.group(1).split(';'):
- if sattr:
- attr = mapcolor(int(sattr), attr)
- _kernel32.SetConsoleTextAttribute(stdout, attr)
- orig(m.group(2), **opts)
- m = re.match(ansire, m.group(3))
- # Explicity reset original attributes
- _kernel32.SetConsoleTextAttribute(stdout, origattr)
+ try:
+ while m:
+ for sattr in m.group(1).split(';'):
+ if sattr:
+ attr = mapcolor(int(sattr), attr)
+ _kernel32.SetConsoleTextAttribute(stdout, attr)
+ orig(m.group(2), **opts)
+ m = re.match(ansire, m.group(3))
+ finally:
+ # Explicity reset original attributes
+ _kernel32.SetConsoleTextAttribute(stdout, origattr)
--- a/hgext/convert/__init__.py Fri Apr 29 11:04:39 2011 +0200
+++ b/hgext/convert/__init__.py Fri Apr 29 11:10:11 2011 +0200
@@ -10,7 +10,7 @@
import convcmd
import cvsps
import subversion
-from mercurial import commands
+from mercurial import commands, templatekw
from mercurial.i18n import _
# Commands definition was moved elsewhere to ease demandload job.
@@ -334,3 +334,34 @@
],
_('hg debugcvsps [OPTION]... [PATH]...')),
}
+
+def kwconverted(ctx, name):
+ rev = ctx.extra().get('convert_revision', '')
+ if rev.startswith('svn:'):
+ if name == 'svnrev':
+ return str(subversion.revsplit(rev)[2])
+ elif name == 'svnpath':
+ return subversion.revsplit(rev)[1]
+ elif name == 'svnuuid':
+ return subversion.revsplit(rev)[0]
+ return rev
+
+def kwsvnrev(repo, ctx, **args):
+ """:svnrev: String. Converted subversion revision number."""
+ return kwconverted(ctx, 'svnrev')
+
+def kwsvnpath(repo, ctx, **args):
+ """:svnpath: String. Converted subversion revision project path."""
+ return kwconverted(ctx, 'svnpath')
+
+def kwsvnuuid(repo, ctx, **args):
+ """:svnuuid: String. Converted subversion revision repository identifier."""
+ return kwconverted(ctx, 'svnuuid')
+
+def extsetup(ui):
+ templatekw.keywords['svnrev'] = kwsvnrev
+ templatekw.keywords['svnpath'] = kwsvnpath
+ templatekw.keywords['svnuuid'] = kwsvnuuid
+
+# tell hggettext to extract docstrings from these functions:
+i18nfunctions = [kwsvnrev, kwsvnpath, kwsvnuuid]
--- a/hgext/convert/common.py Fri Apr 29 11:04:39 2011 +0200
+++ b/hgext/convert/common.py Fri Apr 29 11:10:11 2011 +0200
@@ -151,6 +151,13 @@
"""
return None
+ def getbookmarks(self):
+ """Return the bookmarks as a dictionary of name: revision
+
+ Bookmark names are to be UTF-8 strings.
+ """
+ return {}
+
class converter_sink(object):
"""Conversion sink (target) interface"""
@@ -228,6 +235,13 @@
def after(self):
pass
+ def putbookmarks(self, bookmarks):
+ """Put bookmarks into sink.
+
+ bookmarks: {bookmarkname: sink_rev_id, ...}
+ where bookmarkname is an UTF-8 string.
+ """
+ pass
class commandline(object):
def __init__(self, ui, command):
@@ -240,7 +254,7 @@
def postrun(self):
pass
- def _cmdline(self, cmd, *args, **kwargs):
+ def _cmdline(self, cmd, closestdin, *args, **kwargs):
cmdline = [self.command, cmd] + list(args)
for k, v in kwargs.iteritems():
if len(k) == 1:
@@ -257,16 +271,23 @@
cmdline = [util.shellquote(arg) for arg in cmdline]
if not self.ui.debugflag:
cmdline += ['2>', util.nulldev]
- cmdline += ['<', util.nulldev]
+ if closestdin:
+ cmdline += ['<', util.nulldev]
cmdline = ' '.join(cmdline)
return cmdline
def _run(self, cmd, *args, **kwargs):
- cmdline = self._cmdline(cmd, *args, **kwargs)
+ return self._dorun(util.popen, cmd, True, *args, **kwargs)
+
+ def _run2(self, cmd, *args, **kwargs):
+ return self._dorun(util.popen2, cmd, False, *args, **kwargs)
+
+ def _dorun(self, openfunc, cmd, closestdin, *args, **kwargs):
+ cmdline = self._cmdline(cmd, closestdin, *args, **kwargs)
self.ui.debug('running: %s\n' % (cmdline,))
self.prerun()
try:
- return util.popen(cmdline)
+ return openfunc(cmdline)
finally:
self.postrun()
@@ -322,8 +343,9 @@
self._argmax = self._argmax / 2 - 1
return self._argmax
- def limit_arglist(self, arglist, cmd, *args, **kwargs):
- limit = self.getargmax() - len(self._cmdline(cmd, *args, **kwargs))
+ def limit_arglist(self, arglist, cmd, closestdin, *args, **kwargs):
+ cmdlen = len(self._cmdline(cmd, closestdin, *args, **kwargs))
+ limit = self.getargmax() - cmdlen
bytes = 0
fl = []
for fn in arglist:
@@ -339,7 +361,7 @@
yield fl
def xargs(self, arglist, cmd, *args, **kwargs):
- for l in self.limit_arglist(arglist, cmd, *args, **kwargs):
+ for l in self.limit_arglist(arglist, cmd, True, *args, **kwargs):
self.run0(cmd, *(list(args) + l), **kwargs)
class mapfile(dict):
--- a/hgext/convert/convcmd.py Fri Apr 29 11:04:39 2011 +0200
+++ b/hgext/convert/convcmd.py Fri Apr 29 11:10:11 2011 +0200
@@ -378,6 +378,16 @@
if tagsparents:
self.map[tagsparents[0][0]] = nrev
+ bookmarks = self.source.getbookmarks()
+ cbookmarks = {}
+ for k in bookmarks:
+ v = bookmarks[k]
+ if self.map.get(v, SKIPREV) != SKIPREV:
+ cbookmarks[k] = self.map[v]
+
+ if c and cbookmarks:
+ self.dest.putbookmarks(cbookmarks)
+
self.writeauthormap()
finally:
self.cleanup()
--- a/hgext/convert/git.py Fri Apr 29 11:04:39 2011 +0200
+++ b/hgext/convert/git.py Fri Apr 29 11:10:11 2011 +0200
@@ -17,19 +17,27 @@
# cannot remove environment variable. Just assume none have
# both issues.
if hasattr(os, 'unsetenv'):
- def gitopen(self, s):
+ def gitopen(self, s, noerr=False):
prevgitdir = os.environ.get('GIT_DIR')
os.environ['GIT_DIR'] = self.path
try:
- return util.popen(s, 'rb')
+ if noerr:
+ (stdin, stdout, stderr) = util.popen3(s)
+ return stdout
+ else:
+ return util.popen(s, 'rb')
finally:
if prevgitdir is None:
del os.environ['GIT_DIR']
else:
os.environ['GIT_DIR'] = prevgitdir
else:
- def gitopen(self, s):
- return util.popen('GIT_DIR=%s %s' % (self.path, s), 'rb')
+ def gitopen(self, s, noerr=False):
+ if noerr:
+ (sin, so, se) = util.popen3('GIT_DIR=%s %s' % (self.path, s))
+ return stdout
+ else:
+ util.popen('GIT_DIR=%s %s' % (self.path, s), 'rb')
def gitread(self, s):
fh = self.gitopen(s)
@@ -168,3 +176,30 @@
raise util.Abort(_('cannot read changes in %s') % version)
return changes
+
+ def getbookmarks(self):
+ bookmarks = {}
+
+ # Interesting references in git are prefixed
+ prefix = 'refs/heads/'
+ prefixlen = len(prefix)
+
+ # factor two commands
+ gitcmd = { 'remote/': 'git ls-remote --heads origin',
+ '': 'git show-ref'}
+
+ # Origin heads
+ for reftype in gitcmd:
+ try:
+ fh = self.gitopen(gitcmd[reftype], noerr=True)
+ for line in fh:
+ line = line.strip()
+ rev, name = line.split(None, 1)
+ if not name.startswith(prefix):
+ continue
+ name = '%s%s' % (reftype, name[prefixlen:])
+ bookmarks[name] = rev
+ except:
+ pass
+
+ return bookmarks
--- a/hgext/convert/hg.py Fri Apr 29 11:04:39 2011 +0200
+++ b/hgext/convert/hg.py Fri Apr 29 11:10:11 2011 +0200
@@ -21,7 +21,7 @@
import os, time, cStringIO
from mercurial.i18n import _
from mercurial.node import bin, hex, nullid
-from mercurial import hg, util, context, error
+from mercurial import hg, util, context, bookmarks, error
from common import NoRepo, commit, converter_source, converter_sink
@@ -214,6 +214,16 @@
def setfilemapmode(self, active):
self.filemapmode = active
+ def putbookmarks(self, updatedbookmark):
+ if not len(updatedbookmark):
+ return
+
+ self.ui.status(_("updating bookmarks\n"))
+ for bookmark in updatedbookmark:
+ self.repo._bookmarks[bookmark] = bin(updatedbookmark[bookmark])
+ bookmarks.write(self.repo)
+
+
class mercurial_source(converter_source):
def __init__(self, ui, path, rev=None):
converter_source.__init__(self, ui, path, rev)
@@ -374,3 +384,6 @@
return hex(self.repo.lookup(rev))
except error.RepoError:
return None
+
+ def getbookmarks(self):
+ return bookmarks.listbookmarks(self.repo)
--- a/hgext/convert/monotone.py Fri Apr 29 11:04:39 2011 +0200
+++ b/hgext/convert/monotone.py Fri Apr 29 11:10:11 2011 +0200
@@ -19,6 +19,8 @@
self.ui = ui
self.path = path
+ self.automatestdio = False
+ self.rev = rev
norepo = NoRepo(_("%s does not look like a monotone repository")
% path)
@@ -64,18 +66,103 @@
checktool('mtn', abort=False)
- # test if there are any revisions
- self.rev = None
- try:
- self.getheads()
- except:
- raise norepo
- self.rev = rev
+ def mtnrun(self, *args, **kwargs):
+ if self.automatestdio:
+ return self.mtnrunstdio(*args, **kwargs)
+ else:
+ return self.mtnrunsingle(*args, **kwargs)
- def mtnrun(self, *args, **kwargs):
+ def mtnrunsingle(self, *args, **kwargs):
kwargs['d'] = self.path
return self.run0('automate', *args, **kwargs)
+ def mtnrunstdio(self, *args, **kwargs):
+ # Prepare the command in automate stdio format
+ command = []
+ for k, v in kwargs.iteritems():
+ command.append("%s:%s" % (len(k), k))
+ if v:
+ command.append("%s:%s" % (len(v), v))
+ if command:
+ command.insert(0, 'o')
+ command.append('e')
+
+ command.append('l')
+ for arg in args:
+ command += "%s:%s" % (len(arg), arg)
+ command.append('e')
+ command = ''.join(command)
+
+ self.ui.debug("mtn: sending '%s'\n" % command)
+ self.mtnwritefp.write(command)
+ self.mtnwritefp.flush()
+
+ return self.mtnstdioreadcommandoutput(command)
+
+ def mtnstdioreadpacket(self):
+ read = None
+ commandnbr = ''
+ while read != ':':
+ read = self.mtnreadfp.read(1)
+ if not read:
+ raise util.Abort(_('bad mtn packet - no end of commandnbr'))
+ commandnbr += read
+ commandnbr = commandnbr[:-1]
+
+ stream = self.mtnreadfp.read(1)
+ if stream not in 'mewptl':
+ raise util.Abort(_('bad mtn packet - bad stream type %s' % stream))
+
+ read = self.mtnreadfp.read(1)
+ if read != ':':
+ raise util.Abort(_('bad mtn packet - no divider before size'))
+
+ read = None
+ lengthstr = ''
+ while read != ':':
+ read = self.mtnreadfp.read(1)
+ if not read:
+ raise util.Abort(_('bad mtn packet - no end of packet size'))
+ lengthstr += read
+ try:
+ length = long(lengthstr[:-1])
+ except TypeError:
+ raise util.Abort(_('bad mtn packet - bad packet size %s')
+ % lengthstr)
+
+ read = self.mtnreadfp.read(length)
+ if len(read) != length:
+ raise util.Abort(_("bad mtn packet - unable to read full packet "
+ "read %s of %s") % (len(read), length))
+
+ return (commandnbr, stream, length, read)
+
+ def mtnstdioreadcommandoutput(self, command):
+ retval = []
+ while True:
+ commandnbr, stream, length, output = self.mtnstdioreadpacket()
+ self.ui.debug('mtn: read packet %s:%s:%s\n' %
+ (commandnbr, stream, length))
+
+ if stream == 'l':
+ # End of command
+ if output != '0':
+ raise util.Abort(_("mtn command '%s' returned %s") %
+ (command, output))
+ break
+ elif stream in 'ew':
+ # Error, warning output
+ self.ui.warn(_('%s error:\n') % self.command)
+ self.ui.warn(output)
+ elif stream == 'p':
+ # Progress messages
+ self.ui.debug('mtn: ' + output)
+ elif stream == 'm':
+ # Main stream - command output
+ retval.append(output)
+
+ return ''.join(retval)
+
def mtnloadmanifest(self, rev):
if self.manifest_rev == rev:
return
@@ -204,14 +291,18 @@
return data, attr
def getcommit(self, rev):
- certs = self.mtngetcerts(rev)
+ extra = {}
+ certs = self.mtngetcerts(rev)
+ if certs.get('suspend') == certs["branch"]:
+ extra['close'] = '1'
return commit(
author=certs["author"],
date=util.datestr(util.strdate(certs["date"], "%Y-%m-%dT%H:%M:%S")),
desc=certs["changelog"],
rev=rev,
parents=self.mtnrun("parents", rev).splitlines(),
- branch=certs["branch"])
+ branch=certs["branch"],
+ extra=extra)
def gettags(self):
tags = {}
@@ -225,3 +316,43 @@
# This function is only needed to support --filemap
# ... and we don't support that
raise NotImplementedError()
+
+ def before(self):
+ # Check if we have a new enough version to use automate stdio
+ version = 0.0
+ try:
+ versionstr = self.mtnrunsingle("interface_version")
+ version = float(versionstr)
+ except Exception:
+ raise util.Abort(_("unable to determine mtn automate interface "
+ "version"))
+
+ if version >= 12.0:
+ self.automatestdio = True
+ self.ui.debug("mtn automate version %s - using automate stdio\n" %
+ version)
+
+ # launch the long-running automate stdio process
+ self.mtnwritefp, self.mtnreadfp = self._run2('automate', 'stdio',
+ '-d', self.path)
+ # read the headers
+ read = self.mtnreadfp.readline()
+ if read != 'format-version: 2\n':
+ raise util.Abort(_('mtn automate stdio header unexpected: %s')
+ % read)
+ while read != '\n':
+ read = self.mtnreadfp.readline()
+ if not read:
+ raise util.Abort(_("failed to reach end of mtn automate "
+ "stdio headers"))
+ else:
+ self.ui.debug("mtn automate version %s - not using automate stdio "
+ "(automate >= 12.0 - mtn >= 0.46 is needed)\n" % version)
+
+ def after(self):
+ if self.automatestdio:
+ self.mtnwritefp.close()
+ self.mtnwritefp = None
+ self.mtnreadfp.close()
+ self.mtnreadfp = None
+
--- a/hgext/convert/subversion.py Fri Apr 29 11:04:39 2011 +0200
+++ b/hgext/convert/subversion.py Fri Apr 29 11:10:11 2011 +0200
@@ -10,7 +10,7 @@
import urllib
import urllib2
-from mercurial import strutil, util, encoding
+from mercurial import strutil, scmutil, util, encoding
from mercurial.i18n import _
# Subversion stuff. Works best with very recent Python SVN bindings
@@ -41,6 +41,15 @@
class SvnPathNotFound(Exception):
pass
+def revsplit(rev):
+ """Parse a revision string and return (uuid, path, revnum)."""
+ url, revnum = rev.rsplit('@', 1)
+ parts = url.split('/', 1)
+ mod = ''
+ if len(parts) > 1:
+ mod = '/' + parts[1]
+ return parts[0][4:], mod, int(revnum)
+
def geturl(path):
try:
return svn.client.url_from_path(svn.core.svn_path_canonicalize(path))
@@ -259,6 +268,7 @@
except ValueError:
raise util.Abort(_('svn: revision %s is not an integer') % rev)
+ self.trunkname = self.ui.config('convert', 'svn.trunk', 'trunk').strip('/')
self.startrev = self.ui.config('convert', 'svn.startrev', default=0)
try:
self.startrev = int(self.startrev)
@@ -285,7 +295,7 @@
def setrevmap(self, revmap):
lastrevs = {}
for revid in revmap.iterkeys():
- uuid, module, revnum = self.revsplit(revid)
+ uuid, module, revnum = revsplit(revid)
lastrevnum = lastrevs.setdefault(module, revnum)
if revnum > lastrevnum:
lastrevs[module] = revnum
@@ -380,7 +390,7 @@
files, self.removed, copies = self.expandpaths(rev, paths, parents)
else:
# Perform a full checkout on roots
- uuid, module, revnum = self.revsplit(rev)
+ uuid, module, revnum = revsplit(rev)
entries = svn.client.ls(self.baseurl + urllib.quote(module),
optrev(revnum), True, self.ctx)
files = [n for n, e in entries.iteritems()
@@ -402,7 +412,7 @@
def getcommit(self, rev):
if rev not in self.commits:
- uuid, module, revnum = self.revsplit(rev)
+ uuid, module, revnum = revsplit(rev)
self.module = module
self.reparent(module)
# We assume that:
@@ -529,16 +539,6 @@
def revnum(self, rev):
return int(rev.split('@')[-1])
- def revsplit(self, rev):
- url, revnum = rev.rsplit('@', 1)
- revnum = int(revnum)
- parts = url.split('/', 1)
- uuid = parts.pop(0)[4:]
- mod = ''
- if parts:
- mod = '/' + parts[0]
- return uuid, mod, revnum
-
def latest(self, path, stop=0):
"""Find the latest revid affecting path, up to stop. It may return
a revision in a different module, since a branch may be moved without
@@ -605,7 +605,7 @@
changed, removed = set(), set()
copies = {}
- new_module, revnum = self.revsplit(rev)[1:]
+ new_module, revnum = revsplit(rev)[1:]
if new_module != self.module:
self.module = new_module
self.reparent(self.module)
@@ -622,7 +622,7 @@
continue
# Copy sources not in parent revisions cannot be
# represented, ignore their origin for now
- pmodule, prevnum = self.revsplit(parents[0])[1:]
+ pmodule, prevnum = revsplit(parents[0])[1:]
if ent.copyfrom_rev < prevnum:
continue
copyfrom_path = self.getrelpath(ent.copyfrom_path, pmodule)
@@ -633,7 +633,7 @@
copies[self.recode(entrypath)] = self.recode(copyfrom_path)
elif kind == 0: # gone, but had better be a deleted *file*
self.ui.debug("gone from %s\n" % ent.copyfrom_rev)
- pmodule, prevnum = self.revsplit(parents[0])[1:]
+ pmodule, prevnum = revsplit(parents[0])[1:]
parentpath = pmodule + "/" + entrypath
fromkind = self._checkpath(entrypath, prevnum, pmodule)
@@ -659,7 +659,7 @@
if ent.action == 'R' and parents:
# If a directory is replacing a file, mark the previous
# file as deleted
- pmodule, prevnum = self.revsplit(parents[0])[1:]
+ pmodule, prevnum = revsplit(parents[0])[1:]
pkind = self._checkpath(entrypath, prevnum, pmodule)
if pkind == svn.core.svn_node_file:
removed.add(self.recode(entrypath))
@@ -681,7 +681,7 @@
continue
# Copy sources not in parent revisions cannot be
# represented, ignore their origin for now
- pmodule, prevnum = self.revsplit(parents[0])[1:]
+ pmodule, prevnum = revsplit(parents[0])[1:]
if ent.copyfrom_rev < prevnum:
continue
copyfrompath = self.getrelpath(ent.copyfrom_path, pmodule)
@@ -736,7 +736,7 @@
# ent.copyfrom_rev may not be the actual last revision
previd = self.latest(newpath, ent.copyfrom_rev)
if previd is not None:
- prevmodule, prevnum = self.revsplit(previd)[1:]
+ prevmodule, prevnum = revsplit(previd)[1:]
if prevnum >= self.startrev:
parents = [previd]
self.ui.note(
@@ -761,9 +761,8 @@
author = author and self.recode(author) or ''
try:
branch = self.module.split("/")[-1]
- trunkname = self.ui.config('convert', 'svn.trunk', 'trunk')
- if branch == trunkname.strip('/'):
- branch = ''
+ if branch == self.trunkname:
+ branch = None
except IndexError:
branch = None
@@ -834,7 +833,7 @@
raise IOError()
mode = ''
try:
- new_module, revnum = self.revsplit(rev)[1:]
+ new_module, revnum = revsplit(rev)[1:]
if self.module != new_module:
self.module = new_module
self.reparent(self.module)
@@ -944,6 +943,7 @@
class svn_sink(converter_sink, commandline):
commit_re = re.compile(r'Committed revision (\d+).', re.M)
+ uuid_re = re.compile(r'Repository UUID:\s*(\S+)', re.M)
def prerun(self):
if self.wc:
@@ -964,8 +964,6 @@
def __init__(self, ui, path):
- if svn is None:
- raise MissingTool(_('Could not load Subversion python bindings'))
converter_sink.__init__(self, ui, path)
commandline.__init__(self, ui, 'svn')
self.delete = []
@@ -1000,8 +998,8 @@
self.run0('checkout', path, wcpath)
self.wc = wcpath
- self.opener = util.opener(self.wc)
- self.wopener = util.opener(self.wc)
+ self.opener = scmutil.opener(self.wc)
+ self.wopener = scmutil.opener(self.wc)
self.childmap = mapfile(ui, self.join('hg-childmap'))
self.is_exec = util.checkexec(self.wc) and util.is_exec or None
@@ -1012,8 +1010,8 @@
fp.close()
util.set_flags(hook, False, True)
- xport = transport.SvnRaTransport(url=geturl(path))
- self.uuid = svn.ra.get_uuid(xport.ra)
+ output = self.run0('info')
+ self.uuid = self.uuid_re.search(output).group(1).strip()
def wjoin(self, *names):
return os.path.join(self.wc, *names)
--- a/hgext/eol.py Fri Apr 29 11:04:39 2011 +0200
+++ b/hgext/eol.py Fri Apr 29 11:10:11 2011 +0200
@@ -73,11 +73,13 @@
only need to these filters until you have prepared a ``.hgeol`` file.
The ``win32text.forbid*`` hooks provided by the win32text extension
-have been unified into a single hook named ``eol.hook``. The hook will
-lookup the expected line endings from the ``.hgeol`` file, which means
-you must migrate to a ``.hgeol`` file first before using the hook.
-Remember to enable the eol extension in the repository where you
-install the hook.
+have been unified into a single hook named ``eol.checkheadshook``. The
+hook will lookup the expected line endings from the ``.hgeol`` file,
+which means you must migrate to a ``.hgeol`` file first before using
+the hook. ``eol.checkheadshook`` only checks heads, intermediate
+invalid revisions will be pushed. To forbid them completely, use the
+``eol.checkallhook`` hook. These hooks are best used as
+``pretxnchangegroup`` hooks.
See :hg:`help patterns` for more information about the glob patterns
used.
@@ -127,36 +129,119 @@
'cleverdecode:': tocrlf
}
+class eolfile(object):
+ def __init__(self, ui, root, data):
+ self._decode = {'LF': 'to-lf', 'CRLF': 'to-crlf', 'BIN': 'is-binary'}
+ self._encode = {'LF': 'to-lf', 'CRLF': 'to-crlf', 'BIN': 'is-binary'}
-def hook(ui, repo, node, hooktype, **kwargs):
- """verify that files have expected EOLs"""
+ self.cfg = config.config()
+ # Our files should not be touched. The pattern must be
+ # inserted first override a '** = native' pattern.
+ self.cfg.set('patterns', '.hg*', 'BIN')
+ # We can then parse the user's patterns.
+ self.cfg.parse('.hgeol', data)
+
+ isrepolf = self.cfg.get('repository', 'native') != 'CRLF'
+ self._encode['NATIVE'] = isrepolf and 'to-lf' or 'to-crlf'
+ iswdlf = ui.config('eol', 'native', os.linesep) in ('LF', '\n')
+ self._decode['NATIVE'] = iswdlf and 'to-lf' or 'to-crlf'
+
+ include = []
+ exclude = []
+ for pattern, style in self.cfg.items('patterns'):
+ key = style.upper()
+ if key == 'BIN':
+ exclude.append(pattern)
+ else:
+ include.append(pattern)
+ # This will match the files for which we need to care
+ # about inconsistent newlines.
+ self.match = match.match(root, '', [], include, exclude)
+
+ def setfilters(self, ui):
+ for pattern, style in self.cfg.items('patterns'):
+ key = style.upper()
+ try:
+ ui.setconfig('decode', pattern, self._decode[key])
+ ui.setconfig('encode', pattern, self._encode[key])
+ except KeyError:
+ ui.warn(_("ignoring unknown EOL style '%s' from %s\n")
+ % (style, self.cfg.source('patterns', pattern)))
+
+ def checkrev(self, repo, ctx, files):
+ failed = []
+ for f in (files or ctx.files()):
+ if f not in ctx:
+ continue
+ for pattern, style in self.cfg.items('patterns'):
+ if not match.match(repo.root, '', [pattern])(f):
+ continue
+ target = self._encode[style.upper()]
+ data = ctx[f].data()
+ if (target == "to-lf" and "\r\n" in data
+ or target == "to-crlf" and singlelf.search(data)):
+ failed.append((str(ctx), target, f))
+ break
+ return failed
+
+def parseeol(ui, repo, nodes):
+ try:
+ for node in nodes:
+ try:
+ if node is None:
+ # Cannot use workingctx.data() since it would load
+ # and cache the filters before we configure them.
+ data = repo.wfile('.hgeol').read()
+ else:
+ data = repo[node]['.hgeol'].data()
+ return eolfile(ui, repo.root, data)
+ except (IOError, LookupError):
+ pass
+ except error.ParseError, inst:
+ ui.warn(_("warning: ignoring .hgeol file due to parse error "
+ "at %s: %s\n") % (inst.args[1], inst.args[0]))
+ return None
+
+def _checkhook(ui, repo, node, headsonly):
+ # Get revisions to check and touched files at the same time
files = set()
+ revs = set()
for rev in xrange(repo[node].rev(), len(repo)):
- files.update(repo[rev].files())
- tip = repo['tip']
- for f in files:
- if f not in tip:
- continue
- for pattern, target in ui.configitems('encode'):
- if match.match(repo.root, '', [pattern])(f):
- data = tip[f].data()
- if target == "to-lf" and "\r\n" in data:
- raise util.Abort(_("%s should not have CRLF line endings")
- % f)
- elif target == "to-crlf" and singlelf.search(data):
- raise util.Abort(_("%s should not have LF line endings")
- % f)
- # Ignore other rules for this file
- break
+ revs.add(rev)
+ if headsonly:
+ ctx = repo[rev]
+ files.update(ctx.files())
+ for pctx in ctx.parents():
+ revs.discard(pctx.rev())
+ failed = []
+ for rev in revs:
+ ctx = repo[rev]
+ eol = parseeol(ui, repo, [ctx.node()])
+ if eol:
+ failed.extend(eol.checkrev(repo, ctx, files))
+ if failed:
+ eols = {'to-lf': 'CRLF', 'to-crlf': 'LF'}
+ msgs = []
+ for node, target, f in failed:
+ msgs.append(_(" %s in %s should not have %s line endings") %
+ (f, node, eols[target]))
+ raise util.Abort(_("end-of-line check failed:\n") + "\n".join(msgs))
+
+def checkallhook(ui, repo, node, hooktype, **kwargs):
+ """verify that files have expected EOLs"""
+ _checkhook(ui, repo, node, False)
+
+def checkheadshook(ui, repo, node, hooktype, **kwargs):
+ """verify that files have expected EOLs"""
+ _checkhook(ui, repo, node, True)
+
+# "checkheadshook" used to be called "hook"
+hook = checkheadshook
def preupdate(ui, repo, hooktype, parent1, parent2):
#print "preupdate for %s: %s -> %s" % (repo.root, parent1, parent2)
- try:
- repo.readhgeol(parent1)
- except error.ParseError, inst:
- ui.warn(_("warning: ignoring .hgeol file due to parse error "
- "at %s: %s\n") % (inst.args[1], inst.args[0]))
+ repo.loadeol([parent1])
return False
def uisetup(ui):
@@ -184,66 +269,15 @@
class eolrepo(repo.__class__):
- _decode = {'LF': 'to-lf', 'CRLF': 'to-crlf', 'BIN': 'is-binary'}
- _encode = {'LF': 'to-lf', 'CRLF': 'to-crlf', 'BIN': 'is-binary'}
-
- def readhgeol(self, node=None, data=None):
- if data is None:
- try:
- if node is None:
- data = self.wfile('.hgeol').read()
- else:
- data = self[node]['.hgeol'].data()
- except (IOError, LookupError):
- return None
-
- if self.ui.config('eol', 'native', os.linesep) in ('LF', '\n'):
- self._decode['NATIVE'] = 'to-lf'
- else:
- self._decode['NATIVE'] = 'to-crlf'
-
- eol = config.config()
- # Our files should not be touched. The pattern must be
- # inserted first override a '** = native' pattern.
- eol.set('patterns', '.hg*', 'BIN')
- # We can then parse the user's patterns.
- eol.parse('.hgeol', data)
-
- if eol.get('repository', 'native') == 'CRLF':
- self._encode['NATIVE'] = 'to-crlf'
- else:
- self._encode['NATIVE'] = 'to-lf'
-
- for pattern, style in eol.items('patterns'):
- key = style.upper()
- try:
- self.ui.setconfig('decode', pattern, self._decode[key])
- self.ui.setconfig('encode', pattern, self._encode[key])
- except KeyError:
- self.ui.warn(_("ignoring unknown EOL style '%s' from %s\n")
- % (style, eol.source('patterns', pattern)))
-
- include = []
- exclude = []
- for pattern, style in eol.items('patterns'):
- key = style.upper()
- if key == 'BIN':
- exclude.append(pattern)
- else:
- include.append(pattern)
-
- # This will match the files for which we need to care
- # about inconsistent newlines.
- return match.match(self.root, '', [], include, exclude)
+ def loadeol(self, nodes):
+ eol = parseeol(self.ui, self, nodes)
+ if eol is None:
+ return None
+ eol.setfilters(self.ui)
+ return eol.match
def _hgcleardirstate(self):
- try:
- self._eolfile = self.readhgeol() or self.readhgeol('tip')
- except error.ParseError, inst:
- ui.warn(_("warning: ignoring .hgeol file due to parse error "
- "at %s: %s\n") % (inst.args[1], inst.args[0]))
- self._eolfile = None
-
+ self._eolfile = self.loadeol([None, 'tip'])
if not self._eolfile:
self._eolfile = util.never
return
--- a/hgext/extdiff.py Fri Apr 29 11:04:39 2011 +0200
+++ b/hgext/extdiff.py Fri Apr 29 11:10:11 2011 +0200
@@ -61,7 +61,7 @@
from mercurial.i18n import _
from mercurial.node import short, nullid
-from mercurial import cmdutil, util, commands, encoding
+from mercurial import cmdutil, scmutil, util, commands, encoding
import os, shlex, shutil, tempfile, re
def snapshot(ui, repo, files, node, tmproot):
@@ -81,7 +81,7 @@
else:
ui.note(_('making snapshot of %d files from working directory\n') %
(len(files)))
- wopener = util.opener(base)
+ wopener = scmutil.opener(base)
fns_and_mtime = []
ctx = repo[node]
for fn in files:
@@ -122,12 +122,12 @@
msg = _('cannot specify --rev and --change at the same time')
raise util.Abort(msg)
elif change:
- node2 = repo.lookup(change)
+ node2 = cmdutil.revsingle(repo, change, None).node()
node1a, node1b = repo.changelog.parents(node2)
else:
node1a, node2 = cmdutil.revpair(repo, revs)
if not revs:
- node1b = repo.dirstate.parents()[1]
+ node1b = repo.dirstate.p2()
else:
node1b = nullid
@@ -188,14 +188,14 @@
# Handle bogus modifies correctly by checking if the files exist
if len(common) == 1:
common_file = util.localpath(common.pop())
- dir1a = os.path.join(dir1a, common_file)
+ dir1a = os.path.join(tmproot, dir1a, common_file)
label1a = common_file + rev1a
- if not os.path.isfile(os.path.join(tmproot, dir1a)):
+ if not os.path.isfile(dir1a):
dir1a = os.devnull
if do3way:
- dir1b = os.path.join(dir1b, common_file)
+ dir1b = os.path.join(tmproot, dir1b, common_file)
label1b = common_file + rev1b
- if not os.path.isfile(os.path.join(tmproot, dir1b)):
+ if not os.path.isfile(dir1b):
dir1b = os.devnull
dir2 = os.path.join(dir2root, dir2, common_file)
label2 = common_file + rev2
--- a/hgext/graphlog.py Fri Apr 29 11:04:39 2011 +0200
+++ b/hgext/graphlog.py Fri Apr 29 11:10:11 2011 +0200
@@ -18,7 +18,7 @@
from mercurial.i18n import _
from mercurial.node import nullrev
from mercurial import cmdutil, commands, extensions
-from mercurial import hg, util, graphmod
+from mercurial import hg, scmutil, util, graphmod
ASCIIDATA = 'ASC'
@@ -250,7 +250,7 @@
return
if path:
- path = util.canonpath(repo.root, os.getcwd(), path)
+ path = scmutil.canonpath(repo.root, os.getcwd(), path)
if path: # could be reset in canonpath
revdag = graphmod.filerevs(repo, path, start, stop, limit)
else:
@@ -324,6 +324,7 @@
except TypeError, e:
if len(args) > wrapfn.func_code.co_argcount:
raise util.Abort(_('--graph option allows at most one file'))
+ raise
return orig(*args, **kwargs)
entry = extensions.wrapcommand(table, cmd, graph)
entry[1].append(('G', 'graph', None, _("show the revision DAG")))
--- a/hgext/hgcia.py Fri Apr 29 11:04:39 2011 +0200
+++ b/hgext/hgcia.py Fri Apr 29 11:10:11 2011 +0200
@@ -75,7 +75,7 @@
def fileelems(self):
n = self.ctx.node()
- f = self.cia.repo.status(self.ctx.parents()[0].node(), n)
+ f = self.cia.repo.status(self.ctx.p1().node(), n)
url = self.url or ''
elems = []
for path in f[0]:
--- a/hgext/keyword.py Fri Apr 29 11:04:39 2011 +0200
+++ b/hgext/keyword.py Fri Apr 29 11:10:11 2011 +0200
@@ -60,11 +60,11 @@
control run :hg:`kwdemo`. See :hg:`help templates` for a list of
available templates and filters.
-Three additional date template filters are provided::
+Three additional date template filters are provided:
- utcdate "2006/09/18 15:13:13"
- svnutcdate "2006-09-18 15:13:13Z"
- svnisodate "2006-09-18 08:13:13 -700 (Mon, 18 Sep 2006)"
+:``utcdate``: "2006/09/18 15:13:13"
+:``svnutcdate``: "2006-09-18 15:13:13Z"
+:``svnisodate``: "2006-09-18 08:13:13 -700 (Mon, 18 Sep 2006)"
The default template mappings (view with :hg:`kwdemo -d`) can be
replaced with customized keywords and templates. Again, run
@@ -83,6 +83,7 @@
from mercurial import commands, context, cmdutil, dispatch, filelog, extensions
from mercurial import localrepo, match, patch, templatefilters, templater, util
+from mercurial import scmutil
from mercurial.hgweb import webcommands
from mercurial.i18n import _
import os, re, shutil, tempfile
@@ -109,11 +110,26 @@
}
# date like in cvs' $Date
-utcdate = lambda x: util.datestr((x[0], 0), '%Y/%m/%d %H:%M:%S')
+def utcdate(text):
+ ''':utcdate: Date. Returns a UTC-date in this format: "2009/08/18 11:00:13".
+ '''
+ return util.datestr((text[0], 0), '%Y/%m/%d %H:%M:%S')
# date like in svn's $Date
-svnisodate = lambda x: util.datestr(x, '%Y-%m-%d %H:%M:%S %1%2 (%a, %d %b %Y)')
+def svnisodate(text):
+ ''':svnisodate: Date. Returns a date in this format: "2009-08-18 13:00:13
+ +0200 (Tue, 18 Aug 2009)".
+ '''
+ return util.datestr(text, '%Y-%m-%d %H:%M:%S %1%2 (%a, %d %b %Y)')
# date like in svn's $Id
-svnutcdate = lambda x: util.datestr((x[0], 0), '%Y-%m-%d %H:%M:%SZ')
+def svnutcdate(text):
+ ''':svnutcdate: Date. Returns a UTC-date in this format: "2009-08-18
+ 11:00:13Z".
+ '''
+ return util.datestr((text[0], 0), '%Y-%m-%d %H:%M:%SZ')
+
+templatefilters.filters.update({'utcdate': utcdate,
+ 'svnisodate': svnisodate,
+ 'svnutcdate': svnutcdate})
# make keyword tools accessible
kwtools = {'templater': None, 'hgcmd': ''}
@@ -176,9 +192,6 @@
for k, v in kwmaps)
else:
self.templates = _defaultkwmaps(self.ui)
- templatefilters.filters.update({'utcdate': utcdate,
- 'svnisodate': svnisodate,
- 'svnutcdate': svnutcdate})
@util.propertycache
def escape(self):
@@ -607,8 +620,8 @@
expansion. '''
source = repo.dirstate.copied(dest)
if 'l' in wctx.flags(source):
- source = util.canonpath(repo.root, cwd,
- os.path.realpath(source))
+ source = scmutil.canonpath(repo.root, cwd,
+ os.path.realpath(source))
return kwt.match(source)
candidates = [f for f in repo.dirstate.copies() if
--- a/hgext/mq.py Fri Apr 29 11:04:39 2011 +0200
+++ b/hgext/mq.py Fri Apr 29 11:10:11 2011 +0200
@@ -45,7 +45,7 @@
from mercurial.i18n import _
from mercurial.node import bin, hex, short, nullid, nullrev
from mercurial.lock import release
-from mercurial import commands, cmdutil, hg, patch, util
+from mercurial import commands, cmdutil, hg, patch, scmutil, util
from mercurial import repair, extensions, url, error
import os, sys, re, errno, shutil
@@ -259,7 +259,7 @@
except IOError:
curpath = os.path.join(path, 'patches')
self.path = patchdir or curpath
- self.opener = util.opener(self.path)
+ self.opener = scmutil.opener(self.path)
self.ui = ui
self.applied_dirty = 0
self.series_dirty = 0
@@ -737,11 +737,29 @@
os.unlink(self.join(p))
if numrevs:
+ qfinished = self.applied[:numrevs]
del self.applied[:numrevs]
self.applied_dirty = 1
- for i in sorted([self.find_series(p) for p in patches], reverse=True):
- del self.full_series[i]
+ unknown = []
+
+ for (i, p) in sorted([(self.find_series(p), p) for p in patches],
+ reverse=True):
+ if i is not None:
+ del self.full_series[i]
+ else:
+ unknown.append(p)
+
+ if unknown:
+ if numrevs:
+ rev = dict((entry.name, entry.node) for entry in qfinished)
+ for p in unknown:
+ msg = _('revision %s refers to unknown patches: %s\n')
+ self.ui.warn(msg % (short(rev[p]), p))
+ else:
+ msg = _('unknown patches: %s\n')
+ raise util.Abort(''.join(msg % p for p in unknown))
+
self.parse_series()
self.series_dirty = 1
@@ -899,7 +917,7 @@
else:
p.write("# HG changeset patch\n")
p.write("# Parent "
- + hex(repo[None].parents()[0].node()) + "\n")
+ + hex(repo[None].p1().node()) + "\n")
if user:
p.write("# User " + user + "\n")
if date:
@@ -1054,7 +1072,7 @@
heads += ls
if not heads:
heads = [nullid]
- if repo.dirstate.parents()[0] not in heads and not exact:
+ if repo.dirstate.p1() not in heads and not exact:
self.ui.status(_("(working directory not at a head)\n"))
if not self.series:
@@ -1148,7 +1166,7 @@
ret = self.apply(repo, s, list, all_files=all_files)
except:
self.ui.warn(_('cleaning up working directory...'))
- node = repo.dirstate.parents()[0]
+ node = repo.dirstate.p1()
hg.revert(repo, node, None)
# only remove unknown files that we know we touched or
# created while patching
@@ -1899,7 +1917,7 @@
With -g/--git, patches imported with --rev will use the git diff
format. See the diffs help topic for information on why this is
important for preserving rename/copy information and permission
- changes.
+ changes. Use :hg:`qfinish` to remove changesets from mq control.
To import a patch from standard input, pass - as the patch file.
When importing from standard input, a patch name must be specified
--- a/hgext/notify.py Fri Apr 29 11:04:39 2011 +0200
+++ b/hgext/notify.py Fri Apr 29 11:10:11 2011 +0200
@@ -249,7 +249,7 @@
def diff(self, ctx, ref=None):
maxdiff = int(self.ui.config('notify', 'maxdiff', 300))
- prev = ctx.parents()[0].node()
+ prev = ctx.p1().node()
ref = ref and ref.node() or ctx.node()
chunks = patch.diff(self.repo, prev, ref, opts=patch.diffopts(self.ui))
difflines = ''.join(chunks).splitlines()
--- a/hgext/rebase.py Fri Apr 29 11:04:39 2011 +0200
+++ b/hgext/rebase.py Fri Apr 29 11:10:11 2011 +0200
@@ -15,7 +15,7 @@
'''
from mercurial import hg, util, repair, merge, cmdutil, commands
-from mercurial import extensions, ancestor, copies, patch
+from mercurial import extensions, copies, patch
from mercurial.commands import templateopts
from mercurial.node import nullrev
from mercurial.lock import release
@@ -90,7 +90,8 @@
contf = opts.get('continue')
abortf = opts.get('abort')
collapsef = opts.get('collapse', False)
- extrafn = opts.get('extrafn')
+ collapsemsg = cmdutil.logmessage(opts)
+ extrafn = opts.get('extrafn') # internal, used by e.g. hgsubversion
keepf = opts.get('keep', False)
keepbranchesf = opts.get('keepbranches', False)
detachf = opts.get('detach', False)
@@ -98,6 +99,10 @@
# other extensions
keepopen = opts.get('keepopen', False)
+ if collapsemsg and not collapsef:
+ raise util.Abort(
+ _('message can only be specified with collapse'))
+
if contf or abortf:
if contf and abortf:
raise util.Abort(_('cannot use both abort and continue'))
@@ -109,6 +114,8 @@
if srcf or basef or destf:
raise util.Abort(
_('abort and continue do not allow specifying revisions'))
+ if opts.get('tool', False):
+ ui.warn(_('tool option will be ignored\n'))
(originalwd, target, state, skipped, collapsef, keepf,
keepbranchesf, external) = restorestatus(repo)
@@ -138,8 +145,7 @@
external = checkexternal(repo, state, targetancestors)
if keepbranchesf:
- if extrafn:
- raise util.Abort(_('cannot use both keepbranches and extrafn'))
+ assert not extrafn, 'cannot use both keepbranches and extrafn'
def extrafn(ctx, extra):
extra['branch'] = ctx.branch()
@@ -163,10 +169,14 @@
if len(repo.parents()) == 2:
repo.ui.debug('resuming interrupted rebase\n')
else:
- stats = rebasenode(repo, rev, p1, p2, state)
- if stats and stats[3] > 0:
- raise util.Abort(_('unresolved conflicts (see hg '
- 'resolve, then hg rebase --continue)'))
+ try:
+ ui.setconfig('ui', 'forcemerge', opts.get('tool', ''))
+ stats = rebasenode(repo, rev, p1, state)
+ if stats and stats[3] > 0:
+ raise util.Abort(_('unresolved conflicts (see hg '
+ 'resolve, then hg rebase --continue)'))
+ finally:
+ ui.setconfig('ui', 'forcemerge', '')
updatedirstate(repo, rev, target, p2)
if not collapsef:
newrev = concludenode(repo, rev, p1, p2, extrafn=extrafn)
@@ -190,11 +200,14 @@
if collapsef and not keepopen:
p1, p2 = defineparents(repo, min(state), target,
state, targetancestors)
- commitmsg = 'Collapsed revision'
- for rebased in state:
- if rebased not in skipped and state[rebased] != nullmerge:
- commitmsg += '\n* %s' % repo[rebased].description()
- commitmsg = ui.edit(commitmsg, repo.ui.username())
+ if collapsemsg:
+ commitmsg = collapsemsg
+ else:
+ commitmsg = 'Collapsed revision'
+ for rebased in state:
+ if rebased not in skipped and state[rebased] != nullmerge:
+ commitmsg += '\n* %s' % repo[rebased].description()
+ commitmsg = ui.edit(commitmsg, repo.ui.username())
newrev = concludenode(repo, rev, p1, external, commitmsg=commitmsg,
extrafn=extrafn)
@@ -221,25 +234,6 @@
finally:
release(lock, wlock)
-def rebasemerge(repo, rev, first=False):
- 'return the correct ancestor'
- oldancestor = ancestor.ancestor
-
- def newancestor(a, b, pfunc):
- if b == rev:
- return repo[rev].parents()[0].rev()
- return oldancestor(a, b, pfunc)
-
- if not first:
- ancestor.ancestor = newancestor
- else:
- repo.ui.debug("first revision, do not change ancestor\n")
- try:
- stats = merge.update(repo, rev, True, True, False)
- return stats
- finally:
- ancestor.ancestor = oldancestor
-
def checkexternal(repo, state, targetancestors):
"""Check whether one or more external revisions need to be taken in
consideration. In the latter case, abort.
@@ -293,7 +287,7 @@
repo.dirstate.invalidate()
raise
-def rebasenode(repo, rev, p1, p2, state):
+def rebasenode(repo, rev, p1, state):
'Rebase a single revision'
# Merge phase
# Update to target and merge it with local
@@ -304,9 +298,10 @@
repo.ui.debug(" already in target\n")
repo.dirstate.write()
repo.ui.debug(" merge against %d:%s\n" % (repo[rev].rev(), repo[rev]))
- first = repo[rev].rev() == repo[min(state)].rev()
- stats = rebasemerge(repo, rev, first)
- return stats
+ base = None
+ if repo[rev].rev() != repo[min(state)].rev():
+ base = repo[rev].p1().node()
+ return merge.update(repo, rev, True, True, False, base)
def defineparents(repo, rev, target, state, targetancestors):
'Return the new parent relationship of the revision that will be rebased'
@@ -354,6 +349,8 @@
'Update rebased mq patches - finalize and then import them'
mqrebase = {}
mq = repo.mq
+ original_series = mq.full_series[:]
+
for p in mq.applied:
rev = repo[p.node].rev()
if rev in state:
@@ -371,6 +368,15 @@
repo.ui.debug('import mq patch %d (%s)\n' % (state[rev], name))
mq.qimport(repo, (), patchname=name, git=isgit,
rev=[str(state[rev])])
+
+ # Restore missing guards
+ for s in original_series:
+ pname = mq.guard_re.split(s, 1)[0]
+ if pname in mq.full_series:
+ repo.ui.debug('restoring guard for patch %s' % (pname))
+ mq.full_series.remove(pname)
+ mq.full_series.append(s)
+ mq.series_dirty = True
mq.save_dirty()
def storestatus(repo, originalwd, target, state, collapse, keep, keepbranches,
@@ -475,9 +481,10 @@
if src:
commonbase = repo[src].ancestor(repo[dest])
+ samebranch = repo[src].branch() == repo[dest].branch()
if commonbase == repo[src]:
raise util.Abort(_('source is ancestor of destination'))
- if commonbase == repo[dest]:
+ if samebranch and commonbase == repo[dest]:
raise util.Abort(_('source is descendant of destination'))
source = repo[src].rev()
if detach:
@@ -565,10 +572,15 @@
('d', 'dest', '',
_('rebase onto the specified changeset'), _('REV')),
('', 'collapse', False, _('collapse the rebased changesets')),
+ ('m', 'message', '',
+ _('use text as collapse commit message'), _('TEXT')),
+ ('l', 'logfile', '',
+ _('read collapse commit message from file'), _('FILE')),
('', 'keep', False, _('keep original changesets')),
('', 'keepbranches', False, _('keep original branch names')),
('', 'detach', False, _('force detaching of source from its original '
'branch')),
+ ('t', 'tool', '', _('specify merge tool')),
('c', 'continue', False, _('continue an interrupted rebase')),
('a', 'abort', False, _('abort an interrupted rebase'))] +
templateopts,
--- a/hgext/record.py Fri Apr 29 11:04:39 2011 +0200
+++ b/hgext/record.py Fri Apr 29 11:10:11 2011 +0200
@@ -324,10 +324,12 @@
for i, chunk in enumerate(h.hunks):
if skipfile is None and skipall is None:
chunk.pretty(ui)
- msg = (total == 1
- and (_('record this change to %r?') % chunk.filename())
- or (_('record change %d/%d to %r?') %
- (pos - len(h.hunks) + i, total, chunk.filename())))
+ if total == 1:
+ msg = _('record this change to %r?') % chunk.filename()
+ else:
+ idx = pos - len(h.hunks) + i
+ msg = _('record change %d/%d to %r?') % (idx, total,
+ chunk.filename())
r, skipfile, skipall = prompt(skipfile, skipall, msg)
if r:
if fixoffset:
@@ -467,7 +469,7 @@
# 3a. apply filtered patch to clean repo (clean)
if backups:
- hg.revert(repo, repo.dirstate.parents()[0],
+ hg.revert(repo, repo.dirstate.p1(),
lambda key: key in backups)
# 3b. (apply)
@@ -533,6 +535,9 @@
"record":
(record, commands.table['^commit|ci'][1], # same options as commit
_('hg record [OPTION]... [FILE]...')),
+ "qrecord":
+ (qrecord, {}, # placeholder until mq is available
+ _('hg qrecord [OPTION]... PATCH [FILE]...')),
}
--- a/hgext/relink.py Fri Apr 29 11:04:39 2011 +0200
+++ b/hgext/relink.py Fri Apr 29 11:10:11 2011 +0200
@@ -38,9 +38,9 @@
"""
if not hasattr(util, 'samefile') or not hasattr(util, 'samedevice'):
raise util.Abort(_('hardlinks are not supported on this system'))
- src = hg.repository(
- hg.remoteui(repo, opts),
- ui.expandpath(origin or 'default-relink', origin or 'default'))
+ src = hg.repository(hg.remoteui(repo, opts),
+ ui.expandpath(origin or 'default-relink',
+ origin or 'default'))
if not src.local():
raise util.Abort(_('must specify local origin repository'))
ui.status(_('relinking %s to %s\n') % (src.store.path, repo.store.path))
@@ -172,8 +172,8 @@
ui.progress(_('relinking'), None)
- ui.status(_('relinked %d files (%d bytes reclaimed)\n') %
- (relinked, savedbytes))
+ ui.status(_('relinked %d files (%s reclaimed)\n') %
+ (relinked, util.bytecount(savedbytes)))
cmdtable = {
'relink': (
--- a/hgext/schemes.py Fri Apr 29 11:04:39 2011 +0200
+++ b/hgext/schemes.py Fri Apr 29 11:10:11 2011 +0200
@@ -40,8 +40,9 @@
same name.
"""
-import re
-from mercurial import hg, templater
+import os, re
+from mercurial import extensions, hg, templater, url as urlmod, util
+from mercurial.i18n import _
class ShortRepository(object):
@@ -58,6 +59,7 @@
return '<ShortRepository: %s>' % self.scheme
def instance(self, ui, url, create):
+ # Should this use urlmod.url(), or is manual parsing better?
url = url.split('://', 1)[1]
parts = url.split('/', self.parts)
if len(parts) > self.parts:
@@ -69,6 +71,12 @@
url = ''.join(self.templater.process(self.url, context)) + tail
return hg._lookup(url).instance(ui, url, create)
+def hasdriveletter(orig, path):
+ for scheme in schemes:
+ if path.startswith(scheme + ':'):
+ return False
+ return orig(path)
+
schemes = {
'py': 'http://hg.python.org/',
'bb': 'https://bitbucket.org/',
@@ -81,4 +89,10 @@
schemes.update(dict(ui.configitems('schemes')))
t = templater.engine(lambda x: x)
for scheme, url in schemes.items():
+ if (os.name == 'nt' and len(scheme) == 1 and scheme.isalpha()
+ and os.path.exists('%s:\\' % scheme)):
+ raise util.Abort(_('custom scheme %s:// conflicts with drive '
+ 'letter %s:\\\n') % (scheme, scheme.upper()))
hg.schemes[scheme] = ShortRepository(url, scheme, t)
+
+ extensions.wrapfunction(urlmod, 'hasdriveletter', hasdriveletter)
--- a/hgext/transplant.py Fri Apr 29 11:04:39 2011 +0200
+++ b/hgext/transplant.py Fri Apr 29 11:10:11 2011 +0200
@@ -16,8 +16,8 @@
from mercurial.i18n import _
import os, tempfile
from mercurial import bundlerepo, cmdutil, hg, merge, match
-from mercurial import patch, revlog, util, error
-from mercurial import revset
+from mercurial import patch, revlog, scmutil, util, error
+from mercurial import revset, templatekw
class transplantentry(object):
def __init__(self, lnode, rnode):
@@ -31,7 +31,7 @@
self.opener = opener
if not opener:
- self.opener = util.opener(self.path)
+ self.opener = scmutil.opener(self.path)
self.transplants = {}
self.dirty = False
self.read()
@@ -74,7 +74,7 @@
def __init__(self, ui, repo):
self.ui = ui
self.path = repo.join('transplant')
- self.opener = util.opener(self.path)
+ self.opener = scmutil.opener(self.path)
self.transplants = transplants(self.path, 'transplants',
opener=self.opener)
@@ -177,12 +177,11 @@
lock.release()
wlock.release()
- def filter(self, filter, changelog, patchfile):
+ def filter(self, filter, node, changelog, patchfile):
'''arbitrarily rewrite changeset before applying it'''
self.ui.status(_('filtering %s\n') % patchfile)
user, date, msg = (changelog[1], changelog[2], changelog[4])
-
fd, headerfile = tempfile.mkstemp(prefix='hg-transplant-')
fp = os.fdopen(fd, 'w')
fp.write("# HG changeset patch\n")
@@ -194,7 +193,9 @@
try:
util.system('%s %s %s' % (filter, util.shellquote(headerfile),
util.shellquote(patchfile)),
- environ={'HGUSER': changelog[1]},
+ environ={'HGUSER': changelog[1],
+ 'HGREVISION': revlog.hex(node),
+ },
onerr=util.Abort, errprefix=_('filter failed'))
user, date, msg = self.parselog(file(headerfile))[1:4]
finally:
@@ -209,7 +210,7 @@
date = "%d %d" % (time, timezone)
extra = {'transplant_source': node}
if filter:
- (user, date, message) = self.filter(filter, cl, patchfile)
+ (user, date, message) = self.filter(filter, node, cl, patchfile)
if log:
# we don't translate messages inserted into commits
@@ -236,7 +237,7 @@
seriespath = os.path.join(self.path, 'series')
if os.path.exists(seriespath):
os.unlink(seriespath)
- p1 = repo.dirstate.parents()[0]
+ p1 = repo.dirstate.p1()
p2 = node
self.log(user, date, message, p1, p2, merge=merge)
self.ui.write(str(inst) + '\n')
@@ -345,6 +346,8 @@
message = []
node = revlog.nullid
inmsg = False
+ user = None
+ date = None
for line in fp.read().splitlines():
if inmsg:
message.append(line)
@@ -359,6 +362,8 @@
elif not line.startswith('# '):
inmsg = True
message.append(line)
+ if None in (user, date):
+ raise util.Abort(_("filter corrupted changeset (no user or date)"))
return (node, user, date, '\n'.join(message), parents)
def log(self, user, date, message, p1, p2, merge=False):
@@ -547,8 +552,8 @@
if source:
sourcerepo = ui.expandpath(source)
source = hg.repository(ui, sourcerepo)
- source, incoming, bundle = bundlerepo.getremotechanges(ui, repo, source,
- force=True)
+ source, common, incoming, bundle = bundlerepo.getremotechanges(ui, repo,
+ source, force=True)
else:
source = repo
@@ -607,8 +612,15 @@
cs.add(r)
return [r for r in s if r in cs]
+def kwtransplanted(repo, ctx, **args):
+ """:transplanted: String. The node identifier of the transplanted
+ changeset if any."""
+ n = ctx.extra().get('transplant_source')
+ return n and revlog.hex(n) or ''
+
def extsetup(ui):
revset.symbols['transplanted'] = revsettransplanted
+ templatekw.keywords['transplanted'] = kwtransplanted
cmdtable = {
"transplant":
@@ -632,4 +644,4 @@
}
# tell hggettext to extract docstrings from these functions:
-i18nfunctions = [revsettransplanted]
+i18nfunctions = [revsettransplanted, kwtransplanted]
--- a/hgext/zeroconf/Zeroconf.py Fri Apr 29 11:04:39 2011 +0200
+++ b/hgext/zeroconf/Zeroconf.py Fri Apr 29 11:10:11 2011 +0200
@@ -237,14 +237,14 @@
"""Class accessor"""
try:
return _CLASSES[clazz]
- except:
+ except KeyError:
return "?(%s)" % (clazz)
def getType(self, type):
"""Type accessor"""
try:
return _TYPES[type]
- except:
+ except KeyError:
return "?(%s)" % (type)
def toString(self, hdr, other):
@@ -360,7 +360,7 @@
"""String representation"""
try:
return socket.inet_ntoa(self.address)
- except:
+ except Exception:
return self.address
class DNSHinfo(DNSRecord):
@@ -790,7 +790,7 @@
"""Adds an entry"""
try:
list = self.cache[entry.key]
- except:
+ except KeyError:
list = self.cache[entry.key] = []
list.append(entry)
@@ -799,7 +799,7 @@
try:
list = self.cache[entry.key]
list.remove(entry)
- except:
+ except KeyError:
pass
def get(self, entry):
@@ -808,7 +808,7 @@
try:
list = self.cache[entry.key]
return list[list.index(entry)]
- except:
+ except KeyError:
return None
def getByDetails(self, name, type, clazz):
@@ -821,7 +821,7 @@
"""Returns a list of entries whose key matches the name."""
try:
return self.cache[name]
- except:
+ except KeyError:
return []
def entries(self):
@@ -829,7 +829,7 @@
def add(x, y): return x+y
try:
return reduce(add, self.cache.values())
- except:
+ except Exception:
return []
@@ -869,10 +869,10 @@
for socket in rr:
try:
self.readers[socket].handle_read()
- except:
+ except Exception:
if not globals()['_GLOBAL_DONE']:
traceback.print_exc()
- except:
+ except Exception:
pass
def getReaders(self):
@@ -988,7 +988,7 @@
callback = lambda x: self.listener.removeService(x, self.type, record.alias)
self.list.append(callback)
return
- except:
+ except Exception:
if not expired:
self.services[record.alias.lower()] = record
callback = lambda x: self.listener.addService(x, self.type, record.alias)
@@ -1117,7 +1117,7 @@
result[key] = value
self.properties = result
- except:
+ except Exception:
traceback.print_exc()
self.properties = None
@@ -1255,7 +1255,7 @@
try:
self.socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
self.socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEPORT, 1)
- except:
+ except Exception:
# SO_REUSEADDR should be equivalent to SO_REUSEPORT for
# multicast UDP sockets (p 731, "TCP/IP Illustrated,
# Volume 2"), but some BSD-derived systems require
@@ -1270,7 +1270,7 @@
self.socket.setsockopt(socket.SOL_IP, socket.IP_MULTICAST_LOOP, 1)
try:
self.socket.bind(self.group)
- except:
+ except Exception:
# Some versions of linux raise an exception even though
# the SO_REUSE* options have been set, so ignore it
#
@@ -1370,7 +1370,7 @@
self.servicetypes[info.type]-=1
else:
del self.servicetypes[info.type]
- except:
+ except KeyError:
pass
now = currentTimeMillis()
nextTime = now
@@ -1455,7 +1455,7 @@
try:
self.listeners.remove(listener)
self.notifyAll()
- except:
+ except Exception:
pass
def updateRecord(self, now, rec):
@@ -1528,7 +1528,7 @@
out.addAnswer(msg, DNSText(question.name, _TYPE_TXT, _CLASS_IN | _CLASS_UNIQUE, _DNS_TTL, service.text))
if question.type == _TYPE_SRV:
out.addAdditionalAnswer(DNSAddress(service.server, _TYPE_A, _CLASS_IN | _CLASS_UNIQUE, _DNS_TTL, service.address))
- except:
+ except Exception:
traceback.print_exc()
if out is not None and out.answers:
@@ -1541,7 +1541,7 @@
#temp = DNSIncoming(out.packet())
try:
self.socket.sendto(out.packet(), 0, (addr, port))
- except:
+ except Exception:
# Ignore this, it may be a temporary loss of network connection
pass
--- a/i18n/da.po Fri Apr 29 11:04:39 2011 +0200
+++ b/i18n/da.po Fri Apr 29 11:10:11 2011 +0200
@@ -17,8 +17,8 @@
msgstr ""
"Project-Id-Version: Mercurial\n"
"Report-Msgid-Bugs-To: <mercurial-devel@selenic.com>\n"
-"POT-Creation-Date: 2011-03-16 17:41+0100\n"
-"PO-Revision-Date: 2011-03-16 17:42+0100\n"
+"POT-Creation-Date: 2011-04-19 09:32+0200\n"
+"PO-Revision-Date: 2011-04-19 09:45+0200\n"
"Last-Translator: <mg@lazybytes.net>\n"
"Language-Team: Danish\n"
"Language: Danish\n"
@@ -294,140 +294,231 @@
msgid ""
"This hook extension adds comments on bugs in Bugzilla when changesets\n"
-"that refer to bugs by Bugzilla ID are seen. The hook does not change\n"
-"bug status."
-msgstr ""
-
-msgid ""
-"The hook updates the Bugzilla database directly. Only Bugzilla\n"
-"installations using MySQL are supported."
-msgstr ""
-
-msgid ""
-"The hook relies on a Bugzilla script to send bug change notification\n"
-"emails. That script changes between Bugzilla versions; the\n"
-"'processmail' script used prior to 2.18 is replaced in 2.18 and\n"
-"subsequent versions by 'config/sendbugmail.pl'. Note that these will\n"
-"be run by Mercurial as the user pushing the change; you will need to\n"
-"ensure the Bugzilla install file permissions are set appropriately."
-msgstr ""
-
-msgid ""
-"The extension is configured through three different configuration\n"
-"sections. These keys are recognized in the [bugzilla] section:"
-msgstr ""
-
-msgid ""
-"host\n"
-" Hostname of the MySQL server holding the Bugzilla database."
-msgstr ""
-
-msgid ""
-"db\n"
-" Name of the Bugzilla database in MySQL. Default 'bugs'."
-msgstr ""
-
-msgid ""
-"user\n"
-" Username to use to access MySQL server. Default 'bugs'."
-msgstr ""
-
-msgid ""
-"password\n"
+"that refer to bugs by Bugzilla ID are seen. The comment is formatted using\n"
+"the Mercurial template mechanism."
+msgstr ""
+
+msgid "The hook does not change bug status."
+msgstr ""
+
+msgid "Three basic modes of access to Bugzilla are provided:"
+msgstr ""
+
+msgid ""
+"1. Access via the Bugzilla XMLRPC interface. Requires Bugzilla 3.4 or later."
+msgstr ""
+
+msgid ""
+"2. Check data via the Bugzilla XMLRPC interface and submit bug change\n"
+" via email to Bugzilla email interface. Requires Bugzilla 3.4 or later."
+msgstr ""
+
+msgid ""
+"3. Writing directly to the Bugzilla database. Only Bugzilla installations\n"
+" using MySQL are supported. Requires Python MySQLdb."
+msgstr ""
+
+msgid ""
+"Writing directly to the database is susceptible to schema changes, and\n"
+"relies on a Bugzilla contrib script to send out bug change\n"
+"notification emails. This script runs as the user running Mercurial,\n"
+"must be run on the host with the Bugzilla install, and requires\n"
+"permission to read Bugzilla configuration details and the necessary\n"
+"MySQL user and password to have full access rights to the Bugzilla\n"
+"database. For these reasons this access mode is now considered\n"
+"deprecated, and will not be updated for new Bugzilla versions going\n"
+"forward."
+msgstr ""
+
+msgid ""
+"Access via XMLRPC needs a Bugzilla username and password to be specified\n"
+"in the configuration. Comments are added under that username. Since the\n"
+"configuration must be readable by all Mercurial users, it is recommended\n"
+"that the rights of that user are restricted in Bugzilla to the minimum\n"
+"necessary to add comments."
+msgstr ""
+
+msgid ""
+"Access via XMLRPC/email uses XMLRPC to query Bugzilla, but sends\n"
+"email to the Bugzilla email interface to submit comments to bugs.\n"
+"The From: address in the email is set to the email address of the Mercurial\n"
+"user, so the comment appears to come from the Mercurial user. In the event\n"
+"that the Mercurial user email is not recognised by Bugzilla as a Bugzilla\n"
+"user, the email associated with the Bugzilla username used to log into\n"
+"Bugzilla is used instead as the source of the comment."
+msgstr ""
+
+msgid "Configuration items common to all access modes:"
+msgstr ""
+
+msgid ""
+"bugzilla.version\n"
+" This access type to use. Values recognised are:"
+msgstr ""
+
+msgid ""
+" :``xmlrpc``: Bugzilla XMLRPC interface.\n"
+" :``xmlrpc+email``: Bugzilla XMLRPC and email interfaces.\n"
+" :``3.0``: MySQL access, Bugzilla 3.0 and later.\n"
+" :``2.18``: MySQL access, Bugzilla 2.18 and up to but not\n"
+" including 3.0.\n"
+" :``2.16``: MySQL access, Bugzilla 2.16 and up to but not\n"
+" including 2.18."
+msgstr ""
+
+msgid ""
+"bugzilla.regexp\n"
+" Regular expression to match bug IDs in changeset commit message.\n"
+" Must contain one \"()\" group. The default expression matches ``Bug\n"
+" 1234``, ``Bug no. 1234``, ``Bug number 1234``, ``Bugs 1234,5678``,\n"
+" ``Bug 1234 and 5678`` and variations thereof. Matching is case\n"
+" insensitive."
+msgstr ""
+
+msgid ""
+"bugzilla.style\n"
+" The style file to use when formatting comments."
+msgstr ""
+
+msgid ""
+"bugzilla.template\n"
+" Template to use when formatting comments. Overrides style if\n"
+" specified. In addition to the usual Mercurial keywords, the\n"
+" extension specifies:"
+msgstr ""
+
+msgid ""
+" :``{bug}``: The Bugzilla bug ID.\n"
+" :``{root}``: The full pathname of the Mercurial repository.\n"
+" :``{webroot}``: Stripped pathname of the Mercurial repository.\n"
+" :``{hgweb}``: Base URL for browsing Mercurial repositories."
+msgstr ""
+
+msgid ""
+" Default ``changeset {node|short} in repo {root} refers to bug\n"
+" {bug}.\\ndetails:\\n\\t{desc|tabindent}``"
+msgstr ""
+
+msgid ""
+"bugzilla.strip\n"
+" The number of path separator characters to strip from the front of\n"
+" the Mercurial repository path (``{root}`` in templates) to produce\n"
+" ``{webroot}``. For example, a repository with ``{root}``\n"
+" ``/var/local/my-project`` with a strip of 2 gives a value for\n"
+" ``{webroot}`` of ``my-project``. Default 0."
+msgstr ""
+
+msgid ""
+"web.baseurl\n"
+" Base URL for browsing Mercurial repositories. Referenced from\n"
+" templates as ``{hgweb}``."
+msgstr ""
+
+msgid "Configuration items common to XMLRPC+email and MySQL access modes:"
+msgstr ""
+
+msgid ""
+"bugzilla.usermap\n"
+" Path of file containing Mercurial committer email to Bugzilla user email\n"
+" mappings. If specified, the file should contain one mapping per\n"
+" line::"
+msgstr ""
+
+msgid " committer = Bugzilla user"
+msgstr ""
+
+msgid " See also the ``[usermap]`` section."
+msgstr ""
+
+msgid ""
+"The ``[usermap]`` section is used to specify mappings of Mercurial\n"
+"committer email to Bugzilla user email. See also ``bugzilla.usermap``.\n"
+"Contains entries of the form ``committer = Bugzilla user``."
+msgstr ""
+
+msgid "XMLRPC access mode configuration:"
+msgstr ""
+
+msgid ""
+"bugzilla.bzurl\n"
+" The base URL for the Bugzilla installation.\n"
+" Default ``http://localhost/bugzilla``."
+msgstr ""
+
+msgid ""
+"bugzilla.user\n"
+" The username to use to log into Bugzilla via XMLRPC. Default\n"
+" ``bugs``."
+msgstr ""
+
+msgid ""
+"bugzilla.password\n"
+" The password for Bugzilla login."
+msgstr ""
+
+msgid ""
+"XMLRPC+email access mode uses the XMLRPC access mode configuration items,\n"
+"and also:"
+msgstr ""
+
+msgid ""
+"bugzilla.bzemail\n"
+" The Bugzilla email address."
+msgstr ""
+
+msgid ""
+"In addition, the Mercurial email settings must be configured. See the\n"
+"documentation in hgrc(5), sections ``[email]`` and ``[smtp]``."
+msgstr ""
+
+msgid "MySQL access mode configuration:"
+msgstr ""
+
+msgid ""
+"bugzilla.host\n"
+" Hostname of the MySQL server holding the Bugzilla database.\n"
+" Default ``localhost``."
+msgstr ""
+
+msgid ""
+"bugzilla.db\n"
+" Name of the Bugzilla database in MySQL. Default ``bugs``."
+msgstr ""
+
+msgid ""
+"bugzilla.user\n"
+" Username to use to access MySQL server. Default ``bugs``."
+msgstr ""
+
+msgid ""
+"bugzilla.password\n"
" Password to use to access MySQL server."
msgstr ""
msgid ""
-"timeout\n"
+"bugzilla.timeout\n"
" Database connection timeout (seconds). Default 5."
msgstr ""
msgid ""
-"version\n"
-" Bugzilla version. Specify '3.0' for Bugzilla versions 3.0 and later,\n"
-" '2.18' for Bugzilla versions from 2.18 and '2.16' for versions prior\n"
-" to 2.18."
-msgstr ""
-
-msgid ""
-"bzuser\n"
+"bugzilla.bzuser\n"
" Fallback Bugzilla user name to record comments with, if changeset\n"
" committer cannot be found as a Bugzilla user."
msgstr ""
msgid ""
-"bzdir\n"
+"bugzilla.bzdir\n"
" Bugzilla install directory. Used by default notify. Default\n"
-" '/var/www/html/bugzilla'."
-msgstr ""
-
-msgid ""
-"notify\n"
+" ``/var/www/html/bugzilla``."
+msgstr ""
+
+msgid ""
+"bugzilla.notify\n"
" The command to run to get Bugzilla to send bug change notification\n"
-" emails. Substitutes from a map with 3 keys, 'bzdir', 'id' (bug id)\n"
-" and 'user' (committer bugzilla email). Default depends on version;\n"
-" from 2.18 it is \"cd %(bzdir)s && perl -T contrib/sendbugmail.pl\n"
-" %(id)s %(user)s\"."
-msgstr ""
-
-msgid ""
-"regexp\n"
-" Regular expression to match bug IDs in changeset commit message.\n"
-" Must contain one \"()\" group. The default expression matches 'Bug\n"
-" 1234', 'Bug no. 1234', 'Bug number 1234', 'Bugs 1234,5678', 'Bug\n"
-" 1234 and 5678' and variations thereof. Matching is case insensitive."
-msgstr ""
-
-msgid ""
-"style\n"
-" The style file to use when formatting comments."
-msgstr ""
-
-msgid ""
-"template\n"
-" Template to use when formatting comments. Overrides style if\n"
-" specified. In addition to the usual Mercurial keywords, the\n"
-" extension specifies::"
-msgstr ""
-
-msgid ""
-" {bug} The Bugzilla bug ID.\n"
-" {root} The full pathname of the Mercurial repository.\n"
-" {webroot} Stripped pathname of the Mercurial repository.\n"
-" {hgweb} Base URL for browsing Mercurial repositories."
-msgstr ""
-
-msgid ""
-" Default 'changeset {node|short} in repo {root} refers '\n"
-" 'to bug {bug}.\\ndetails:\\n\\t{desc|tabindent}'"
-msgstr ""
-
-msgid ""
-"strip\n"
-" The number of slashes to strip from the front of {root} to produce\n"
-" {webroot}. Default 0."
-msgstr ""
-
-msgid ""
-"usermap\n"
-" Path of file containing Mercurial committer ID to Bugzilla user ID\n"
-" mappings. If specified, the file should contain one mapping per\n"
-" line, \"committer\"=\"Bugzilla user\". See also the [usermap] section."
-msgstr ""
-
-msgid ""
-"The [usermap] section is used to specify mappings of Mercurial\n"
-"committer ID to Bugzilla user ID. See also [bugzilla].usermap.\n"
-"\"committer\"=\"Bugzilla user\""
-msgstr ""
-
-msgid "Finally, the [web] section supports one entry:"
-msgstr ""
-
-msgid ""
-"baseurl\n"
-" Base URL for browsing Mercurial repositories. Reference from\n"
-" templates as {hgweb}."
+" emails. Substitutes from a map with 3 keys, ``bzdir``, ``id`` (bug\n"
+" id) and ``user`` (committer bugzilla email). Default depends on\n"
+" version; from 2.18 it is \"cd %(bzdir)s && perl -T\n"
+" contrib/sendbugmail.pl %(id)s %(user)s\"."
msgstr ""
msgid "Activating the extension::"
@@ -446,13 +537,92 @@
" incoming.bugzilla = python:hgext.bugzilla.hook"
msgstr ""
-msgid "Example configuration:"
-msgstr ""
-
-msgid ""
-"This example configuration is for a collection of Mercurial\n"
-"repositories in /var/local/hg/repos/ used with a local Bugzilla 3.2\n"
-"installation in /opt/bugzilla-3.2. ::"
+msgid "Example configurations:"
+msgstr "Eksempelkonfigurationer:"
+
+msgid ""
+"XMLRPC example configuration. This uses the Bugzilla at\n"
+"``http://my-project.org/bugzilla``, logging in as user\n"
+"``bugmail@my-project.org`` with password ``plugh``. It is used with a\n"
+"collection of Mercurial repositories in ``/var/local/hg/repos/``,\n"
+"with a web interface at ``http://my-project.org/hg``. ::"
+msgstr ""
+
+msgid ""
+" [bugzilla]\n"
+" bzurl=http://my-project.org/bugzilla\n"
+" user=bugmail@my-project.org\n"
+" password=plugh\n"
+" version=xmlrpc\n"
+" template=Changeset {node|short} in {root|basename}.\n"
+" {hgweb}/{webroot}/rev/{node|short}\\n\n"
+" {desc}\\n\n"
+" strip=5"
+msgstr ""
+" [bugzilla]\n"
+" bzurl=http://my-project.org/bugzilla\n"
+" user=bugmail@my-project.org\n"
+" password=plugh\n"
+" version=xmlrpc\n"
+" template=Changeset {node|short} in {root|basename}.\n"
+" {hgweb}/{webroot}/rev/{node|short}\\n\n"
+" {desc}\\n\n"
+" strip=5"
+
+msgid ""
+" [web]\n"
+" baseurl=http://my-project.org/hg"
+msgstr ""
+" [web]\n"
+" baseurl=http://my-project.org/hg"
+
+msgid ""
+"XMLRPC+email example configuration. This uses the Bugzilla at\n"
+"``http://my-project.org/bugzilla``, logging in as user\n"
+"``bugmail@my-project.org`` wityh password ``plugh``. It is used with a\n"
+"collection of Mercurial repositories in ``/var/local/hg/repos/``,\n"
+"with a web interface at ``http://my-project.org/hg``. Bug comments\n"
+"are sent to the Bugzilla email address\n"
+"``bugzilla@my-project.org``. ::"
+msgstr ""
+
+msgid ""
+" [bugzilla]\n"
+" bzurl=http://my-project.org/bugzilla\n"
+" user=bugmail@my-project.org\n"
+" password=plugh\n"
+" version=xmlrpc\n"
+" bzemail=bugzilla@my-project.org\n"
+" template=Changeset {node|short} in {root|basename}.\n"
+" {hgweb}/{webroot}/rev/{node|short}\\n\n"
+" {desc}\\n\n"
+" strip=5"
+msgstr ""
+" [bugzilla]\n"
+" bzurl=http://my-project.org/bugzilla\n"
+" user=bugmail@my-project.org\n"
+" password=plugh\n"
+" version=xmlrpc\n"
+" bzemail=bugzilla@my-project.org\n"
+" template=Changeset {node|short} in {root|basename}.\n"
+" {hgweb}/{webroot}/rev/{node|short}\\n\n"
+" {desc}\\n\n"
+" strip=5"
+
+msgid ""
+" [usermap]\n"
+" user@emaildomain.com=user.name@bugzilladomain.com"
+msgstr ""
+" [usermap]\n"
+" user@emaildomain.com=user.name@bugzilladomain.com"
+
+msgid ""
+"MySQL example configuration. This has a local Bugzilla 3.2 installation\n"
+"in ``/opt/bugzilla-3.2``. The MySQL database is on ``localhost``,\n"
+"the Bugzilla database name is ``bugs`` and MySQL is\n"
+"accessed with MySQL username ``bugs`` password ``XYZZY``. It is used\n"
+"with a collection of Mercurial repositories in ``/var/local/hg/repos/``,\n"
+"with a web interface at ``http://my-project.org/hg``. ::"
msgstr ""
msgid ""
@@ -478,32 +648,22 @@
" {desc}\\n\n"
" strip=5"
-msgid ""
-" [web]\n"
-" baseurl=http://dev.domain.com/hg"
-msgstr ""
-" [web]\n"
-" baseurl=http://dev.domain.com/hg"
-
-msgid ""
-" [usermap]\n"
-" user@emaildomain.com=user.name@bugzilladomain.com"
-msgstr ""
-" [usermap]\n"
-" user@emaildomain.com=user.name@bugzilladomain.com"
-
-msgid "Commits add a comment to the Bugzilla bug record of the form::"
+msgid "All the above add a comment to the Bugzilla bug record of the form::"
msgstr ""
msgid ""
" Changeset 3b16791d6642 in repository-name.\n"
-" http://dev.domain.com/hg/repository-name/rev/3b16791d6642"
+" http://my-project.org/hg/repository-name/rev/3b16791d6642"
msgstr ""
msgid " Changeset commit comment. Bug 1234.\n"
msgstr ""
#, python-format
+msgid "python mysql support not available: %s"
+msgstr "python mysql-understøttelse ikke tilgængelig: %s"
+
+#, python-format
msgid "connecting to %s:%s as %s, password %s\n"
msgstr "forbinder til %s:%s som %s, kodeord %s\n"
@@ -552,6 +712,13 @@
msgid "cannot find bugzilla user id for %s or %s"
msgstr "kan ikke finde bugzilla bruger-id for %s eller %s"
+msgid "configuration 'bzemail' missing"
+msgstr "konfigurationen af 'bzemail' mangler"
+
+#, python-format
+msgid "default bugzilla user %s email not found"
+msgstr ""
+
#, python-format
msgid "bugzilla version %s not supported"
msgstr "bugzilla version %s ikke understøttet"
@@ -563,16 +730,12 @@
msgstr ""
#, python-format
-msgid "python mysql support not available: %s"
-msgstr "python mysql-understøttelse ikke tilgængelig: %s"
-
-#, python-format
msgid "hook type %s does not pass a changeset id"
msgstr ""
#, python-format
-msgid "database error: %s"
-msgstr "databasefejl: %s"
+msgid "Bugzilla error: %s"
+msgstr "Bugzilla fejl: %s"
msgid "command to display child changesets"
msgstr "kommando til at vise børne-ændringer"
@@ -736,12 +899,11 @@
msgstr "farvelæg output for nogle kommandoer"
msgid ""
-"This extension modifies the status and resolve commands to add color to "
-"their\n"
-"output to reflect file status, the qseries command to add color to reflect\n"
-"patch status (applied, unapplied, missing), and to diff-related\n"
-"commands to highlight additions, removals, diff headers, and trailing\n"
-"whitespace."
+"This extension modifies the status and resolve commands to add color\n"
+"to their output to reflect file status, the qseries command to add\n"
+"color to reflect patch status (applied, unapplied, missing), and to\n"
+"diff-related commands to highlight additions, removals, diff headers,\n"
+"and trailing whitespace."
msgstr ""
"Denne udvidelse ændrer status- og resolve-kommandoerne så de tilføjer\n"
"farve til deres output for at afspejle filstatus, qseries-kommandoen\n"
@@ -1332,6 +1494,15 @@
msgid "hg debugcvsps [OPTION]... [PATH]..."
msgstr "hg debugcvsps [TILVALG]... [STI]..."
+msgid ":svnrev: String. Converted subversion revision number."
+msgstr ""
+
+msgid ":svnpath: String. Converted subversion revision project path."
+msgstr ""
+
+msgid ":svnuuid: String. Converted subversion revision repository identifier."
+msgstr ""
+
#, python-format
msgid "%s does not look like a Bazaar repository"
msgstr "%s ser ikke ud som et Bazaar depot"
@@ -1664,6 +1835,9 @@
msgid "updating tags\n"
msgstr "opdaterer mærkater\n"
+msgid "updating bookmarks\n"
+msgstr "opdaterer bogmærker\n"
+
#, python-format
msgid "%s is not a valid start revision"
msgstr "%s er ikke en gyldig startrevision"
@@ -1676,10 +1850,45 @@
msgid "%s does not look like a monotone repository"
msgstr "%s ser ikke ud som et monotone depot"
+msgid "bad mtn packet - no end of commandnbr"
+msgstr ""
+
+#, python-format
+msgid "bad mtn packet - bad stream type %s"
+msgstr ""
+
+msgid "bad mtn packet - no divider before size"
+msgstr ""
+
+msgid "bad mtn packet - no end of packet size"
+msgstr ""
+
+#, python-format
+msgid "bad mtn packet - bad packet size %s"
+msgstr ""
+
+#, python-format
+msgid "bad mtn packet - unable to read full packet read %s of %s"
+msgstr ""
+
+#, python-format
+msgid "mtn command '%s' returned %s"
+msgstr "mtn kommando '%s' returnerede %s"
+
#, python-format
msgid "copying file in renamed directory from '%s' to '%s'"
msgstr "kopierer fil i omdøbt katalog fra '%s' til '%s'"
+msgid "unable to determine mtn automate interface version"
+msgstr ""
+
+#, python-format
+msgid "mtn automate stdio header unexpected: %s"
+msgstr ""
+
+msgid "failed to reach end of mtn automate stdio headers"
+msgstr ""
+
#, python-format
msgid "%s does not look like a P4 repository"
msgstr "%s ser ikke ud som et P4 depot"
@@ -1935,11 +2144,13 @@
msgid ""
"The ``win32text.forbid*`` hooks provided by the win32text extension\n"
-"have been unified into a single hook named ``eol.hook``. The hook will\n"
-"lookup the expected line endings from the ``.hgeol`` file, which means\n"
-"you must migrate to a ``.hgeol`` file first before using the hook.\n"
-"Remember to enable the eol extension in the repository where you\n"
-"install the hook."
+"have been unified into a single hook named ``eol.checkheadshook``. The\n"
+"hook will lookup the expected line endings from the ``.hgeol`` file,\n"
+"which means you must migrate to a ``.hgeol`` file first before using\n"
+"the hook. ``eol.checkheadshook`` only checks heads, intermediate\n"
+"invalid revisions will be pushed. To forbid them completely, use the\n"
+"``eol.checkallhook`` hook. These hooks are best used as\n"
+"``pretxnchangegroup`` hooks."
msgstr ""
msgid ""
@@ -1950,25 +2161,24 @@
"glob-mønstre.\n"
#, python-format
-msgid "%s should not have CRLF line endings"
-msgstr "%s skulle ikke have CRLF linieskift"
-
-#, python-format
-msgid "%s should not have LF line endings"
-msgstr "%s skulle ikke have LF linieskift"
+msgid "ignoring unknown EOL style '%s' from %s\n"
+msgstr "ignorerer ukendt linieskifttype '%s' fra %s\n"
#, python-format
msgid "warning: ignoring .hgeol file due to parse error at %s: %s\n"
msgstr ""
+#, python-format
+msgid " %s in %s should not have %s line endings"
+msgstr " %s i %s skulle ikke have %s linieskift"
+
+msgid "end-of-line check failed:\n"
+msgstr ""
+
msgid "the eol extension is incompatible with the win32text extension\n"
msgstr "eol-udvidelsen er inkompatibel med win32text-udvidelsen\n"
#, python-format
-msgid "ignoring unknown EOL style '%s' from %s\n"
-msgstr "ignorerer ukendt linieskifttype '%s' fra %s\n"
-
-#, python-format
msgid "inconsistent newline style in %s\n"
msgstr "inkonsistent linieskift i %s\n"
@@ -2873,17 +3083,17 @@
"available templates and filters."
msgstr ""
-msgid "Three additional date template filters are provided::"
-msgstr ""
-
-msgid ""
-" utcdate \"2006/09/18 15:13:13\"\n"
-" svnutcdate \"2006-09-18 15:13:13Z\"\n"
-" svnisodate \"2006-09-18 08:13:13 -700 (Mon, 18 Sep 2006)\""
-msgstr ""
-" utcdate \"2006/09/18 15:13:13\"\n"
-" svnutcdate \"2006-09-18 15:13:13Z\"\n"
-" svnisodate \"2006-09-18 08:13:13 -700 (Mon, 18 Sep 2006)\""
+msgid "Three additional date template filters are provided:"
+msgstr ""
+
+msgid ""
+":``utcdate``: \"2006/09/18 15:13:13\"\n"
+":``svnutcdate``: \"2006-09-18 15:13:13Z\"\n"
+":``svnisodate``: \"2006-09-18 08:13:13 -700 (Mon, 18 Sep 2006)\""
+msgstr ""
+":``utcdate``: \"2006/09/18 15:13:13\"\n"
+":``svnutcdate``: \"2006-09-18 15:13:13Z\"\n"
+":``svnisodate``: \"2006-09-18 08:13:13 -700 (Mon, 18 Sep 2006)\""
msgid ""
"The default template mappings (view with :hg:`kwdemo -d`) can be\n"
@@ -3609,14 +3819,15 @@
" With -g/--git, patches imported with --rev will use the git diff\n"
" format. See the diffs help topic for information on why this is\n"
" important for preserving rename/copy information and permission\n"
-" changes."
-msgstr ""
-" En eksisterende ændrin kan blive sat under mq kontrol med -r/--rev\n"
+" changes. Use :hg:`qfinish` to remove changesets from mq control."
+msgstr ""
+" En eksisterende ændring kan blive sat under mq kontrol med -r/--rev\n"
" (e.g. qimport --rev tip -n patch vil sætte tip under mq kontrol).\n"
" Med -g/--git vil patches importeret med --rev bruge git diff\n"
" formatet. Se 'hg help diffs' for mere information om hvorfor dette\n"
" er vigtigt for at bevare omdøbnings/kopierings-information og\n"
-" ændriner i rettigheder."
+" ændriner i rettigheder. Brug :hg:`qfinish` for at fjerne ændringer\n"
+" fra mq."
msgid ""
" To import a patch from standard input, pass - as the patch file.\n"
@@ -3929,13 +4140,6 @@
" Når -f/--force er angivet, så vil alle lokale ændringer i de\n"
" rettede filer gå tabt."
-msgid ""
-" Return 0 on succces.\n"
-" "
-msgstr ""
-" Returnerer 0 ved succes.\n"
-" "
-
msgid "no saved queues found, please use -n\n"
msgstr "fandt ingen gemte køer, brug venligst -r\n"
@@ -4053,12 +4257,12 @@
" eksempel::"
msgid ""
-" qguard foo.patch -stable (negative guard)\n"
-" qguard bar.patch +stable (positive guard)\n"
+" qguard foo.patch -- -stable (negative guard)\n"
+" qguard bar.patch +stable (positive guard)\n"
" qselect stable"
msgstr ""
-" qguard foo.patch -stable (negativt filter)\n"
-" qguard bar.patch +stable (positivt filter)\n"
+" qguard foo.patch -- -stable (negativt filter)\n"
+" qguard bar.patch +stable (positivt filter)\n"
" qselect stable"
msgid ""
@@ -4805,8 +5009,8 @@
msgstr ""
msgid ""
-"To configure other defaults, add a section like this to your hgrc\n"
-"file::"
+"To configure other defaults, add a section like this to your\n"
+"configuration file::"
msgstr ""
msgid ""
@@ -5311,6 +5515,9 @@
" "
msgstr ""
+msgid "message can only be specified with collapse"
+msgstr ""
+
msgid "cannot use both abort and continue"
msgstr "abort og continue kan ikke angives samtidig"
@@ -5323,6 +5530,9 @@
msgid "abort and continue do not allow specifying revisions"
msgstr "abort og continue tillader ikke at der angives revisioner"
+msgid "tool option will be ignored\n"
+msgstr ""
+
msgid "cannot specify both a revision and a base"
msgstr "man kan ikke angive både en revision og en basis"
@@ -5335,9 +5545,6 @@
msgid "nothing to rebase\n"
msgstr ""
-msgid "cannot use both keepbranches and extrafn"
-msgstr "man kan ikke bruge både keepbranches og extrafn"
-
msgid "rebasing"
msgstr ""
@@ -5407,6 +5614,12 @@
msgid "collapse the rebased changesets"
msgstr ""
+msgid "use text as collapse commit message"
+msgstr "brug tekst som deponeringsbesked"
+
+msgid "read collapse commit message from file"
+msgstr "læs deponeringsbeskeden fra fil"
+
msgid "keep original changesets"
msgstr "behold de originale ændringer"
@@ -5416,6 +5629,9 @@
msgid "force detaching of source from its original branch"
msgstr ""
+msgid "specify merge tool"
+msgstr "angiv sammenføjningsværktøj"
+
msgid "continue an interrupted rebase"
msgstr ""
@@ -5536,6 +5752,17 @@
msgid " This command is not available when committing a merge."
msgstr ""
+msgid "interactively record a new patch"
+msgstr "vælg ændringer interaktivt til deponering"
+
+msgid ""
+" See :hg:`help qnew` & :hg:`help record` for more information and\n"
+" usage.\n"
+" "
+msgstr ""
+" Se :hg:`help qnew` & :hg:`help record` for mere information.\n"
+" "
+
msgid "'mq' extension not loaded"
msgstr "'mq' udvidelsen er ikke indlæst"
@@ -5641,7 +5868,7 @@
msgstr "sammenkæder"
#, python-format
-msgid "relinked %d files (%d bytes reclaimed)\n"
+msgid "relinked %d files (%s reclaimed)\n"
msgstr ""
msgid "[ORIGIN]"
@@ -5711,6 +5938,10 @@
"same name.\n"
msgstr ""
+#, python-format
+msgid "custom scheme %s:// conflicts with drive letter %s:\\\n"
+msgstr ""
+
msgid "share a common history between several working directories"
msgstr "del en fælles historie mellem flere arbejdsbiblioteker"
@@ -5800,6 +6031,9 @@
msgid "commit failed"
msgstr "deponering fejlede"
+msgid "filter corrupted changeset (no user or date)"
+msgstr "filtrer ødelagte ændringer (ingen bruger eller dato)"
+
msgid ""
"y: transplant this changeset\n"
"n: skip this changeset\n"
@@ -5901,6 +6135,11 @@
" Transplanted changesets in set."
msgstr ""
+msgid ""
+":transplanted: String. The node identifier of the transplanted\n"
+" changeset if any."
+msgstr ""
+
msgid "pull patches from REPO"
msgstr "hiv rettelser fra DEPOT"
@@ -6161,6 +6400,18 @@
msgid "bookmark '%s' contains illegal character"
msgstr ""
+#, python-format
+msgid "branch %s not found"
+msgstr "gren %s blev ikke fundet"
+
+#, python-format
+msgid "updating bookmark %s\n"
+msgstr "opdaterer bogmærke %s\n"
+
+#, python-format
+msgid "not updating divergent bookmark %s\n"
+msgstr "opdaterer ikke divergerende bogmærke %s\n"
+
msgid "searching for changed bookmarks\n"
msgstr "leder efter ændrede bogmærker\n"
@@ -6699,7 +6950,8 @@
#, python-format
msgid ""
"Not all ancestors of this changeset have been checked.\n"
-"To check the other ancestors, start from the common ancestor, %s.\n"
+"Use bisect --extend to continue the bisection from\n"
+"the common ancestor, %s.\n"
msgstr ""
msgid "Due to skipped revisions, the first good revision could be any of:\n"
@@ -6737,6 +6989,13 @@
msgstr "Ændring %d:%s: %s\n"
#, python-format
+msgid "Extending search to changeset %d:%s\n"
+msgstr "Udvider søgning til ændring %d:%s\n"
+
+msgid "nothing to extend"
+msgstr "kan ikke udvidde"
+
+#, python-format
msgid "Testing changeset %d:%s (%d changesets remaining, ~%d tests)\n"
msgstr "Tester ændring %d:%s (%d ændringer tilbage, ~%d test)\n"
@@ -6781,11 +7040,13 @@
" bookmark-udviddelsen skal være slået til.\n"
" "
-msgid "a bookmark of this name does not exist"
-msgstr "et bogmærke med dette navn findes ikke"
-
-msgid "a bookmark of the same name already exists"
-msgstr "et bogmærke med samme navn findes allerede"
+#, python-format
+msgid "bookmark '%s' does not exist"
+msgstr "mærkaten '%s' eksisterer ikke"
+
+#, python-format
+msgid "bookmark '%s' already exists (use -f to force)"
+msgstr "mærkaten '%s' eksisterer allerede (brug -f for at gennemtvinge)"
msgid "new bookmark name required"
msgstr "nyt bogmærkenavn påkrævet"
@@ -7144,6 +7405,10 @@
msgid "can only close branch heads"
msgstr "kan kun lukke grenhoveder"
+#, python-format
+msgid "nothing changed (%d missing files, see 'hg status')\n"
+msgstr ""
+
msgid "nothing changed\n"
msgstr "ingen ændringer\n"
@@ -7314,6 +7579,29 @@
msgid "only one config item permitted"
msgstr ""
+msgid "test whether node ids are known to a repo"
+msgstr ""
+
+msgid ""
+" Every ID must be a full-length hex node id string. Returns a list of 0s "
+"and 1s\n"
+" indicating unknown/known.\n"
+" "
+msgstr ""
+
+msgid "lists the contents of a bundle"
+msgstr "vis indholdet af et bundt"
+
+msgid "retrieves a bundle from a repo"
+msgstr ""
+
+msgid ""
+" Every ID must be a full-length hex node id string. Saves the bundle to "
+"the\n"
+" given file.\n"
+" "
+msgstr ""
+
msgid "access the pushkey key/value protocol"
msgstr ""
@@ -7412,28 +7700,6 @@
msgid " (templates seem to have been installed incorrectly)\n"
msgstr ""
-msgid "Checking patch...\n"
-msgstr ""
-
-msgid " patch call failed:\n"
-msgstr ""
-
-msgid " unexpected patch output!\n"
-msgstr ""
-
-msgid " patch test failed!\n"
-msgstr ""
-
-msgid ""
-" (Current patch tool may be incompatible with patch, or misconfigured. "
-"Please check your configuration file)\n"
-msgstr ""
-
-msgid ""
-" Internal patcher failure, please report this error to http://mercurial."
-"selenic.com/wiki/BugTracker\n"
-msgstr ""
-
msgid "Checking commit editor...\n"
msgstr ""
@@ -7742,6 +8008,10 @@
"detaljer"
#, python-format
+msgid "use \"hg help %s\" to show the full help text"
+msgstr "brug \"hg help %s\" for at se udførlig hjælp"
+
+#, python-format
msgid "use \"hg -v help%s\" to show builtin aliases and global options"
msgstr ""
"brug \"hg -v help%s\" for at vise indbyggede aliaser og globale "
@@ -7840,7 +8110,14 @@
msgstr ""
msgid ""
-" With no revision, print a summary of the current state of the\n"
+" Print a summary identifiying the repository state at REV\n"
+" using one or two parent hash identifiers, followed by a\n"
+" \"+\" if there are uncommitted changes in the working directory,\n"
+" the branch name (omitted if default) and a list of tags, bookmarks."
+msgstr ""
+
+msgid ""
+" When REV is not given, print a summary of the current state of the\n"
" repository."
msgstr ""
@@ -7849,14 +8126,7 @@
" cause lookup to operate on that repository/bundle."
msgstr ""
-msgid ""
-" This summary identifies the repository state using one or two\n"
-" parent hash identifiers, followed by a \"+\" if there are\n"
-" uncommitted changes in the working directory, a list of tags for\n"
-" this revision and a branch name for non-default branches."
-msgstr ""
-
-msgid "can't query remote revision number, branch, tags, or bookmarks"
+msgid "can't query remote revision number, branch, or tags"
msgstr ""
msgid "import an ordered set of patches"
@@ -8099,7 +8369,7 @@
msgid ""
" ``--tool`` can be used to specify the merge tool used for file\n"
" merges. It overrides the HGMERGE environment variable and your\n"
-" configuration files."
+" configuration files. See :hg:`help merge-tools` for options."
msgstr ""
msgid ""
@@ -8239,6 +8509,12 @@
msgid "(run 'hg heads' to see heads, 'hg merge' to merge)\n"
msgstr "(kør 'hg heads' for at se hoveder, 'hg merge' for at sammenføje)\n"
+msgid "(run 'hg heads .' to see heads, 'hg merge' to merge)\n"
+msgstr "(kør 'hg heads .' for at se hoveder, 'hg merge' for at sammenføje)\n"
+
+msgid "(run 'hg heads' to see heads)\n"
+msgstr "(kør 'hg heads' for at se hoveder)\n"
+
msgid "(run 'hg update' to get a working copy)\n"
msgstr "(kør 'hg update' for at få en arbejdskopi)\n"
@@ -8582,8 +8858,9 @@
msgstr ""
msgid ""
-" If a file has been deleted, it is restored. If the executable mode\n"
-" of a file was changed, it is reset."
+" If a file has been deleted, it is restored. Files scheduled for\n"
+" addition are just unscheduled and left as they are. If the\n"
+" executable mode of a file was changed, it is reset."
msgstr ""
msgid ""
@@ -9333,9 +9610,6 @@
msgid "parent to choose when backing out merge"
msgstr ""
-msgid "specify merge tool"
-msgstr "angiv sammenføjningsværktøj"
-
msgid "revision to backout"
msgstr "revision som skal bakkes ud"
@@ -9354,6 +9628,9 @@
msgid "skip testing changeset"
msgstr "spring testen af denne ændring over"
+msgid "extend the bisect range"
+msgstr "udvid halveringsområdet"
+
msgid "use command to check changeset state"
msgstr "brug kommando for at kontrollere tilstanden af ændringen"
@@ -9474,6 +9751,9 @@
msgid "[OPTION]... TEXT"
msgstr "[TILVALG]... TEKST"
+msgid "show all details"
+msgstr "vis alle detaljer"
+
msgid "[COMMAND]"
msgstr "[KOMMANDO]"
@@ -9510,9 +9790,21 @@
msgid "[PATH]"
msgstr "[STI]"
+msgid "id of head node"
+msgstr ""
+
+msgid "id of common node"
+msgstr "id på fælles knude"
+
+msgid "REPO FILE [-H|-C ID]..."
+msgstr ""
+
msgid "revlog format"
msgstr ""
+msgid "REPO ID..."
+msgstr ""
+
msgid "REPO NAMESPACE [KEY OLD NEW]"
msgstr ""
@@ -9534,12 +9826,18 @@
msgid "do not display the saved mtime"
msgstr "vis ikke den gemte mtime"
+msgid "sort by saved mtime"
+msgstr "sorter efter den gemte mtime"
+
msgid "[OPTION]..."
msgstr "[TILVALG]..."
msgid "revision to check"
msgstr "revision som skal undersøges"
+msgid "REPO [OPTIONS]... [ONE [TWO]]"
+msgstr "DEPOT [TILVALG]... [EN [TO]]"
+
msgid "[OPTION]... ([-c REV] | [-r REV1 [-r REV2]]) [FILE]..."
msgstr "[TILVALG]... ([-c REV] | [-r REV1 [-r REV2]]) [FIL]..."
@@ -10014,10 +10312,6 @@
msgstr "arbejdskatalogtilstand virker beskadiget!"
#, python-format
-msgid "'\\n' and '\\r' disallowed in filenames: %r"
-msgstr "'\\n' og '\\r' må ikke forekomme i filnavne: %r"
-
-#, python-format
msgid "directory %r already in dirstate"
msgstr "katalog %r er allerede i dirstate"
@@ -10566,29 +10860,29 @@
msgstr "- ``1165432709 0`` (Ons 6. dec. 13:18:29 2006 UTC)"
msgid ""
-"This is the internal representation format for dates. unixtime is the\n"
-"number of seconds since the epoch (1970-01-01 00:00 UTC). offset is\n"
-"the offset of the local timezone, in seconds west of UTC (negative if\n"
-"the timezone is east of UTC)."
-msgstr ""
-"Dette er den interne repræsentation af datoer. unixtime er\n"
+"This is the internal representation format for dates. The first number\n"
+"is the number of seconds since the epoch (1970-01-01 00:00 UTC). The\n"
+"second is the offset of the local timezone, in seconds west of UTC\n"
+"(negative if the timezone is east of UTC)."
+msgstr ""
+"Dette er den interne repræsentation af datoer. Det første tal er\n"
"antallet af sekunder siden begyndelsen af epoken (1970-01-01 00:00\n"
-"UTC). offset er den lokale tidszone, angivet i antal sekunder vest\n"
-"for UTC (negativ hvis tidszonen er øst for UTC)."
+"UTC). Det andet tal er den lokale tidszone, angivet i antal sekunder\n"
+"vest for UTC (negativ hvis tidszonen er øst for UTC)."
msgid "The log command also accepts date ranges:"
msgstr "Kommandoen log accepterer også datointervaller:"
msgid ""
-"- ``<{datetime}`` - at or before a given date/time\n"
-"- ``>{datetime}`` - on or after a given date/time\n"
-"- ``{datetime} to {datetime}`` - a date range, inclusive\n"
-"- ``-{days}`` - within a given number of days of today\n"
-msgstr ""
-"- ``<{date}`` - på eller før den angivne dato/tidspunkt\n"
-"- ``>{date}`` - på eller efter den angivne dato/tidspunkt\n"
-"- ``{date} to {date}`` - et datointerval, inklusiv endepunkterne\n"
-"- ``-{days}`` - indenfor et angivet antal dage, fra dags dato\n"
+"- ``<DATE`` - at or before a given date/time\n"
+"- ``>DATE`` - on or after a given date/time\n"
+"- ``DATE to DATE`` - a date range, inclusive\n"
+"- ``-DAYS`` - within a given number of days of today\n"
+msgstr ""
+"- ``<DATO`` - på eller før den angivne dato/tidspunkt\n"
+"- ``>DATO`` - på eller efter den angivne dato/tidspunkt\n"
+"- ``DATO to DATO`` - et datointerval, inklusiv endepunkterne\n"
+"- ``-DAGE`` - indenfor et angivet antal dage, fra dags dato\n"
msgid ""
"Mercurial's default format for showing changes between two versions of\n"
@@ -10714,6 +11008,18 @@
msgstr ""
msgid ""
+"HGPLAINEXCEPT\n"
+" This is a comma-separated list of features to preserve when\n"
+" HGPLAIN is enabled. Currently the only value supported is \"i18n\",\n"
+" which preserves internationalization in plain mode."
+msgstr ""
+
+msgid ""
+" Setting HGPLAINEXCEPT to anything (even an empty string) will\n"
+" enable plain mode."
+msgstr ""
+
+msgid ""
"HGUSER\n"
" This is the string used as the author of a commit. If not set,\n"
" available values will be considered in this order:"
@@ -11367,8 +11673,8 @@
msgstr ""
msgid ""
-"This file uses the same syntax as hgrc configuration files, but only\n"
-"the following sections are recognized:"
+"This file uses the same syntax as other Mercurial configuration files,\n"
+"but only the following sections are recognized:"
msgstr ""
msgid ""
@@ -11382,7 +11688,8 @@
msgid ""
"The ``web`` section can specify all the settings described in the web\n"
-"section of the hgrc documentation."
+"section of the hgrc(5) documentation. See :hg:`help config` for\n"
+"information on where to find the manual page."
msgstr ""
msgid ""
@@ -11889,7 +12196,7 @@
msgid " hg log -r \"1.3::1.5 and keyword(bug) and file('hgext/*')\""
msgstr " hg log -r \"1.3::1.5 and keyword(bug) and file(\"hgext/*\")\""
-msgid "- Changesets in committed May 2008, sorted by user::"
+msgid "- Changesets committed in May 2008, sorted by user::"
msgstr ""
msgid " hg log -r \"sort(date('May 2008'), user)\""
@@ -12134,78 +12441,7 @@
"keywords are usually available for templating a log-like command:"
msgstr ""
-msgid ":author: String. The unmodified author of the changeset."
-msgstr ""
-
-msgid ""
-":branch: String. The name of the branch on which the changeset was\n"
-" committed."
-msgstr ""
-
-msgid ""
-":branches: List of strings. The name of the branch on which the\n"
-" changeset was committed. Will be empty if the branch name was\n"
-" default."
-msgstr ""
-
-msgid ":children: List of strings. The children of the changeset."
-msgstr ""
-
-msgid ":date: Date information. The date when the changeset was committed."
-msgstr ""
-
-msgid ":desc: String. The text of the changeset description."
-msgstr ""
-
-msgid ""
-":diffstat: String. Statistics of changes with the following format:\n"
-" \"modified files: +added/-removed lines\""
-msgstr ""
-
-msgid ""
-":files: List of strings. All files modified, added, or removed by this\n"
-" changeset."
-msgstr ""
-
-msgid ":file_adds: List of strings. Files added by this changeset."
-msgstr ""
-
-msgid ""
-":file_copies: List of strings. Files copied in this changeset with\n"
-" their sources."
-msgstr ""
-
-msgid ""
-":file_copies_switch: List of strings. Like \"file_copies\" but displayed\n"
-" only if the --copied switch is set."
-msgstr ""
-
-msgid ":file_mods: List of strings. Files modified by this changeset."
-msgstr ""
-
-msgid ":file_dels: List of strings. Files removed by this changeset."
-msgstr ""
-
-msgid ""
-":node: String. The changeset identification hash, as a 40 hexadecimal\n"
-" digit string."
-msgstr ""
-
-msgid ":parents: List of strings. The parents of the changeset."
-msgstr ""
-
-msgid ":rev: Integer. The repository-local changeset revision number."
-msgstr ""
-
-msgid ":tags: List of strings. Any tags associated with the changeset."
-msgstr ""
-
-msgid ""
-":latesttag: String. Most recent global tag in the ancestors of this\n"
-" changeset."
-msgstr ""
-
-msgid ":latesttagdistance: Integer. Longest path to the latest tag."
+msgid ".. keywordsmarker"
msgstr ""
msgid ""
@@ -12227,131 +12463,7 @@
msgid "List of filters:"
msgstr ""
-msgid ""
-":addbreaks: Any text. Add an XHTML \"<br />\" tag before the end of\n"
-" every line except the last."
-msgstr ""
-
-msgid ""
-":age: Date. Returns a human-readable date/time difference between the\n"
-" given date/time and the current date/time."
-msgstr ""
-
-msgid ""
-":basename: Any text. Treats the text as a path, and returns the last\n"
-" component of the path after splitting by the path separator\n"
-" (ignoring trailing separators). For example, \"foo/bar/baz\" becomes\n"
-" \"baz\" and \"foo/bar//\" becomes \"bar\"."
-msgstr ""
-
-msgid ""
-":stripdir: Treat the text as path and strip a directory level, if\n"
-" possible. For example, \"foo\" and \"foo/bar\" becomes \"foo\"."
-msgstr ""
-
-msgid ""
-":date: Date. Returns a date in a Unix date format, including the\n"
-" timezone: \"Mon Sep 04 15:13:13 2006 0700\"."
-msgstr ""
-
-msgid ""
-":domain: Any text. Finds the first string that looks like an email\n"
-" address, and extracts just the domain component. Example: ``User\n"
-" <user@example.com>`` becomes ``example.com``."
-msgstr ""
-
-msgid ""
-":email: Any text. Extracts the first string that looks like an email\n"
-" address. Example: ``User <user@example.com>`` becomes\n"
-" ``user@example.com``."
-msgstr ""
-
-msgid ""
-":escape: Any text. Replaces the special XML/XHTML characters \"&\", \"<\"\n"
-" and \">\" with XML entities."
-msgstr ""
-
-msgid ""
-":hex: Any text. Convert a binary Mercurial node identifier into\n"
-" its long hexadecimal representation."
-msgstr ""
-
-msgid ":fill68: Any text. Wraps the text to fit in 68 columns."
-msgstr ""
-
-msgid ":fill76: Any text. Wraps the text to fit in 76 columns."
-msgstr ""
-
-msgid ":firstline: Any text. Returns the first line of text."
-msgstr ""
-
-msgid ":nonempty: Any text. Returns '(none)' if the string is empty."
-msgstr ""
-
-msgid ""
-":hgdate: Date. Returns the date as a pair of numbers: \"1157407993\n"
-" 25200\" (Unix timestamp, timezone offset)."
-msgstr ""
-
-msgid ""
-":isodate: Date. Returns the date in ISO 8601 format: \"2009-08-18 13:00\n"
-" +0200\"."
-msgstr ""
-
-msgid ""
-":isodatesec: Date. Returns the date in ISO 8601 format, including\n"
-" seconds: \"2009-08-18 13:00:13 +0200\". See also the rfc3339date\n"
-" filter."
-msgstr ""
-
-msgid ":localdate: Date. Converts a date to local date."
-msgstr ""
-
-msgid ""
-":obfuscate: Any text. Returns the input text rendered as a sequence of\n"
-" XML entities."
-msgstr ""
-
-msgid ":person: Any text. Returns the text before an email address."
-msgstr ""
-
-msgid ""
-":rfc822date: Date. Returns a date using the same format used in email\n"
-" headers: \"Tue, 18 Aug 2009 13:00:13 +0200\"."
-msgstr ""
-
-msgid ""
-":rfc3339date: Date. Returns a date using the Internet date format\n"
-" specified in RFC 3339: \"2009-08-18T13:00:13+02:00\"."
-msgstr ""
-
-msgid ""
-":short: Changeset hash. Returns the short form of a changeset hash,\n"
-" i.e. a 12 hexadecimal digit string."
-msgstr ""
-
-msgid ":shortdate: Date. Returns a date like \"2006-09-18\"."
-msgstr ""
-
-msgid ""
-":stringify: Any type. Turns the value into text by converting values into\n"
-" text and concatenating them."
-msgstr ""
-
-msgid ":strip: Any text. Strips all leading and trailing whitespace."
-msgstr ""
-
-msgid ""
-":tabindent: Any text. Returns the text, with every line except the\n"
-" first starting with a tab character."
-msgstr ""
-
-msgid ""
-":urlescape: Any text. Escapes all \"special\" characters. For example,\n"
-" \"foo bar\" becomes \"foo%20bar\"."
-msgstr ""
-
-msgid ":user: Any text. Returns the user portion of an email address.\n"
+msgid ".. filtersmarker\n"
msgstr ""
msgid "Valid URLs are of the form::"
@@ -12800,14 +12912,6 @@
msgstr ""
#, python-format
-msgid "updating bookmark %s\n"
-msgstr "opdaterer bogmærke %s\n"
-
-#, python-format
-msgid "not updating divergent bookmark %s\n"
-msgstr "opdaterer ikke divergerende bogmærke %s\n"
-
-#, python-format
msgid "%d changesets found\n"
msgstr "fandt %d ændringer\n"
@@ -12918,6 +13022,10 @@
msgstr "ugyldig lokal adresse: %s"
#, python-format
+msgid "'\\n' and '\\r' disallowed in filenames: %r"
+msgstr "'\\n' og '\\r' må ikke forekomme i filnavne: %r"
+
+#, python-format
msgid "failed to remove %s from manifest"
msgstr "kunne ikke fjerne %s fra manifest"
@@ -13004,10 +13112,6 @@
msgid "note: possible conflict - %s was renamed multiple times to:\n"
msgstr ""
-#, python-format
-msgid "branch %s not found"
-msgstr "gren %s blev ikke fundet"
-
msgid "merging with a working directory ancestor has no effect"
msgstr "sammenføjning med en forfader til arbejdskataloget har ingen effekt"
@@ -13122,10 +13226,6 @@
msgstr ""
#, python-format
-msgid "malformed patch %s %s"
-msgstr ""
-
-#, python-format
msgid "unsupported parser state: %s"
msgstr ""
@@ -13238,86 +13338,12 @@
msgstr "ikke en funktion: %s"
msgid ""
-"``id(string)``\n"
-" Revision non-ambiguously specified by the given hex string prefix."
-msgstr ""
-
-#. i18n: "id" is a keyword
-msgid "id requires one argument"
-msgstr "id kræver et argument"
-
-#. i18n: "id" is a keyword
-msgid "id requires a string"
-msgstr "id kræver en streng"
-
-msgid ""
-"``rev(number)``\n"
-" Revision with the given numeric identifier."
-msgstr ""
-
-#. i18n: "rev" is a keyword
-msgid "rev requires one argument"
-msgstr "rev kræver et argument"
-
-#. i18n: "rev" is a keyword
-msgid "rev requires a number"
-msgstr "rev kræver et tal"
-
-#. i18n: "rev" is a keyword
-msgid "rev expects a number"
-msgstr "rev forventer et revisionsnummer"
-
-msgid ""
-"``p1([set])``\n"
-" First parent of changesets in set, or the working directory."
-msgstr ""
-
-msgid ""
-"``p2([set])``\n"
-" Second parent of changesets in set, or the working directory."
-msgstr ""
-
-msgid ""
-"``parents([set])``\n"
-" The set of all parents for all changesets in set, or the working "
-"directory."
-msgstr ""
-
-msgid ""
-"``max(set)``\n"
-" Changeset with highest revision number in set."
-msgstr ""
-
-msgid ""
-"``min(set)``\n"
-" Changeset with lowest revision number in set."
-msgstr ""
-
-msgid ""
-"``limit(set, n)``\n"
-" First n members of set."
-msgstr ""
-
-#. i18n: "limit" is a keyword
-msgid "limit requires two arguments"
-msgstr "limit kræver to argumenter"
-
-#. i18n: "limit" is a keyword
-msgid "limit requires a number"
-msgstr "limit kræver et tal"
-
-#. i18n: "limit" is a keyword
-msgid "limit expects a number"
-msgstr ""
-
-msgid ""
-"``children(set)``\n"
-" Child changesets of changesets in set."
-msgstr ""
-
-msgid ""
-"``branch(set)``\n"
-" All changesets belonging to the branches of changesets in set."
+"``adds(pattern)``\n"
+" Changesets that add a file matching pattern."
+msgstr ""
+
+#. i18n: "adds" is a keyword
+msgid "adds requires a pattern"
msgstr ""
msgid ""
@@ -13339,6 +13365,80 @@
msgstr ""
msgid ""
+"``author(string)``\n"
+" Alias for ``user(string)``."
+msgstr ""
+
+#. i18n: "author" is a keyword
+msgid "author requires a string"
+msgstr ""
+
+msgid ""
+"``bisected(string)``\n"
+" Changesets marked in the specified bisect state (good, bad, skip)."
+msgstr ""
+
+msgid "bisect requires a string"
+msgstr "bisect kræver en streng"
+
+msgid "invalid bisect state"
+msgstr "ugyldig tilstand for halvering"
+
+msgid ""
+"``bookmark([name])``\n"
+" The named bookmark or all bookmarks."
+msgstr ""
+"``bookmark([name])``\n"
+" Det navngivne bogmærke eller alle bogmærker."
+
+#. i18n: "bookmark" is a keyword
+msgid "bookmark takes one or no arguments"
+msgstr "bookmark tager et eller to argumenter"
+
+#. i18n: "bookmark" is a keyword
+msgid "the argument to bookmark must be a string"
+msgstr "argumentet til bookmark skal være en streng"
+
+msgid ""
+"``branch(string or set)``\n"
+" All changesets belonging to the given branch or the branches of the "
+"given\n"
+" changesets."
+msgstr ""
+
+msgid ""
+"``children(set)``\n"
+" Child changesets of changesets in set."
+msgstr ""
+
+msgid ""
+"``closed()``\n"
+" Changeset is closed."
+msgstr ""
+
+#. i18n: "closed" is a keyword
+msgid "closed takes no arguments"
+msgstr ""
+
+msgid ""
+"``contains(pattern)``\n"
+" Revision contains pattern."
+msgstr ""
+
+#. i18n: "contains" is a keyword
+msgid "contains requires a pattern"
+msgstr ""
+
+msgid ""
+"``date(interval)``\n"
+" Changesets within the interval, see :hg:`help dates`."
+msgstr ""
+
+#. i18n: "date" is a keyword
+msgid "date requires a string"
+msgstr ""
+
+msgid ""
"``descendants(set)``\n"
" Changesets which are descendants of changesets in set."
msgstr ""
@@ -13353,22 +13453,12 @@
msgstr ""
msgid ""
-"``date(interval)``\n"
-" Changesets within the interval, see :hg:`help dates`."
-msgstr ""
-
-#. i18n: "date" is a keyword
-msgid "date requires a string"
-msgstr ""
-
-msgid ""
-"``keyword(string)``\n"
-" Search commit message, user name, and names of changed files for\n"
-" string."
-msgstr ""
-
-#. i18n: "keyword" is a keyword
-msgid "keyword requires a string"
+"``all()``\n"
+" All changesets, the same as ``0:tip``."
+msgstr ""
+
+#. i18n: "all" is a keyword
+msgid "all takes no arguments"
msgstr ""
msgid ""
@@ -13386,20 +13476,6 @@
msgstr "ugyldigt søgemønster: %s"
msgid ""
-"``author(string)``\n"
-" Alias for ``user(string)``."
-msgstr ""
-
-#. i18n: "author" is a keyword
-msgid "author requires a string"
-msgstr ""
-
-msgid ""
-"``user(string)``\n"
-" User name is string."
-msgstr ""
-
-msgid ""
"``file(pattern)``\n"
" Changesets affecting files matched by pattern."
msgstr ""
@@ -13409,12 +13485,63 @@
msgstr ""
msgid ""
-"``contains(pattern)``\n"
-" Revision contains pattern."
-msgstr ""
-
-#. i18n: "contains" is a keyword
-msgid "contains requires a pattern"
+"``head()``\n"
+" Changeset is a named branch head."
+msgstr ""
+
+#. i18n: "head" is a keyword
+msgid "head takes no arguments"
+msgstr ""
+
+msgid ""
+"``heads(set)``\n"
+" Members of set with no children in set."
+msgstr ""
+
+msgid ""
+"``keyword(string)``\n"
+" Search commit message, user name, and names of changed files for\n"
+" string."
+msgstr ""
+
+#. i18n: "keyword" is a keyword
+msgid "keyword requires a string"
+msgstr ""
+
+msgid ""
+"``limit(set, n)``\n"
+" First n members of set."
+msgstr ""
+
+#. i18n: "limit" is a keyword
+msgid "limit requires two arguments"
+msgstr "limit kræver to argumenter"
+
+#. i18n: "limit" is a keyword
+msgid "limit requires a number"
+msgstr "limit kræver et tal"
+
+#. i18n: "limit" is a keyword
+msgid "limit expects a number"
+msgstr ""
+
+msgid ""
+"``max(set)``\n"
+" Changeset with highest revision number in set."
+msgstr ""
+
+msgid ""
+"``merge()``\n"
+" Changeset is a merge changeset."
+msgstr ""
+
+#. i18n: "merge" is a keyword
+msgid "merge takes no arguments"
+msgstr ""
+
+msgid ""
+"``min(set)``\n"
+" Changeset with lowest revision number in set."
msgstr ""
msgid ""
@@ -13427,12 +13554,48 @@
msgstr ""
msgid ""
-"``adds(pattern)``\n"
-" Changesets that add a file matching pattern."
-msgstr ""
-
-#. i18n: "adds" is a keyword
-msgid "adds requires a pattern"
+"``id(string)``\n"
+" Revision non-ambiguously specified by the given hex string prefix."
+msgstr ""
+
+#. i18n: "id" is a keyword
+msgid "id requires one argument"
+msgstr "id kræver et argument"
+
+#. i18n: "id" is a keyword
+msgid "id requires a string"
+msgstr "id kræver en streng"
+
+msgid ""
+"``outgoing([path])``\n"
+" Changesets not found in the specified destination repository, or the\n"
+" default push location."
+msgstr ""
+
+#. i18n: "outgoing" is a keyword
+msgid "outgoing requires a repository path"
+msgstr ""
+
+msgid ""
+"``p1([set])``\n"
+" First parent of changesets in set, or the working directory."
+msgstr ""
+
+msgid ""
+"``p2([set])``\n"
+" Second parent of changesets in set, or the working directory."
+msgstr ""
+
+msgid ""
+"``parents([set])``\n"
+" The set of all parents for all changesets in set, or the working "
+"directory."
+msgstr ""
+
+msgid ""
+"``present(set)``\n"
+" An empty set, if any revision in set isn't found; otherwise,\n"
+" all revisions in set."
msgstr ""
msgid ""
@@ -13445,31 +13608,21 @@
msgstr ""
msgid ""
-"``merge()``\n"
-" Changeset is a merge changeset."
-msgstr ""
-
-#. i18n: "merge" is a keyword
-msgid "merge takes no arguments"
-msgstr ""
-
-msgid ""
-"``closed()``\n"
-" Changeset is closed."
-msgstr ""
-
-#. i18n: "closed" is a keyword
-msgid "closed takes no arguments"
-msgstr ""
-
-msgid ""
-"``head()``\n"
-" Changeset is a named branch head."
-msgstr ""
-
-#. i18n: "head" is a keyword
-msgid "head takes no arguments"
-msgstr ""
+"``rev(number)``\n"
+" Revision with the given numeric identifier."
+msgstr ""
+
+#. i18n: "rev" is a keyword
+msgid "rev requires one argument"
+msgstr "rev kræver et argument"
+
+#. i18n: "rev" is a keyword
+msgid "rev requires a number"
+msgstr "rev kræver et tal"
+
+#. i18n: "rev" is a keyword
+msgid "rev expects a number"
+msgstr "rev forventer et revisionsnummer"
msgid ""
"``reverse(set)``\n"
@@ -13477,9 +13630,8 @@
msgstr ""
msgid ""
-"``present(set)``\n"
-" An empty set, if any revision in set isn't found; otherwise,\n"
-" all revisions in set."
+"``roots(set)``\n"
+" Changesets with no parent changeset in set."
msgstr ""
msgid ""
@@ -13511,35 +13663,6 @@
msgstr "ukendt sorteringsnøgle %r"
msgid ""
-"``all()``\n"
-" All changesets, the same as ``0:tip``."
-msgstr ""
-
-#. i18n: "all" is a keyword
-msgid "all takes no arguments"
-msgstr ""
-
-msgid ""
-"``heads(set)``\n"
-" Members of set with no children in set."
-msgstr ""
-
-msgid ""
-"``roots(set)``\n"
-" Changesets with no parent changeset in set."
-msgstr ""
-
-msgid ""
-"``outgoing([path])``\n"
-" Changesets not found in the specified destination repository, or the\n"
-" default push location."
-msgstr ""
-
-#. i18n: "outgoing" is a keyword
-msgid "outgoing requires a repository path"
-msgstr ""
-
-msgid ""
"``tag(name)``\n"
" The specified tag by name, or all tagged revisions if no name is given."
msgstr ""
@@ -13556,19 +13679,9 @@
msgstr "argumentet til tag skal være en streng"
msgid ""
-"``bookmark([name])``\n"
-" The named bookmark or all bookmarks."
-msgstr ""
-"``bookmark([name])``\n"
-" Det navngivne bogmærke eller alle bogmærker."
-
-#. i18n: "bookmark" is a keyword
-msgid "bookmark takes one or no arguments"
-msgstr "bookmark tager et eller to argumenter"
-
-#. i18n: "bookmark" is a keyword
-msgid "the argument to bookmark must be a string"
-msgstr "argumentet til bookmark skal være en streng"
+"``user(string)``\n"
+" User name is string."
+msgstr ""
msgid "can't negate that"
msgstr ""
@@ -13716,12 +13829,8 @@
msgstr "fjerner ikke depotet %s fordi det er ændret.\n"
#, python-format
-msgid "cloning subrepo %s\n"
-msgstr "kloner underdepot %s\n"
-
-#, python-format
-msgid "pulling subrepo %s\n"
-msgstr "hiver underdepot %s\n"
+msgid "cloning subrepo %s from %s\n"
+msgstr "kloner underdepot %s fra %s\n"
#, python-format
msgid "revision %s does not exist in subrepo %s\n"
@@ -13861,6 +13970,9 @@
msgid "edit failed"
msgstr "redigering fejlede"
+msgid "file:// URLs can only refer to localhost"
+msgstr ""
+
#, python-format
msgid "ignoring invalid [auth] key '%s'\n"
msgstr "ignorerer ugyldig [auth] nøgle '%s'\n"
@@ -13937,6 +14049,18 @@
msgstr "kommandoen '%s' fejlede: %s"
#, python-format
+msgid "filename contains '%s', which is reserved on Windows"
+msgstr ""
+
+#, python-format
+msgid "filename contains %r, which is invalid on Windows"
+msgstr ""
+
+#, python-format
+msgid "filename ends with '%s', which is not allowed on Windows"
+msgstr ""
+
+#, python-format
msgid "path ends in directory separator: %s"
msgstr ""
@@ -13945,8 +14069,8 @@
msgstr "stien indeholder ugyldig komponent: %s"
#, python-format
-msgid "path %r is inside repo %r"
-msgstr "stien %r er inde i repo %r"
+msgid "path %r is inside nested repo %r"
+msgstr "stien %r er inden i depotet %r"
#, python-format
msgid "path %r traverses symbolic link %r"
@@ -13979,11 +14103,24 @@
msgid "impossible time zone offset: %d"
msgstr "umuligt tidszone: %d"
+msgid "dates cannot consist entirely of whitespace"
+msgstr "datoer kan ikke bestå udelukkende af tomrum"
+
+msgid "invalid day spec, use '<DATE'"
+msgstr "ugyldig datospecifikation, brug '<DATO'"
+
+msgid "invalid day spec, use '>DATE'"
+msgstr "ugyldig datospecifikation, brug '>DATO'"
+
#, python-format
msgid "invalid day spec: %s"
msgstr "ugyldig datospecifikation: %s"
#, python-format
+msgid "%s must be nonnegative (see 'hg help dates')"
+msgstr ""
+
+#, python-format
msgid "%.0f GB"
msgstr "%.0f GB"
--- a/mercurial/ancestor.py Fri Apr 29 11:04:39 2011 +0200
+++ b/mercurial/ancestor.py Fri Apr 29 11:10:11 2011 +0200
@@ -9,9 +9,10 @@
def ancestor(a, b, pfunc):
"""
- return a minimal-distance ancestor of nodes a and b, or None if there is no
- such ancestor. Note that there can be several ancestors with the same
- (minimal) distance, and the one returned is arbitrary.
+ Returns the common ancestor of a and b that is furthest from a
+ root (as measured by longest path) or None if no ancestor is
+ found. If there are multiple common ancestors at the same
+ distance, the first one found is returned.
pfunc must return a list of parent vertices for a given vertex
"""
@@ -22,6 +23,7 @@
a, b = sorted([a, b])
# find depth from root of all ancestors
+ # depth is stored as a negative for heapq
parentcache = {}
visit = [a, b]
depth = {}
@@ -39,6 +41,7 @@
if p not in depth:
visit.append(p)
if visit[-1] == vertex:
+ # -(maximum distance of parents + 1)
depth[vertex] = min([depth[p] for p in pl]) - 1
visit.pop()
--- a/mercurial/archival.py Fri Apr 29 11:04:39 2011 +0200
+++ b/mercurial/archival.py Fri Apr 29 11:10:11 2011 +0200
@@ -8,8 +8,8 @@
from i18n import _
from node import hex
import cmdutil
-import util, encoding
-import cStringIO, os, stat, tarfile, time, zipfile
+import scmutil, util, encoding
+import cStringIO, os, tarfile, time, zipfile
import zlib, gzip
def tidyprefix(dest, kind, prefix):
@@ -172,10 +172,10 @@
# unzip will not honor unix file modes unless file creator is
# set to unix (id 3).
i.create_system = 3
- ftype = stat.S_IFREG
+ ftype = 0x8000 # UNX_IFREG in unzip source code
if islink:
mode = 0777
- ftype = stat.S_IFLNK
+ ftype = 0xa000 # UNX_IFLNK in unzip source code
i.external_attr = (mode | ftype) << 16L
self.z.writestr(i, data)
@@ -187,7 +187,7 @@
def __init__(self, name, mtime):
self.basedir = name
- self.opener = util.opener(self.basedir)
+ self.opener = scmutil.opener(self.basedir)
def addfile(self, name, mode, islink, data):
if islink:
--- a/mercurial/bdiff.c Fri Apr 29 11:04:39 2011 +0200
+++ b/mercurial/bdiff.c Fri Apr 29 11:10:11 2011 +0200
@@ -49,7 +49,7 @@
#include "util.h"
struct line {
- int h, len, n, e;
+ int hash, len, n, e;
const char *l;
};
@@ -63,9 +63,10 @@
struct hunk *next;
};
-int splitlines(const char *a, int len, struct line **lr)
+static int splitlines(const char *a, int len, struct line **lr)
{
- int h, i;
+ unsigned hash;
+ int i;
const char *p, *b = a;
const char * const plast = a + len - 1;
struct line *l;
@@ -81,14 +82,14 @@
return -1;
/* build the line array and calculate hashes */
- h = 0;
+ hash = 0;
for (p = a; p < a + len; p++) {
/* Leonid Yuriev's hash */
- h = (h * 1664525) + *p + 1013904223;
+ hash = (hash * 1664525) + (unsigned char)*p + 1013904223;
if (*p == '\n' || p == plast) {
- l->h = h;
- h = 0;
+ l->hash = hash;
+ hash = 0;
l->len = p - b + 1;
l->l = b;
l->n = INT_MAX;
@@ -98,14 +99,15 @@
}
/* set up a sentinel */
- l->h = l->len = 0;
+ l->hash = 0;
+ l->len = 0;
l->l = a + len;
return i - 1;
}
-int inline cmp(struct line *a, struct line *b)
+static inline int cmp(struct line *a, struct line *b)
{
- return a->h != b->h || a->len != b->len || memcmp(a->l, b->l, a->len);
+ return a->hash != b->hash || a->len != b->len || memcmp(a->l, b->l, a->len);
}
static int equatelines(struct line *a, int an, struct line *b, int bn)
@@ -138,7 +140,7 @@
/* add lines to the hash table chains */
for (i = bn - 1; i >= 0; i--) {
/* find the equivalence class */
- for (j = b[i].h & buckets; h[j].pos != INT_MAX;
+ for (j = b[i].hash & buckets; h[j].pos != INT_MAX;
j = (j + 1) & buckets)
if (!cmp(b + i, b + h[j].pos))
break;
@@ -156,7 +158,7 @@
/* match items in a to their equivalence class in b */
for (i = 0; i < an; i++) {
/* find the equivalence class */
- for (j = a[i].h & buckets; h[j].pos != INT_MAX;
+ for (j = a[i].hash & buckets; h[j].pos != INT_MAX;
j = (j + 1) & buckets)
if (!cmp(a + i, b + h[j].pos))
break;
--- a/mercurial/bookmarks.py Fri Apr 29 11:04:39 2011 +0200
+++ b/mercurial/bookmarks.py Fri Apr 29 11:10:11 2011 +0200
@@ -29,7 +29,7 @@
sha, refspec = line.strip().split(' ', 1)
refspec = encoding.tolocal(refspec)
bookmarks[refspec] = repo.changelog.lookup(sha)
- except:
+ except IOError:
pass
return bookmarks
@@ -101,13 +101,7 @@
if current == mark:
return
- refs = repo._bookmarks
-
- # do not update if we do update to a rev equal to the current bookmark
- if (mark and mark not in refs and
- current and refs[current] == repo.changectx('.').node()):
- return
- if mark not in refs:
+ if mark not in repo._bookmarks:
mark = ''
if not valid(mark):
raise util.Abort(_("bookmark '%s' contains illegal "
@@ -122,6 +116,15 @@
wlock.release()
repo._bookmarkcurrent = mark
+def updatecurrentbookmark(repo, oldnode, curbranch):
+ try:
+ update(repo, oldnode, repo.branchtags()[curbranch])
+ except KeyError:
+ if curbranch == "default": # no default branch!
+ update(repo, oldnode, repo.lookup("tip"))
+ else:
+ raise util.Abort(_("branch %s not found") % curbranch)
+
def update(repo, parents, node):
marks = repo._bookmarks
update = False
@@ -163,6 +166,28 @@
finally:
w.release()
+def updatefromremote(ui, repo, remote):
+ ui.debug("checking for updated bookmarks\n")
+ rb = remote.listkeys('bookmarks')
+ changed = False
+ for k in rb.keys():
+ if k in repo._bookmarks:
+ nr, nl = rb[k], repo._bookmarks[k]
+ if nr in repo:
+ cr = repo[nr]
+ cl = repo[nl]
+ if cl.rev() >= cr.rev():
+ continue
+ if cr in cl.descendants():
+ repo._bookmarks[k] = cr.node()
+ changed = True
+ ui.status(_("updating bookmark %s\n") % k)
+ else:
+ ui.warn(_("not updating divergent"
+ " bookmark %s\n") % k)
+ if changed:
+ write(repo)
+
def diff(ui, repo, remote):
ui.status(_("searching for changed bookmarks\n"))
--- a/mercurial/bundlerepo.py Fri Apr 29 11:04:39 2011 +0200
+++ b/mercurial/bundlerepo.py Fri Apr 29 11:10:11 2011 +0200
@@ -15,7 +15,7 @@
from i18n import _
import os, struct, tempfile, shutil
import changegroup, util, mdiff, discovery
-import localrepo, changelog, manifest, filelog, revlog, error
+import localrepo, changelog, manifest, filelog, revlog, error, url
class bundlerevlog(revlog.revlog):
def __init__(self, opener, indexfile, bundle,
@@ -274,9 +274,9 @@
cwd = os.path.join(cwd,'')
if parentpath.startswith(cwd):
parentpath = parentpath[len(cwd):]
- path = util.drop_scheme('file', path)
- if path.startswith('bundle:'):
- path = util.drop_scheme('bundle', path)
+ u = url.url(path)
+ path = u.localpath()
+ if u.scheme == 'bundle':
s = path.split("+", 1)
if len(s) == 1:
repopath, bundlename = parentpath, s[0]
@@ -286,15 +286,17 @@
repopath, bundlename = parentpath, path
return bundlerepository(ui, repopath, bundlename)
-def getremotechanges(ui, repo, other, revs=None, bundlename=None, force=False):
- tmp = discovery.findcommonincoming(repo, other, heads=revs, force=force)
+def getremotechanges(ui, repo, other, revs=None, bundlename=None,
+ force=False, usecommon=False):
+ tmp = discovery.findcommonincoming(repo, other, heads=revs, force=force,
+ commononly=usecommon)
common, incoming, rheads = tmp
if not incoming:
try:
os.unlink(bundlename)
- except:
+ except OSError:
pass
- return other, None, None
+ return other, None, None, None
bundle = None
if bundlename or not other.local():
@@ -303,7 +305,9 @@
if revs is None and other.capable('changegroupsubset'):
revs = rheads
- if revs is None:
+ if usecommon:
+ cg = other.getbundle('incoming', common=common, heads=revs)
+ elif revs is None:
cg = other.changegroup(incoming, "incoming")
else:
cg = other.changegroupsubset(incoming, revs, 'incoming')
@@ -315,5 +319,5 @@
if not other.local():
# use the created uncompressed bundlerepo
other = bundlerepository(ui, repo.root, fname)
- return (other, incoming, bundle)
+ return (other, common, incoming, bundle)
--- a/mercurial/changegroup.py Fri Apr 29 11:04:39 2011 +0200
+++ b/mercurial/changegroup.py Fri Apr 29 11:10:11 2011 +0200
@@ -49,15 +49,6 @@
"HG10GZ": ("HG10GZ", lambda: zlib.compressobj()),
}
-def collector(cl, mmfs, files):
- # Gather information about changeset nodes going out in a bundle.
- # We want to gather manifests needed and filelogs affected.
- def collect(node):
- c = cl.read(node)
- files.update(c[3])
- mmfs.setdefault(c[0], node)
- return collect
-
# hgweb uses this list to communicate its preferred type
bundlepriority = ['HG10GZ', 'HG10BZ', 'HG10UN']
@@ -203,3 +194,18 @@
if version != '10':
raise util.Abort(_('%s: unknown bundle version %s') % (fname, version))
return unbundle10(fh, alg)
+
+class bundle10(object):
+ def __init__(self, lookup):
+ self._lookup = lookup
+ def close(self):
+ return closechunk()
+ def fileheader(self, fname):
+ return chunkheader(len(fname)) + fname
+ def revchunk(self, revlog, node='', p1='', p2='', prefix='', data=''):
+ linknode = self._lookup(revlog, node)
+ meta = node + p1 + p2 + linknode + prefix
+ l = len(meta) + len(data)
+ yield chunkheader(l)
+ yield meta
+ yield data
--- a/mercurial/changelog.py Fri Apr 29 11:04:39 2011 +0200
+++ b/mercurial/changelog.py Fri Apr 29 11:10:11 2011 +0200
@@ -185,7 +185,7 @@
try:
# various tools did silly things with the time zone field.
timezone = int(extra_data[0])
- except:
+ except ValueError:
timezone = 0
extra = {}
else:
--- a/mercurial/cmdutil.py Fri Apr 29 11:04:39 2011 +0200
+++ b/mercurial/cmdutil.py Fri Apr 29 11:10:11 2011 +0200
@@ -8,7 +8,7 @@
from node import hex, nullid, nullrev, short
from i18n import _
import os, sys, errno, re, glob, tempfile
-import util, templater, patch, error, encoding, templatekw
+import util, scmutil, templater, patch, error, encoding, templatekw
import match as matchmod
import similar, revset, subrepo
@@ -72,7 +72,7 @@
return p
def bail_if_changed(repo):
- if repo.dirstate.parents()[1] != nullid:
+ if repo.dirstate.p2() != nullid:
raise util.Abort(_('outstanding uncommitted merge'))
modified, added, removed, deleted = repo.status()[:4]
if modified or added or removed or deleted:
@@ -122,12 +122,12 @@
def revpair(repo, revs):
if not revs:
- return repo.dirstate.parents()[0], None
+ return repo.dirstate.p1(), None
l = revrange(repo, revs)
if len(l) == 0:
- return repo.dirstate.parents()[0], None
+ return repo.dirstate.p1(), None
if len(l) == 1:
return repo.lookup(l[0]), None
@@ -230,7 +230,7 @@
def make_file(repo, pat, node=None,
total=None, seqno=None, revwidth=None, mode='wb', pathname=None):
- writable = 'w' in mode or 'a' in mode
+ writable = mode not in ('r', 'rb')
if not pat or pat == '-':
fp = writable and sys.stdout or sys.stdin
@@ -286,14 +286,14 @@
similarity = float(opts.get('similarity') or 0)
# we'd use status here, except handling of symlinks and ignore is tricky
added, unknown, deleted, removed = [], [], [], []
- audit_path = util.path_auditor(repo.root)
+ audit_path = scmutil.path_auditor(repo.root)
m = match(repo, pats, opts)
for abs in repo.walk(m):
target = repo.wjoin(abs)
good = True
try:
audit_path(abs)
- except:
+ except (OSError, util.Abort):
good = False
rel = m.rel(abs)
exact = m.exact(abs)
@@ -429,12 +429,14 @@
# relsrc: ossep
# otarget: ossep
def copyfile(abssrc, relsrc, otarget, exact):
- abstarget = util.canonpath(repo.root, cwd, otarget)
+ abstarget = scmutil.canonpath(repo.root, cwd, otarget)
reltarget = repo.pathto(abstarget, cwd)
target = repo.wjoin(abstarget)
src = repo.wjoin(abssrc)
state = repo.dirstate[abstarget]
+ scmutil.checkportable(ui, abstarget)
+
# check for collisions
prevsrc = targets.get(abstarget)
if prevsrc is not None:
@@ -495,7 +497,7 @@
# return: function that takes hgsep and returns ossep
def targetpathfn(pat, dest, srcs):
if os.path.isdir(pat):
- abspfx = util.canonpath(repo.root, cwd, pat)
+ abspfx = scmutil.canonpath(repo.root, cwd, pat)
abspfx = util.localpath(abspfx)
if destdirexists:
striplen = len(os.path.split(abspfx)[0])
@@ -521,7 +523,7 @@
res = lambda p: os.path.join(dest,
os.path.basename(util.localpath(p)))
else:
- abspfx = util.canonpath(repo.root, cwd, pat)
+ abspfx = scmutil.canonpath(repo.root, cwd, pat)
if len(abspfx) < len(srcs[0][0]):
# A directory. Either the target path contains the last
# component of the source path or it does not.
--- a/mercurial/commands.py Fri Apr 29 11:04:39 2011 +0200
+++ b/mercurial/commands.py Fri Apr 29 11:10:11 2011 +0200
@@ -5,15 +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.
-from node import hex, nullid, nullrev, short
+from node import hex, bin, nullid, nullrev, short
from lock import release
from i18n import _, gettext
import os, re, sys, difflib, time, tempfile
-import hg, util, revlog, extensions, copies, error, bookmarks
+import hg, scmutil, util, revlog, extensions, copies, error, bookmarks
import patch, help, mdiff, url, encoding, templatekw, discovery
import archival, changegroup, cmdutil, sshserver, hbisect, hgweb, hgweb.server
import merge as mergemod
-import minirst, revset
+import minirst, revset, templatefilters
import dagparser
# Commands start here, listed alphabetically
@@ -126,8 +126,12 @@
lastfunc = funcmap[-1]
funcmap[-1] = lambda x: "%s:%s" % (lastfunc(x), x[1])
+ def bad(x, y):
+ raise util.Abort("%s: %s" % (x, y))
+
ctx = cmdutil.revsingle(repo, opts.get('rev'))
m = cmdutil.match(repo, pats, opts)
+ m.bad = bad
follow = not opts.get('no_follow')
for abs in ctx.walk(m):
fctx = ctx[abs]
@@ -303,7 +307,8 @@
return 0
def bisect(ui, repo, rev=None, extra=None, command=None,
- reset=None, good=None, bad=None, skip=None, noupdate=None):
+ reset=None, good=None, bad=None, skip=None, extend=None,
+ noupdate=None):
"""subdivision search of changesets
This command helps to find changesets which introduce problems. To
@@ -326,6 +331,17 @@
Returns 0 on success.
"""
+ def extendbisectrange(nodes, good):
+ # bisect is incomplete when it ends on a merge node and
+ # one of the parent was not checked.
+ parents = repo[nodes[0]].parents()
+ if len(parents) > 1:
+ side = good and state['bad'] or state['good']
+ num = len(set(i.node() for i in parents) & set(side))
+ if num == 1:
+ return parents[0].ancestor(parents[1])
+ return None
+
def print_result(nodes, good):
displayer = cmdutil.show_changeset(ui, repo, {})
if len(nodes) == 1:
@@ -336,14 +352,12 @@
ui.write(_("The first bad revision is:\n"))
displayer.show(repo[nodes[0]])
parents = repo[nodes[0]].parents()
- if len(parents) > 1:
- side = good and state['bad'] or state['good']
- num = len(set(i.node() for i in parents) & set(side))
- if num == 1:
- common = parents[0].ancestor(parents[1])
- ui.write(_('Not all ancestors of this changeset have been'
- ' checked.\nTo check the other ancestors, start'
- ' from the common ancestor, %s.\n' % common))
+ extendnode = extendbisectrange(nodes, good)
+ if extendnode is not None:
+ ui.write(_('Not all ancestors of this changeset have been'
+ ' checked.\nUse bisect --extend to continue the '
+ 'bisection from\nthe common ancestor, %s.\n')
+ % short(extendnode.node()))
else:
# multiple possible revisions
if good:
@@ -376,7 +390,7 @@
bad = True
else:
reset = True
- elif extra or good + bad + skip + reset + bool(command) > 1:
+ elif extra or good + bad + skip + reset + extend + bool(command) > 1:
raise util.Abort(_('incompatible arguments'))
if reset:
@@ -440,6 +454,18 @@
# actually bisect
nodes, changesets, good = hbisect.bisect(repo.changelog, state)
+ if extend:
+ if not changesets:
+ extendnode = extendbisectrange(nodes, good)
+ if extendnode is not None:
+ ui.write(_("Extending search to changeset %d:%s\n"
+ % (extendnode.rev(), short(extendnode.node()))))
+ if noupdate:
+ return
+ cmdutil.bail_if_changed(repo)
+ return hg.clean(repo, extendnode.node())
+ raise util.Abort(_("nothing to extend"))
+
if changesets == 0:
print_result(nodes, good)
else:
@@ -481,9 +507,10 @@
if rename:
if rename not in marks:
- raise util.Abort(_("a bookmark of this name does not exist"))
+ raise util.Abort(_("bookmark '%s' does not exist") % rename)
if mark in marks and not force:
- raise util.Abort(_("a bookmark of the same name already exists"))
+ raise util.Abort(_("bookmark '%s' already exists "
+ "(use -f to force)") % mark)
if mark is None:
raise util.Abort(_("new bookmark name required"))
marks[mark] = marks[rename]
@@ -497,7 +524,7 @@
if mark is None:
raise util.Abort(_("bookmark name required"))
if mark not in marks:
- raise util.Abort(_("a bookmark of this name does not exist"))
+ raise util.Abort(_("bookmark '%s' does not exist") % mark)
if mark == repo._bookmarkcurrent:
bookmarks.setcurrent(repo, None)
del marks[mark]
@@ -512,7 +539,8 @@
raise util.Abort(_("bookmark names cannot consist entirely of "
"whitespace"))
if mark in marks and not force:
- raise util.Abort(_("a bookmark of the same name already exists"))
+ raise util.Abort(_("bookmark '%s' already exists "
+ "(use -f to force)") % mark)
if ((mark in repo.branchtags() or mark == repo.dirstate.branch())
and not force):
raise util.Abort(
@@ -570,7 +598,7 @@
"""
if opts.get('clean'):
- label = repo[None].parents()[0].branch()
+ label = repo[None].p1().branch()
repo.dirstate.setbranch(label)
ui.status(_('reset working directory to branch %s\n') % label)
elif label:
@@ -868,7 +896,12 @@
node = cmdutil.commit(ui, repo, commitfunc, pats, opts)
if not node:
- ui.status(_("nothing changed\n"))
+ stat = repo.status(match=cmdutil.match(repo, pats, opts))
+ if stat[3]:
+ ui.status(_("nothing changed (%d missing files, see 'hg status')\n")
+ % len(stat[3]))
+ else:
+ ui.status(_("nothing changed\n"))
return 1
ctx = repo[node]
@@ -942,7 +975,7 @@
"""find the ancestor revision of two revisions in a given index"""
if len(args) == 3:
index, rev1, rev2 = args
- r = revlog.revlog(util.opener(os.getcwd(), audit=False), index)
+ r = revlog.revlog(scmutil.opener(os.getcwd(), audit=False), index)
lookup = r.lookup
elif len(args) == 2:
if not repo:
@@ -1166,7 +1199,7 @@
Returns 0 on success.
"""
- for f in util.rcpath():
+ for f in scmutil.rcpath():
ui.debug(_('read config from: %s\n') % f)
untrusted = bool(opts.get('untrusted'))
if values:
@@ -1175,6 +1208,7 @@
if len(items) > 1 or items and sections:
raise util.Abort(_('only one config item permitted'))
for section, name, value in ui.walkconfig(untrusted=untrusted):
+ value = str(value).replace('\n', '\\n')
sectname = section + '.' + name
if values:
for v in values:
@@ -1191,6 +1225,81 @@
ui.configsource(section, name, untrusted))
ui.write('%s=%s\n' % (sectname, value))
+def debugknown(ui, repopath, *ids, **opts):
+ """test whether node ids are known to a repo
+
+ Every ID must be a full-length hex node id string. Returns a list of 0s and 1s
+ indicating unknown/known.
+ """
+ repo = hg.repository(ui, repopath)
+ if not repo.capable('known'):
+ raise util.Abort("known() not supported by target repository")
+ flags = repo.known([bin(s) for s in ids])
+ ui.write("%s\n" % ("".join([f and "1" or "0" for f in flags])))
+
+def debugbundle(ui, bundlepath, all=None, **opts):
+ """lists the contents of a bundle"""
+ f = url.open(ui, bundlepath)
+ try:
+ gen = changegroup.readbundle(f, bundlepath)
+ if all:
+ ui.write("format: id, p1, p2, cset, len(delta)\n")
+
+ def showchunks(named):
+ ui.write("\n%s\n" % named)
+ while 1:
+ chunkdata = gen.parsechunk()
+ if not chunkdata:
+ break
+ node = chunkdata['node']
+ p1 = chunkdata['p1']
+ p2 = chunkdata['p2']
+ cs = chunkdata['cs']
+ delta = chunkdata['data']
+ ui.write("%s %s %s %s %s\n" %
+ (hex(node), hex(p1), hex(p2),
+ hex(cs), len(delta)))
+
+ showchunks("changelog")
+ showchunks("manifest")
+ while 1:
+ fname = gen.chunk()
+ if not fname:
+ break
+ showchunks(fname)
+ else:
+ while 1:
+ chunkdata = gen.parsechunk()
+ if not chunkdata:
+ break
+ node = chunkdata['node']
+ ui.write("%s\n" % hex(node))
+ finally:
+ f.close()
+
+def debuggetbundle(ui, repopath, bundlepath, head=None, common=None, **opts):
+ """retrieves a bundle from a repo
+
+ Every ID must be a full-length hex node id string. Saves the bundle to the
+ given file.
+ """
+ repo = hg.repository(ui, repopath)
+ if not repo.capable('getbundle'):
+ raise util.Abort("getbundle() not supported by target repository")
+ args = {}
+ if common:
+ args['common'] = [bin(s) for s in common]
+ if head:
+ args['heads'] = [bin(s) for s in head]
+ bundle = repo.getbundle('debug', **args)
+
+ bundletype = opts.get('type', 'bzip2').lower()
+ btypes = {'none': 'HG10UN', 'bzip2': 'HG10BZ', 'gzip': 'HG10GZ'}
+ bundletype = btypes.get(bundletype)
+ if bundletype not in changegroup.bundletypes:
+ raise util.Abort(_('unknown bundle type specified with --type'))
+ changegroup.writebundle(bundle, bundlepath, bundletype)
+
def debugpushkey(ui, repopath, namespace, *keyinfo):
'''access the pushkey key/value protocol
@@ -1214,7 +1323,7 @@
def debugrevspec(ui, repo, expr):
'''parse and apply a revision specification'''
if ui.verbose:
- tree = revset.parse(expr)
+ tree = revset.parse(expr)[0]
ui.note(tree, "\n")
func = revset.match(expr)
for c in func(repo, range(len(repo))):
@@ -1238,11 +1347,15 @@
finally:
wlock.release()
-def debugstate(ui, repo, nodates=None):
+def debugstate(ui, repo, nodates=None, datesort=None):
"""show the contents of the current dirstate"""
timestr = ""
showdate = not nodates
- for file_, ent in sorted(repo.dirstate._map.iteritems()):
+ if datesort:
+ keyfunc = lambda x: (x[1][3], x[0]) # sort by mtime, then by filename
+ else:
+ keyfunc = None # sort by filename
+ for file_, ent in sorted(repo.dirstate._map.iteritems(), key=keyfunc):
if showdate:
if ent[3] == -1:
# Pad or slice to locale representation
@@ -1280,7 +1393,7 @@
spaces = opts.get('spaces')
dots = opts.get('dots')
if file_:
- rlog = revlog.revlog(util.opener(os.getcwd(), audit=False), file_)
+ rlog = revlog.revlog(scmutil.opener(os.getcwd(), audit=False), file_)
revs = set((int(r) for r in revs))
def events():
for r in rlog:
@@ -1330,7 +1443,8 @@
if len(filelog):
r = filelog
if not r:
- r = revlog.revlog(util.opener(os.getcwd(), audit=False), file_[:-2] + ".i")
+ r = revlog.revlog(scmutil.opener(os.getcwd(), audit=False),
+ file_[:-2] + ".i")
try:
ui.write(r.revision(r.lookup(rev)))
except KeyError:
@@ -1369,7 +1483,7 @@
raise util.Abort(_("unknown format %d") % format)
if not r:
- r = revlog.revlog(util.opener(os.getcwd(), audit=False), file_)
+ r = revlog.revlog(scmutil.opener(os.getcwd(), audit=False), file_)
if format == 0:
ui.write(" rev offset length base linkrev"
@@ -1402,7 +1516,7 @@
if len(filelog):
r = filelog
if not r:
- r = revlog.revlog(util.opener(os.getcwd(), audit=False), file_)
+ r = revlog.revlog(scmutil.opener(os.getcwd(), audit=False), file_)
ui.write("digraph G {\n")
for i in r:
node = r.node(i)
@@ -1457,45 +1571,6 @@
ui.write(_(" (templates seem to have been installed incorrectly)\n"))
problems += 1
- # patch
- ui.status(_("Checking patch...\n"))
- patchproblems = 0
- a = "1\n2\n3\n4\n"
- b = "1\n2\n3\ninsert\n4\n"
- fa = writetemp(a)
- d = mdiff.unidiff(a, None, b, None, os.path.basename(fa),
- os.path.basename(fa))
- fd = writetemp(d)
-
- files = {}
- try:
- patch.patch(fd, ui, cwd=os.path.dirname(fa), files=files)
- except util.Abort, e:
- ui.write(_(" patch call failed:\n"))
- ui.write(" " + str(e) + "\n")
- patchproblems += 1
- else:
- if list(files) != [os.path.basename(fa)]:
- ui.write(_(" unexpected patch output!\n"))
- patchproblems += 1
- a = open(fa).read()
- if a != b:
- ui.write(_(" patch test failed!\n"))
- patchproblems += 1
-
- if patchproblems:
- if ui.config('ui', 'patch'):
- ui.write(_(" (Current patch tool may be incompatible with patch,"
- " or misconfigured. Please check your configuration"
- " file)\n"))
- else:
- ui.write(_(" Internal patcher failure, please report this error"
- " to http://mercurial.selenic.com/wiki/BugTracker\n"))
- problems += patchproblems
-
- os.unlink(fa)
- os.unlink(fd)
-
# editor
ui.status(_("Checking commit editor...\n"))
editor = ui.geteditor()
@@ -1555,6 +1630,21 @@
line = fmt % (abs, m.rel(abs), m.exact(abs) and 'exact' or '')
ui.write("%s\n" % line.rstrip())
+def debugwireargs(ui, repopath, *vals, **opts):
+ repo = hg.repository(hg.remoteui(ui, opts), repopath)
+ for opt in remoteopts:
+ del opts[opt[1]]
+ args = {}
+ for k, v in opts.iteritems():
+ if v:
+ args[k] = v
+ # run twice to check that we don't mess up the stream for the next command
+ res1 = repo.debugwireargs(*vals, **args)
+ res2 = repo.debugwireargs(*vals, **args)
+ ui.write("%s\n" % res1)
+ if res1 != res2:
+ ui.warn("%s\n" % res2)
+
def diff(ui, repo, *pats, **opts):
"""diff repository (or selected files)
@@ -1595,8 +1685,8 @@
msg = _('cannot specify --rev and --change at the same time')
raise util.Abort(msg)
elif change:
- node2 = repo.lookup(change)
- node1 = repo[node2].parents()[0].node()
+ node2 = cmdutil.revsingle(repo, change, None).node()
+ node1 = repo[node2].p1().node()
else:
node1, node2 = cmdutil.revpair(repo, revs)
@@ -1782,6 +1872,10 @@
datefunc = ui.quiet and util.shortdate or util.datestr
found = False
filerevmatches = {}
+ def binary():
+ flog = getfile(fn)
+ return util.binary(flog.read(ctx.filenode(fn)))
+
if opts.get('all'):
iter = difflinestates(pstates, states)
else:
@@ -1808,9 +1902,12 @@
after = l.line[l.colend:]
ui.write(sep.join(cols))
if before is not None:
- ui.write(sep + before)
- ui.write(match, label='grep.match')
- ui.write(after)
+ if not opts.get('text') and binary():
+ ui.write(sep + " Binary file matches")
+ else:
+ ui.write(sep + before)
+ ui.write(match, label='grep.match')
+ ui.write(after)
ui.write(eol)
found = True
return found
@@ -1823,7 +1920,7 @@
def prep(ctx, fns):
rev = ctx.rev()
- pctx = ctx.parents()[0]
+ pctx = ctx.p1()
parent = pctx.rev()
matches.setdefault(rev, {})
matches.setdefault(parent, {})
@@ -1858,7 +1955,7 @@
for ctx in cmdutil.walkchangerevs(repo, matchfn, opts, prep):
rev = ctx.rev()
- parent = ctx.parents()[0].rev()
+ parent = ctx.p1().rev()
for fn in sorted(revfiles.get(rev, [])):
states = matches[rev][fn]
copy = copies.get(rev, {}).get(fn)
@@ -1951,7 +2048,7 @@
displayer.show(ctx)
displayer.close()
-def help_(ui, name=None, with_version=False, unknowncmd=False):
+def help_(ui, name=None, with_version=False, unknowncmd=False, full=True):
"""show help for a given topic or a help overview
With no arguments, print a list of commands with short help messages.
@@ -1962,7 +2059,7 @@
Returns 0 if successful.
"""
option_lists = []
- textwidth = ui.termwidth() - 2
+ textwidth = min(ui.termwidth(), 80) - 2
def addglobalopts(aliases):
if ui.verbose:
@@ -1974,6 +2071,8 @@
if name == 'shortlist':
msg = _('use "hg help" for the full list of commands '
'or "hg -v" for details')
+ elif name and not full:
+ msg = _('use "hg help %s" to show the full help text' % name)
elif aliases:
msg = _('use "hg -v help%s" to show builtin aliases and '
'global options') % (name and " " + name or "")
@@ -2012,7 +2111,7 @@
ui.write('hg %s\n' % aliases[0])
# aliases
- if not ui.quiet and len(aliases) > 1:
+ if full and not ui.quiet and len(aliases) > 1:
ui.write(_("\naliases: %s\n") % ', '.join(aliases[1:]))
# description
@@ -2024,7 +2123,7 @@
doc = _('shell alias for::\n\n %s') % entry[0].definition[1:]
else:
doc = _('alias for: hg %s\n\n%s') % (entry[0].definition, doc)
- if ui.quiet:
+ if ui.quiet or not full:
doc = doc.splitlines()[0]
keep = ui.verbose and ['verbose'] or []
formatted, pruned = minirst.format(doc, textwidth, keep=keep)
@@ -2141,6 +2240,8 @@
'extensions\n'))
help.addtopichook('revsets', revset.makedoc)
+ help.addtopichook('templates', templatekw.makedoc)
+ help.addtopichook('templates', templatefilters.makedoc)
if name and name != 'shortlist':
i = None
@@ -2265,64 +2366,86 @@
hexfunc = ui.debugflag and hex or short
default = not (num or id or branch or tags or bookmarks)
output = []
-
revs = []
+
if source:
source, branches = hg.parseurl(ui.expandpath(source))
repo = hg.repository(ui, source)
revs, checkout = hg.addbranchrevs(repo, repo, branches, None)
if not repo.local():
+ if num or branch or tags:
+ raise util.Abort(
+ _("can't query remote revision number, branch, or tags"))
if not rev and revs:
rev = revs[0]
if not rev:
rev = "tip"
- if num or branch or tags or bookmarks:
- raise util.Abort(_("can't query remote revision number,"
- " branch, tags, or bookmarks"))
- output = [hexfunc(repo.lookup(rev))]
- elif not rev:
- ctx = repo[None]
- parents = ctx.parents()
- changed = False
- if default or id or num:
- changed = util.any(repo.status())
- if default or id:
- output = ["%s%s" % ('+'.join([hexfunc(p.node()) for p in parents]),
- (changed) and "+" or "")]
- if num:
- output.append("%s%s" % ('+'.join([str(p.rev()) for p in parents]),
- (changed) and "+" or ""))
- else:
- ctx = cmdutil.revsingle(repo, rev)
+
+ remoterev = repo.lookup(rev)
if default or id:
- output = [hexfunc(ctx.node())]
- if num:
- output.append(str(ctx.rev()))
-
- if repo.local() and default and not ui.quiet:
- b = ctx.branch()
- if b != 'default':
- output.append("(%s)" % b)
-
- # multiple tags for a single parent separated by '/'
- t = "/".join(ctx.tags())
- if t:
- output.append(t)
-
- # multiple bookmarks for a single parent separated by '/'
- bm = '/'.join(ctx.bookmarks())
- if bm:
- output.append(bm)
-
- if branch:
- output.append(ctx.branch())
-
- if tags:
- output.extend(ctx.tags())
-
- if bookmarks:
- output.extend(ctx.bookmarks())
+ output = [hexfunc(remoterev)]
+
+ def getbms():
+ bms = []
+
+ if 'bookmarks' in repo.listkeys('namespaces'):
+ hexremoterev = hex(remoterev)
+ bms = [bm for bm, bmr in repo.listkeys('bookmarks').iteritems()
+ if bmr == hexremoterev]
+
+ return bms
+
+ if bookmarks:
+ output.extend(getbms())
+ elif default and not ui.quiet:
+ # multiple bookmarks for a single parent separated by '/'
+ bm = '/'.join(getbms())
+ if bm:
+ output.append(bm)
+ else:
+ if not rev:
+ ctx = repo[None]
+ parents = ctx.parents()
+ changed = ""
+ if default or id or num:
+ changed = util.any(repo.status()) and "+" or ""
+ if default or id:
+ output = ["%s%s" %
+ ('+'.join([hexfunc(p.node()) for p in parents]), changed)]
+ if num:
+ output.append("%s%s" %
+ ('+'.join([str(p.rev()) for p in parents]), changed))
+ else:
+ ctx = cmdutil.revsingle(repo, rev)
+ if default or id:
+ output = [hexfunc(ctx.node())]
+ if num:
+ output.append(str(ctx.rev()))
+
+ if default and not ui.quiet:
+ b = ctx.branch()
+ if b != 'default':
+ output.append("(%s)" % b)
+
+ # multiple tags for a single parent separated by '/'
+ t = '/'.join(ctx.tags())
+ if t:
+ output.append(t)
+
+ # multiple bookmarks for a single parent separated by '/'
+ bm = '/'.join(ctx.bookmarks())
+ if bm:
+ output.append(bm)
+ else:
+ if branch:
+ output.append(ctx.branch())
+
+ if tags:
+ output.extend(ctx.tags())
+
+ if bookmarks:
+ output.extend(ctx.bookmarks())
ui.write("%s\n" % ' '.join(output))
@@ -2711,7 +2834,7 @@
``--tool`` can be used to specify the merge tool used for file
merges. It overrides the HGMERGE environment variable and your
- configuration files.
+ configuration files. See :hg:`help merge-tools` for options.
If no revision is specified, the working directory's parent is a
head revision, and the current branch contains exactly one other
@@ -2742,7 +2865,7 @@
'(run \'hg heads .\' to see heads)')
% (branch, len(bheads)))
- parent = repo.dirstate.parents()[0]
+ parent = repo.dirstate.p1()
if len(bheads) == 1:
if len(repo.heads()) > 1:
raise util.Abort(_(
@@ -2891,7 +3014,13 @@
else:
ui.status(_("not updating, since new heads added\n"))
if modheads > 1:
- ui.status(_("(run 'hg heads' to see heads, 'hg merge' to merge)\n"))
+ currentbranchheads = len(repo.branchheads())
+ if currentbranchheads == modheads:
+ ui.status(_("(run 'hg heads' to see heads, 'hg merge' to merge)\n"))
+ elif currentbranchheads > 1:
+ ui.status(_("(run 'hg heads .' to see heads, 'hg merge' to merge)\n"))
+ else:
+ ui.status(_("(run 'hg heads' to see heads)\n"))
else:
ui.status(_("(run 'hg update' to get a working copy)\n"))
@@ -2938,6 +3067,7 @@
raise util.Abort(err)
modheads = repo.pull(other, heads=revs, force=opts.get('force'))
+ bookmarks.updatefromremote(ui, repo, other)
if checkout:
checkout = str(repo.changelog.rev(other.lookup(checkout)))
repo._subtoppath = source
@@ -3265,8 +3395,9 @@
directory, the reverted files will thus appear modified
afterwards.
- If a file has been deleted, it is restored. If the executable mode
- of a file was changed, it is reset.
+ If a file has been deleted, it is restored. Files scheduled for
+ addition are just unscheduled and left as they are. If the
+ executable mode of a file was changed, it is reset.
If names are given, all files matching the names are reverted.
If no arguments are given, no files are reverted.
@@ -3423,7 +3554,7 @@
fc = ctx[f]
repo.wwrite(f, fc.data(), fc.flags())
- audit_path = util.path_auditor(repo.root)
+ audit_path = scmutil.path_auditor(repo.root)
for f in remove[0]:
if repo.dirstate[f] == 'a':
repo.dirstate.forget(f)
@@ -3645,7 +3776,7 @@
raise util.Abort(msg)
elif change:
node2 = repo.lookup(change)
- node1 = repo[node2].parents()[0].node()
+ node1 = repo[node2].p1().node()
else:
node1, node2 = cmdutil.revpair(repo, revs)
@@ -3955,19 +4086,16 @@
ui.write("%s\n" % t)
continue
- try:
- hn = hexfunc(n)
- r = "%5d:%s" % (repo.changelog.rev(n), hn)
- except error.LookupError:
- r = " ?:%s" % hn
- else:
- spaces = " " * (30 - encoding.colwidth(t))
- if ui.verbose:
- if repo.tagtype(t) == 'local':
- tagtype = " local"
- else:
- tagtype = ""
- ui.write("%s%s %s%s\n" % (t, spaces, r, tagtype))
+ hn = hexfunc(n)
+ r = "%5d:%s" % (repo.changelog.rev(n), hn)
+ spaces = " " * (30 - encoding.colwidth(t))
+
+ if ui.verbose:
+ if repo.tagtype(t) == 'local':
+ tagtype = " local"
+ else:
+ tagtype = ""
+ ui.write("%s%s %s%s\n" % (t, spaces, r, tagtype))
def tip(ui, repo, **opts):
"""show the tip revision
@@ -3998,15 +4126,16 @@
fnames = (fname1,) + fnames
lock = repo.lock()
+ wc = repo['.']
try:
for fname in fnames:
f = url.open(ui, fname)
gen = changegroup.readbundle(f, fname)
modheads = repo.addchangegroup(gen, 'unbundle', 'bundle:' + fname,
lock=lock)
+ bookmarks.updatecurrentbookmark(repo, wc.node(), wc.branch())
finally:
lock.release()
-
return postincoming(ui, repo, modheads, opts.get('update'), None)
def update(ui, repo, node=None, rev=None, clean=False, date=None, check=False):
@@ -4053,7 +4182,7 @@
if rev and node:
raise util.Abort(_("please specify just one revision"))
- if not rev:
+ if rev is None or rev == '':
rev = node
# if we defined a bookmark, we have to remember the original bookmark name
@@ -4070,7 +4199,7 @@
raise util.Abort(_("uncommitted local changes"))
if date:
- if rev:
+ if rev is not None:
raise util.Abort(_("you can't specify a revision and a date"))
rev = cmdutil.finddate(ui, repo, date)
@@ -4269,6 +4398,7 @@
('g', 'good', False, _('mark changeset good')),
('b', 'bad', False, _('mark changeset bad')),
('s', 'skip', False, _('skip testing changeset')),
+ ('e', 'extend', False, _('extend the bisect range')),
('c', 'command', '',
_('use command to check changeset state'), _('CMD')),
('U', 'noupdate', False, _('do not update to target'))],
@@ -4359,6 +4489,11 @@
('n', 'new-file', None, _('add new file at each rev')),
],
_('[OPTION]... TEXT')),
+ "debugbundle":
+ (debugbundle,
+ [('a', 'all', None, _('show all details')),
+ ],
+ _('FILE')),
"debugcheckstate": (debugcheckstate, [], ''),
"debugcommands": (debugcommands, [], _('[COMMAND]')),
"debugcomplete":
@@ -4379,12 +4514,20 @@
_('[-e] DATE [RANGE]')),
"debugdata": (debugdata, [], _('FILE REV')),
"debugfsinfo": (debugfsinfo, [], _('[PATH]')),
+ "debuggetbundle":
+ (debuggetbundle,
+ [('H', 'head', [], _('id of head node'), _('ID')),
+ ('C', 'common', [], _('id of common node'), _('ID')),
+ ('t', 'type', 'bzip2', _('bundle compression type to use'), _('TYPE')),
+ ],
+ _('REPO FILE [-H|-C ID]...')),
"debugignore": (debugignore, [], ''),
"debugindex": (debugindex,
[('f', 'format', 0, _('revlog format'), _('FORMAT'))],
_('FILE')),
"debugindexdot": (debugindexdot, [], _('FILE')),
"debuginstall": (debuginstall, [], ''),
+ "debugknown": (debugknown, [], _('REPO ID...')),
"debugpushkey": (debugpushkey, [], _('REPO NAMESPACE [KEY OLD NEW]')),
"debugrebuildstate":
(debugrebuildstate,
@@ -4402,7 +4545,8 @@
(debugsetparents, [], _('REV1 [REV2]')),
"debugstate":
(debugstate,
- [('', 'nodates', None, _('do not display the saved mtime'))],
+ [('', 'nodates', None, _('do not display the saved mtime')),
+ ('', 'datesort', None, _('sort by saved mtime'))],
_('[OPTION]...')),
"debugsub":
(debugsub,
@@ -4410,6 +4554,12 @@
_('revision to check'), _('REV'))],
_('[-r REV] [REV]')),
"debugwalk": (debugwalk, walkopts, _('[OPTION]... [FILE]...')),
+ "debugwireargs":
+ (debugwireargs,
+ [('', 'three', '', 'three'),
+ ('', 'four', '', 'four'),
+ ] + remoteopts,
+ _('REPO [OPTIONS]... [ONE [TWO]]')),
"^diff":
(diff,
[('r', 'rev', [],
@@ -4435,6 +4585,7 @@
(grep,
[('0', 'print0', None, _('end fields with NUL')),
('', 'all', None, _('print all revisions that match')),
+ ('a', 'text', None, _('treat all files as text')),
('f', 'follow', None,
_('follow changeset history,'
' or file history across copies and renames')),
@@ -4743,6 +4894,7 @@
}
norepo = ("clone init version help debugcommands debugcomplete"
- " debugdate debuginstall debugfsinfo debugpushkey")
+ " debugdate debuginstall debugfsinfo debugpushkey debugwireargs"
+ " debugknown debuggetbundle debugbundle")
optionalrepo = ("identify paths serve showconfig debugancestor debugdag"
" debugdata debugindex debugindexdot")
--- a/mercurial/config.py Fri Apr 29 11:04:39 2011 +0200
+++ b/mercurial/config.py Fri Apr 29 11:10:11 2011 +0200
@@ -138,5 +138,5 @@
def read(self, path, fp=None, sections=None, remap=None):
if not fp:
- fp = open(path)
+ fp = util.posixfile(path)
self.parse(path, fp.read(), sections, remap, self.read)
--- a/mercurial/context.py Fri Apr 29 11:04:39 2011 +0200
+++ b/mercurial/context.py Fri Apr 29 11:10:11 2011 +0200
@@ -7,7 +7,7 @@
from node import nullid, nullrev, short, hex
from i18n import _
-import ancestor, bdiff, error, util, subrepo, patch, encoding
+import ancestor, bdiff, error, util, scmutil, subrepo, patch, encoding
import os, errno, stat
propertycache = util.propertycache
@@ -402,6 +402,15 @@
return [filectx(self._repo, p, fileid=n, filelog=l)
for p, n, l in pl if n != nullid]
+ def p1(self):
+ return self.parents()[0]
+
+ def p2(self):
+ p = self.parents()
+ if len(p) == 2:
+ return p[1]
+ return filectx(self._repo, self._path, fileid=-1, filelog=self._filelog)
+
def children(self):
# hard for renames
c = self._filelog.children(self._filenode)
@@ -792,10 +801,11 @@
try:
rejected = []
for f in list:
+ scmutil.checkportable(ui, join(f))
p = self._repo.wjoin(f)
try:
st = os.lstat(p)
- except:
+ except OSError:
ui.warn(_("%s does not exist!\n") % join(f))
rejected.append(f)
continue
--- a/mercurial/copies.py Fri Apr 29 11:04:39 2011 +0200
+++ b/mercurial/copies.py Fri Apr 29 11:10:11 2011 +0200
@@ -93,7 +93,7 @@
return {}, {}
# avoid silly behavior for parent -> working dir
- if c2.node() is None and c1.node() == repo.dirstate.parents()[0]:
+ if c2.node() is None and c1.node() == repo.dirstate.p1():
return repo.dirstate.copies(), {}
limit = _findlimit(repo, c1.rev(), c2.rev())
--- a/mercurial/dirstate.py Fri Apr 29 11:04:39 2011 +0200
+++ b/mercurial/dirstate.py Fri Apr 29 11:10:11 2011 +0200
@@ -7,7 +7,7 @@
from node import nullid
from i18n import _
-import util, ignore, osutil, parsers, encoding
+import scmutil, util, ignore, osutil, parsers, encoding
import struct, os, stat, errno
import cStringIO
@@ -49,6 +49,7 @@
self._rootdir = os.path.join(root, '')
self._dirty = False
self._dirtypl = False
+ self._lastnormaltime = None
self._ui = ui
@propertycache
@@ -202,6 +203,12 @@
def parents(self):
return [self._validate(p) for p in self._pl]
+ def p1(self):
+ return self._validate(self._pl[0])
+
+ def p2(self):
+ return self._validate(self._pl[1])
+
def branch(self):
return encoding.tolocal(self._branch)
@@ -236,6 +243,7 @@
"_ignore"):
if a in self.__dict__:
delattr(self, a)
+ self._lastnormaltime = None
self._dirty = False
def copy(self, source, dest):
@@ -261,9 +269,7 @@
def _addpath(self, f, check=False):
oldstate = self[f]
if check or oldstate == "r":
- if '\r' in f or '\n' in f:
- raise util.Abort(
- _("'\\n' and '\\r' disallowed in filenames: %r") % f)
+ scmutil.checkfilename(f)
if f in self._dirs:
raise util.Abort(_('directory %r already in dirstate') % f)
# shadows
@@ -281,9 +287,15 @@
self._dirty = True
self._addpath(f)
s = os.lstat(self._join(f))
- self._map[f] = ('n', s.st_mode, s.st_size, int(s.st_mtime))
+ mtime = int(s.st_mtime)
+ self._map[f] = ('n', s.st_mode, s.st_size, mtime)
if f in self._copymap:
del self._copymap[f]
+ if mtime > self._lastnormaltime:
+ # Remember the most recent modification timeslot for status(),
+ # to make sure we won't miss future size-preserving file content
+ # modifications that happen within the same timeslot.
+ self._lastnormaltime = mtime
def normallookup(self, f):
'''Mark a file normal, but possibly dirty.'''
@@ -397,6 +409,7 @@
delattr(self, "_dirs")
self._copymap = {}
self._pl = [nullid, nullid]
+ self._lastnormaltime = None
self._dirty = True
def rebuild(self, parent, files):
@@ -444,6 +457,7 @@
write(f)
st.write(cs.getvalue())
st.rename()
+ self._lastnormaltime = None
self._dirty = self._dirtypl = False
def _dirignore(self, f):
@@ -680,6 +694,7 @@
# lines are an expansion of "islink => checklink"
# where islink means "is this a link?" and checklink
# means "can we check links?".
+ mtime = int(st.st_mtime)
if (size >= 0 and
(size != st.st_size
or ((mode ^ st.st_mode) & 0100 and self._checkexec))
@@ -687,9 +702,15 @@
or size == -2 # other parent
or fn in self._copymap):
madd(fn)
- elif (time != int(st.st_mtime)
+ elif (mtime != time
and (mode & lnkkind != lnkkind or self._checklink)):
ladd(fn)
+ elif mtime == self._lastnormaltime:
+ # fn may have been changed in the same timeslot without
+ # changing its size. This can happen if we quickly do
+ # multiple commits in a single transaction.
+ # Force lookup, so we don't miss such a racy file change.
+ ladd(fn)
elif listclean:
cadd(fn)
elif state == 'm':
--- a/mercurial/discovery.py Fri Apr 29 11:04:39 2011 +0200
+++ b/mercurial/discovery.py Fri Apr 29 11:10:11 2011 +0200
@@ -9,9 +9,10 @@
from i18n import _
import util, error
-def findcommonincoming(repo, remote, heads=None, force=False):
- """Return a tuple (common, missing roots, heads) used to identify
- missing nodes from remote.
+def findcommonincoming(repo, remote, heads=None, force=False, commononly=False):
+ """Return a tuple (common, missing, heads) used to identify missing nodes
+ from remote. "missing" is either a boolean indicating if any nodes are missing
+ (when commononly=True), or else a list of the root nodes of the missing set.
If a list of heads is specified, return only nodes which are heads
or ancestors of these heads.
@@ -36,6 +37,13 @@
# and start by examining the heads
repo.ui.status(_("searching for changes\n"))
+ if commononly:
+ myheads = repo.heads()
+ known = remote.known(myheads)
+ if util.all(known):
+ hasincoming = set(heads).difference(set(myheads)) and True
+ return myheads, hasincoming, heads
+
unknown = []
for h in heads:
if h not in m:
--- a/mercurial/dispatch.py Fri Apr 29 11:04:39 2011 +0200
+++ b/mercurial/dispatch.py Fri Apr 29 11:10:11 2011 +0200
@@ -90,7 +90,7 @@
except error.CommandError, inst:
if inst.args[0]:
ui.warn(_("hg %s: %s\n") % (inst.args[0], inst.args[1]))
- commands.help_(ui, inst.args[0])
+ commands.help_(ui, inst.args[0], full=False)
else:
ui.warn(_("hg: %s\n") % inst.args[1])
commands.help_(ui, 'shortlist')
@@ -133,7 +133,7 @@
elif hasattr(inst, "reason"):
try: # usually it is in the form (errno, strerror)
reason = inst.reason.args[1]
- except: # it might be anything, for example a string
+ except AttributeError: # it might be anything, for example a string
reason = inst.reason
ui.warn(_("abort: error: %s\n") % reason)
elif hasattr(inst, "args") and inst.args[0] == errno.EPIPE:
@@ -586,8 +586,8 @@
if guess and repos.count(guess) == len(repos):
return _dispatch(ui, ['--repository', guess] + fullargs)
if not path:
- raise error.RepoError(_("There is no Mercurial repository"
- " here (.hg not found)"))
+ raise error.RepoError(_("no repository found in %r"
+ " (.hg not found)") % os.getcwd())
raise
args.insert(0, repo)
elif rpath:
--- a/mercurial/help.py Fri Apr 29 11:04:39 2011 +0200
+++ b/mercurial/help.py Fri Apr 29 11:10:11 2011 +0200
@@ -86,7 +86,7 @@
return loader
-helptable = [
+helptable = sorted([
(["config", "hgrc"], _("Configuration Files"), loaddoc('config')),
(["dates"], _("Date Formats"), loaddoc('dates')),
(["patterns"], _("File Name Patterns"), loaddoc('patterns')),
@@ -106,7 +106,7 @@
(["subrepo", "subrepos"], _("Subrepositories"), loaddoc('subrepos')),
(["hgweb"], _("Configuring hgweb"), loaddoc('hgweb')),
(["glossary"], _("Glossary"), loaddoc('glossary')),
-]
+])
# Map topics to lists of callable taking the current topic help and
# returning the updated version
@@ -115,3 +115,19 @@
def addtopichook(topic, rewriter):
helphooks.setdefault(topic, []).append(rewriter)
+
+def makeitemsdoc(topic, doc, marker, items):
+ """Extract docstring from the items key to function mapping, build a
+ .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:
+ continue
+ text = gettext(text)
+ lines = text.splitlines()
+ lines[1:] = [(' ' + l.strip()) for l in lines[1:]]
+ entries.append('\n'.join(lines))
+ entries = '\n\n'.join(entries)
+ return doc.replace(marker, entries)
--- a/mercurial/help/dates.txt Fri Apr 29 11:04:39 2011 +0200
+++ b/mercurial/help/dates.txt Fri Apr 29 11:10:11 2011 +0200
@@ -30,7 +30,7 @@
The log command also accepts date ranges:
-- ``<{datetime}`` - at or before a given date/time
-- ``>{datetime}`` - on or after a given date/time
-- ``{datetime} to {datetime}`` - a date range, inclusive
-- ``-{days}`` - within a given number of days of today
+- ``<DATE`` - at or before a given date/time
+- ``>DATE`` - on or after a given date/time
+- ``DATE to DATE`` - a date range, inclusive
+- ``-DAYS`` - within a given number of days of today
--- a/mercurial/help/environment.txt Fri Apr 29 11:04:39 2011 +0200
+++ b/mercurial/help/environment.txt Fri Apr 29 11:10:11 2011 +0200
@@ -59,6 +59,14 @@
Equivalent options set via command line flags or environment
variables are not overridden.
+HGPLAINEXCEPT
+ This is a comma-separated list of features to preserve when
+ HGPLAIN is enabled. Currently the only value supported is "i18n",
+ which preserves internationalization in plain mode.
+
+ Setting HGPLAINEXCEPT to anything (even an empty string) will
+ enable plain mode.
+
HGUSER
This is the string used as the author of a commit. If not set,
available values will be considered in this order:
--- a/mercurial/help/templates.txt Fri Apr 29 11:04:39 2011 +0200
+++ b/mercurial/help/templates.txt Fri Apr 29 11:10:11 2011 +0200
@@ -23,52 +23,7 @@
keywords depends on the exact context of the templater. These
keywords are usually available for templating a log-like command:
-:author: String. The unmodified author of the changeset.
-
-:branch: String. The name of the branch on which the changeset was
- committed.
-
-:branches: List of strings. The name of the branch on which the
- changeset was committed. Will be empty if the branch name was
- default.
-
-:children: List of strings. The children of the changeset.
-
-:date: Date information. The date when the changeset was committed.
-
-:desc: String. The text of the changeset description.
-
-:diffstat: String. Statistics of changes with the following format:
- "modified files: +added/-removed lines"
-
-:files: List of strings. All files modified, added, or removed by this
- changeset.
-
-:file_adds: List of strings. Files added by this changeset.
-
-:file_copies: List of strings. Files copied in this changeset with
- their sources.
-
-:file_copies_switch: List of strings. Like "file_copies" but displayed
- only if the --copied switch is set.
-
-:file_mods: List of strings. Files modified by this changeset.
-
-:file_dels: List of strings. Files removed by this changeset.
-
-:node: String. The changeset identification hash, as a 40 hexadecimal
- digit string.
-
-:parents: List of strings. The parents of the changeset.
-
-:rev: Integer. The repository-local changeset revision number.
-
-:tags: List of strings. Any tags associated with the changeset.
-
-:latesttag: String. Most recent global tag in the ancestors of this
- changeset.
-
-:latesttagdistance: Integer. Longest path to the latest tag.
+.. keywordsmarker
The "date" keyword does not produce human-readable output. If you
want to use a date in your output, you can use a filter to process
@@ -82,82 +37,4 @@
List of filters:
-:addbreaks: Any text. Add an XHTML "<br />" tag before the end of
- every line except the last.
-
-:age: Date. Returns a human-readable date/time difference between the
- given date/time and the current date/time.
-
-:basename: Any text. Treats the text as a path, and returns the last
- component of the path after splitting by the path separator
- (ignoring trailing separators). For example, "foo/bar/baz" becomes
- "baz" and "foo/bar//" becomes "bar".
-
-:stripdir: Treat the text as path and strip a directory level, if
- possible. For example, "foo" and "foo/bar" becomes "foo".
-
-:date: Date. Returns a date in a Unix date format, including the
- timezone: "Mon Sep 04 15:13:13 2006 0700".
-
-:domain: Any text. Finds the first string that looks like an email
- address, and extracts just the domain component. Example: ``User
- <user@example.com>`` becomes ``example.com``.
-
-:email: Any text. Extracts the first string that looks like an email
- address. Example: ``User <user@example.com>`` becomes
- ``user@example.com``.
-
-:escape: Any text. Replaces the special XML/XHTML characters "&", "<"
- and ">" with XML entities.
-
-:hex: Any text. Convert a binary Mercurial node identifier into
- its long hexadecimal representation.
-
-:fill68: Any text. Wraps the text to fit in 68 columns.
-
-:fill76: Any text. Wraps the text to fit in 76 columns.
-
-:firstline: Any text. Returns the first line of text.
-
-:nonempty: Any text. Returns '(none)' if the string is empty.
-
-:hgdate: Date. Returns the date as a pair of numbers: "1157407993
- 25200" (Unix timestamp, timezone offset).
-
-:isodate: Date. Returns the date in ISO 8601 format: "2009-08-18 13:00
- +0200".
-
-:isodatesec: Date. Returns the date in ISO 8601 format, including
- seconds: "2009-08-18 13:00:13 +0200". See also the rfc3339date
- filter.
-
-:localdate: Date. Converts a date to local date.
-
-:obfuscate: Any text. Returns the input text rendered as a sequence of
- XML entities.
-
-:person: Any text. Returns the text before an email address.
-
-:rfc822date: Date. Returns a date using the same format used in email
- headers: "Tue, 18 Aug 2009 13:00:13 +0200".
-
-:rfc3339date: Date. Returns a date using the Internet date format
- specified in RFC 3339: "2009-08-18T13:00:13+02:00".
-
-:short: Changeset hash. Returns the short form of a changeset hash,
- i.e. a 12 hexadecimal digit string.
-
-:shortdate: Date. Returns a date like "2006-09-18".
-
-:stringify: Any type. Turns the value into text by converting values into
- text and concatenating them.
-
-:strip: Any text. Strips all leading and trailing whitespace.
-
-:tabindent: Any text. Returns the text, with every line except the
- first starting with a tab character.
-
-:urlescape: Any text. Escapes all "special" characters. For example,
- "foo bar" becomes "foo%20bar".
-
-:user: Any text. Returns the user portion of an email address.
+.. filtersmarker
--- a/mercurial/hg.py Fri Apr 29 11:04:39 2011 +0200
+++ b/mercurial/hg.py Fri Apr 29 11:10:11 2011 +0200
@@ -9,7 +9,7 @@
from i18n import _
from lock import release
from node import hex, nullid, nullrev, short
-import localrepo, bundlerepo, httprepo, sshrepo, statichttprepo
+import localrepo, bundlerepo, httprepo, sshrepo, statichttprepo, bookmarks
import lock, util, extensions, error, encoding, node
import cmdutil, discovery, url
import merge as mergemod
@@ -17,7 +17,7 @@
import errno, os, shutil
def _local(path):
- path = util.expandpath(util.drop_scheme('file', path))
+ path = util.expandpath(url.localpath(path))
return (os.path.isfile(path) and bundlerepo or localrepo)
def addbranchrevs(lrepo, repo, branches, revs):
@@ -51,13 +51,15 @@
revs.append(hashbranch)
return revs, revs[0]
-def parseurl(url, branches=None):
+def parseurl(path, branches=None):
'''parse url#branch, returning (url, (branch, branches))'''
- if '#' not in url:
- return url, (None, branches or [])
- url, branch = url.split('#', 1)
- return url, (branch, branches or [])
+ u = url.url(path)
+ branch = None
+ if u.fragment:
+ branch = u.fragment
+ u.fragment = None
+ return str(u), (branch, branches or [])
schemes = {
'bundle': bundlerepo,
@@ -69,11 +71,8 @@
}
def _lookup(path):
- scheme = 'file'
- if path:
- c = path.find(':')
- if c > 0:
- scheme = path[:c]
+ u = url.url(path)
+ scheme = u.scheme or 'file'
thing = schemes.get(scheme) or schemes['file']
try:
return thing(path)
@@ -103,15 +102,6 @@
'''return default destination of clone if none is given'''
return os.path.basename(os.path.normpath(source))
-def localpath(path):
- if path.startswith('file://localhost/'):
- return path[16:]
- if path.startswith('file://'):
- return path[7:]
- if path.startswith('file:'):
- return path[5:]
- return path
-
def share(ui, source, dest=None, update=True):
'''create a shared repository'''
@@ -143,7 +133,7 @@
if not os.path.isdir(root):
os.mkdir(root)
- os.mkdir(roothg)
+ util.makedir(roothg, notindexed=True)
requirements = ''
try:
@@ -231,8 +221,8 @@
else:
dest = ui.expandpath(dest)
- dest = localpath(dest)
- source = localpath(source)
+ dest = url.localpath(dest)
+ source = url.localpath(source)
if os.path.exists(dest):
if not os.path.isdir(dest):
@@ -258,7 +248,7 @@
abspath = origsource
copy = False
if src_repo.cancopy() and islocal(dest):
- abspath = os.path.abspath(util.drop_scheme('file', origsource))
+ abspath = os.path.abspath(url.localpath(origsource))
copy = not pull and not rev
if copy:
@@ -281,7 +271,7 @@
dir_cleanup.dir_ = hgdir
try:
dest_path = hgdir
- os.mkdir(dest_path)
+ util.makedir(dest_path, notindexed=True)
except OSError, inst:
if inst.errno == errno.EEXIST:
dir_cleanup.close()
@@ -366,6 +356,21 @@
dest_repo.ui.status(_("updating to branch %s\n") % bn)
_update(dest_repo, uprev)
+ # clone all bookmarks
+ if dest_repo.local() and src_repo.capable("pushkey"):
+ rb = src_repo.listkeys('bookmarks')
+ for k, n in rb.iteritems():
+ try:
+ m = dest_repo.lookup(n)
+ dest_repo._bookmarks[k] = m
+ except error.RepoLookupError:
+ pass
+ if rb:
+ bookmarks.write(dest_repo)
+ elif src_repo.local() and dest_repo.capable("pushkey"):
+ for k, n in src_repo._bookmarks.iteritems():
+ dest_repo.pushkey('bookmarks', k, '', hex(n))
+
return src_repo, dest_repo
finally:
release(src_lock, dest_lock)
@@ -421,14 +426,19 @@
if revs:
revs = [other.lookup(rev) for rev in revs]
- other, incoming, bundle = bundlerepo.getremotechanges(ui, repo, other, revs,
- opts["bundle"], opts["force"])
- if incoming is None:
+ usecommon = other.capable('getbundle')
+ other, common, incoming, bundle = bundlerepo.getremotechanges(ui, repo, other,
+ revs, opts["bundle"], opts["force"],
+ usecommon=usecommon)
+ if not incoming:
ui.status(_("no changes found\n"))
return subreporecurse()
try:
- chlist = other.changelog.nodesbetween(incoming, revs)[0]
+ if usecommon:
+ chlist = other.changelog.findmissing(common, revs)
+ else:
+ chlist = other.changelog.nodesbetween(incoming, revs)[0]
displayer = cmdutil.show_changeset(ui, other, opts, buffered)
# XXX once graphlog extension makes it into core,
--- a/mercurial/hgweb/common.py Fri Apr 29 11:04:39 2011 +0200
+++ b/mercurial/hgweb/common.py Fri Apr 29 11:10:11 2011 +0200
@@ -73,10 +73,29 @@
def __init__(self, code, message=None, headers=[]):
if message is None:
message = _statusmessage(code)
- Exception.__init__(self, code, message)
+ Exception.__init__(self)
self.code = code
self.message = message
self.headers = headers
+ def __str__(self):
+ return self.message
+
+class continuereader(object):
+ def __init__(self, f, write):
+ self.f = f
+ self._write = write
+ self.continued = False
+
+ def read(self, amt=-1):
+ if not self.continued:
+ self.continued = True
+ self._write('HTTP/1.1 100 Continue\r\n\r\n')
+ return self.f.read(amt)
+
+ def __getattr__(self, attr):
+ if attr in ('close', 'readline', 'readlines', '__iter__'):
+ return getattr(self.f, attr)
+ raise AttributeError()
def _statusmessage(code):
from BaseHTTPServer import BaseHTTPRequestHandler
--- a/mercurial/hgweb/hgweb_mod.py Fri Apr 29 11:04:39 2011 +0200
+++ b/mercurial/hgweb/hgweb_mod.py Fri Apr 29 11:10:11 2011 +0200
@@ -17,6 +17,7 @@
perms = {
'changegroup': 'pull',
'changegroupsubset': 'pull',
+ 'getbundle': 'pull',
'stream_out': 'pull',
'listkeys': 'pull',
'unbundle': 'push',
@@ -125,7 +126,11 @@
self.check_perm(req, perms[cmd])
return protocol.call(self.repo, req, cmd)
except ErrorResponse, inst:
- if cmd == 'unbundle':
+ # A client that sends unbundle without 100-continue will
+ # break if we respond early.
+ if (cmd == 'unbundle' and
+ req.env.get('HTTP_EXPECT',
+ '').lower() != '100-continue'):
req.drain()
req.respond(inst, protocol.HGTYPE)
return '0\n%s\n' % inst.message
@@ -183,7 +188,8 @@
req.form['cmd'] = [tmpl.cache['default']]
cmd = req.form['cmd'][0]
- caching(self, req) # sets ETag header or raises NOT_MODIFIED
+ if self.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)
@@ -228,6 +234,7 @@
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/")
staticurl = self.config("web", "staticurl") or req.url + 'static/'
if not staticurl.endswith('/'):
staticurl += '/'
@@ -267,6 +274,7 @@
tmpl = templater.templater(mapfile,
defaults={"url": req.url,
+ "logourl": logourl,
"staticurl": staticurl,
"urlbase": urlbase,
"repo": self.reponame,
--- a/mercurial/hgweb/hgwebdir_mod.py Fri Apr 29 11:04:39 2011 +0200
+++ b/mercurial/hgweb/hgwebdir_mod.py Fri Apr 29 11:10:11 2011 +0200
@@ -6,10 +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.
-import os, re, time, urlparse
+import os, re, time
from mercurial.i18n import _
-from mercurial import ui, hg, util, templater
-from mercurial import error, encoding
+from mercurial import ui, hg, scmutil, util, templater
+from mercurial import error, encoding, url
from common import ErrorResponse, get_mtime, staticfile, paritygen, \
get_contact, HTTP_OK, HTTP_NOT_FOUND, HTTP_SERVER_ERROR
from hgweb_mod import hgweb
@@ -33,16 +33,17 @@
repos.append((prefix, root))
continue
roothead = os.path.normpath(os.path.abspath(roothead))
- paths = util.walkrepos(roothead, followsym=True, recurse=recurse)
+ paths = scmutil.walkrepos(roothead, followsym=True, recurse=recurse)
repos.extend(urlrepos(prefix, roothead, paths))
return repos
def urlrepos(prefix, roothead, paths):
"""yield url paths and filesystem paths from a list of repo paths
- >>> list(urlrepos('hg', '/opt', ['/opt/r', '/opt/r/r', '/opt']))
+ >>> conv = lambda seq: [(v, util.pconvert(p)) for v,p in seq]
+ >>> conv(urlrepos('hg', '/opt', ['/opt/r', '/opt/r/r', '/opt']))
[('hg/r', '/opt/r'), ('hg/r/r', '/opt/r/r'), ('hg', '/opt')]
- >>> list(urlrepos('', '/opt', ['/opt/r', '/opt/r/r', '/opt']))
+ >>> conv(urlrepos('', '/opt', ['/opt/r', '/opt/r/r', '/opt']))
[('r', '/opt/r'), ('r/r', '/opt/r/r'), ('', '/opt')]
"""
for path in paths:
@@ -76,7 +77,10 @@
if not os.path.exists(self.conf):
raise util.Abort(_('config file %s not found!') % self.conf)
u.readconfig(self.conf, remap=map, trust=True)
- paths = u.configitems('hgweb-paths')
+ paths = []
+ for name, ignored in u.configitems('hgweb-paths'):
+ for path in u.configlist('hgweb-paths', name):
+ paths.append((name, path))
elif isinstance(self.conf, (list, tuple)):
paths = self.conf
elif isinstance(self.conf, dict):
@@ -85,7 +89,7 @@
repos = findrepos(paths)
for prefix, root in u.configitems('collections'):
prefix = util.pconvert(prefix)
- for path in util.walkrepos(root, followsym=True):
+ for path in scmutil.walkrepos(root, followsym=True):
repo = os.path.normpath(path)
name = util.pconvert(repo)
if name.startswith(prefix):
@@ -247,6 +251,9 @@
# update time with local timezone
try:
r = hg.repository(self.ui, path)
+ except IOError:
+ u.warn(_('error accessing repository at %s\n') % path)
+ continue
except error.RepoError:
u.warn(_('error accessing repository at %s\n') % path)
continue
@@ -340,6 +347,7 @@
start = url[-1] == '?' and '&' or '?'
sessionvars = webutil.sessionvars(vars, start)
+ logourl = config('web', 'logourl', 'http://mercurial.selenic.com/')
staticurl = config('web', 'staticurl') or url + 'static/'
if not staticurl.endswith('/'):
staticurl += '/'
@@ -349,22 +357,15 @@
"footer": footer,
"motd": motd,
"url": url,
+ "logourl": logourl,
"staticurl": staticurl,
"sessionvars": sessionvars})
return tmpl
def updatereqenv(self, env):
- def splitnetloc(netloc):
- if ':' in netloc:
- return netloc.split(':', 1)
- else:
- return (netloc, None)
-
if self._baseurl is not None:
- urlcomp = urlparse.urlparse(self._baseurl)
- host, port = splitnetloc(urlcomp[1])
- path = urlcomp[2]
- env['SERVER_NAME'] = host
- if port:
- env['SERVER_PORT'] = port
- env['SCRIPT_NAME'] = path
+ u = url.url(self._baseurl)
+ env['SERVER_NAME'] = u.host
+ if u.port:
+ env['SERVER_PORT'] = u.port
+ env['SCRIPT_NAME'] = '/' + u.path
--- a/mercurial/hgweb/protocol.py Fri Apr 29 11:04:39 2011 +0200
+++ b/mercurial/hgweb/protocol.py Fri Apr 29 11:10:11 2011 +0200
@@ -22,7 +22,7 @@
if k == '*':
star = {}
for key in self.req.form.keys():
- if key not in keys:
+ if key != 'cmd' and key not in keys:
star[key] = self.req.form[key][0]
data['*'] = star
else:
--- a/mercurial/hgweb/server.py Fri Apr 29 11:04:39 2011 +0200
+++ b/mercurial/hgweb/server.py Fri Apr 29 11:10:11 2011 +0200
@@ -8,6 +8,7 @@
import os, sys, errno, urllib, BaseHTTPServer, socket, SocketServer, traceback
from mercurial import util, error
+from mercurial.hgweb import common
from mercurial.i18n import _
def _splitURI(uri):
@@ -111,6 +112,9 @@
env['SERVER_PROTOCOL'] = self.request_version
env['wsgi.version'] = (1, 0)
env['wsgi.url_scheme'] = self.url_scheme
+ if env.get('HTTP_EXPECT', '').lower() == '100-continue':
+ self.rfile = common.continuereader(self.rfile, self.wfile.write)
+
env['wsgi.input'] = self.rfile
env['wsgi.errors'] = _error_logger(self)
env['wsgi.multithread'] = isinstance(self.server,
--- a/mercurial/hgweb/webcommands.py Fri Apr 29 11:04:39 2011 +0200
+++ b/mercurial/hgweb/webcommands.py Fri Apr 29 11:10:11 2011 +0200
@@ -108,10 +108,11 @@
revcount = web.maxchanges
if 'revcount' in req.form:
revcount = int(req.form.get('revcount', [revcount])[0])
+ revcount = max(revcount, 1)
tmpl.defaults['sessionvars']['revcount'] = revcount
lessvars = copy.copy(tmpl.defaults['sessionvars'])
- lessvars['revcount'] = revcount / 2
+ lessvars['revcount'] = max(revcount / 2, 1)
lessvars['rev'] = query
morevars = copy.copy(tmpl.defaults['sessionvars'])
morevars['revcount'] = revcount * 2
@@ -220,10 +221,11 @@
revcount = shortlog and web.maxshortchanges or web.maxchanges
if 'revcount' in req.form:
revcount = int(req.form.get('revcount', [revcount])[0])
+ revcount = max(revcount, 1)
tmpl.defaults['sessionvars']['revcount'] = revcount
lessvars = copy.copy(tmpl.defaults['sessionvars'])
- lessvars['revcount'] = revcount / 2
+ lessvars['revcount'] = max(revcount / 2, 1)
morevars = copy.copy(tmpl.defaults['sessionvars'])
morevars['revcount'] = revcount * 2
@@ -393,14 +395,11 @@
def bookmarks(web, req, tmpl):
i = web.repo._bookmarks.items()
- i.reverse()
parity = paritygen(web.stripecount)
- def entries(notip=False, limit=0, **map):
+ def entries(limit=0, **map):
count = 0
- for k, n in i:
- if notip and k == "tip":
- continue
+ for k, n in sorted(i):
if limit > 0 and count >= limit:
continue
count = count + 1
@@ -411,9 +410,8 @@
return tmpl("bookmarks",
node=hex(web.repo.changelog.tip()),
- entries=lambda **x: entries(False, 0, **x),
- entriesnotip=lambda **x: entries(True, 0, **x),
- latestentry=lambda **x: entries(True, 1, **x))
+ entries=lambda **x: entries(0, **x),
+ latestentry=lambda **x: entries(1, **x))
def branches(web, req, tmpl):
tips = (web.repo[n] for t, n in web.repo.branchtags().iteritems())
@@ -464,6 +462,15 @@
node=hex(n),
date=web.repo[n].date())
+ def bookmarks(**map):
+ parity = paritygen(web.stripecount)
+ b = web.repo._bookmarks.items()
+ for k, n in sorted(b)[:10]: # limit to 10 bookmarks
+ yield {'parity': parity.next(),
+ 'bookmark': k,
+ 'date': web.repo[n].date(),
+ 'node': hex(n)}
+
def branches(**map):
parity = paritygen(web.stripecount)
@@ -508,6 +515,7 @@
owner=get_contact(web.config) or "unknown",
lastchange=tip.date(),
tags=tagentries,
+ bookmarks=bookmarks,
branches=branches,
shortlog=changelist,
node=tip.hex(),
@@ -624,10 +632,11 @@
revcount = web.maxshortchanges
if 'revcount' in req.form:
revcount = int(req.form.get('revcount', [revcount])[0])
+ revcount = max(revcount, 1)
tmpl.defaults['sessionvars']['revcount'] = revcount
lessvars = copy.copy(tmpl.defaults['sessionvars'])
- lessvars['revcount'] = revcount / 2
+ lessvars['revcount'] = max(revcount / 2, 1)
morevars = copy.copy(tmpl.defaults['sessionvars'])
morevars['revcount'] = revcount * 2
@@ -725,10 +734,11 @@
revcount = web.maxshortchanges
if 'revcount' in req.form:
revcount = int(req.form.get('revcount', [revcount])[0])
+ revcount = max(revcount, 1)
tmpl.defaults['sessionvars']['revcount'] = revcount
lessvars = copy.copy(tmpl.defaults['sessionvars'])
- lessvars['revcount'] = revcount / 2
+ lessvars['revcount'] = max(revcount / 2, 1)
morevars = copy.copy(tmpl.defaults['sessionvars'])
morevars['revcount'] = revcount * 2
--- a/mercurial/hgweb/webutil.py Fri Apr 29 11:04:39 2011 +0200
+++ b/mercurial/hgweb/webutil.py Fri Apr 29 11:10:11 2011 +0200
@@ -7,7 +7,7 @@
# GNU General Public License version 2 or any later version.
import os, copy
-from mercurial import match, patch, util, error, ui
+from mercurial import match, patch, scmutil, error, ui
from mercurial.node import hex, nullid
def up(p):
@@ -127,7 +127,7 @@
def cleanpath(repo, path):
path = path.lstrip('/')
- return util.canonpath(repo.root, '', path)
+ return scmutil.canonpath(repo.root, '', path)
def changectx(repo, req):
changeid = "tip"
--- a/mercurial/hgweb/wsgicgi.py Fri Apr 29 11:04:39 2011 +0200
+++ b/mercurial/hgweb/wsgicgi.py Fri Apr 29 11:10:11 2011 +0200
@@ -10,6 +10,7 @@
import os, sys
from mercurial import util
+from mercurial.hgweb import common
def launch(application):
util.set_binary(sys.stdin)
@@ -23,7 +24,11 @@
if environ['PATH_INFO'].startswith(scriptname):
environ['PATH_INFO'] = environ['PATH_INFO'][len(scriptname):]
- environ['wsgi.input'] = sys.stdin
+ stdin = sys.stdin
+ if environ.get('HTTP_EXPECT', '').lower() == '100-continue':
+ stdin = common.continuereader(stdin, sys.stdout.write)
+
+ environ['wsgi.input'] = stdin
environ['wsgi.errors'] = sys.stderr
environ['wsgi.version'] = (1, 0)
environ['wsgi.multithread'] = False
--- a/mercurial/httprepo.py Fri Apr 29 11:04:39 2011 +0200
+++ b/mercurial/httprepo.py Fri Apr 29 11:10:11 2011 +0200
@@ -9,7 +9,7 @@
from node import nullid
from i18n import _
import changegroup, statichttprepo, error, url, util, wireproto
-import os, urllib, urllib2, urlparse, zlib, httplib
+import os, urllib, urllib2, zlib, httplib
import errno, socket
def zgenerator(f):
@@ -28,13 +28,13 @@
self.path = path
self.caps = None
self.handler = None
- scheme, netloc, urlpath, query, frag = urlparse.urlsplit(path)
- if query or frag:
+ u = url.url(path)
+ if u.query or u.fragment:
raise util.Abort(_('unsupported URL component: "%s"') %
- (query or frag))
+ (u.query or u.fragment))
# urllib cannot handle URLs with embedded user or passwd
- self._url, authinfo = url.getauthinfo(path)
+ self._url, authinfo = u.authinfo()
self.ui = ui
self.ui.debug('using %s\n' % self._url)
@@ -52,10 +52,13 @@
# look up capabilities only when needed
+ def _fetchcaps(self):
+ self.caps = set(self._call('capabilities').split())
+
def get_caps(self):
if self.caps is None:
try:
- self.caps = set(self._call('capabilities').split())
+ self._fetchcaps()
except error.RepoError:
self.caps = set()
self.ui.debug('capabilities: %s\n' %
@@ -73,8 +76,7 @@
data = args.pop('data', None)
headers = args.pop('headers', {})
self.ui.debug("sending %s command\n" % cmd)
- q = {"cmd": cmd}
- q.update(args)
+ q = [('cmd', cmd)] + sorted(args.items())
qs = '?%s' % urllib.urlencode(q)
cu = "%s%s" % (self._url, qs)
req = urllib2.Request(cu, data, headers)
@@ -196,7 +198,13 @@
inst = httpsrepository(ui, path)
else:
inst = httprepository(ui, path)
- inst.between([(nullid, nullid)])
+ try:
+ # Try to do useful work when checking compatibility.
+ # Usually saves a roundtrip since we want the caps anyway.
+ inst._fetchcaps()
+ except error.RepoError:
+ # No luck, try older compatibility check.
+ inst.between([(nullid, nullid)])
return inst
except error.RepoError:
ui.note('(falling back to static-http)\n')
--- a/mercurial/i18n.py Fri Apr 29 11:04:39 2011 +0200
+++ b/mercurial/i18n.py Fri Apr 29 11:10:11 2011 +0200
@@ -51,7 +51,13 @@
# An unknown encoding results in a LookupError.
return message
-if 'HGPLAIN' in os.environ:
+def _plain():
+ if 'HGPLAIN' not in os.environ and 'HGPLAINEXCEPT' not in os.environ:
+ return False
+ exceptions = os.environ.get('HGPLAINEXCEPT', '').strip().split(',')
+ return 'i18n' not in exceptions
+
+if _plain():
_ = lambda message: message
else:
_ = gettext
--- a/mercurial/localrepo.py Fri Apr 29 11:04:39 2011 +0200
+++ b/mercurial/localrepo.py Fri Apr 29 11:10:11 2011 +0200
@@ -10,7 +10,7 @@
import repo, changegroup, subrepo, discovery, pushkey
import changelog, dirstate, filelog, manifest, context, bookmarks
import lock, transaction, store, encoding
-import util, extensions, hook, error
+import scmutil, util, extensions, hook, error
import match as matchmod
import merge as mergemod
import tags as tagsmod
@@ -20,7 +20,8 @@
propertycache = util.propertycache
class localrepository(repo.repository):
- capabilities = set(('lookup', 'changegroupsubset', 'branchmap', 'pushkey'))
+ capabilities = set(('lookup', 'changegroupsubset', 'branchmap', 'pushkey',
+ 'known', 'getbundle'))
supportedformats = set(('revlogv1', 'parentdelta'))
supported = supportedformats | set(('store', 'fncache', 'shared',
'dotencode'))
@@ -30,9 +31,9 @@
self.root = os.path.realpath(util.expandpath(path))
self.path = os.path.join(self.root, ".hg")
self.origroot = path
- self.auditor = util.path_auditor(self.root, self._checknested)
- self.opener = util.opener(self.path)
- self.wopener = util.opener(self.root)
+ self.auditor = scmutil.path_auditor(self.root, self._checknested)
+ self.opener = scmutil.opener(self.path)
+ self.wopener = scmutil.opener(self.root)
self.baseui = baseui
self.ui = baseui.copy()
@@ -46,7 +47,7 @@
if create:
if not os.path.exists(path):
util.makedirs(path)
- os.mkdir(self.path)
+ util.makedir(self.path, notindexed=True)
requirements = ["revlogv1"]
if self.ui.configbool('format', 'usestore', True):
os.mkdir(os.path.join(self.path, "store"))
@@ -89,7 +90,7 @@
if inst.errno != errno.ENOENT:
raise
- self.store = store.store(requirements, self.sharedpath, util.opener)
+ self.store = store.store(requirements, self.sharedpath, scmutil.opener)
self.spath = self.store.path
self.sopener = self.store.opener
self.sjoin = self.store.join
@@ -361,7 +362,12 @@
tags = {}
for (name, (node, hist)) in alltags.iteritems():
if node != nullid:
- tags[encoding.tolocal(name)] = node
+ try:
+ # ignore tags to unknown nodes
+ self.changelog.lookup(node)
+ tags[encoding.tolocal(name)] = node
+ except error.LookupError:
+ pass
tags['tip'] = self.changelog.tip()
tagtypes = dict([(encoding.tolocal(name), value)
for (name, value) in tagtypes.iteritems()])
@@ -384,10 +390,7 @@
'''return a list of tags ordered by revision'''
l = []
for t, n in self.tags().iteritems():
- try:
- r = self.changelog.rev(n)
- except:
- r = -2 # sort to the beginning of the list if unknown
+ r = self.changelog.rev(n)
l.append((r, t, n))
return [(t, n) for r, t, n in sorted(l)]
@@ -521,7 +524,7 @@
if isinstance(key, int):
return self.changelog.node(key)
elif key == '.':
- return self.dirstate.parents()[0]
+ return self.dirstate.p1()
elif key == 'null':
return nullid
elif key == 'tip':
@@ -546,7 +549,7 @@
try:
if len(key) == 20:
key = hex(key)
- except:
+ except TypeError:
pass
raise error.RepoLookupError(_("unknown revision '%s'") % key)
@@ -558,6 +561,10 @@
repo = (remote and remote.local()) and remote or self
return repo[key].branch()
+ def known(self, nodes):
+ nm = self.changelog.nodemap
+ return [(n in nm) for n in nodes]
+
def local(self):
return True
@@ -1014,10 +1021,7 @@
raise
# update bookmarks, dirstate and mergestate
- parents = (p1, p2)
- if p2 == nullid:
- parents = (p1,)
- bookmarks.update(self, parents, ret)
+ bookmarks.update(self, p1, ret)
for f in changes[0] + changes[1]:
self.dirstate.normal(f)
for f in changes[2]:
@@ -1221,14 +1225,15 @@
modified, added, clean = [], [], []
for fn in mf2:
if fn in mf1:
- if (mf1.flags(fn) != mf2.flags(fn) or
- (mf1[fn] != mf2[fn] and
- (mf2[fn] or ctx1[fn].cmp(ctx2[fn])))):
+ if (fn not in deleted and
+ (mf1.flags(fn) != mf2.flags(fn) or
+ (mf1[fn] != mf2[fn] and
+ (mf2[fn] or ctx1[fn].cmp(ctx2[fn]))))):
modified.append(fn)
elif listclean:
clean.append(fn)
del mf1[fn]
- else:
+ elif fn not in deleted:
added.append(fn)
removed = mf1.keys()
@@ -1320,20 +1325,24 @@
def pull(self, remote, heads=None, force=False):
lock = self.lock()
try:
+ usecommon = remote.capable('getbundle')
tmp = discovery.findcommonincoming(self, remote, heads=heads,
- force=force)
+ force=force, commononly=usecommon)
common, fetch, rheads = tmp
if not fetch:
self.ui.status(_("no changes found\n"))
result = 0
else:
- if heads is None and fetch == [nullid]:
+ if heads is None and list(common) == [nullid]:
self.ui.status(_("requesting all changes\n"))
elif heads is None and remote.capable('changegroupsubset'):
# issue1320, avoid a race if remote changed after discovery
heads = rheads
- if heads is None:
+ if usecommon:
+ cg = remote.getbundle('pull', common=common,
+ heads=heads or rheads)
+ elif heads is None:
cg = remote.changegroup(fetch, 'pull')
elif not remote.capable('changegroupsubset'):
raise util.Abort(_("partial pull cannot be done because "
@@ -1346,27 +1355,6 @@
finally:
lock.release()
- self.ui.debug("checking for updated bookmarks\n")
- rb = remote.listkeys('bookmarks')
- changed = False
- for k in rb.keys():
- if k in self._bookmarks:
- nr, nl = rb[k], self._bookmarks[k]
- if nr in self:
- cr = self[nr]
- cl = self[nl]
- if cl.rev() >= cr.rev():
- continue
- if cr in cl.descendants():
- self._bookmarks[k] = cr.node()
- changed = True
- self.ui.status(_("updating bookmark %s\n") % k)
- else:
- self.ui.warn(_("not updating divergent"
- " bookmark %s\n") % k)
- if changed:
- bookmarks.write(self)
-
return result
def checkpush(self, force, revs):
@@ -1446,7 +1434,7 @@
for node in nodes:
self.ui.debug("%s\n" % hex(node))
- def changegroupsubset(self, bases, heads, source, extranodes=None):
+ def changegroupsubset(self, bases, heads, source):
"""Compute a changegroup consisting of all the nodes that are
descendents of any of the bases and ancestors of any of the heads.
Return a chunkbuffer object whose read() method will return
@@ -1458,214 +1446,127 @@
Another wrinkle is doing the reverse, figuring out which changeset in
the changegroup a particular filenode or manifestnode belongs to.
-
- The caller can specify some nodes that must be included in the
- changegroup using the extranodes argument. It should be a dict
- where the keys are the filenames (or 1 for the manifest), and the
- values are lists of (node, linknode) tuples, where node is a wanted
- node and linknode is the changelog node that should be transmitted as
- the linkrev.
"""
-
- # Set up some initial variables
- # Make it easy to refer to self.changelog
cl = self.changelog
- # Compute the list of changesets in this changegroup.
- # Some bases may turn out to be superfluous, and some heads may be
- # too. nodesbetween will return the minimal set of bases and heads
- # necessary to re-create the changegroup.
if not bases:
bases = [nullid]
- msng_cl_lst, bases, heads = cl.nodesbetween(bases, heads)
+ csets, bases, heads = cl.nodesbetween(bases, heads)
+ # We assume that all ancestors of bases are known
+ common = set(cl.ancestors(*[cl.rev(n) for n in bases]))
+ return self._changegroupsubset(common, csets, heads, source)
+
+ def getbundle(self, source, heads=None, common=None):
+ """Like changegroupsubset, but returns the set difference between the
+ ancestors of heads and the ancestors common.
+
+ If heads is None, use the local heads. If common is None, use [nullid].
- if extranodes is None:
- # can we go through the fast path ?
- heads.sort()
- allheads = self.heads()
- allheads.sort()
- if heads == allheads:
- return self._changegroup(msng_cl_lst, source)
+ The nodes in common might not all be known locally due to the way the
+ current discovery protocol works.
+ """
+ cl = self.changelog
+ if common:
+ nm = cl.nodemap
+ common = [n for n in common if n in nm]
+ else:
+ common = [nullid]
+ if not heads:
+ heads = cl.heads()
+ common, missing = cl.findcommonmissing(common, heads)
+ return self._changegroupsubset(common, missing, heads, source)
+
+ def _changegroupsubset(self, commonrevs, csets, heads, source):
+
+ cl = self.changelog
+ mf = self.manifest
+ mfs = {} # needed manifests
+ fnodes = {} # needed file nodes
+ changedfiles = set()
+ fstate = ['', {}]
+ count = [0]
+
+ # can we go through the fast path ?
+ heads.sort()
+ if heads == sorted(self.heads()):
+ return self._changegroup(csets, source)
# slow path
self.hook('preoutgoing', throw=True, source=source)
-
- self.changegroupinfo(msng_cl_lst, source)
-
- # We assume that all ancestors of bases are known
- commonrevs = set(cl.ancestors(*[cl.rev(n) for n in bases]))
+ self.changegroupinfo(csets, source)
- # Make it easy to refer to self.manifest
- mnfst = self.manifest
- # We don't know which manifests are missing yet
- msng_mnfst_set = {}
- # Nor do we know which filenodes are missing.
- msng_filenode_set = {}
-
- # A changeset always belongs to itself, so the changenode lookup
- # function for a changenode is identity.
- def identity(x):
- return x
+ # filter any nodes that claim to be part of the known set
+ def prune(revlog, missing):
+ for n in missing:
+ if revlog.linkrev(revlog.rev(n)) not in commonrevs:
+ yield n
- # A function generating function that sets up the initial environment
- # the inner function.
- def filenode_collector(changedfiles):
- # This gathers information from each manifestnode included in the
- # changegroup about which filenodes the manifest node references
- # so we can include those in the changegroup too.
- #
- # It also remembers which changenode each filenode belongs to. It
- # does this by assuming the a filenode belongs to the changenode
- # the first manifest that references it belongs to.
- def collect_msng_filenodes(mnfstnode):
- r = mnfst.rev(mnfstnode)
- if mnfst.deltaparent(r) in mnfst.parentrevs(r):
- # If the previous rev is one of the parents,
- # we only need to see a diff.
- deltamf = mnfst.readdelta(mnfstnode)
- # For each line in the delta
- for f, fnode in deltamf.iteritems():
- # And if the file is in the list of files we care
- # about.
- if f in changedfiles:
- # Get the changenode this manifest belongs to
- clnode = msng_mnfst_set[mnfstnode]
- # Create the set of filenodes for the file if
- # there isn't one already.
- ndset = msng_filenode_set.setdefault(f, {})
- # And set the filenode's changelog node to the
- # manifest's if it hasn't been set already.
- ndset.setdefault(fnode, clnode)
- else:
- # Otherwise we need a full manifest.
- m = mnfst.read(mnfstnode)
- # For every file in we care about.
- for f in changedfiles:
- fnode = m.get(f, None)
- # If it's in the manifest
- if fnode is not None:
- # See comments above.
- clnode = msng_mnfst_set[mnfstnode]
- ndset = msng_filenode_set.setdefault(f, {})
- ndset.setdefault(fnode, clnode)
- return collect_msng_filenodes
+ def lookup(revlog, x):
+ if revlog == cl:
+ c = cl.read(x)
+ changedfiles.update(c[3])
+ mfs.setdefault(c[0], x)
+ count[0] += 1
+ self.ui.progress(_('bundling'), count[0], unit=_('changesets'))
+ return x
+ elif revlog == mf:
+ clnode = mfs[x]
+ mdata = mf.readfast(x)
+ for f in changedfiles:
+ if f in mdata:
+ fnodes.setdefault(f, {}).setdefault(mdata[f], clnode)
+ count[0] += 1
+ self.ui.progress(_('bundling'), count[0],
+ unit=_('manifests'), total=len(mfs))
+ return mfs[x]
+ else:
+ self.ui.progress(
+ _('bundling'), count[0], item=fstate[0],
+ unit=_('files'), total=len(changedfiles))
+ return fstate[1][x]
- # If we determine that a particular file or manifest node must be a
- # node that the recipient of the changegroup will already have, we can
- # also assume the recipient will have all the parents. This function
- # prunes them from the set of missing nodes.
- def prune(revlog, missingnodes):
- hasset = set()
- # If a 'missing' filenode thinks it belongs to a changenode we
- # assume the recipient must have, then the recipient must have
- # that filenode.
- for n in missingnodes:
- clrev = revlog.linkrev(revlog.rev(n))
- if clrev in commonrevs:
- hasset.add(n)
- for n in hasset:
- missingnodes.pop(n, None)
- for r in revlog.ancestors(*[revlog.rev(n) for n in hasset]):
- missingnodes.pop(revlog.node(r), None)
+ bundler = changegroup.bundle10(lookup)
- # Add the nodes that were explicitly requested.
- def add_extra_nodes(name, nodes):
- if not extranodes or name not in extranodes:
- return
-
- for node, linknode in extranodes[name]:
- if node not in nodes:
- nodes[node] = linknode
-
- # Now that we have all theses utility functions to help out and
- # logically divide up the task, generate the group.
def gengroup():
- # The set of changed files starts empty.
- changedfiles = set()
- collect = changegroup.collector(cl, msng_mnfst_set, changedfiles)
-
# Create a changenode group generator that will call our functions
# back to lookup the owning changenode and collect information.
- group = cl.group(msng_cl_lst, identity, collect)
- for cnt, chnk in enumerate(group):
- yield chnk
- # revlog.group yields three entries per node, so
- # dividing by 3 gives an approximation of how many
- # nodes have been processed.
- self.ui.progress(_('bundling'), cnt / 3,
- unit=_('changesets'))
- changecount = cnt / 3
+ for chunk in cl.group(csets, bundler):
+ yield chunk
self.ui.progress(_('bundling'), None)
- prune(mnfst, msng_mnfst_set)
- add_extra_nodes(1, msng_mnfst_set)
- msng_mnfst_lst = msng_mnfst_set.keys()
- # Sort the manifestnodes by revision number.
- msng_mnfst_lst.sort(key=mnfst.rev)
# Create a generator for the manifestnodes that calls our lookup
# and data collection functions back.
- group = mnfst.group(msng_mnfst_lst,
- lambda mnode: msng_mnfst_set[mnode],
- filenode_collector(changedfiles))
- efiles = {}
- for cnt, chnk in enumerate(group):
- if cnt % 3 == 1:
- mnode = chnk[:20]
- efiles.update(mnfst.readdelta(mnode))
- yield chnk
- # see above comment for why we divide by 3
- self.ui.progress(_('bundling'), cnt / 3,
- unit=_('manifests'), total=changecount)
+ count[0] = 0
+ for chunk in mf.group(prune(mf, mfs), bundler):
+ yield chunk
self.ui.progress(_('bundling'), None)
- efiles = len(efiles)
- # These are no longer needed, dereference and toss the memory for
- # them.
- msng_mnfst_lst = None
- msng_mnfst_set.clear()
+ mfs.clear()
- if extranodes:
- for fname in extranodes:
- if isinstance(fname, int):
- continue
- msng_filenode_set.setdefault(fname, {})
- changedfiles.add(fname)
# Go through all our files in order sorted by name.
- for idx, fname in enumerate(sorted(changedfiles)):
+ count[0] = 0
+ for fname in sorted(changedfiles):
filerevlog = self.file(fname)
if not len(filerevlog):
raise util.Abort(_("empty or missing revlog for %s") % fname)
- # Toss out the filenodes that the recipient isn't really
- # missing.
- missingfnodes = msng_filenode_set.pop(fname, {})
- prune(filerevlog, missingfnodes)
- add_extra_nodes(fname, missingfnodes)
- # If any filenodes are left, generate the group for them,
- # otherwise don't bother.
- if missingfnodes:
- yield changegroup.chunkheader(len(fname))
- yield fname
- # Sort the filenodes by their revision # (topological order)
- nodeiter = list(missingfnodes)
- nodeiter.sort(key=filerevlog.rev)
- # Create a group generator and only pass in a changenode
- # lookup function as we need to collect no information
- # from filenodes.
- group = filerevlog.group(nodeiter,
- lambda fnode: missingfnodes[fnode])
- for chnk in group:
- # even though we print the same progress on
- # most loop iterations, put the progress call
- # here so that time estimates (if any) can be updated
- self.ui.progress(
- _('bundling'), idx, item=fname,
- unit=_('files'), total=efiles)
- yield chnk
+ fstate[0] = fname
+ fstate[1] = fnodes.pop(fname, {})
+ first = True
+
+ for chunk in filerevlog.group(prune(filerevlog, fstate[1]),
+ bundler):
+ if first:
+ if chunk == bundler.close():
+ break
+ count[0] += 1
+ yield bundler.fileheader(fname)
+ first = False
+ yield chunk
# Signal that no more groups are left.
- yield changegroup.closechunk()
+ yield bundler.close()
self.ui.progress(_('bundling'), None)
- if msng_cl_lst:
- self.hook('outgoing', node=hex(msng_cl_lst[0]), source=source)
+ if csets:
+ self.hook('outgoing', node=hex(csets[0]), source=source)
return changegroup.unbundle10(util.chunkbuffer(gengroup()), 'UN')
@@ -1683,75 +1584,75 @@
nodes is the set of nodes to send"""
- self.hook('preoutgoing', throw=True, source=source)
+ cl = self.changelog
+ mf = self.manifest
+ mfs = {}
+ changedfiles = set()
+ fstate = ['']
+ count = [0]
- cl = self.changelog
- revset = set([cl.rev(n) for n in nodes])
+ self.hook('preoutgoing', throw=True, source=source)
self.changegroupinfo(nodes, source)
- def identity(x):
- return x
+ revset = set([cl.rev(n) for n in nodes])
def gennodelst(log):
for r in log:
if log.linkrev(r) in revset:
yield log.node(r)
- def lookuplinkrev_func(revlog):
- def lookuplinkrev(n):
- return cl.node(revlog.linkrev(revlog.rev(n)))
- return lookuplinkrev
+ def lookup(revlog, x):
+ if revlog == cl:
+ c = cl.read(x)
+ changedfiles.update(c[3])
+ mfs.setdefault(c[0], x)
+ count[0] += 1
+ self.ui.progress(_('bundling'), count[0], unit=_('changesets'))
+ return x
+ elif revlog == mf:
+ count[0] += 1
+ self.ui.progress(_('bundling'), count[0],
+ unit=_('manifests'), total=len(mfs))
+ return cl.node(revlog.linkrev(revlog.rev(x)))
+ else:
+ self.ui.progress(
+ _('bundling'), count[0], item=fstate[0],
+ total=len(changedfiles), unit=_('files'))
+ return cl.node(revlog.linkrev(revlog.rev(x)))
+
+ bundler = changegroup.bundle10(lookup)
def gengroup():
'''yield a sequence of changegroup chunks (strings)'''
# construct a list of all changed files
- changedfiles = set()
- mmfs = {}
- collect = changegroup.collector(cl, mmfs, changedfiles)
- for cnt, chnk in enumerate(cl.group(nodes, identity, collect)):
- # revlog.group yields three entries per node, so
- # dividing by 3 gives an approximation of how many
- # nodes have been processed.
- self.ui.progress(_('bundling'), cnt / 3, unit=_('changesets'))
- yield chnk
- changecount = cnt / 3
+ for chunk in cl.group(nodes, bundler):
+ yield chunk
self.ui.progress(_('bundling'), None)
- mnfst = self.manifest
- nodeiter = gennodelst(mnfst)
- efiles = {}
- for cnt, chnk in enumerate(mnfst.group(nodeiter,
- lookuplinkrev_func(mnfst))):
- if cnt % 3 == 1:
- mnode = chnk[:20]
- efiles.update(mnfst.readdelta(mnode))
- # see above comment for why we divide by 3
- self.ui.progress(_('bundling'), cnt / 3,
- unit=_('manifests'), total=changecount)
- yield chnk
- efiles = len(efiles)
+ count[0] = 0
+ for chunk in mf.group(gennodelst(mf), bundler):
+ yield chunk
self.ui.progress(_('bundling'), None)
- for idx, fname in enumerate(sorted(changedfiles)):
+ count[0] = 0
+ for fname in sorted(changedfiles):
filerevlog = self.file(fname)
if not len(filerevlog):
raise util.Abort(_("empty or missing revlog for %s") % fname)
- nodeiter = gennodelst(filerevlog)
- nodeiter = list(nodeiter)
- if nodeiter:
- yield changegroup.chunkheader(len(fname))
- yield fname
- lookup = lookuplinkrev_func(filerevlog)
- for chnk in filerevlog.group(nodeiter, lookup):
- self.ui.progress(
- _('bundling'), idx, item=fname,
- total=efiles, unit=_('files'))
- yield chnk
+ fstate[0] = fname
+ first = True
+ for chunk in filerevlog.group(gennodelst(filerevlog), bundler):
+ if first:
+ if chunk == bundler.close():
+ break
+ count[0] += 1
+ yield bundler.fileheader(fname)
+ first = False
+ yield chunk
+ yield bundler.close()
self.ui.progress(_('bundling'), None)
- yield changegroup.closechunk()
-
if nodes:
self.hook('outgoing', node=hex(nodes[0]), source=source)
@@ -1915,10 +1816,6 @@
self.hook("incoming", node=hex(cl.node(i)),
source=srctype, url=url)
- # FIXME - why does this care about tip?
- if newheads == oldheads:
- bookmarks.update(self, self.dirstate.parents(), self['tip'].node())
-
# never return 0 here:
if newheads < oldheads:
return newheads - oldheads - 1
@@ -2019,6 +1916,10 @@
def listkeys(self, namespace):
return pushkey.list(self, namespace)
+ def debugwireargs(self, one, two, three=None, four=None):
+ '''used to test argument passing over the wire'''
+ return "%s %s %s %s" % (one, two, three, four)
+
# used to avoid circular references so destructors work
def aftertrans(files):
renamefiles = [tuple(t) for t in files]
@@ -2028,7 +1929,7 @@
return a
def instance(ui, path, create):
- return localrepository(ui, util.drop_scheme('file', path), create)
+ return localrepository(ui, urlmod.localpath(path), create)
def islocal(path):
return True
--- a/mercurial/manifest.py Fri Apr 29 11:04:39 2011 +0200
+++ b/mercurial/manifest.py Fri Apr 29 11:10:11 2011 +0200
@@ -38,6 +38,13 @@
r = self.rev(node)
return self.parse(mdiff.patchtext(self.revdiff(self.deltaparent(r), r)))
+ def readfast(self, node):
+ '''use the faster of readdelta or read'''
+ r = self.rev(node)
+ if self.deltaparent(r) in self.parentrevs(r):
+ return self.readdelta(node)
+ return self.read(node)
+
def read(self, node):
if node == revlog.nullid:
return manifestdict() # don't upset local cache
--- a/mercurial/match.py Fri Apr 29 11:04:39 2011 +0200
+++ b/mercurial/match.py Fri Apr 29 11:10:11 2011 +0200
@@ -6,7 +6,7 @@
# GNU General Public License version 2 or any later version.
import re
-import util
+import scmutil, util
from i18n import _
class match(object):
@@ -269,7 +269,7 @@
pats = []
for kind, name in [_patsplit(p, default) for p in names]:
if kind in ('glob', 'relpath'):
- name = util.canonpath(root, cwd, name, auditor)
+ name = scmutil.canonpath(root, cwd, name, auditor)
elif kind in ('relglob', 'path'):
name = util.normpath(name)
elif kind in ('listfile', 'listfile0'):
--- a/mercurial/merge.py Fri Apr 29 11:04:39 2011 +0200
+++ b/mercurial/merge.py Fri Apr 29 11:10:11 2011 +0200
@@ -7,7 +7,7 @@
from node import nullid, nullrev, hex, bin
from i18n import _
-import util, filemerge, copies, subrepo
+import scmutil, util, filemerge, copies, subrepo
import errno, os, shutil
class mergestate(object):
@@ -268,7 +268,7 @@
updated, merged, removed, unresolved = 0, 0, 0, 0
ms = mergestate(repo)
- ms.reset(wctx.parents()[0].node())
+ ms.reset(wctx.p1().node())
moves = []
action.sort(key=actionkey)
substate = wctx.substate # prime
@@ -286,7 +286,7 @@
fco = mctx[f2]
if mctx == actx: # backwards, use working dir parent as ancestor
if fcl.parents():
- fca = fcl.parents()[0]
+ fca = fcl.p1()
else:
fca = repo.filectx(f, fileid=nullrev)
else:
@@ -303,7 +303,7 @@
repo.ui.debug("removing %s\n" % f)
os.unlink(repo.wjoin(f))
- audit_path = util.path_auditor(repo.root)
+ audit_path = scmutil.path_auditor(repo.root)
numupdates = len(action)
for i, a in enumerate(action):
@@ -439,7 +439,7 @@
if f:
repo.dirstate.forget(f)
-def update(repo, node, branchmerge, force, partial):
+def update(repo, node, branchmerge, force, partial, ancestor=None):
"""
Perform a merge between the working directory and the given node
@@ -492,9 +492,12 @@
overwrite = force and not branchmerge
pl = wc.parents()
p1, p2 = pl[0], repo[node]
- pa = p1.ancestor(p2)
+ if ancestor:
+ pa = repo[ancestor]
+ else:
+ pa = p1.ancestor(p2)
+
fp1, fp2, xp1, xp2 = p1.node(), p2.node(), str(p1), str(p2)
- fastforward = False
### check phase
if not overwrite and len(pl) > 1:
@@ -504,9 +507,7 @@
raise util.Abort(_("merging with a working directory ancestor"
" has no effect"))
elif pa == p1:
- if p1.branch() != p2.branch():
- fastforward = True
- else:
+ if p1.branch() == p2.branch():
raise util.Abort(_("nothing to merge (use 'hg update'"
" or check 'hg heads')"))
if not force and (wc.files() or wc.deleted()):
@@ -551,7 +552,7 @@
if not partial:
repo.dirstate.setparents(fp1, fp2)
recordupdates(repo, action, branchmerge)
- if not branchmerge and not fastforward:
+ if not branchmerge:
repo.dirstate.setbranch(p2.branch())
finally:
wlock.release()
--- a/mercurial/osutil.c Fri Apr 29 11:04:39 2011 +0200
+++ b/mercurial/osutil.c Fri Apr 29 11:10:11 2011 +0200
@@ -514,6 +514,22 @@
}
#endif
+#ifdef __APPLE__
+#include <ApplicationServices/ApplicationServices.h>
+
+static PyObject *isgui(PyObject *self)
+{
+ CFDictionaryRef dict = CGSessionCopyCurrentDictionary();
+
+ if (dict != NULL) {
+ CFRelease(dict);
+ return Py_True;
+ } else {
+ return Py_False;
+ }
+}
+#endif
+
static char osutil_doc[] = "Native operating system services.";
static PyMethodDef methods[] = {
@@ -524,6 +540,12 @@
"Open a file with POSIX-like semantics.\n"
"On error, this function may raise either a WindowsError or an IOError."},
#endif
+#ifdef __APPLE__
+ {
+ "isgui", (PyCFunction)isgui, METH_NOARGS,
+ "Is a CoreGraphics session available?"
+ },
+#endif
{NULL, NULL}
};
--- a/mercurial/parser.py Fri Apr 29 11:04:39 2011 +0200
+++ b/mercurial/parser.py Fri Apr 29 11:10:11 2011 +0200
@@ -78,7 +78,9 @@
'generate a parse tree from a message'
self._iter = self._tokenizer(message)
self._advance()
- return self._parse()
+ res = self._parse()
+ token, value, pos = self.current
+ return res, pos
def eval(self, tree):
'recursively evaluate a parse tree using node methods'
if not isinstance(tree, tuple):
--- a/mercurial/patch.py Fri Apr 29 11:04:39 2011 +0200
+++ b/mercurial/patch.py Fri Apr 29 11:10:11 2011 +0200
@@ -11,7 +11,7 @@
from i18n import _
from node import hex, nullid, short
-import base85, mdiff, util, diffhelpers, copies, encoding
+import base85, mdiff, scmutil, util, diffhelpers, copies, encoding
gitre = re.compile('diff --git a/(.*) b/(.*)')
@@ -21,7 +21,8 @@
# helper functions
def copyfile(src, dst, basedir):
- abssrc, absdst = [util.canonpath(basedir, basedir, x) for x in [src, dst]]
+ abssrc, absdst = [scmutil.canonpath(basedir, basedir, x)
+ for x in [src, dst]]
if os.path.lexists(absdst):
raise util.Abort(_("cannot create %s: destination already exists") %
dst)
@@ -488,11 +489,6 @@
cand.sort(key=lambda x: abs(x - linenum))
return cand
- def hashlines(self):
- self.hash = {}
- for x, s in enumerate(self.lines):
- self.hash.setdefault(s, []).append(x)
-
def makerejlines(self, fname):
base = os.path.basename(fname)
yield "--- %s\n+++ %s\n" % (base, base)
@@ -574,8 +570,10 @@
self.dirty = 1
return 0
- # ok, we couldn't match the hunk. Lets look for offsets and fuzz it
- self.hashlines()
+ # ok, we couldn't match the hunk. Lets look for offsets and fuzz it
+ self.hash = {}
+ for x, s in enumerate(self.lines):
+ self.hash.setdefault(s, []).append(x)
if h.hunk[-1][0] != ' ':
# if the hunk tried to put something at the bottom of the file
# override the start line and use eof here
@@ -613,6 +611,12 @@
self.rej.append(horig)
return -1
+ def close(self):
+ if self.dirty:
+ self.writelines(self.fname, self.lines)
+ self.write_rej()
+ return len(self.rej)
+
class hunk(object):
def __init__(self, desc, num, lr, context, create=False, remove=False):
self.number = num
@@ -680,6 +684,7 @@
del self.b[-1]
self.lena -= 1
self.lenb -= 1
+ self._fixnewline(lr)
def read_context_hunk(self, lr):
self.desc = lr.readline()
@@ -782,9 +787,14 @@
self.desc = "@@ -%d,%d +%d,%d @@\n" % (self.starta, self.lena,
self.startb, self.lenb)
self.hunk[0] = self.desc
+ self._fixnewline(lr)
- def fix_newline(self):
- diffhelpers.fix_newline(self.hunk, self.a, self.b)
+ def _fixnewline(self, lr):
+ l = lr.readline()
+ if l.startswith('\ '):
+ diffhelpers.fix_newline(self.hunk, self.a, self.b)
+ else:
+ lr.push(l)
def complete(self):
return len(self.a) == self.lena and len(self.b) == self.lenb
@@ -993,12 +1003,11 @@
maps filenames to gitpatch records. Unique event.
"""
changed = {}
- current_hunk = None
afile = ""
bfile = ""
state = None
hunknum = 0
- emitfile = False
+ emitfile = newfile = False
git = False
# our states
@@ -1007,15 +1016,9 @@
lr = linereader(fp)
while True:
- newfile = newgitfile = False
x = lr.readline()
if not x:
break
- if current_hunk:
- if x.startswith('\ '):
- current_hunk.fix_newline()
- yield 'hunk', current_hunk
- current_hunk = None
if (state == BFILE and ((not context and x[0] == '@') or
((context is not False) and x.startswith('***************')))):
if context is None and x.startswith('***************'):
@@ -1023,18 +1026,20 @@
gpatch = changed.get(bfile)
create = afile == '/dev/null' or gpatch and gpatch.op == 'ADD'
remove = bfile == '/dev/null' or gpatch and gpatch.op == 'DELETE'
- current_hunk = hunk(x, hunknum + 1, lr, context, create, remove)
+ h = hunk(x, hunknum + 1, lr, context, create, remove)
hunknum += 1
if emitfile:
emitfile = False
- yield 'file', (afile, bfile, current_hunk)
+ yield 'file', (afile, bfile, h)
+ yield 'hunk', h
elif state == BFILE and x.startswith('GIT binary patch'):
- current_hunk = binhunk(changed[bfile])
+ h = binhunk(changed[bfile])
hunknum += 1
if emitfile:
emitfile = False
- yield 'file', ('a/' + afile, 'b/' + bfile, current_hunk)
- current_hunk.extract(lr)
+ yield 'file', ('a/' + afile, 'b/' + bfile, h)
+ h.extract(lr)
+ yield 'hunk', h
elif x.startswith('diff --git'):
# check for git diff, scanning the whole patch file if needed
m = gitre.match(x)
@@ -1052,7 +1057,7 @@
if gp and (gp.op in ('COPY', 'DELETE', 'RENAME', 'ADD')
or gp.mode):
afile = bfile
- newgitfile = True
+ newfile = True
elif x.startswith('---'):
# check for a unified diff
l2 = lr.readline()
@@ -1079,16 +1084,11 @@
afile = parsefilename(x)
bfile = parsefilename(l2)
- if newgitfile or newfile:
+ if newfile:
+ newfile = False
emitfile = True
state = BFILE
hunknum = 0
- if current_hunk:
- if current_hunk.complete():
- yield 'hunk', current_hunk
- else:
- raise PatchError(_("malformed patch %s %s") % (afile,
- current_hunk.desc))
def applydiff(ui, fp, changed, strip=1, eolmode='strict'):
"""Reads a patch from fp and tries to apply it.
@@ -1112,15 +1112,7 @@
err = 0
current_file = None
cwd = os.getcwd()
- opener = util.opener(cwd)
-
- def closefile():
- if not current_file:
- return 0
- if current_file.dirty:
- current_file.writelines(current_file.fname, current_file.lines)
- current_file.write_rej()
- return len(current_file.rej)
+ opener = scmutil.opener(cwd)
for state, values in iterhunks(ui, fp):
if state == 'hunk':
@@ -1132,7 +1124,8 @@
if ret > 0:
err = 1
elif state == 'file':
- rejects += closefile()
+ if current_file:
+ rejects += current_file.close()
afile, bfile, first_hunk = values
try:
current_file, missing = selectfile(afile, bfile,
@@ -1157,13 +1150,14 @@
else:
raise util.Abort(_('unsupported parser state: %s') % state)
- rejects += closefile()
+ if current_file:
+ rejects += current_file.close()
if rejects:
return -1
return err
-def externalpatch(patcher, patchname, ui, strip, cwd, files):
+def _externalpatch(patcher, patchname, ui, strip, cwd, files):
"""use <patcher> to apply <patchname> to the working directory.
returns whether patch was applied with fuzz factor."""
@@ -1247,7 +1241,7 @@
files = {}
try:
if patcher:
- return externalpatch(patcher, patchname, ui, strip, cwd, files)
+ return _externalpatch(patcher, patchname, ui, strip, cwd, files)
return internalpatch(patchname, ui, strip, cwd, files, eolmode)
except PatchError, err:
raise util.Abort(str(err))
@@ -1331,7 +1325,7 @@
opts = mdiff.defaultopts
if not node1 and not node2:
- node1 = repo.dirstate.parents()[0]
+ node1 = repo.dirstate.p1()
def lrugetfilectx():
cache = {}
--- a/mercurial/posix.py Fri Apr 29 11:04:39 2011 +0200
+++ b/mercurial/posix.py Fri Apr 29 11:10:11 2011 +0200
@@ -6,8 +6,7 @@
# GNU General Public License version 2 or any later version.
from i18n import _
-import osutil
-import os, sys, errno, stat, getpass, pwd, grp
+import os, sys, errno, stat, getpass, pwd, grp, tempfile
posixfile = open
nulldev = '/dev/null'
@@ -29,29 +28,6 @@
'''return number of hardlinks for the given file'''
return os.lstat(name).st_nlink
-def rcfiles(path):
- rcs = [os.path.join(path, 'hgrc')]
- rcdir = os.path.join(path, 'hgrc.d')
- try:
- rcs.extend([os.path.join(rcdir, f)
- for f, kind in osutil.listdir(rcdir)
- if f.endswith(".rc")])
- except OSError:
- pass
- return rcs
-
-def system_rcpath():
- path = []
- # old mod_python does not set sys.argv
- if len(getattr(sys, 'argv', [])) > 0:
- path.extend(rcfiles(os.path.dirname(sys.argv[0]) +
- '/../etc/mercurial'))
- path.extend(rcfiles('/etc/mercurial'))
- return path
-
-def user_rcpath():
- return [os.path.expanduser('~/.hgrc')]
-
def parse_patch_output(output_line):
"""parses the output produced by patch and returns the filename"""
pf = output_line[14:]
@@ -83,7 +59,7 @@
os.unlink(f)
try:
os.symlink(data, f)
- except:
+ except OSError:
# failed to make a link, rewrite file
fp = open(f, "w")
fp.write(data)
@@ -108,6 +84,50 @@
# Turn off all +x bits
os.chmod(f, s & 0666)
+def checkexec(path):
+ """
+ Check whether the given path is on a filesystem with UNIX-like exec flags
+
+ Requires a directory (like /foo/.hg)
+ """
+
+ # VFAT on some Linux versions can flip mode but it doesn't persist
+ # a FS remount. Frequently we can detect it if files are created
+ # with exec bit on.
+
+ try:
+ EXECFLAGS = stat.S_IXUSR | stat.S_IXGRP | stat.S_IXOTH
+ fh, fn = tempfile.mkstemp(dir=path, prefix='hg-checkexec-')
+ try:
+ os.close(fh)
+ m = os.stat(fn).st_mode & 0777
+ new_file_has_exec = m & EXECFLAGS
+ os.chmod(fn, m ^ EXECFLAGS)
+ exec_flags_cannot_flip = ((os.stat(fn).st_mode & 0777) == m)
+ finally:
+ os.unlink(fn)
+ except (IOError, OSError):
+ # we don't care, the user probably won't be able to commit anyway
+ return False
+ return not (new_file_has_exec or exec_flags_cannot_flip)
+
+def checklink(path):
+ """check whether the given path is on a symlink-capable filesystem"""
+ # mktemp is not racy because symlink creation will fail if the
+ # file already exists
+ name = tempfile.mktemp(dir=path, prefix='hg-checklink-')
+ try:
+ os.symlink(".", name)
+ os.unlink(name)
+ return True
+ except (OSError, AttributeError):
+ return False
+
+def checkosfilename(path):
+ '''Check that the base-relative path is a valid filename on this platform.
+ Returns None if the path is ok, or a UI string describing the problem.'''
+ pass # on posix platforms, every path is ok
+
def set_binary(fd):
pass
--- a/mercurial/repair.py Fri Apr 29 11:04:39 2011 +0200
+++ b/mercurial/repair.py Fri Apr 29 11:10:11 2011 +0200
@@ -11,9 +11,9 @@
from i18n import _
import os
-def _bundle(repo, bases, heads, node, suffix, extranodes=None, compress=True):
+def _bundle(repo, bases, heads, node, suffix, compress=True):
"""create a bundle with the specified revisions as a backup"""
- cg = repo.changegroupsubset(bases, heads, 'strip', extranodes)
+ cg = repo.changegroupsubset(bases, heads, 'strip')
backupdir = repo.join("strip-backup")
if not os.path.isdir(backupdir):
os.mkdir(backupdir)
@@ -33,40 +33,26 @@
return sorted(files)
-def _collectextranodes(repo, files, link):
- """return the nodes that have to be saved before the strip"""
- def collectone(cl, revlog):
- extra = []
- startrev = count = len(revlog)
+def _collectbrokencsets(repo, files, striprev):
+ """return the changesets which will be broken by the truncation"""
+ s = set()
+ def collectone(revlog):
+ links = (revlog.linkrev(i) for i in revlog)
# find the truncation point of the revlog
- for i in xrange(count):
- lrev = revlog.linkrev(i)
- if lrev >= link:
- startrev = i + 1
+ for lrev in links:
+ if lrev >= striprev:
break
+ # see if any revision after this point has a linkrev
+ # less than striprev (those will be broken by strip)
+ for lrev in links:
+ if lrev < striprev:
+ s.add(lrev)
- # see if any revision after that point has a linkrev less than link
- # (we have to manually save these guys)
- for i in xrange(startrev, count):
- node = revlog.node(i)
- lrev = revlog.linkrev(i)
- if lrev < link:
- extra.append((node, cl.node(lrev)))
-
- return extra
+ collectone(repo.manifest)
+ for fname in files:
+ collectone(repo.file(fname))
- extranodes = {}
- cl = repo.changelog
- extra = collectone(cl, repo.manifest)
- if extra:
- extranodes[1] = extra
- for fname in files:
- f = repo.file(fname)
- extra = collectone(cl, f)
- if extra:
- extranodes[fname] = extra
-
- return extranodes
+ return s
def strip(ui, repo, node, backup="all"):
cl = repo.changelog
@@ -82,28 +68,26 @@
# the list of heads and bases of the set of interesting revisions.
# (head = revision in the set that has no descendant in the set;
# base = revision in the set that has no ancestor in the set)
- tostrip = set((striprev,))
- saveheads = set()
- savebases = []
+ tostrip = set(cl.descendants(striprev))
+ tostrip.add(striprev)
+
+ files = _collectfiles(repo, striprev)
+ saverevs = _collectbrokencsets(repo, files, striprev)
+
+ # compute heads
+ saveheads = set(saverevs)
for r in xrange(striprev + 1, len(cl)):
- parents = cl.parentrevs(r)
- if parents[0] in tostrip or parents[1] in tostrip:
- # r is a descendant of striprev
- tostrip.add(r)
- # if this is a merge and one of the parents does not descend
- # from striprev, mark that parent as a savehead.
- if parents[1] != nullrev:
- for p in parents:
- if p not in tostrip and p > striprev:
- saveheads.add(p)
- else:
- # if no parents of this revision will be stripped, mark it as
- # a savebase
- if parents[0] < striprev and parents[1] < striprev:
- savebases.append(cl.node(r))
+ if r not in tostrip:
+ saverevs.add(r)
+ saveheads.difference_update(cl.parentrevs(r))
+ saveheads.add(r)
+ saveheads = [cl.node(r) for r in saveheads]
- saveheads.difference_update(parents)
- saveheads.add(r)
+ # compute base nodes
+ if saverevs:
+ descendants = set(cl.descendants(*saverevs))
+ saverevs.difference_update(descendants)
+ savebases = [cl.node(r) for r in saverevs]
bm = repo._bookmarks
updatebm = []
@@ -112,20 +96,15 @@
if rev in tostrip:
updatebm.append(m)
- saveheads = [cl.node(r) for r in saveheads]
- files = _collectfiles(repo, striprev)
-
- extranodes = _collectextranodes(repo, files, striprev)
-
# create a changegroup for all the branches we need to keep
backupfile = None
if backup == "all":
backupfile = _bundle(repo, [node], cl.heads(), node, 'backup')
repo.ui.status(_("saved backup bundle to %s\n") % backupfile)
- if saveheads or extranodes:
+ if saveheads or savebases:
# do not compress partial bundle if we remove it from disk later
chgrpfile = _bundle(repo, savebases, saveheads, node, 'temp',
- extranodes=extranodes, compress=keeppartialbundle)
+ compress=keeppartialbundle)
mfst = repo.manifest
@@ -149,7 +128,7 @@
tr.abort()
raise
- if saveheads or extranodes:
+ if saveheads or savebases:
ui.note(_("adding branch\n"))
f = open(chgrpfile, "rb")
gen = changegroup.readbundle(f, chgrpfile)
--- a/mercurial/revlog.py Fri Apr 29 11:04:39 2011 +0200
+++ b/mercurial/revlog.py Fri Apr 29 11:10:11 2011 +0200
@@ -399,11 +399,12 @@
yield i
break
- def findmissing(self, common=None, heads=None):
- """Return the ancestors of heads that are not ancestors of common.
+ def findcommonmissing(self, common=None, heads=None):
+ """Return a tuple of the ancestors of common and the ancestors of heads
+ that are not ancestors of common.
- More specifically, return a list of nodes N such that every N
- satisfies the following constraints:
+ More specifically, the second element is a list of nodes N such that
+ every N satisfies the following constraints:
1. N is an ancestor of some node in 'heads'
2. N is not an ancestor of any node in 'common'
@@ -441,7 +442,25 @@
visit.append(p)
missing = list(missing)
missing.sort()
- return [self.node(r) for r in missing]
+ return has, [self.node(r) for r in missing]
+
+ def findmissing(self, common=None, heads=None):
+ """Return the ancestors of heads that are not ancestors of common.
+
+ More specifically, return a list of nodes N such that every N
+ satisfies the following constraints:
+
+ 1. N is an ancestor of some node in 'heads'
+ 2. N is not an ancestor of any node in 'common'
+
+ The list is sorted by revision number, meaning it is
+ topologically sorted.
+
+ 'heads' and 'common' are both lists of node IDs. If heads is
+ not supplied, uses all of the revlog's heads. If common is not
+ supplied, uses nullid."""
+ _common, missing = self.findcommonmissing(common, heads)
+ return missing
def nodesbetween(self, roots=None, heads=None):
"""Return a topological path from 'roots' to 'heads'.
@@ -1039,7 +1058,7 @@
self._cache = (node, curr, text)
return node
- def group(self, nodelist, lookup, infocollect=None, fullrev=False):
+ def group(self, nodelist, bundler):
"""Calculate a delta group, yielding a sequence of changegroup chunks
(strings).
@@ -1049,45 +1068,35 @@
guaranteed to have this parent as it has all history before
these changesets. In the case firstparent is nullrev the
changegroup starts with a full revision.
- fullrev forces the insertion of the full revision, necessary
- in the case of shallow clones where the first parent might
- not exist at the reciever.
"""
- revs = [self.rev(n) for n in nodelist]
+ revs = sorted([self.rev(n) for n in nodelist])
# if we don't have any revisions touched by these changesets, bail
if not revs:
- yield changegroup.closechunk()
+ yield bundler.close()
return
# add the parent of the first rev
p = self.parentrevs(revs[0])[0]
revs.insert(0, p)
- if p == nullrev:
- fullrev = True
# build deltas
- for d in xrange(len(revs) - 1):
- a, b = revs[d], revs[d + 1]
+ for r in xrange(len(revs) - 1):
+ a, b = revs[r], revs[r + 1]
nb = self.node(b)
-
- if infocollect is not None:
- infocollect(nb)
+ p1, p2 = self.parents(nb)
+ prefix = ''
- p = self.parents(nb)
- meta = nb + p[0] + p[1] + lookup(nb)
- if fullrev:
+ if a == nullrev:
d = self.revision(nb)
- meta += mdiff.trivialdiffheader(len(d))
- fullrev = False
+ prefix = mdiff.trivialdiffheader(len(d))
else:
d = self.revdiff(a, b)
- yield changegroup.chunkheader(len(meta) + len(d))
- yield meta
- yield d
+ for c in bundler.revchunk(self, nb, p1, p2, prefix, d):
+ yield c
- yield changegroup.closechunk()
+ yield bundler.close()
def addgroup(self, bundle, linkmapper, transaction):
"""
--- a/mercurial/revset.py Fri Apr 29 11:04:39 2011 +0200
+++ b/mercurial/revset.py Fri Apr 29 11:10:11 2011 +0200
@@ -6,10 +6,10 @@
# GNU General Public License version 2 or any later version.
import re
-import parser, util, error, discovery
+import parser, util, error, discovery, help, hbisect
import bookmarks as bookmarksmod
import match as matchmod
-from i18n import _, gettext
+from i18n import _
elements = {
"(": (20, ("group", 1, ")"), ("func", 1, ")")),
@@ -123,7 +123,7 @@
x = repo[x].rev()
if x == -1 and len(subset) == len(repo):
return [-1]
- if x in subset:
+ if len(subset) == len(repo) or x in subset:
return [x]
return []
@@ -156,9 +156,10 @@
return getset(repo, getset(repo, subset, x), y)
def orset(repo, subset, x, y):
- s = set(getset(repo, subset, x))
- s |= set(getset(repo, [r for r in subset if r not in s], y))
- return [r for r in subset if r in s]
+ xl = getset(repo, subset, x)
+ s = set(xl)
+ yl = getset(repo, [r for r in subset if r not in s], y)
+ return xl + yl
def notset(repo, subset, x):
s = set(getset(repo, subset, x))
@@ -174,6 +175,322 @@
# functions
+def adds(repo, subset, x):
+ """``adds(pattern)``
+ Changesets that add a file matching pattern.
+ """
+ # i18n: "adds" is a keyword
+ pat = getstring(x, _("adds requires a pattern"))
+ return checkstatus(repo, subset, pat, 1)
+
+def ancestor(repo, subset, x):
+ """``ancestor(single, single)``
+ Greatest common ancestor of the two changesets.
+ """
+ # i18n: "ancestor" is a keyword
+ l = getargs(x, 2, 2, _("ancestor requires two arguments"))
+ r = range(len(repo))
+ a = getset(repo, r, l[0])
+ b = getset(repo, r, l[1])
+ if len(a) != 1 or len(b) != 1:
+ # i18n: "ancestor" is a keyword
+ raise error.ParseError(_("ancestor arguments must be single revisions"))
+ an = [repo[a[0]].ancestor(repo[b[0]]).rev()]
+
+ return [r for r in an if r in subset]
+
+def ancestors(repo, subset, x):
+ """``ancestors(set)``
+ Changesets that are ancestors of a changeset in set.
+ """
+ args = getset(repo, range(len(repo)), x)
+ if not args:
+ return []
+ s = set(repo.changelog.ancestors(*args)) | set(args)
+ return [r for r in subset if r in s]
+
+def author(repo, subset, x):
+ """``author(string)``
+ Alias for ``user(string)``.
+ """
+ # i18n: "author" is a keyword
+ n = getstring(x, _("author requires a string")).lower()
+ return [r for r in subset if n in repo[r].user().lower()]
+
+def bisected(repo, subset, x):
+ """``bisected(string)``
+ Changesets marked in the specified bisect state (good, bad, skip).
+ """
+ state = getstring(x, _("bisect requires a string")).lower()
+ if state not in ('good', 'bad', 'skip', 'unknown'):
+ raise ParseError(_('invalid bisect state'))
+ marked = set(repo.changelog.rev(n) for n in hbisect.load_state(repo)[state])
+ return [r for r in subset if r in marked]
+
+def bookmark(repo, subset, x):
+ """``bookmark([name])``
+ The named bookmark or all bookmarks.
+ """
+ # i18n: "bookmark" is a keyword
+ args = getargs(x, 0, 1, _('bookmark takes one or no arguments'))
+ if args:
+ bm = getstring(args[0],
+ # i18n: "bookmark" is a keyword
+ _('the argument to bookmark must be a string'))
+ bmrev = bookmarksmod.listbookmarks(repo).get(bm, None)
+ if not bmrev:
+ raise util.Abort(_("bookmark '%s' does not exist") % bm)
+ bmrev = repo[bmrev].rev()
+ return [r for r in subset if r == bmrev]
+ bms = set([repo[r].rev()
+ for r in bookmarksmod.listbookmarks(repo).values()])
+ return [r for r in subset if r in bms]
+
+def branch(repo, subset, x):
+ """``branch(string or set)``
+ All changesets belonging to the given branch or the branches of the given
+ changesets.
+ """
+ try:
+ b = getstring(x, '')
+ if b in repo.branchmap():
+ return [r for r in subset if repo[r].branch() == b]
+ except error.ParseError:
+ # not a string, but another revspec, e.g. tip()
+ pass
+
+ s = getset(repo, range(len(repo)), x)
+ b = set()
+ for r in s:
+ b.add(repo[r].branch())
+ s = set(s)
+ return [r for r in subset if r in s or repo[r].branch() in b]
+
+def checkstatus(repo, subset, pat, field):
+ m = matchmod.match(repo.root, repo.getcwd(), [pat])
+ s = []
+ fast = (m.files() == [pat])
+ for r in subset:
+ c = repo[r]
+ if fast:
+ if pat not in c.files():
+ continue
+ else:
+ for f in c.files():
+ if m(f):
+ break
+ else:
+ continue
+ files = repo.status(c.p1().node(), c.node())[field]
+ if fast:
+ if pat in files:
+ s.append(r)
+ else:
+ for f in files:
+ if m(f):
+ s.append(r)
+ break
+ return s
+
+def children(repo, subset, x):
+ """``children(set)``
+ Child changesets of changesets in set.
+ """
+ cs = set()
+ cl = repo.changelog
+ s = set(getset(repo, range(len(repo)), x))
+ for r in xrange(0, len(repo)):
+ for p in cl.parentrevs(r):
+ if p in s:
+ cs.add(r)
+ return [r for r in subset if r in cs]
+
+def closed(repo, subset, x):
+ """``closed()``
+ Changeset is closed.
+ """
+ # i18n: "closed" is a keyword
+ getargs(x, 0, 0, _("closed takes no arguments"))
+ return [r for r in subset if repo[r].extra().get('close')]
+
+def contains(repo, subset, x):
+ """``contains(pattern)``
+ Revision contains pattern.
+ """
+ # i18n: "contains" is a keyword
+ pat = getstring(x, _("contains requires a pattern"))
+ m = matchmod.match(repo.root, repo.getcwd(), [pat])
+ s = []
+ if m.files() == [pat]:
+ for r in subset:
+ if pat in repo[r]:
+ s.append(r)
+ else:
+ for r in subset:
+ for f in repo[r].manifest():
+ if m(f):
+ s.append(r)
+ break
+ return s
+
+def date(repo, subset, x):
+ """``date(interval)``
+ Changesets within the interval, see :hg:`help dates`.
+ """
+ # i18n: "date" is a keyword
+ ds = getstring(x, _("date requires a string"))
+ dm = util.matchdate(ds)
+ return [r for r in subset if dm(repo[r].date()[0])]
+
+def descendants(repo, subset, x):
+ """``descendants(set)``
+ Changesets which are descendants of changesets in set.
+ """
+ args = getset(repo, range(len(repo)), x)
+ if not args:
+ return []
+ s = set(repo.changelog.descendants(*args)) | set(args)
+ return [r for r in subset if r in s]
+
+def follow(repo, subset, x):
+ """``follow()``
+ An alias for ``::.`` (ancestors of the working copy's first parent).
+ """
+ # i18n: "follow" is a keyword
+ getargs(x, 0, 0, _("follow takes no arguments"))
+ p = repo['.'].rev()
+ s = set(repo.changelog.ancestors(p)) | set([p])
+ return [r for r in subset if r in s]
+
+def getall(repo, subset, x):
+ """``all()``
+ All changesets, the same as ``0:tip``.
+ """
+ # i18n: "all" is a keyword
+ getargs(x, 0, 0, _("all takes no arguments"))
+ return subset
+
+def grep(repo, subset, x):
+ """``grep(regex)``
+ Like ``keyword(string)`` but accepts a regex. Use ``grep(r'...')``
+ to ensure special escape characters are handled correctly.
+ """
+ try:
+ # i18n: "grep" is a keyword
+ gr = re.compile(getstring(x, _("grep requires a string")))
+ except re.error, e:
+ raise error.ParseError(_('invalid match pattern: %s') % e)
+ l = []
+ for r in subset:
+ c = repo[r]
+ for e in c.files() + [c.user(), c.description()]:
+ if gr.search(e):
+ l.append(r)
+ break
+ return l
+
+def hasfile(repo, subset, x):
+ """``file(pattern)``
+ Changesets affecting files matched by pattern.
+ """
+ # i18n: "file" is a keyword
+ pat = getstring(x, _("file requires a pattern"))
+ m = matchmod.match(repo.root, repo.getcwd(), [pat])
+ s = []
+ for r in subset:
+ for f in repo[r].files():
+ if m(f):
+ s.append(r)
+ break
+ return s
+
+def head(repo, subset, x):
+ """``head()``
+ Changeset is a named branch head.
+ """
+ # i18n: "head" is a keyword
+ getargs(x, 0, 0, _("head takes no arguments"))
+ hs = set()
+ for b, ls in repo.branchmap().iteritems():
+ hs.update(repo[h].rev() for h in ls)
+ return [r for r in subset if r in hs]
+
+def heads(repo, subset, x):
+ """``heads(set)``
+ Members of set with no children in set.
+ """
+ s = getset(repo, subset, x)
+ ps = set(parents(repo, subset, x))
+ return [r for r in s if r not in ps]
+
+def keyword(repo, subset, x):
+ """``keyword(string)``
+ Search commit message, user name, and names of changed files for
+ string.
+ """
+ # i18n: "keyword" is a keyword
+ kw = getstring(x, _("keyword requires a string")).lower()
+ l = []
+ for r in subset:
+ c = repo[r]
+ t = " ".join(c.files() + [c.user(), c.description()])
+ if kw in t.lower():
+ l.append(r)
+ return l
+
+def limit(repo, subset, x):
+ """``limit(set, n)``
+ First n members of set.
+ """
+ # i18n: "limit" is a keyword
+ l = getargs(x, 2, 2, _("limit requires two arguments"))
+ try:
+ # i18n: "limit" is a keyword
+ lim = int(getstring(l[1], _("limit requires a number")))
+ except ValueError:
+ # i18n: "limit" is a keyword
+ raise error.ParseError(_("limit expects a number"))
+ return getset(repo, subset, l[0])[:lim]
+
+def maxrev(repo, subset, x):
+ """``max(set)``
+ Changeset with highest revision number in set.
+ """
+ s = getset(repo, subset, x)
+ if s:
+ m = max(s)
+ if m in subset:
+ return [m]
+ return []
+
+def merge(repo, subset, x):
+ """``merge()``
+ Changeset is a merge changeset.
+ """
+ # i18n: "merge" is a keyword
+ getargs(x, 0, 0, _("merge takes no arguments"))
+ cl = repo.changelog
+ return [r for r in subset if cl.parentrevs(r)[1] != -1]
+
+def minrev(repo, subset, x):
+ """``min(set)``
+ Changeset with lowest revision number in set.
+ """
+ s = getset(repo, subset, x)
+ if s:
+ m = min(s)
+ if m in subset:
+ return [m]
+ return []
+
+def modifies(repo, subset, x):
+ """``modifies(pattern)``
+ Changesets modifying files matched by pattern.
+ """
+ # i18n: "modifies" is a keyword
+ pat = getstring(x, _("modifies requires a pattern"))
+ return checkstatus(repo, subset, pat, 0)
+
def node(repo, subset, x):
"""``id(string)``
Revision non-ambiguously specified by the given hex string prefix.
@@ -188,26 +505,35 @@
rn = repo.changelog.rev(repo.changelog._partialmatch(n))
return [r for r in subset if r == rn]
-def rev(repo, subset, x):
- """``rev(number)``
- Revision with the given numeric identifier.
+def outgoing(repo, subset, x):
+ """``outgoing([path])``
+ Changesets not found in the specified destination repository, or the
+ default push location.
"""
- # i18n: "rev" is a keyword
- l = getargs(x, 1, 1, _("rev requires one argument"))
- try:
- # i18n: "rev" is a keyword
- l = int(getstring(l[0], _("rev requires a number")))
- except ValueError:
- # i18n: "rev" is a keyword
- raise error.ParseError(_("rev expects a number"))
- return [r for r in subset if r == l]
+ import hg # avoid start-up nasties
+ # i18n: "outgoing" is a keyword
+ l = getargs(x, 0, 1, _("outgoing requires a repository path"))
+ # i18n: "outgoing" is a keyword
+ dest = l and getstring(l[0], _("outgoing requires a repository path")) or ''
+ dest = repo.ui.expandpath(dest or 'default-push', dest or 'default')
+ dest, branches = hg.parseurl(dest)
+ revs, checkout = hg.addbranchrevs(repo, repo, branches, [])
+ if revs:
+ revs = [repo.lookup(rev) for rev in revs]
+ other = hg.repository(hg.remoteui(repo, {}), dest)
+ repo.ui.pushbuffer()
+ o = discovery.findoutgoing(repo, other)
+ repo.ui.popbuffer()
+ cl = repo.changelog
+ o = set([cl.rev(r) for r in repo.changelog.nodesbetween(o, revs)[0]])
+ return [r for r in subset if r in o]
def p1(repo, subset, x):
"""``p1([set])``
First parent of changesets in set, or the working directory.
"""
if x is None:
- p = repo[x].parents()[0].rev()
+ p = repo[x].p1().rev()
return [r for r in subset if r == p]
ps = set()
@@ -248,247 +574,15 @@
ps.update(cl.parentrevs(r))
return [r for r in subset if r in ps]
-def maxrev(repo, subset, x):
- """``max(set)``
- Changeset with highest revision number in set.
- """
- s = getset(repo, subset, x)
- if s:
- m = max(s)
- if m in subset:
- return [m]
- return []
-
-def minrev(repo, subset, x):
- """``min(set)``
- Changeset with lowest revision number in set.
- """
- s = getset(repo, subset, x)
- if s:
- m = min(s)
- if m in subset:
- return [m]
- return []
-
-def limit(repo, subset, x):
- """``limit(set, n)``
- First n members of set.
- """
- # i18n: "limit" is a keyword
- l = getargs(x, 2, 2, _("limit requires two arguments"))
- try:
- # i18n: "limit" is a keyword
- lim = int(getstring(l[1], _("limit requires a number")))
- except ValueError:
- # i18n: "limit" is a keyword
- raise error.ParseError(_("limit expects a number"))
- return getset(repo, subset, l[0])[:lim]
-
-def children(repo, subset, x):
- """``children(set)``
- Child changesets of changesets in set.
- """
- cs = set()
- cl = repo.changelog
- s = set(getset(repo, range(len(repo)), x))
- for r in xrange(0, len(repo)):
- for p in cl.parentrevs(r):
- if p in s:
- cs.add(r)
- return [r for r in subset if r in cs]
-
-def branch(repo, subset, x):
- """``branch(set)``
- All changesets belonging to the branches of changesets in set.
- """
- s = getset(repo, range(len(repo)), x)
- b = set()
- for r in s:
- b.add(repo[r].branch())
- s = set(s)
- return [r for r in subset if r in s or repo[r].branch() in b]
-
-def ancestor(repo, subset, x):
- """``ancestor(single, single)``
- Greatest common ancestor of the two changesets.
- """
- # i18n: "ancestor" is a keyword
- l = getargs(x, 2, 2, _("ancestor requires two arguments"))
- r = range(len(repo))
- a = getset(repo, r, l[0])
- b = getset(repo, r, l[1])
- if len(a) != 1 or len(b) != 1:
- # i18n: "ancestor" is a keyword
- raise error.ParseError(_("ancestor arguments must be single revisions"))
- an = [repo[a[0]].ancestor(repo[b[0]]).rev()]
-
- return [r for r in an if r in subset]
-
-def ancestors(repo, subset, x):
- """``ancestors(set)``
- Changesets that are ancestors of a changeset in set.
- """
- args = getset(repo, range(len(repo)), x)
- if not args:
- return []
- s = set(repo.changelog.ancestors(*args)) | set(args)
- return [r for r in subset if r in s]
-
-def descendants(repo, subset, x):
- """``descendants(set)``
- Changesets which are descendants of changesets in set.
- """
- args = getset(repo, range(len(repo)), x)
- if not args:
- return []
- s = set(repo.changelog.descendants(*args)) | set(args)
- return [r for r in subset if r in s]
-
-def follow(repo, subset, x):
- """``follow()``
- An alias for ``::.`` (ancestors of the working copy's first parent).
- """
- # i18n: "follow" is a keyword
- getargs(x, 0, 0, _("follow takes no arguments"))
- p = repo['.'].rev()
- s = set(repo.changelog.ancestors(p)) | set([p])
- return [r for r in subset if r in s]
-
-def date(repo, subset, x):
- """``date(interval)``
- Changesets within the interval, see :hg:`help dates`.
- """
- # i18n: "date" is a keyword
- ds = getstring(x, _("date requires a string"))
- dm = util.matchdate(ds)
- return [r for r in subset if dm(repo[r].date()[0])]
-
-def keyword(repo, subset, x):
- """``keyword(string)``
- Search commit message, user name, and names of changed files for
- string.
- """
- # i18n: "keyword" is a keyword
- kw = getstring(x, _("keyword requires a string")).lower()
- l = []
- for r in subset:
- c = repo[r]
- t = " ".join(c.files() + [c.user(), c.description()])
- if kw in t.lower():
- l.append(r)
- return l
-
-def grep(repo, subset, x):
- """``grep(regex)``
- Like ``keyword(string)`` but accepts a regex. Use ``grep(r'...')``
- to ensure special escape characters are handled correctly.
+def present(repo, subset, x):
+ """``present(set)``
+ An empty set, if any revision in set isn't found; otherwise,
+ all revisions in set.
"""
try:
- # i18n: "grep" is a keyword
- gr = re.compile(getstring(x, _("grep requires a string")))
- except re.error, e:
- raise error.ParseError(_('invalid match pattern: %s') % e)
- l = []
- for r in subset:
- c = repo[r]
- for e in c.files() + [c.user(), c.description()]:
- if gr.search(e):
- l.append(r)
- continue
- return l
-
-def author(repo, subset, x):
- """``author(string)``
- Alias for ``user(string)``.
- """
- # i18n: "author" is a keyword
- n = getstring(x, _("author requires a string")).lower()
- return [r for r in subset if n in repo[r].user().lower()]
-
-def user(repo, subset, x):
- """``user(string)``
- User name is string.
- """
- return author(repo, subset, x)
-
-def hasfile(repo, subset, x):
- """``file(pattern)``
- Changesets affecting files matched by pattern.
- """
- # i18n: "file" is a keyword
- pat = getstring(x, _("file requires a pattern"))
- m = matchmod.match(repo.root, repo.getcwd(), [pat])
- s = []
- for r in subset:
- for f in repo[r].files():
- if m(f):
- s.append(r)
- continue
- return s
-
-def contains(repo, subset, x):
- """``contains(pattern)``
- Revision contains pattern.
- """
- # i18n: "contains" is a keyword
- pat = getstring(x, _("contains requires a pattern"))
- m = matchmod.match(repo.root, repo.getcwd(), [pat])
- s = []
- if m.files() == [pat]:
- for r in subset:
- if pat in repo[r]:
- s.append(r)
- continue
- else:
- for r in subset:
- for f in repo[r].manifest():
- if m(f):
- s.append(r)
- continue
- return s
-
-def checkstatus(repo, subset, pat, field):
- m = matchmod.match(repo.root, repo.getcwd(), [pat])
- s = []
- fast = (m.files() == [pat])
- for r in subset:
- c = repo[r]
- if fast:
- if pat not in c.files():
- continue
- else:
- for f in c.files():
- if m(f):
- break
- else:
- continue
- files = repo.status(c.p1().node(), c.node())[field]
- if fast:
- if pat in files:
- s.append(r)
- continue
- else:
- for f in files:
- if m(f):
- s.append(r)
- continue
- return s
-
-def modifies(repo, subset, x):
- """``modifies(pattern)``
- Changesets modifying files matched by pattern.
- """
- # i18n: "modifies" is a keyword
- pat = getstring(x, _("modifies requires a pattern"))
- return checkstatus(repo, subset, pat, 0)
-
-def adds(repo, subset, x):
- """``adds(pattern)``
- Changesets that add a file matching pattern.
- """
- # i18n: "adds" is a keyword
- pat = getstring(x, _("adds requires a pattern"))
- return checkstatus(repo, subset, pat, 1)
+ return getset(repo, subset, x)
+ except error.RepoLookupError:
+ return []
def removes(repo, subset, x):
"""``removes(pattern)``
@@ -498,33 +592,19 @@
pat = getstring(x, _("removes requires a pattern"))
return checkstatus(repo, subset, pat, 2)
-def merge(repo, subset, x):
- """``merge()``
- Changeset is a merge changeset.
- """
- # i18n: "merge" is a keyword
- getargs(x, 0, 0, _("merge takes no arguments"))
- cl = repo.changelog
- return [r for r in subset if cl.parentrevs(r)[1] != -1]
-
-def closed(repo, subset, x):
- """``closed()``
- Changeset is closed.
+def rev(repo, subset, x):
+ """``rev(number)``
+ Revision with the given numeric identifier.
"""
- # i18n: "closed" is a keyword
- getargs(x, 0, 0, _("closed takes no arguments"))
- return [r for r in subset if repo[r].extra().get('close')]
-
-def head(repo, subset, x):
- """``head()``
- Changeset is a named branch head.
- """
- # i18n: "head" is a keyword
- getargs(x, 0, 0, _("head takes no arguments"))
- hs = set()
- for b, ls in repo.branchmap().iteritems():
- hs.update(repo[h].rev() for h in ls)
- return [r for r in subset if r in hs]
+ # i18n: "rev" is a keyword
+ l = getargs(x, 1, 1, _("rev requires one argument"))
+ try:
+ # i18n: "rev" is a keyword
+ l = int(getstring(l[0], _("rev requires a number")))
+ except ValueError:
+ # i18n: "rev" is a keyword
+ raise error.ParseError(_("rev expects a number"))
+ return [r for r in subset if r == l]
def reverse(repo, subset, x):
"""``reverse(set)``
@@ -534,15 +614,13 @@
l.reverse()
return l
-def present(repo, subset, x):
- """``present(set)``
- An empty set, if any revision in set isn't found; otherwise,
- all revisions in set.
+def roots(repo, subset, x):
+ """``roots(set)``
+ Changesets with no parent changeset in set.
"""
- try:
- return getset(repo, subset, x)
- except error.RepoLookupError:
- return []
+ s = getset(repo, subset, x)
+ cs = set(children(repo, subset, x))
+ return [r for r in s if r not in cs]
def sort(repo, subset, x):
"""``sort(set[, [-]key...])``
@@ -599,53 +677,6 @@
l.sort()
return [e[-1] for e in l]
-def getall(repo, subset, x):
- """``all()``
- All changesets, the same as ``0:tip``.
- """
- # i18n: "all" is a keyword
- getargs(x, 0, 0, _("all takes no arguments"))
- return subset
-
-def heads(repo, subset, x):
- """``heads(set)``
- Members of set with no children in set.
- """
- s = getset(repo, subset, x)
- ps = set(parents(repo, subset, x))
- return [r for r in s if r not in ps]
-
-def roots(repo, subset, x):
- """``roots(set)``
- Changesets with no parent changeset in set.
- """
- s = getset(repo, subset, x)
- cs = set(children(repo, subset, x))
- return [r for r in s if r not in cs]
-
-def outgoing(repo, subset, x):
- """``outgoing([path])``
- Changesets not found in the specified destination repository, or the
- default push location.
- """
- import hg # avoid start-up nasties
- # i18n: "outgoing" is a keyword
- l = getargs(x, 0, 1, _("outgoing requires a repository path"))
- # i18n: "outgoing" is a keyword
- dest = l and getstring(l[0], _("outgoing requires a repository path")) or ''
- dest = repo.ui.expandpath(dest or 'default-push', dest or 'default')
- dest, branches = hg.parseurl(dest)
- revs, checkout = hg.addbranchrevs(repo, repo, branches, [])
- if revs:
- revs = [repo.lookup(rev) for rev in revs]
- other = hg.repository(hg.remoteui(repo, {}), dest)
- repo.ui.pushbuffer()
- o = discovery.findoutgoing(repo, other)
- repo.ui.popbuffer()
- cl = repo.changelog
- o = set([cl.rev(r) for r in repo.changelog.nodesbetween(o, revs)[0]])
- return [r for r in subset if r in o]
-
def tag(repo, subset, x):
"""``tag(name)``
The specified tag by name, or all tagged revisions if no name is given.
@@ -657,6 +688,8 @@
tn = getstring(args[0],
# i18n: "tag" is a keyword
_('the argument to tag must be a string'))
+ if not repo.tags().get(tn, None):
+ raise util.Abort(_("tag '%s' does not exist") % tn)
s = set([cl.rev(n) for t, n in repo.tagslist() if t == tn])
else:
s = set([cl.rev(n) for t, n in repo.tagslist() if t != 'tip'])
@@ -665,23 +698,11 @@
def tagged(repo, subset, x):
return tag(repo, subset, x)
-def bookmark(repo, subset, x):
- """``bookmark([name])``
- The named bookmark or all bookmarks.
+def user(repo, subset, x):
+ """``user(string)``
+ User name is string.
"""
- # i18n: "bookmark" is a keyword
- args = getargs(x, 0, 1, _('bookmark takes one or no arguments'))
- if args:
- bm = getstring(args[0],
- # i18n: "bookmark" is a keyword
- _('the argument to bookmark must be a string'))
- bmrev = bookmarksmod.listbookmarks(repo).get(bm, None)
- if bmrev:
- bmrev = repo[bmrev].rev()
- return [r for r in subset if r == bmrev]
- bms = set([repo[r].rev()
- for r in bookmarksmod.listbookmarks(repo).values()])
- return [r for r in subset if r in bms]
+ return author(repo, subset, x)
symbols = {
"adds": adds,
@@ -689,6 +710,7 @@
"ancestor": ancestor,
"ancestors": ancestors,
"author": author,
+ "bisected": bisected,
"bookmark": bookmark,
"branch": branch,
"children": children,
@@ -786,7 +808,7 @@
elif op == 'func':
f = getstring(x[1], _("not a symbol"))
wa, ta = optimize(x[2], small)
- if f in "grep date user author keyword branch file outgoing":
+ if f in "grep date user author keyword branch file outgoing closed":
w = 10 # slow
elif f in "modifies adds removes":
w = 30 # slower
@@ -808,26 +830,16 @@
def match(spec):
if not spec:
raise error.ParseError(_("empty query"))
- tree = parse(spec)
+ tree, pos = parse(spec)
+ if (pos != len(spec)):
+ raise error.ParseError("invalid token", pos)
weight, tree = optimize(tree, True)
def mfunc(repo, subset):
return getset(repo, subset, tree)
return mfunc
def makedoc(topic, doc):
- """Generate and include predicates help in revsets topic."""
- predicates = []
- for name in sorted(symbols):
- text = symbols[name].__doc__
- if not text:
- continue
- text = gettext(text.rstrip())
- lines = text.splitlines()
- lines[1:] = [(' ' + l.strip()) for l in lines[1:]]
- predicates.append('\n'.join(lines))
- predicates = '\n\n'.join(predicates)
- doc = doc.replace('.. predicatesmarker', predicates)
- return doc
+ return help.makeitemsdoc(topic, doc, '.. predicatesmarker', symbols)
# tell hggettext to extract docstrings from these functions:
i18nfunctions = symbols.values()
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/mercurial/scmutil.py Fri Apr 29 11:10:11 2011 +0200
@@ -0,0 +1,401 @@
+# scmutil.py - Mercurial core utility functions
+#
+# Copyright Matt Mackall <mpm@selenic.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 i18n import _
+import util, error, osutil
+import os, errno, stat, sys
+
+def checkfilename(f):
+ '''Check that the filename f is an acceptable filename for a tracked file'''
+ if '\r' in f or '\n' in f:
+ raise util.Abort(_("'\\n' and '\\r' disallowed in filenames: %r") % f)
+
+def checkportable(ui, f):
+ '''Check if filename f is portable and warn or abort depending on config'''
+ checkfilename(f)
+ val = ui.config('ui', 'portablefilenames', 'warn')
+ lval = val.lower()
+ abort = os.name == 'nt' or lval == 'abort'
+ bval = util.parsebool(val)
+ if abort or lval == 'warn' or bval:
+ msg = util.checkwinfilename(f)
+ if msg:
+ if abort:
+ raise util.Abort("%s: %r" % (msg, f))
+ ui.warn(_("warning: %s: %r\n") % (msg, f))
+ elif bval is None and lval != 'ignore':
+ raise error.ConfigError(
+ _("ui.portablefilenames value is invalid ('%s')") % val)
+
+class path_auditor(object):
+ '''ensure that a filesystem path contains no banned components.
+ the following properties of a path are checked:
+
+ - ends with a directory separator
+ - under top-level .hg
+ - starts at the root of a windows drive
+ - contains ".."
+ - traverses a symlink (e.g. a/symlink_here/b)
+ - inside a nested repository (a callback can be used to approve
+ some nested repositories, e.g., subrepositories)
+ '''
+
+ def __init__(self, root, callback=None):
+ self.audited = set()
+ self.auditeddir = set()
+ self.root = root
+ self.callback = callback
+
+ def __call__(self, path):
+ '''Check the relative path.
+ path may contain a pattern (e.g. foodir/**.txt)'''
+
+ if path in self.audited:
+ return
+ # AIX ignores "/" at end of path, others raise EISDIR.
+ if util.endswithsep(path):
+ raise util.Abort(_("path ends in directory separator: %s") % path)
+ normpath = os.path.normcase(path)
+ parts = util.splitpath(normpath)
+ if (os.path.splitdrive(path)[0]
+ or parts[0].lower() in ('.hg', '.hg.', '')
+ or os.pardir in parts):
+ raise util.Abort(_("path contains illegal component: %s") % path)
+ if '.hg' in path.lower():
+ lparts = [p.lower() for p in parts]
+ for p in '.hg', '.hg.':
+ if p in lparts[1:]:
+ pos = lparts.index(p)
+ base = os.path.join(*parts[:pos])
+ raise util.Abort(_('path %r is inside nested repo %r')
+ % (path, base))
+
+ parts.pop()
+ prefixes = []
+ while parts:
+ prefix = os.sep.join(parts)
+ if prefix in self.auditeddir:
+ break
+ curpath = os.path.join(self.root, prefix)
+ try:
+ st = os.lstat(curpath)
+ except OSError, err:
+ # EINVAL can be raised as invalid path syntax under win32.
+ # They must be ignored for patterns can be checked too.
+ if err.errno not in (errno.ENOENT, errno.ENOTDIR, errno.EINVAL):
+ raise
+ else:
+ if stat.S_ISLNK(st.st_mode):
+ raise util.Abort(
+ _('path %r traverses symbolic link %r')
+ % (path, prefix))
+ elif (stat.S_ISDIR(st.st_mode) and
+ os.path.isdir(os.path.join(curpath, '.hg'))):
+ if not self.callback or not self.callback(curpath):
+ raise util.Abort(_('path %r is inside nested repo %r') %
+ (path, prefix))
+ prefixes.append(prefix)
+ parts.pop()
+
+ self.audited.add(path)
+ # only add prefixes to the cache after checking everything: we don't
+ # want to add "foo/bar/baz" before checking if there's a "foo/.hg"
+ self.auditeddir.update(prefixes)
+
+class opener(object):
+ '''Open files relative to a base directory
+
+ This class is used to hide the details of COW semantics and
+ remote file access from higher level code.
+ '''
+ def __init__(self, base, audit=True):
+ self.base = base
+ if audit:
+ self.auditor = path_auditor(base)
+ else:
+ self.auditor = util.always
+ self.createmode = None
+ self._trustnlink = None
+
+ @util.propertycache
+ def _can_symlink(self):
+ return util.checklink(self.base)
+
+ def _fixfilemode(self, name):
+ if self.createmode is None:
+ return
+ os.chmod(name, self.createmode & 0666)
+
+ def __call__(self, path, mode="r", text=False, atomictemp=False):
+ r = util.checkosfilename(path)
+ if r:
+ raise util.Abort("%s: %r" % (r, path))
+ self.auditor(path)
+ f = os.path.join(self.base, path)
+
+ if not text and "b" not in mode:
+ mode += "b" # for that other OS
+
+ nlink = -1
+ dirname, basename = os.path.split(f)
+ # If basename is empty, then the path is malformed because it points
+ # to a directory. Let the posixfile() call below raise IOError.
+ if basename and mode not in ('r', 'rb'):
+ if atomictemp:
+ if not os.path.isdir(dirname):
+ util.makedirs(dirname, self.createmode)
+ return util.atomictempfile(f, mode, self.createmode)
+ try:
+ if 'w' in mode:
+ util.unlink(f)
+ nlink = 0
+ else:
+ # nlinks() may behave differently for files on Windows
+ # shares if the file is open.
+ fd = util.posixfile(f)
+ nlink = util.nlinks(f)
+ if nlink < 1:
+ nlink = 2 # force mktempcopy (issue1922)
+ fd.close()
+ except (OSError, IOError), e:
+ if e.errno != errno.ENOENT:
+ raise
+ nlink = 0
+ if not os.path.isdir(dirname):
+ util.makedirs(dirname, self.createmode)
+ if nlink > 0:
+ if self._trustnlink is None:
+ self._trustnlink = nlink > 1 or util.checknlink(f)
+ if nlink > 1 or not self._trustnlink:
+ util.rename(util.mktempcopy(f), f)
+ fp = util.posixfile(f, mode)
+ if nlink == 0:
+ self._fixfilemode(f)
+ return fp
+
+ def symlink(self, src, dst):
+ self.auditor(dst)
+ linkname = os.path.join(self.base, dst)
+ try:
+ os.unlink(linkname)
+ except OSError:
+ pass
+
+ dirname = os.path.dirname(linkname)
+ if not os.path.exists(dirname):
+ util.makedirs(dirname, self.createmode)
+
+ if self._can_symlink:
+ try:
+ os.symlink(src, linkname)
+ except OSError, err:
+ raise OSError(err.errno, _('could not symlink to %r: %s') %
+ (src, err.strerror), linkname)
+ else:
+ f = self(dst, "w")
+ f.write(src)
+ f.close()
+ self._fixfilemode(dst)
+
+def canonpath(root, cwd, myname, auditor=None):
+ '''return the canonical path of myname, given cwd and root'''
+ if util.endswithsep(root):
+ rootsep = root
+ else:
+ rootsep = root + os.sep
+ name = myname
+ if not os.path.isabs(name):
+ name = os.path.join(root, cwd, name)
+ name = os.path.normpath(name)
+ if auditor is None:
+ auditor = path_auditor(root)
+ if name != rootsep and name.startswith(rootsep):
+ name = name[len(rootsep):]
+ auditor(name)
+ return util.pconvert(name)
+ elif name == root:
+ return ''
+ else:
+ # Determine whether `name' is in the hierarchy at or beneath `root',
+ # by iterating name=dirname(name) until that causes no change (can't
+ # check name == '/', because that doesn't work on windows). For each
+ # `name', compare dev/inode numbers. If they match, the list `rel'
+ # holds the reversed list of components making up the relative file
+ # name we want.
+ root_st = os.stat(root)
+ rel = []
+ while True:
+ try:
+ name_st = os.stat(name)
+ except OSError:
+ break
+ if util.samestat(name_st, root_st):
+ if not rel:
+ # name was actually the same as root (maybe a symlink)
+ return ''
+ rel.reverse()
+ name = os.path.join(*rel)
+ auditor(name)
+ return util.pconvert(name)
+ dirname, basename = os.path.split(name)
+ rel.append(basename)
+ if dirname == name:
+ break
+ name = dirname
+
+ raise util.Abort('%s not under root' % myname)
+
+def walkrepos(path, followsym=False, seen_dirs=None, recurse=False):
+ '''yield every hg repository under path, recursively.'''
+ def errhandler(err):
+ if err.filename == path:
+ raise err
+ if followsym and hasattr(os.path, 'samestat'):
+ def _add_dir_if_not_there(dirlst, dirname):
+ match = False
+ samestat = os.path.samestat
+ dirstat = os.stat(dirname)
+ for lstdirstat in dirlst:
+ if samestat(dirstat, lstdirstat):
+ match = True
+ break
+ if not match:
+ dirlst.append(dirstat)
+ return not match
+ else:
+ followsym = False
+
+ if (seen_dirs is None) and followsym:
+ seen_dirs = []
+ _add_dir_if_not_there(seen_dirs, path)
+ for root, dirs, files in os.walk(path, topdown=True, onerror=errhandler):
+ dirs.sort()
+ if '.hg' in dirs:
+ yield root # found a repository
+ qroot = os.path.join(root, '.hg', 'patches')
+ if os.path.isdir(os.path.join(qroot, '.hg')):
+ yield qroot # we have a patch queue repo here
+ if recurse:
+ # avoid recursing inside the .hg directory
+ dirs.remove('.hg')
+ else:
+ dirs[:] = [] # don't descend further
+ elif followsym:
+ newdirs = []
+ for d in dirs:
+ fname = os.path.join(root, d)
+ if _add_dir_if_not_there(seen_dirs, fname):
+ if os.path.islink(fname):
+ for hgname in walkrepos(fname, True, seen_dirs):
+ yield hgname
+ else:
+ newdirs.append(d)
+ dirs[:] = newdirs
+
+def os_rcpath():
+ '''return default os-specific hgrc search path'''
+ path = system_rcpath()
+ path.extend(user_rcpath())
+ path = [os.path.normpath(f) for f in path]
+ return path
+
+_rcpath = None
+
+def rcpath():
+ '''return hgrc search path. if env var HGRCPATH is set, use it.
+ for each item in path, if directory, use files ending in .rc,
+ else use item.
+ make HGRCPATH empty to only look in .hg/hgrc of current repo.
+ if no HGRCPATH, use default os-specific path.'''
+ global _rcpath
+ if _rcpath is None:
+ if 'HGRCPATH' in os.environ:
+ _rcpath = []
+ for p in os.environ['HGRCPATH'].split(os.pathsep):
+ if not p:
+ continue
+ p = util.expandpath(p)
+ if os.path.isdir(p):
+ for f, kind in osutil.listdir(p):
+ if f.endswith('.rc'):
+ _rcpath.append(os.path.join(p, f))
+ else:
+ _rcpath.append(p)
+ else:
+ _rcpath = os_rcpath()
+ return _rcpath
+
+if os.name != 'nt':
+
+ def rcfiles(path):
+ rcs = [os.path.join(path, 'hgrc')]
+ rcdir = os.path.join(path, 'hgrc.d')
+ try:
+ rcs.extend([os.path.join(rcdir, f)
+ for f, kind in osutil.listdir(rcdir)
+ if f.endswith(".rc")])
+ except OSError:
+ pass
+ return rcs
+
+ def system_rcpath():
+ path = []
+ # old mod_python does not set sys.argv
+ if len(getattr(sys, 'argv', [])) > 0:
+ path.extend(rcfiles(os.path.dirname(sys.argv[0]) +
+ '/../etc/mercurial'))
+ path.extend(rcfiles('/etc/mercurial'))
+ return path
+
+ def user_rcpath():
+ return [os.path.expanduser('~/.hgrc')]
+
+else:
+
+ _HKEY_LOCAL_MACHINE = 0x80000002L
+
+ def system_rcpath():
+ '''return default os-specific hgrc search path'''
+ rcpath = []
+ filename = util.executable_path()
+ # Use mercurial.ini found in directory with hg.exe
+ progrc = os.path.join(os.path.dirname(filename), 'mercurial.ini')
+ if os.path.isfile(progrc):
+ rcpath.append(progrc)
+ return rcpath
+ # Use hgrc.d found in directory with hg.exe
+ progrcd = os.path.join(os.path.dirname(filename), 'hgrc.d')
+ if os.path.isdir(progrcd):
+ for f, kind in osutil.listdir(progrcd):
+ if f.endswith('.rc'):
+ rcpath.append(os.path.join(progrcd, f))
+ return rcpath
+ # else look for a system rcpath in the registry
+ value = util.lookup_reg('SOFTWARE\\Mercurial', None,
+ _HKEY_LOCAL_MACHINE)
+ if not isinstance(value, str) or not value:
+ return rcpath
+ value = value.replace('/', os.sep)
+ for p in value.split(os.pathsep):
+ if p.lower().endswith('mercurial.ini'):
+ rcpath.append(p)
+ elif os.path.isdir(p):
+ for f, kind in osutil.listdir(p):
+ if f.endswith('.rc'):
+ rcpath.append(os.path.join(p, f))
+ return rcpath
+
+ def user_rcpath():
+ '''return os-specific hgrc search path to the user dir'''
+ home = os.path.expanduser('~')
+ path = [os.path.join(home, 'mercurial.ini'),
+ os.path.join(home, '.hgrc')]
+ userprofile = os.environ.get('USERPROFILE')
+ if userprofile:
+ path.append(os.path.join(userprofile, 'mercurial.ini'))
+ path.append(os.path.join(userprofile, '.hgrc'))
+ return path
--- a/mercurial/simplemerge.py Fri Apr 29 11:04:39 2011 +0200
+++ b/mercurial/simplemerge.py Fri Apr 29 11:10:11 2011 +0200
@@ -18,7 +18,7 @@
# s: "i hate that."
from i18n import _
-import util, mdiff
+import scmutil, util, mdiff
import sys, os
class CantReprocessAndShowBase(Exception):
@@ -429,7 +429,7 @@
local = os.path.realpath(local)
if not opts.get('print'):
- opener = util.opener(os.path.dirname(local))
+ opener = scmutil.opener(os.path.dirname(local))
out = opener(os.path.basename(local), "w", atomictemp=True)
else:
out = sys.stdout
--- a/mercurial/sshrepo.py Fri Apr 29 11:04:39 2011 +0200
+++ b/mercurial/sshrepo.py Fri Apr 29 11:10:11 2011 +0200
@@ -6,8 +6,7 @@
# GNU General Public License version 2 or any later version.
from i18n import _
-import util, error, wireproto
-import re
+import util, error, wireproto, url
class remotelock(object):
def __init__(self, repo):
@@ -24,16 +23,16 @@
self._url = path
self.ui = ui
- m = re.match(r'^ssh://(([^@]+)@)?([^:/]+)(:(\d+))?(/(.*))?$', path)
- if not m:
+ u = url.url(path, parsequery=False, parsefragment=False)
+ if u.scheme != 'ssh' or not u.host or u.path is None:
self._abort(error.RepoError(_("couldn't parse location %s") % path))
- self.user = m.group(2)
- if self.user and ':' in self.user:
+ self.user = u.user
+ if u.passwd is not None:
self._abort(error.RepoError(_("password in URL not supported")))
- self.host = m.group(3)
- self.port = m.group(5)
- self.path = m.group(7) or "."
+ self.host = u.host
+ self.port = u.port
+ self.path = u.path or "."
sshcmd = self.ui.config("ui", "ssh", "ssh")
remotecmd = self.ui.config("ui", "remotecmd", "hg")
@@ -119,9 +118,24 @@
def _callstream(self, cmd, **args):
self.ui.debug("sending %s command\n" % cmd)
self.pipeo.write("%s\n" % cmd)
- for k, v in sorted(args.iteritems()):
+ _func, names = wireproto.commands[cmd]
+ keys = names.split()
+ wireargs = {}
+ for k in keys:
+ if k == '*':
+ wireargs['*'] = args
+ break
+ else:
+ wireargs[k] = args[k]
+ del args[k]
+ for k, v in sorted(wireargs.iteritems()):
self.pipeo.write("%s %d\n" % (k, len(v)))
- self.pipeo.write(v)
+ if isinstance(v, dict):
+ for dk, dv in v.iteritems():
+ self.pipeo.write("%s %d\n" % (dk, len(dv)))
+ self.pipeo.write(dv)
+ else:
+ self.pipeo.write(v)
self.pipeo.flush()
return self.pipei
@@ -153,7 +167,7 @@
self.readerr()
try:
l = int(l)
- except:
+ except ValueError:
self._abort(error.ResponseError(_("unexpected response:"), l))
return self.pipei.read(l)
@@ -194,7 +208,7 @@
return 1
try:
return int(r)
- except:
+ except ValueError:
self._abort(error.ResponseError(_("unexpected response:"), r))
instance = sshrepository
--- a/mercurial/sshserver.py Fri Apr 29 11:04:39 2011 +0200
+++ b/mercurial/sshserver.py Fri Apr 29 11:10:11 2011 +0200
@@ -27,21 +27,21 @@
def getargs(self, args):
data = {}
keys = args.split()
- count = len(keys)
for n in xrange(len(keys)):
argline = self.fin.readline()[:-1]
arg, l = argline.split()
- val = self.fin.read(int(l))
if arg not in keys:
raise util.Abort("unexpected parameter %r" % arg)
if arg == '*':
star = {}
- for n in xrange(int(l)):
+ for k in xrange(int(l)):
+ argline = self.fin.readline()[:-1]
arg, l = argline.split()
val = self.fin.read(int(l))
star[arg] = val
data['*'] = star
else:
+ val = self.fin.read(int(l))
data[arg] = val
return [data[k] for k in keys]
--- a/mercurial/statichttprepo.py Fri Apr 29 11:04:39 2011 +0200
+++ b/mercurial/statichttprepo.py Fri Apr 29 11:10:11 2011 +0200
@@ -71,7 +71,7 @@
"""return a function that opens files over http"""
p = base
def o(path, mode="r", atomictemp=None):
- if 'a' in mode or 'w' in mode:
+ if mode not in ('r', 'rb'):
raise IOError('Permission denied')
f = "/".join((p, urllib.quote(path)))
return httprangereader(f, urlopener)
@@ -85,7 +85,8 @@
self.ui = ui
self.root = path
- self.path, authinfo = url.getauthinfo(path.rstrip('/') + "/.hg")
+ u = url.url(path.rstrip('/') + "/.hg")
+ self.path, authinfo = u.authinfo()
opener = build_opener(ui, authinfo)
self.opener = opener(self.path)
--- a/mercurial/store.py Fri Apr 29 11:04:39 2011 +0200
+++ b/mercurial/store.py Fri Apr 29 11:10:11 2011 +0200
@@ -14,6 +14,14 @@
# This avoids a collision between a file named foo and a dir named
# foo.i or foo.d
def encodedir(path):
+ '''
+ >>> encodedir('data/foo.i')
+ 'data/foo.i'
+ >>> encodedir('data/foo.i/bla.i')
+ 'data/foo.i.hg/bla.i'
+ >>> encodedir('data/foo.i.hg/bla.i')
+ 'data/foo.i.hg.hg/bla.i'
+ '''
if not path.startswith('data/'):
return path
return (path
@@ -22,6 +30,14 @@
.replace(".d/", ".d.hg/"))
def decodedir(path):
+ '''
+ >>> decodedir('data/foo.i')
+ 'data/foo.i'
+ >>> decodedir('data/foo.i.hg/bla.i')
+ 'data/foo.i/bla.i'
+ >>> decodedir('data/foo.i.hg.hg/bla.i')
+ 'data/foo.i.hg/bla.i'
+ '''
if not path.startswith('data/') or ".hg/" not in path:
return path
return (path
@@ -30,6 +46,29 @@
.replace(".hg.hg/", ".hg/"))
def _buildencodefun():
+ '''
+ >>> enc, dec = _buildencodefun()
+
+ >>> enc('nothing/special.txt')
+ 'nothing/special.txt'
+ >>> dec('nothing/special.txt')
+ 'nothing/special.txt'
+
+ >>> enc('HELLO')
+ '_h_e_l_l_o'
+ >>> dec('_h_e_l_l_o')
+ 'HELLO'
+
+ >>> enc('hello:world?')
+ 'hello~3aworld~3f'
+ >>> dec('hello~3aworld~3f')
+ 'hello:world?'
+
+ >>> enc('the\x07quick\xADshot')
+ 'the~07quick~adshot'
+ >>> dec('the~07quick~adshot')
+ 'the\\x07quick\\xadshot'
+ '''
e = '_'
win_reserved = [ord(x) for x in '\\:*?"<>|']
cmap = dict([(chr(x), chr(x)) for x in xrange(127)])
@@ -58,6 +97,17 @@
encodefilename, decodefilename = _buildencodefun()
def _build_lower_encodefun():
+ '''
+ >>> f = _build_lower_encodefun()
+ >>> f('nothing/special.txt')
+ 'nothing/special.txt'
+ >>> f('HELLO')
+ 'hello'
+ >>> f('hello:world?')
+ 'hello~3aworld~3f'
+ >>> f('the\x07quick\xADshot')
+ 'the~07quick~adshot'
+ '''
win_reserved = [ord(x) for x in '\\:*?"<>|']
cmap = dict([(chr(x), chr(x)) for x in xrange(127)])
for x in (range(32) + range(126, 256) + win_reserved):
@@ -72,6 +122,23 @@
com1 com2 com3 com4 com5 com6 com7 com8 com9
lpt1 lpt2 lpt3 lpt4 lpt5 lpt6 lpt7 lpt8 lpt9'''.split()
def _auxencode(path, dotencode):
+ '''
+ Encodes filenames containing names reserved by Windows or which end in
+ period or space. Does not touch other single reserved characters c.
+ Specifically, c in '\\:*?"<>|' or ord(c) <= 31 are *not* encoded here.
+ Additionally encodes space or period at the beginning, if dotencode is
+ True.
+ path is assumed to be all lowercase.
+
+ >>> _auxencode('.foo/aux.txt/txt.aux/con/prn/nul/foo.', True)
+ '~2efoo/au~78.txt/txt.aux/co~6e/pr~6e/nu~6c/foo~2e'
+ >>> _auxencode('.com1com2/lpt9.lpt4.lpt1/conprn/foo.', False)
+ '.com1com2/lp~749.lpt4.lpt1/conprn/foo~2e'
+ >>> _auxencode('foo. ', True)
+ 'foo.~20'
+ >>> _auxencode(' .foo', True)
+ '~20.foo'
+ '''
res = []
for n in path.split('/'):
if n:
--- a/mercurial/subrepo.py Fri Apr 29 11:04:39 2011 +0200
+++ b/mercurial/subrepo.py Fri Apr 29 11:10:11 2011 +0200
@@ -5,10 +5,10 @@
# This software may be used and distributed according to the terms of the
# GNU General Public License version 2 or any later version.
-import errno, os, re, xml.dom.minidom, shutil, urlparse, posixpath
+import errno, os, re, xml.dom.minidom, shutil, posixpath
import stat, subprocess, tarfile
from i18n import _
-import config, util, node, error, cmdutil
+import config, scmutil, util, node, error, cmdutil, url, bookmarks
hg = None
nullstate = ('', '', 'empty')
@@ -144,7 +144,7 @@
debug(s, "prompt remove")
wctx.sub(s).remove()
- for s, r in s2.items():
+ for s, r in sorted(s2.items()):
if s in s1:
continue
elif s not in sa:
@@ -193,25 +193,16 @@
"""return pull/push path of repo - either based on parent repo .hgsub info
or on the top repo config. Abort or return None if no source found."""
if hasattr(repo, '_subparent'):
- source = repo._subsource
- if source.startswith('/') or '://' in source:
- return source
+ source = url.url(repo._subsource)
+ source.path = posixpath.normpath(source.path)
+ if posixpath.isabs(source.path) or source.scheme:
+ return str(source)
parent = _abssource(repo._subparent, push, abort=False)
if parent:
- if '://' in parent:
- if parent[-1] == '/':
- parent = parent[:-1]
- r = urlparse.urlparse(parent + '/' + source)
- if parent.startswith('ssh://'):
- host, path = r[2][2:].split('/', 1)
- r2 = '//%s/%s' % (host, posixpath.normpath(path))
- else:
- r2 = posixpath.normpath(r[2])
- r = urlparse.urlunparse((r[0], r[1], r2,
- r[3], r[4], r[5]))
- return r
- else: # plain file system path
- return posixpath.normpath(os.path.join(parent, repo._subsource))
+ parent = url.url(parent)
+ parent.path = posixpath.join(parent.path, source.path)
+ parent.path = posixpath.normpath(parent.path)
+ return str(parent)
else: # recursion reached top repo
if hasattr(repo, '_subtoppath'):
return repo._subtoppath
@@ -243,7 +234,7 @@
import hg as h
hg = h
- util.path_auditor(ctx._repo.root)(path)
+ scmutil.path_auditor(ctx._repo.root)(path)
state = ctx.substate.get(path, nullstate)
if state[2] not in types:
raise util.Abort(_('unknown subrepo type %s') % state[2])
@@ -436,15 +427,14 @@
def _get(self, state):
source, revision, kind = state
- try:
- self._repo.lookup(revision)
- except error.RepoError:
+ if revision not in self._repo:
self._repo._subsource = source
srcurl = _abssource(self._repo)
self._repo.ui.status(_('pulling subrepo %s from %s\n')
% (subrelpath(self), srcurl))
other = hg.repository(self._repo.ui, srcurl)
self._repo.pull(other)
+ bookmarks.updatefromremote(self._repo.ui, self._repo, other)
def get(self, state, overwrite=False):
self._get(state)
@@ -718,6 +708,12 @@
current = None
return current
+ def _gitremote(self, remote):
+ out = self._gitcommand(['remote', 'show', '-n', remote])
+ line = out.split('\n')[1]
+ i = line.index('URL: ') + len('URL: ')
+ return line[i:]
+
def _githavelocally(self, revision):
out, code = self._gitdir(['cat-file', '-e', revision])
return code == 0
@@ -771,11 +767,14 @@
def _fetch(self, source, revision):
if self._gitmissing():
- self._ui.status(_('cloning subrepo %s\n') % self._relpath)
- self._gitnodir(['clone', self._abssource(source), self._abspath])
+ source = self._abssource(source)
+ self._ui.status(_('cloning subrepo %s from %s\n') %
+ (self._relpath, source))
+ self._gitnodir(['clone', source, self._abspath])
if self._githavelocally(revision):
return
- self._ui.status(_('pulling subrepo %s\n') % self._relpath)
+ self._ui.status(_('pulling subrepo %s from %s\n') %
+ (self._relpath, self._gitremote('origin')))
# try only origin: the originally cloned repo
self._gitcommand(['fetch'])
if not self._githavelocally(revision):
@@ -803,7 +802,7 @@
return
elif self._gitstate() == revision:
if overwrite:
- # first reset the index to unmark new files for commit, because
+ # first reset the index to unmark new files for commit, because
# reset --hard will otherwise throw away files added for commit,
# not just unmark them.
self._gitcommand(['reset', 'HEAD'])
--- a/mercurial/tags.py Fri Apr 29 11:04:39 2011 +0200
+++ b/mercurial/tags.py Fri Apr 29 11:10:11 2011 +0200
@@ -185,7 +185,7 @@
if len(line) == 3:
fnode = bin(line[2])
cachefnode[headnode] = fnode
- except (ValueError, TypeError):
+ except Exception:
# corruption of the tags cache, just recompute it
ui.warn(_('.hg/cache/tags is corrupt, rebuilding it\n'))
cacheheads = []
--- a/mercurial/templatefilters.py Fri Apr 29 11:04:39 2011 +0200
+++ b/mercurial/templatefilters.py Fri Apr 29 11:10:11 2011 +0200
@@ -6,13 +6,13 @@
# GNU General Public License version 2 or any later version.
import cgi, re, os, time, urllib
-import encoding, node, util
+import encoding, node, util, help
-def stringify(thing):
- '''turn nested template iterator into string.'''
- if hasattr(thing, '__iter__') and not isinstance(thing, str):
- return "".join([stringify(t) for t in thing if t is not None])
- return str(thing)
+def addbreaks(text):
+ """:addbreaks: Any text. Add an XHTML "<br />" tag before the end of
+ every line except the last.
+ """
+ return text.replace('\n', '<br/>\n')
agescales = [("year", 3600 * 24 * 365),
("month", 3600 * 24 * 30),
@@ -23,7 +23,9 @@
("second", 1)]
def age(date):
- '''turn a (timestamp, tzoff) tuple into an age string.'''
+ """:age: Date. Returns a human-readable date/time difference between the
+ given date/time and the current date/time.
+ """
def plural(t, c):
if c == 1:
@@ -34,18 +36,65 @@
now = time.time()
then = date[0]
+ future = False
if then > now:
- return 'in the future'
-
- delta = max(1, int(now - then))
- if delta > agescales[0][1] * 2:
- return util.shortdate(date)
+ future = True
+ delta = max(1, int(then - now))
+ if delta > agescales[0][1] * 30:
+ return 'in the distant future'
+ else:
+ delta = max(1, int(now - then))
+ if delta > agescales[0][1] * 2:
+ return util.shortdate(date)
for t, s in agescales:
n = delta // s
if n >= 2 or s == 1:
+ if future:
+ return '%s from now' % fmt(t, n)
return '%s ago' % fmt(t, n)
+def basename(path):
+ """:basename: Any text. Treats the text as a path, and returns the last
+ component of the path after splitting by the path separator
+ (ignoring trailing separators). For example, "foo/bar/baz" becomes
+ "baz" and "foo/bar//" becomes "bar".
+ """
+ return os.path.basename(path)
+
+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
+ <user@example.com>`` becomes ``example.com``.
+ """
+ f = author.find('@')
+ if f == -1:
+ return ''
+ author = author[f + 1:]
+ f = author.find('>')
+ if f >= 0:
+ author = author[:f]
+ return author
+
+def email(text):
+ """:email: Any text. Extracts the first string that looks like an email
+ address. Example: ``User <user@example.com>`` becomes
+ ``user@example.com``.
+ """
+ return util.email(text)
+
+def escape(text):
+ """:escape: Any text. Replaces the special XML/XHTML characters "&", "<"
+ and ">" with XML entities.
+ """
+ return cgi.escape(text, True)
+
para_re = None
space_re = None
@@ -74,40 +123,45 @@
return "".join([space_re.sub(' ', util.wrap(para, width=width)) + rest
for para, rest in findparas()])
+def fill68(text):
+ """:fill68: Any text. Wraps the text to fit in 68 columns."""
+ return fill(text, 68)
+
+def fill76(text):
+ """:fill76: Any text. Wraps the text to fit in 76 columns."""
+ return fill(text, 76)
+
def firstline(text):
- '''return the first line of text'''
+ """:firstline: Any text. Returns the first line of text."""
try:
return text.splitlines(True)[0].rstrip('\r\n')
except IndexError:
return ''
-def nl2br(text):
- '''replace raw newlines with xhtml line breaks.'''
- return text.replace('\n', '<br/>\n')
+def hexfilter(text):
+ """:hex: Any text. Convert a binary Mercurial node identifier into
+ its long hexadecimal representation.
+ """
+ return node.hex(text)
-def obfuscate(text):
- text = unicode(text, encoding.encoding, 'replace')
- return ''.join(['&#%d;' % ord(c) for c in text])
+def hgdate(text):
+ """:hgdate: Date. Returns the date as a pair of numbers: "1157407993
+ 25200" (Unix timestamp, timezone offset).
+ """
+ return "%d %d" % text
-def domain(author):
- '''get domain of author, or empty string if none.'''
- f = author.find('@')
- if f == -1:
- return ''
- author = author[f + 1:]
- f = author.find('>')
- if f >= 0:
- author = author[:f]
- return author
+def isodate(text):
+ """:isodate: Date. Returns the date in ISO 8601 format: "2009-08-18 13:00
+ +0200".
+ """
+ return util.datestr(text, '%Y-%m-%d %H:%M %1%2')
-def person(author):
- '''get name of author, or else username.'''
- if not '@' in author:
- return author
- f = author.find('<')
- if f == -1:
- return util.shortuser(author)
- return author[:f].rstrip()
+def isodatesec(text):
+ """:isodatesec: Date. Returns the date in ISO 8601 format, including
+ seconds: "2009-08-18 13:00:13 +0200". See also the rfc3339date
+ filter.
+ """
+ return util.datestr(text, '%Y-%m-%d %H:%M:%S %1%2')
def indent(text, prefix):
'''indent each non-empty line of text after first with prefix.'''
@@ -124,38 +178,6 @@
yield '\n'
return "".join(indenter())
-def permissions(flags):
- if "l" in flags:
- return "lrwxrwxrwx"
- if "x" in flags:
- return "-rwxr-xr-x"
- return "-rw-r--r--"
-
-def xmlescape(text):
- text = (text
- .replace('&', '&')
- .replace('<', '<')
- .replace('>', '>')
- .replace('"', '"')
- .replace("'", ''')) # ' invalid in HTML
- return re.sub('[\x00-\x08\x0B\x0C\x0E-\x1F]', ' ', text)
-
-def uescape(c):
- if ord(c) < 0x80:
- return c
- else:
- return '\\u%04x' % ord(c)
-
-_escapes = [
- ('\\', '\\\\'), ('"', '\\"'), ('\t', '\\t'), ('\n', '\\n'),
- ('\r', '\\r'), ('\f', '\\f'), ('\b', '\\b'),
-]
-
-def jsonescape(s):
- for k, v in _escapes:
- s = s.replace(k, v)
- return ''.join(uescape(c) for c in s)
-
def json(obj):
if obj is None or obj is False or obj is True:
return {None: 'null', False: 'false', True: 'true'}[obj]
@@ -180,49 +202,164 @@
else:
raise TypeError('cannot encode type %s' % obj.__class__.__name__)
+def _uescape(c):
+ if ord(c) < 0x80:
+ return c
+ else:
+ return '\\u%04x' % ord(c)
+
+_escapes = [
+ ('\\', '\\\\'), ('"', '\\"'), ('\t', '\\t'), ('\n', '\\n'),
+ ('\r', '\\r'), ('\f', '\\f'), ('\b', '\\b'),
+]
+
+def jsonescape(s):
+ for k, v in _escapes:
+ 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 (text[0], util.makedate()[1])
+
+def nonempty(str):
+ """:nonempty: Any text. Returns '(none)' if the string is empty."""
+ return str or "(none)"
+
+def obfuscate(text):
+ """:obfuscate: Any text. Returns the input text rendered as a sequence of
+ XML entities.
+ """
+ text = unicode(text, encoding.encoding, 'replace')
+ return ''.join(['&#%d;' % ord(c) for c in text])
+
+def permissions(flags):
+ if "l" in flags:
+ return "lrwxrwxrwx"
+ if "x" in flags:
+ return "-rwxr-xr-x"
+ return "-rw-r--r--"
+
+def person(author):
+ """:person: Any text. Returns the text before an email address."""
+ if not '@' in author:
+ return author
+ f = author.find('<')
+ if f != -1:
+ return author[:f].rstrip()
+ f = author.find('@')
+ return author[:f].replace('.', ' ')
+
+def rfc3339date(text):
+ """:rfc3339date: Date. Returns a date using the Internet date format
+ specified in RFC 3339: "2009-08-18T13:00:13+02:00".
+ """
+ return util.datestr(text, "%Y-%m-%dT%H:%M:%S%1:%2")
+
+def rfc822date(text):
+ """:rfc822date: Date. Returns a date using the same format used in email
+ headers: "Tue, 18 Aug 2009 13:00:13 +0200".
+ """
+ return util.datestr(text, "%a, %d %b %Y %H:%M:%S %1%2")
+
+def short(text):
+ """:short: Changeset hash. Returns the short form of a changeset hash,
+ i.e. a 12 hexadecimal digit string.
+ """
+ return text[:12]
+
+def shortdate(text):
+ """:shortdate: Date. Returns a date like "2006-09-18"."""
+ return util.shortdate(text)
+
+def stringescape(text):
+ return text.encode('string_escape')
+
+def stringify(thing):
+ """:stringify: Any type. Turns the value into text by converting values into
+ text and concatenating them.
+ """
+ if hasattr(thing, '__iter__') and not isinstance(thing, str):
+ return "".join([stringify(t) for t in thing if t is not None])
+ return str(thing)
+
+def strip(text):
+ """:strip: Any text. Strips all leading and trailing whitespace."""
+ return text.strip()
+
def stripdir(text):
- '''Treat the text as path and strip a directory level, if possible.'''
+ """:stripdir: Treat the text as path and strip a directory level, if
+ possible. For example, "foo" and "foo/bar" becomes "foo".
+ """
dir = os.path.dirname(text)
if dir == "":
return os.path.basename(text)
else:
return dir
-def nonempty(str):
- return str or "(none)"
+def tabindent(text):
+ """:tabindent: Any text. Returns the text, with every line except the
+ first starting with a tab character.
+ """
+ return indent(text, '\t')
+
+def urlescape(text):
+ """:urlescape: Any text. Escapes all "special" characters. For example,
+ "foo bar" becomes "foo%20bar".
+ """
+ return urllib.quote(text)
+
+def userfilter(text):
+ """:user: Any text. Returns the user portion of an email address."""
+ return util.shortuser(text)
+
+def xmlescape(text):
+ text = (text
+ .replace('&', '&')
+ .replace('<', '<')
+ .replace('>', '>')
+ .replace('"', '"')
+ .replace("'", ''')) # ' invalid in HTML
+ return re.sub('[\x00-\x08\x0B\x0C\x0E-\x1F]', ' ', text)
filters = {
- "addbreaks": nl2br,
- "basename": os.path.basename,
- "stripdir": stripdir,
+ "addbreaks": addbreaks,
"age": age,
- "date": lambda x: util.datestr(x),
+ "basename": basename,
+ "date": datefilter,
"domain": domain,
- "email": util.email,
- "escape": lambda x: cgi.escape(x, True),
- "fill68": lambda x: fill(x, width=68),
- "fill76": lambda x: fill(x, width=76),
+ "email": email,
+ "escape": escape,
+ "fill68": fill68,
+ "fill76": fill76,
"firstline": firstline,
- "tabindent": lambda x: indent(x, '\t'),
- "hgdate": lambda x: "%d %d" % x,
- "isodate": lambda x: util.datestr(x, '%Y-%m-%d %H:%M %1%2'),
- "isodatesec": lambda x: util.datestr(x, '%Y-%m-%d %H:%M:%S %1%2'),
+ "hex": hexfilter,
+ "hgdate": hgdate,
+ "isodate": isodate,
+ "isodatesec": isodatesec,
"json": json,
"jsonescape": jsonescape,
- "localdate": lambda x: (x[0], util.makedate()[1]),
+ "localdate": localdate,
"nonempty": nonempty,
"obfuscate": obfuscate,
"permissions": permissions,
"person": person,
- "rfc822date": lambda x: util.datestr(x, "%a, %d %b %Y %H:%M:%S %1%2"),
- "rfc3339date": lambda x: util.datestr(x, "%Y-%m-%dT%H:%M:%S%1:%2"),
- "hex": node.hex,
- "short": lambda x: x[:12],
- "shortdate": util.shortdate,
+ "rfc3339date": rfc3339date,
+ "rfc822date": rfc822date,
+ "short": short,
+ "shortdate": shortdate,
+ "stringescape": stringescape,
"stringify": stringify,
- "strip": lambda x: x.strip(),
- "urlescape": lambda x: urllib.quote(x),
- "user": lambda x: util.shortuser(x),
- "stringescape": lambda x: x.encode('string_escape'),
+ "strip": strip,
+ "stripdir": stripdir,
+ "tabindent": tabindent,
+ "urlescape": urlescape,
+ "user": userfilter,
"xmlescape": xmlescape,
}
+
+def makedoc(topic, doc):
+ return help.makeitemsdoc(topic, doc, '.. filtersmarker', filters)
+
+# tell hggettext to extract docstrings from these functions:
+i18nfunctions = filters.values()
--- a/mercurial/templatekw.py Fri Apr 29 11:04:39 2011 +0200
+++ b/mercurial/templatekw.py Fri Apr 29 11:10:11 2011 +0200
@@ -6,7 +6,7 @@
# GNU General Public License version 2 or any later version.
from node import hex
-import encoding, patch, util, error
+import encoding, patch, util, error, help
def showlist(name, values, plural=None, **args):
'''expand set of values.
@@ -73,8 +73,7 @@
def getfiles(repo, ctx, revcache):
if 'files' not in revcache:
- revcache['files'] = repo.status(ctx.parents()[0].node(),
- ctx.node())[:3]
+ revcache['files'] = repo.status(ctx.p1().node(), ctx.node())[:3]
return revcache['files']
def getlatesttags(repo, ctx, cache):
@@ -143,32 +142,49 @@
def showauthor(repo, ctx, templ, **args):
+ """:author: String. The unmodified author of the changeset."""
return ctx.user()
def showbranch(**args):
+ """:branch: String. The name of the branch on which the changeset was
+ committed.
+ """
return args['ctx'].branch()
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.
+ """
branch = args['ctx'].branch()
if branch != 'default':
return showlist('branch', [branch], plural='branches', **args)
def showbookmarks(**args):
+ """:bookmarks: List of strings. Any bookmarks associated with the
+ changeset.
+ """
bookmarks = args['ctx'].bookmarks()
return showlist('bookmark', bookmarks, **args)
def showchildren(**args):
+ """:children: List of strings. The children of the changeset."""
ctx = args['ctx']
childrevs = ['%d:%s' % (cctx, cctx) for cctx in ctx.children()]
return showlist('children', childrevs, **args)
def showdate(repo, ctx, templ, **args):
+ """:date: Date information. The date when the changeset was committed."""
return ctx.date()
def showdescription(repo, ctx, templ, **args):
+ """:desc: String. The text of the changeset description."""
return ctx.description().strip()
def showdiffstat(repo, ctx, templ, **args):
+ """:diffstat: String. Statistics of changes with the following format:
+ "modified files: +added/-removed lines"
+ """
files, adds, removes = 0, 0, 0
for i in patch.diffstatdata(util.iterlines(ctx.diff())):
files += 1
@@ -184,10 +200,14 @@
yield templ('extra', **args)
def showfileadds(**args):
+ """:file_adds: List of strings. Files added by this changeset."""
repo, ctx, revcache = args['repo'], args['ctx'], args['revcache']
return showlist('file_add', getfiles(repo, ctx, revcache)[1], **args)
def showfilecopies(**args):
+ """:file_copies: List of strings. Files copied in this changeset with
+ their sources.
+ """
cache, ctx = args['cache'], args['ctx']
copies = args['revcache'].get('copies')
if copies is None:
@@ -207,25 +227,37 @@
# provided before calling the templater, usually with a --copies
# command line switch.
def showfilecopiesswitch(**args):
+ """:file_copies_switch: List of strings. Like "file_copies" but displayed
+ only if the --copied switch is set.
+ """
copies = args['revcache'].get('copies') or []
c = [{'name': x[0], 'source': x[1]} for x in copies]
return showlist('file_copy', c, plural='file_copies', **args)
def showfiledels(**args):
+ """:file_dels: List of strings. Files removed by this changeset."""
repo, ctx, revcache = args['repo'], args['ctx'], args['revcache']
return showlist('file_del', getfiles(repo, ctx, revcache)[2], **args)
def showfilemods(**args):
+ """:file_mods: List of strings. Files modified by this changeset."""
repo, ctx, revcache = args['repo'], args['ctx'], args['revcache']
return showlist('file_mod', getfiles(repo, ctx, revcache)[0], **args)
def showfiles(**args):
+ """:files: List of strings. All files modified, added, or removed by this
+ changeset.
+ """
return showlist('file', args['ctx'].files(), **args)
def showlatesttag(repo, ctx, templ, cache, **args):
+ """:latesttag: String. Most recent global tag in the ancestors of this
+ changeset.
+ """
return getlatesttags(repo, ctx, cache)[2]
def showlatesttagdistance(repo, ctx, templ, cache, **args):
+ """:latesttagdistance: Integer. Longest path to the latest tag."""
return getlatesttags(repo, ctx, cache)[1]
def showmanifest(**args):
@@ -236,12 +268,17 @@
return templ('manifest', **args)
def shownode(repo, ctx, templ, **args):
+ """:node: String. The changeset identification hash, as a 40 hexadecimal
+ digit string.
+ """
return ctx.hex()
def showrev(repo, ctx, templ, **args):
+ """:rev: Integer. The repository-local changeset revision number."""
return ctx.rev()
def showtags(**args):
+ """:tags: List of strings. Any tags associated with the changeset."""
return showlist('tag', args['ctx'].tags(), **args)
# keywords are callables like:
@@ -276,3 +313,8 @@
'tags': showtags,
}
+def makedoc(topic, doc):
+ return help.makeitemsdoc(topic, doc, '.. keywordsmarker', keywords)
+
+# tell hggettext to extract docstrings from these functions:
+i18nfunctions = keywords.values()
--- a/mercurial/templater.py Fri Apr 29 11:04:39 2011 +0200
+++ b/mercurial/templater.py Fri Apr 29 11:10:11 2011 +0200
@@ -69,7 +69,6 @@
else:
raise error.ParseError(_("syntax error"), pos)
pos += 1
- data[2] = pos
yield ('end', None, pos)
def compiletemplate(tmpl, context):
@@ -91,8 +90,8 @@
parsed.append(("string", tmpl[pos:n]))
pd = [tmpl, n + 1, stop]
- parsed.append(p.parse(pd))
- pos = pd[2]
+ parseres, pos = p.parse(pd)
+ parsed.append(parseres)
return [compileexp(e, context) for e in parsed]
--- a/mercurial/templates/coal/map Fri Apr 29 11:04:39 2011 +0200
+++ b/mercurial/templates/coal/map Fri Apr 29 11:10:11 2011 +0200
@@ -94,14 +94,12 @@
filerename = '{file|escape}@'
filelogrename = '
- <tr>
- <th>base:</th>
- <td>
- <a href="{url}file/{node|short}/{file|urlescape}{sessionvars%urlparameter}">
- {file|escape}@{node|short}
- </a>
- </td>
- </tr>'
+ <span class="base">
+ base
+ <a href="{url}file/{node|short}/{file|urlescape}{sessionvars%urlparameter}">
+ {file|escape}@{node|short}
+ </a>
+ </span>'
fileannotateparent = '
<tr>
<td class="metatag">parent:</td>
--- a/mercurial/templates/gitweb/bookmarks.tmpl Fri Apr 29 11:04:39 2011 +0200
+++ b/mercurial/templates/gitweb/bookmarks.tmpl Fri Apr 29 11:10:11 2011 +0200
@@ -8,7 +8,7 @@
<body>
<div class="page_header">
-<a href="http://mercurial.selenic.com/" title="Mercurial" style="float: right;">Mercurial</a><a href="{url}summary{sessionvars%urlparameter}">{repo|escape}</a> / bookmarks
+<a href="{logourl}" title="Mercurial" style="float: right;">Mercurial</a><a href="{url}summary{sessionvars%urlparameter}">{repo|escape}</a> / bookmarks
</div>
<div class="page_nav">
--- a/mercurial/templates/gitweb/branches.tmpl Fri Apr 29 11:04:39 2011 +0200
+++ b/mercurial/templates/gitweb/branches.tmpl Fri Apr 29 11:10:11 2011 +0200
@@ -8,7 +8,7 @@
<body>
<div class="page_header">
-<a href="http://mercurial.selenic.com/" title="Mercurial" style="float: right;">Mercurial</a><a href="{url}summary{sessionvars%urlparameter}">{repo|escape}</a> / branches
+<a href="{logourl}" title="Mercurial" style="float: right;">Mercurial</a><a href="{url}summary{sessionvars%urlparameter}">{repo|escape}</a> / branches
</div>
<div class="page_nav">
--- a/mercurial/templates/gitweb/changelog.tmpl Fri Apr 29 11:04:39 2011 +0200
+++ b/mercurial/templates/gitweb/changelog.tmpl Fri Apr 29 11:10:11 2011 +0200
@@ -8,7 +8,7 @@
<body>
<div class="page_header">
-<a href="http://mercurial.selenic.com/" title="Mercurial" style="float: right;">Mercurial</a><a href="{url}summary{sessionvars%urlparameter}">{repo|escape}</a> / changelog
+<a href="{logourl}" title="Mercurial" style="float: right;">Mercurial</a><a href="{url}summary{sessionvars%urlparameter}">{repo|escape}</a> / changelog
</div>
<form action="{url}log">
--- a/mercurial/templates/gitweb/changeset.tmpl Fri Apr 29 11:04:39 2011 +0200
+++ b/mercurial/templates/gitweb/changeset.tmpl Fri Apr 29 11:10:11 2011 +0200
@@ -8,7 +8,7 @@
<body>
<div class="page_header">
-<a href="http://mercurial.selenic.com/" title="Mercurial" style="float: right;">Mercurial</a><a href="{url}summary{sessionvars%urlparameter}">{repo|escape}</a> / changeset
+<a href="{logourl}" title="Mercurial" style="float: right;">Mercurial</a><a href="{url}summary{sessionvars%urlparameter}">{repo|escape}</a> / changeset
</div>
<div class="page_nav">
--- a/mercurial/templates/gitweb/error.tmpl Fri Apr 29 11:04:39 2011 +0200
+++ b/mercurial/templates/gitweb/error.tmpl Fri Apr 29 11:10:11 2011 +0200
@@ -8,7 +8,7 @@
<body>
<div class="page_header">
-<a href="http://mercurial.selenic.com/" title="Mercurial" style="float: right;">Mercurial</a><a href="{url}summary{sessionvars%urlparameter}">{repo|escape}</a> / error
+<a href="{logourl}" title="Mercurial" style="float: right;">Mercurial</a><a href="{url}summary{sessionvars%urlparameter}">{repo|escape}</a> / error
</div>
<div class="page_nav">
--- a/mercurial/templates/gitweb/fileannotate.tmpl Fri Apr 29 11:04:39 2011 +0200
+++ b/mercurial/templates/gitweb/fileannotate.tmpl Fri Apr 29 11:10:11 2011 +0200
@@ -8,7 +8,7 @@
<body>
<div class="page_header">
-<a href="http://mercurial.selenic.com/" title="Mercurial" style="float: right;">Mercurial</a><a href="{url}summary{sessionvars%urlparameter}">{repo|escape}</a> / annotate
+<a href="{logourl}" title="Mercurial" style="float: right;">Mercurial</a><a href="{url}summary{sessionvars%urlparameter}">{repo|escape}</a> / annotate
</div>
<div class="page_nav">
--- a/mercurial/templates/gitweb/filediff.tmpl Fri Apr 29 11:04:39 2011 +0200
+++ b/mercurial/templates/gitweb/filediff.tmpl Fri Apr 29 11:10:11 2011 +0200
@@ -8,7 +8,7 @@
<body>
<div class="page_header">
-<a href="http://mercurial.selenic.com/" title="Mercurial" style="float: right;">Mercurial</a><a href="{url}summary{sessionvars%urlparameter}">{repo|escape}</a> / diff
+<a href="{logourl}" title="Mercurial" style="float: right;">Mercurial</a><a href="{url}summary{sessionvars%urlparameter}">{repo|escape}</a> / diff
</div>
<div class="page_nav">
--- a/mercurial/templates/gitweb/filelog.tmpl Fri Apr 29 11:04:39 2011 +0200
+++ b/mercurial/templates/gitweb/filelog.tmpl Fri Apr 29 11:10:11 2011 +0200
@@ -8,7 +8,7 @@
<body>
<div class="page_header">
-<a href="http://mercurial.selenic.com/" title="Mercurial" style="float: right;">Mercurial</a><a href="{url}summary{sessionvars%urlparameter}">{repo|escape}</a> / file revisions
+<a href="{logourl}" title="Mercurial" style="float: right;">Mercurial</a><a href="{url}summary{sessionvars%urlparameter}">{repo|escape}</a> / file revisions
</div>
<div class="page_nav">
--- a/mercurial/templates/gitweb/filerevision.tmpl Fri Apr 29 11:04:39 2011 +0200
+++ b/mercurial/templates/gitweb/filerevision.tmpl Fri Apr 29 11:10:11 2011 +0200
@@ -8,7 +8,7 @@
<body>
<div class="page_header">
-<a href="http://mercurial.selenic.com/" title="Mercurial" style="float: right;">Mercurial</a><a href="{url}summary{sessionvars%urlparameter}">{repo|escape}</a> / file revision
+<a href="{logourl}" title="Mercurial" style="float: right;">Mercurial</a><a href="{url}summary{sessionvars%urlparameter}">{repo|escape}</a> / file revision
</div>
<div class="page_nav">
--- a/mercurial/templates/gitweb/graph.tmpl Fri Apr 29 11:04:39 2011 +0200
+++ b/mercurial/templates/gitweb/graph.tmpl Fri Apr 29 11:10:11 2011 +0200
@@ -9,7 +9,7 @@
<body>
<div class="page_header">
-<a href="http://mercurial.selenic.com/" title="Mercurial" style="float: right;">Mercurial</a><a href="{url}summary{sessionvars%urlparameter}">{repo|escape}</a> / graph
+<a href="{logourl}" title="Mercurial" style="float: right;">Mercurial</a><a href="{url}summary{sessionvars%urlparameter}">{repo|escape}</a> / graph
</div>
<form action="{url}log">
--- a/mercurial/templates/gitweb/help.tmpl Fri Apr 29 11:04:39 2011 +0200
+++ b/mercurial/templates/gitweb/help.tmpl Fri Apr 29 11:10:11 2011 +0200
@@ -8,7 +8,7 @@
<body>
<div class="page_header">
-<a href="http://mercurial.selenic.com/" title="Mercurial" style="float: right;">Mercurial</a><a href="{url}summary{sessionvars%urlparameter}">{repo|escape}</a> / help
+<a href="{logourl}" title="Mercurial" style="float: right;">Mercurial</a><a href="{url}summary{sessionvars%urlparameter}">{repo|escape}</a> / help
</div>
<div class="page_nav">
--- a/mercurial/templates/gitweb/helptopics.tmpl Fri Apr 29 11:04:39 2011 +0200
+++ b/mercurial/templates/gitweb/helptopics.tmpl Fri Apr 29 11:10:11 2011 +0200
@@ -8,7 +8,7 @@
<body>
<div class="page_header">
-<a href="http://mercurial.selenic.com/" title="Mercurial" style="float: right;">Mercurial</a><a href="{url}summary{sessionvars%urlparameter}">{repo|escape}</a> / help
+<a href="{logourl}" title="Mercurial" style="float: right;">Mercurial</a><a href="{url}summary{sessionvars%urlparameter}">{repo|escape}</a> / help
</div>
<div class="page_nav">
--- a/mercurial/templates/gitweb/index.tmpl Fri Apr 29 11:04:39 2011 +0200
+++ b/mercurial/templates/gitweb/index.tmpl Fri Apr 29 11:10:11 2011 +0200
@@ -4,7 +4,7 @@
<body>
<div class="page_header">
- <a href="http://mercurial.selenic.com/" title="Mercurial" style="float: right;">Mercurial</a>
+ <a href="{logourl}" title="Mercurial" style="float: right;">Mercurial</a>
Repositories list
</div>
--- a/mercurial/templates/gitweb/manifest.tmpl Fri Apr 29 11:04:39 2011 +0200
+++ b/mercurial/templates/gitweb/manifest.tmpl Fri Apr 29 11:10:11 2011 +0200
@@ -8,7 +8,7 @@
<body>
<div class="page_header">
-<a href="http://mercurial.selenic.com/" title="Mercurial" style="float: right;">Mercurial</a><a href="{url}summary{sessionvars%urlparameter}">{repo|escape}</a> / files
+<a href="{logourl}" title="Mercurial" style="float: right;">Mercurial</a><a href="{url}summary{sessionvars%urlparameter}">{repo|escape}</a> / files
</div>
<div class="page_nav">
--- a/mercurial/templates/gitweb/notfound.tmpl Fri Apr 29 11:04:39 2011 +0200
+++ b/mercurial/templates/gitweb/notfound.tmpl Fri Apr 29 11:10:11 2011 +0200
@@ -5,7 +5,7 @@
<body>
<div class="page_header">
-<a href="http://mercurial.selenic.com/" title="Mercurial" style="float: right;">Mercurial</a> Not found: {repo|escape}
+<a href="{logourl}" title="Mercurial" style="float: right;">Mercurial</a> Not found: {repo|escape}
</div>
<div class="page_body">
--- a/mercurial/templates/gitweb/search.tmpl Fri Apr 29 11:04:39 2011 +0200
+++ b/mercurial/templates/gitweb/search.tmpl Fri Apr 29 11:10:11 2011 +0200
@@ -8,7 +8,7 @@
<body>
<div class="page_header">
-<a href="http://mercurial.selenic.com/" title="Mercurial" style="float: right;">Mercurial</a><a href="{url}summary{sessionvars%urlparameter}">{repo|escape}</a> / search
+<a href="{logourl}" title="Mercurial" style="float: right;">Mercurial</a><a href="{url}summary{sessionvars%urlparameter}">{repo|escape}</a> / search
<form action="{url}log">
{sessionvars%hiddenformentry}
--- a/mercurial/templates/gitweb/shortlog.tmpl Fri Apr 29 11:04:39 2011 +0200
+++ b/mercurial/templates/gitweb/shortlog.tmpl Fri Apr 29 11:10:11 2011 +0200
@@ -8,7 +8,7 @@
<body>
<div class="page_header">
-<a href="http://mercurial.selenic.com/" title="Mercurial" style="float: right;">Mercurial</a><a href="{url}summary{sessionvars%urlparameter}">{repo|escape}</a> / shortlog
+<a href="{logourl}" title="Mercurial" style="float: right;">Mercurial</a><a href="{url}summary{sessionvars%urlparameter}">{repo|escape}</a> / shortlog
</div>
<form action="{url}log">
--- a/mercurial/templates/gitweb/summary.tmpl Fri Apr 29 11:04:39 2011 +0200
+++ b/mercurial/templates/gitweb/summary.tmpl Fri Apr 29 11:10:11 2011 +0200
@@ -8,7 +8,7 @@
<body>
<div class="page_header">
-<a href="http://mercurial.selenic.com/" title="Mercurial" style="float: right;">Mercurial</a><a href="{url}summary{sessionvars%urlparameter}">{repo|escape}</a> / summary
+<a href="{logourl}" title="Mercurial" style="float: right;">Mercurial</a><a href="{url}summary{sessionvars%urlparameter}">{repo|escape}</a> / summary
<form action="{url}log">
{sessionvars%hiddenformentry}
@@ -50,6 +50,12 @@
<tr class="light"><td colspan="3"><a class="list" href="{url}tags{sessionvars%urlparameter}">...</a></td></tr>
</table>
+<div><a class="title" href="{url}bookmarks{sessionvars%urlparameter}">bookmarks</a></div>
+<table cellspacing="0">
+{bookmarks%bookmarkentry}
+<tr class="light"><td colspan="3"><a class="list" href="{url}bookmarks{sessionvars%urlparameter}">...</a></td></tr>
+</table>
+
<div><a class="title" href="#">branches</a></div>
<table cellspacing="0">
{branches%branchentry}
--- a/mercurial/templates/gitweb/tags.tmpl Fri Apr 29 11:04:39 2011 +0200
+++ b/mercurial/templates/gitweb/tags.tmpl Fri Apr 29 11:10:11 2011 +0200
@@ -8,7 +8,7 @@
<body>
<div class="page_header">
-<a href="http://mercurial.selenic.com/" title="Mercurial" style="float: right;">Mercurial</a><a href="{url}summary{sessionvars%urlparameter}">{repo|escape}</a> / tags
+<a href="{logourl}" title="Mercurial" style="float: right;">Mercurial</a><a href="{url}summary{sessionvars%urlparameter}">{repo|escape}</a> / tags
</div>
<div class="page_nav">
--- a/mercurial/templates/map-cmdline.compact Fri Apr 29 11:04:39 2011 +0200
+++ b/mercurial/templates/map-cmdline.compact Fri Apr 29 11:10:11 2011 +0200
@@ -1,4 +1,4 @@
-changeset = '{rev}{tags}{parents} {node|short} {date|isodate} {author|user}\n {desc|firstline|strip}\n\n'
+changeset = '{rev}{tags}{bookmarks}{parents} {node|short} {date|isodate} {author|user}\n {desc|firstline|strip}\n\n'
changeset_quiet = '{rev}:{node|short}\n'
changeset_verbose = '{rev}{tags}{parents} {node|short} {date|isodate} {author}\n {desc|strip}\n\n'
start_tags = '['
@@ -7,3 +7,6 @@
start_parents = ':'
parent = '{rev},'
last_parent = '{rev}'
+start_bookmarks = '['
+bookmark = '{bookmark},'
+last_bookmark = '{bookmark}]'
--- a/mercurial/templates/monoblue/footer.tmpl Fri Apr 29 11:04:39 2011 +0200
+++ b/mercurial/templates/monoblue/footer.tmpl Fri Apr 29 11:10:11 2011 +0200
@@ -8,7 +8,7 @@
</div>
<div id="powered-by">
- <p><a href="http://mercurial.selenic.com/" title="Mercurial"><img src="{staticurl}hglogo.png" width=75 height=90 border=0 alt="mercurial"></a></p>
+ <p><a href="{logourl}" title="Mercurial"><img src="{staticurl}hglogo.png" width=75 height=90 border=0 alt="mercurial"></a></p>
</div>
<div id="corner-top-left"></div>
--- a/mercurial/templates/monoblue/index.tmpl Fri Apr 29 11:04:39 2011 +0200
+++ b/mercurial/templates/monoblue/index.tmpl Fri Apr 29 11:10:11 2011 +0200
@@ -26,7 +26,7 @@
</div>
<div id="powered-by">
- <p><a href="http://mercurial.selenic.com/" title="Mercurial"><img src="{staticurl}hglogo.png" width=75 height=90 border=0 alt="mercurial"></a></p>
+ <p><a href="{logourl}" title="Mercurial"><img src="{staticurl}hglogo.png" width=75 height=90 border=0 alt="mercurial"></a></p>
</div>
<div id="corner-top-left"></div>
--- a/mercurial/templates/monoblue/summary.tmpl Fri Apr 29 11:04:39 2011 +0200
+++ b/mercurial/templates/monoblue/summary.tmpl Fri Apr 29 11:10:11 2011 +0200
@@ -58,6 +58,14 @@
</tr>
</table>
+ <h2><a href="{url}bookmarks{sessionvars%urlparameter}">Bookmarks</a></h2>
+ <table>
+ {bookmarks%bookmarkentry}
+ <tr class="light">
+ <td colspan="3"><a class="list" href="{url}bookmarks{sessionvars%urlparameter}">...</a></td>
+ </tr>
+ </table>
+
<h2 class="no-link">Branches</h2>
<table>
{branches%branchentry}
--- a/mercurial/templates/paper/bookmarks.tmpl Fri Apr 29 11:04:39 2011 +0200
+++ b/mercurial/templates/paper/bookmarks.tmpl Fri Apr 29 11:10:11 2011 +0200
@@ -10,7 +10,7 @@
<div class="container">
<div class="menu">
<div class="logo">
-<a href="http://mercurial.selenic.com/">
+<a href="{logourl}">
<img src="{staticurl}hglogo.png" alt="mercurial" /></a>
</div>
<ul>
--- a/mercurial/templates/paper/branches.tmpl Fri Apr 29 11:04:39 2011 +0200
+++ b/mercurial/templates/paper/branches.tmpl Fri Apr 29 11:10:11 2011 +0200
@@ -10,7 +10,7 @@
<div class="container">
<div class="menu">
<div class="logo">
-<a href="http://mercurial.selenic.com/">
+<a href="{logourl}">
<img src="{staticurl}hglogo.png" alt="mercurial" /></a>
</div>
<ul>
--- a/mercurial/templates/paper/changeset.tmpl Fri Apr 29 11:04:39 2011 +0200
+++ b/mercurial/templates/paper/changeset.tmpl Fri Apr 29 11:10:11 2011 +0200
@@ -5,7 +5,7 @@
<div class="container">
<div class="menu">
<div class="logo">
-<a href="http://mercurial.selenic.com/">
+<a href="{logourl}">
<img src="{staticurl}hglogo.png" alt="mercurial" /></a>
</div>
<ul>
@@ -40,7 +40,7 @@
files, or words in the commit message</div>
</form>
-<div class="description">{desc|strip|escape|addbreaks|nonempty}</div>
+<div class="description">{desc|strip|escape|nonempty}</div>
<table id="changesetEntry">
<tr>
--- a/mercurial/templates/paper/error.tmpl Fri Apr 29 11:04:39 2011 +0200
+++ b/mercurial/templates/paper/error.tmpl Fri Apr 29 11:10:11 2011 +0200
@@ -6,7 +6,7 @@
<div class="container">
<div class="menu">
<div class="logo">
-<a href="http://mercurial.selenic.com/">
+<a href="{logourl}">
<img src="{staticurl}hglogo.png" width=75 height=90 border=0 alt="mercurial" /></a>
</div>
<ul>
--- a/mercurial/templates/paper/fileannotate.tmpl Fri Apr 29 11:04:39 2011 +0200
+++ b/mercurial/templates/paper/fileannotate.tmpl Fri Apr 29 11:10:11 2011 +0200
@@ -6,7 +6,7 @@
<div class="container">
<div class="menu">
<div class="logo">
-<a href="http://mercurial.selenic.com/">
+<a href="{logourl}">
<img src="{staticurl}hglogo.png" alt="mercurial" /></a>
</div>
<ul>
@@ -45,7 +45,7 @@
files, or words in the commit message</div>
</form>
-<div class="description">{desc|strip|escape|addbreaks|nonempty}</div>
+<div class="description">{desc|strip|escape|nonempty}</div>
<table id="changesetEntry">
<tr>
--- a/mercurial/templates/paper/filediff.tmpl Fri Apr 29 11:04:39 2011 +0200
+++ b/mercurial/templates/paper/filediff.tmpl Fri Apr 29 11:10:11 2011 +0200
@@ -6,7 +6,7 @@
<div class="container">
<div class="menu">
<div class="logo">
-<a href="http://mercurial.selenic.com/">
+<a href="{logourl}">
<img src="{staticurl}hglogo.png" alt="mercurial" /></a>
</div>
<ul>
@@ -44,7 +44,7 @@
files, or words in the commit message</div>
</form>
-<div class="description">{desc|strip|escape|addbreaks|nonempty}</div>
+<div class="description">{desc|strip|escape|nonempty}</div>
<table id="changesetEntry">
<tr>
--- a/mercurial/templates/paper/filelog.tmpl Fri Apr 29 11:04:39 2011 +0200
+++ b/mercurial/templates/paper/filelog.tmpl Fri Apr 29 11:10:11 2011 +0200
@@ -10,7 +10,7 @@
<div class="container">
<div class="menu">
<div class="logo">
-<a href="http://mercurial.selenic.com/">
+<a href="{logourl}">
<img src="{staticurl}hglogo.png" alt="mercurial" /></a>
</div>
<ul>
--- a/mercurial/templates/paper/filelogentry.tmpl Fri Apr 29 11:04:39 2011 +0200
+++ b/mercurial/templates/paper/filelogentry.tmpl Fri Apr 29 11:10:11 2011 +0200
@@ -1,5 +1,5 @@
<tr class="parity{parity}">
<td class="age">{date|age}</td>
<td class="author">{author|person}</td>
- <td class="description"><a href="{url}rev/{node|short}{sessionvars%urlparameter}">{desc|strip|firstline|escape|nonempty}</a>{inbranch%changelogbranchname}{branches%changelogbranchhead}{tags%changelogtag}</td>
+ <td class="description"><a href="{url}rev/{node|short}{sessionvars%urlparameter}">{desc|strip|firstline|escape|nonempty}</a>{inbranch%changelogbranchname}{branches%changelogbranchhead}{tags%changelogtag}{rename%filelogrename}</td>
</tr>
--- a/mercurial/templates/paper/filerevision.tmpl Fri Apr 29 11:04:39 2011 +0200
+++ b/mercurial/templates/paper/filerevision.tmpl Fri Apr 29 11:10:11 2011 +0200
@@ -6,7 +6,7 @@
<div class="container">
<div class="menu">
<div class="logo">
-<a href="http://mercurial.selenic.com/">
+<a href="{logourl}">
<img src="{staticurl}hglogo.png" alt="mercurial" /></a>
</div>
<ul>
@@ -43,7 +43,7 @@
files, or words in the commit message</div>
</form>
-<div class="description">{desc|strip|escape|addbreaks|nonempty}</div>
+<div class="description">{desc|strip|escape|nonempty}</div>
<table id="changesetEntry">
<tr>
--- a/mercurial/templates/paper/graph.tmpl Fri Apr 29 11:04:39 2011 +0200
+++ b/mercurial/templates/paper/graph.tmpl Fri Apr 29 11:10:11 2011 +0200
@@ -11,7 +11,7 @@
<div class="container">
<div class="menu">
<div class="logo">
-<a href="http://mercurial.selenic.com/">
+<a href="{logourl}">
<img src="{staticurl}hglogo.png" alt="mercurial" /></a>
</div>
<ul>
--- a/mercurial/templates/paper/help.tmpl Fri Apr 29 11:04:39 2011 +0200
+++ b/mercurial/templates/paper/help.tmpl Fri Apr 29 11:10:11 2011 +0200
@@ -10,7 +10,7 @@
<div class="container">
<div class="menu">
<div class="logo">
-<a href="http://mercurial.selenic.com/">
+<a href="{logourl}">
<img src="{staticurl}hglogo.png" alt="mercurial" /></a>
</div>
<ul>
--- a/mercurial/templates/paper/helptopics.tmpl Fri Apr 29 11:04:39 2011 +0200
+++ b/mercurial/templates/paper/helptopics.tmpl Fri Apr 29 11:10:11 2011 +0200
@@ -10,7 +10,7 @@
<div class="container">
<div class="menu">
<div class="logo">
-<a href="http://mercurial.selenic.com/">
+<a href="{logourl}">
<img src="{staticurl}hglogo.png" alt="mercurial" /></a>
</div>
<ul>
--- a/mercurial/templates/paper/index.tmpl Fri Apr 29 11:04:39 2011 +0200
+++ b/mercurial/templates/paper/index.tmpl Fri Apr 29 11:10:11 2011 +0200
@@ -5,7 +5,7 @@
<div class="container">
<div class="menu">
-<a href="http://mercurial.selenic.com/">
+<a href="{logourl}">
<img src="{staticurl}hglogo.png" width=75 height=90 border=0 alt="mercurial" /></a>
</div>
<div class="main">
--- a/mercurial/templates/paper/manifest.tmpl Fri Apr 29 11:04:39 2011 +0200
+++ b/mercurial/templates/paper/manifest.tmpl Fri Apr 29 11:10:11 2011 +0200
@@ -6,7 +6,7 @@
<div class="container">
<div class="menu">
<div class="logo">
-<a href="http://mercurial.selenic.com/">
+<a href="{logourl}">
<img src="{staticurl}hglogo.png" alt="mercurial" /></a>
</div>
<ul>
--- a/mercurial/templates/paper/map Fri Apr 29 11:04:39 2011 +0200
+++ b/mercurial/templates/paper/map Fri Apr 29 11:10:11 2011 +0200
@@ -93,14 +93,12 @@
filerename = '{file|escape}@'
filelogrename = '
- <tr>
- <th>base:</th>
- <td>
- <a href="{url}file/{node|short}/{file|urlescape}{sessionvars%urlparameter}">
- {file|escape}@{node|short}
- </a>
- </td>
- </tr>'
+ <span class="base">
+ base
+ <a href="{url}file/{node|short}/{file|urlescape}{sessionvars%urlparameter}">
+ {file|escape}@{node|short}
+ </a>
+ </span>'
fileannotateparent = '
<tr>
<td class="metatag">parent:</td>
--- a/mercurial/templates/paper/search.tmpl Fri Apr 29 11:04:39 2011 +0200
+++ b/mercurial/templates/paper/search.tmpl Fri Apr 29 11:10:11 2011 +0200
@@ -6,7 +6,7 @@
<div class="container">
<div class="menu">
<div class="logo">
-<a href="http://mercurial.selenic.com/">
+<a href="{logourl}">
<img src="{staticurl}hglogo.png" width=75 height=90 border=0 alt="mercurial"></a>
</div>
<ul>
--- a/mercurial/templates/paper/shortlog.tmpl Fri Apr 29 11:04:39 2011 +0200
+++ b/mercurial/templates/paper/shortlog.tmpl Fri Apr 29 11:10:11 2011 +0200
@@ -10,7 +10,7 @@
<div class="container">
<div class="menu">
<div class="logo">
-<a href="http://mercurial.selenic.com/">
+<a href="{logourl}">
<img src="{staticurl}hglogo.png" alt="mercurial" /></a>
</div>
<ul>
--- a/mercurial/templates/paper/tags.tmpl Fri Apr 29 11:04:39 2011 +0200
+++ b/mercurial/templates/paper/tags.tmpl Fri Apr 29 11:10:11 2011 +0200
@@ -10,7 +10,7 @@
<div class="container">
<div class="menu">
<div class="logo">
-<a href="http://mercurial.selenic.com/">
+<a href="{logourl}">
<img src="{staticurl}hglogo.png" alt="mercurial" /></a>
</div>
<ul>
--- a/mercurial/templates/raw/map Fri Apr 29 11:04:39 2011 +0200
+++ b/mercurial/templates/raw/map Fri Apr 29 11:10:11 2011 +0200
@@ -24,5 +24,7 @@
indexentry = '{url}\n'
tags = '{entries%tagentry}'
tagentry = '{tag} {node}\n'
+bookmarks = '{entries%bookmarkentry}'
+bookmarkentry = '{bookmark} {node}\n'
branches = '{entries%branchentry}'
branchentry = '{branch} {node} {status}\n'
--- a/mercurial/templates/spartan/footer.tmpl Fri Apr 29 11:04:39 2011 +0200
+++ b/mercurial/templates/spartan/footer.tmpl Fri Apr 29 11:10:11 2011 +0200
@@ -1,6 +1,6 @@
{motd}
<div class="logo">
-<a href="http://mercurial.selenic.com/">
+<a href="{logourl}">
<img src="{staticurl}hglogo.png" width=75 height=90 border=0 alt="mercurial"></a>
</div>
--- a/mercurial/templates/static/style-coal.css Fri Apr 29 11:04:39 2011 +0200
+++ b/mercurial/templates/static/style-coal.css Fri Apr 29 11:10:11 2011 +0200
@@ -173,6 +173,7 @@
.bigtable .age { width: 6em; }
.bigtable .author { width: 12em; }
.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;}
--- a/mercurial/templates/static/style-paper.css Fri Apr 29 11:04:39 2011 +0200
+++ b/mercurial/templates/static/style-paper.css Fri Apr 29 11:10:11 2011 +0200
@@ -164,6 +164,7 @@
.bigtable .age { width: 7em; }
.bigtable .author { width: 12em; }
.bigtable .description { }
+.bigtable .description .base { font-size: 70%; float: right; line-height: 1.66; }
.bigtable .node { width: 5em; font-family: monospace;}
.bigtable .permissions { width: 8em; text-align: left;}
.bigtable .size { width: 5em; text-align: right; }
@@ -209,6 +210,8 @@
border-left: 2px solid #999;
margin: 1em 0 1em 0;
padding: .3em;
+ white-space: pre;
+ font-family: monospace;
}
/* Graph */
--- a/mercurial/templates/template-vars.txt Fri Apr 29 11:04:39 2011 +0200
+++ b/mercurial/templates/template-vars.txt Fri Apr 29 11:10:11 2011 +0200
@@ -29,6 +29,7 @@
entries the entries relevant to the page
url base url of hgweb interface
+logourl base url of logo
staticurl base url for static resources
--- a/mercurial/ui.py Fri Apr 29 11:04:39 2011 +0200
+++ b/mercurial/ui.py Fri Apr 29 11:10:11 2011 +0200
@@ -7,7 +7,7 @@
from i18n import _
import errno, getpass, os, socket, sys, tempfile, traceback
-import config, util, error
+import config, scmutil, util, error, url
class ui(object):
def __init__(self, src=None):
@@ -32,7 +32,7 @@
# shared read-only environment
self.environ = os.environ
# we always trust global config files
- for f in util.rcpath():
+ for f in scmutil.rcpath():
self.readconfig(f, trust=True)
def copy(self):
@@ -111,7 +111,7 @@
% (n, p, self.configsource('paths', n)))
p = p.replace('%%', '%')
p = util.expandpath(p)
- if '://' not in p and not os.path.isabs(p):
+ if not url.hasscheme(p) and not os.path.isabs(p):
p = os.path.normpath(os.path.join(root, p))
c.set("paths", n, p)
@@ -273,20 +273,27 @@
cfg = self._data(untrusted)
for section in cfg.sections():
for name, value in self.configitems(section, untrusted):
- yield section, name, str(value).replace('\n', '\\n')
+ yield section, name, value
def plain(self):
'''is plain mode active?
- Plain mode means that all configuration variables which affect the
- behavior and output of Mercurial should be ignored. Additionally, the
- output should be stable, reproducible and suitable for use in scripts or
- applications.
+ Plain mode means that all configuration variables which affect
+ the behavior and output of Mercurial should be
+ ignored. Additionally, the output should be stable,
+ reproducible and suitable for use in scripts or applications.
+
+ The only way to trigger plain mode is by setting either the
+ `HGPLAIN' or `HGPLAINEXCEPT' environment variables.
- The only way to trigger plain mode is by setting the `HGPLAIN'
- environment variable.
+ The return value can either be False, True, or a list of
+ features that plain mode should not apply to (e.g., i18n,
+ progress, etc).
'''
- return 'HGPLAIN' in os.environ
+ if 'HGPLAIN' not in os.environ and 'HGPLAINEXCEPT' not in os.environ:
+ return False
+ exceptions = os.environ.get('HGPLAINEXCEPT', '').strip().split(',')
+ return exceptions or True
def username(self):
"""Return default username to be used in commits.
@@ -325,7 +332,7 @@
def expandpath(self, loc, default=None):
"""Return repository location relative to cwd or from [paths]"""
- if "://" in loc or os.path.isdir(os.path.join(loc, '.hg')):
+ if url.hasscheme(loc) or os.path.isdir(os.path.join(loc, '.hg')):
return loc
path = self.config('paths', loc)
@@ -483,7 +490,7 @@
self.write(msg, ' ', default, "\n")
return default
try:
- r = self._readline(msg + ' ')
+ r = self._readline(self.label(msg, 'ui.prompt') + ' ')
if not r:
return default
return r
--- a/mercurial/url.py Fri Apr 29 11:04:39 2011 +0200
+++ b/mercurial/url.py Fri Apr 29 11:10:11 2011 +0200
@@ -7,36 +7,272 @@
# 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, urlparse, httplib, os, re, socket, cStringIO
+import urllib, urllib2, httplib, os, socket, cStringIO, re
import __builtin__
from i18n import _
import keepalive, util
-def _urlunparse(scheme, netloc, path, params, query, fragment, url):
- '''Handle cases where urlunparse(urlparse(x://)) doesn't preserve the "//"'''
- result = urlparse.urlunparse((scheme, netloc, path, params, query, fragment))
- if (scheme and
- result.startswith(scheme + ':') and
- not result.startswith(scheme + '://') and
- url.startswith(scheme + '://')
- ):
- result = scheme + '://' + result[len(scheme + ':'):]
- return result
+class url(object):
+ """Reliable URL parser.
+
+ This parses URLs and provides attributes for the following
+ components:
+
+ <scheme>://<user>:<passwd>@<host>:<port>/<path>?<query>#<fragment>
+
+ Missing components are set to None. The only exception is
+ fragment, which is set to '' if present but empty.
+
+ If parsefragment is False, fragment is included in query. If
+ parsequery is False, query is included in path. If both are
+ False, both fragment and query are included in path.
+
+ See http://www.ietf.org/rfc/rfc2396.txt for more information.
+
+ Note that for backward compatibility reasons, bundle URLs do not
+ take host names. That means 'bundle://../' has a path of '../'.
+
+ Examples:
+
+ >>> url('http://www.ietf.org/rfc/rfc2396.txt')
+ <url scheme: 'http', host: 'www.ietf.org', path: 'rfc/rfc2396.txt'>
+ >>> url('ssh://[::1]:2200//home/joe/repo')
+ <url scheme: 'ssh', host: '[::1]', port: '2200', path: '/home/joe/repo'>
+ >>> url('file:///home/joe/repo')
+ <url scheme: 'file', path: '/home/joe/repo'>
+ >>> url('bundle:foo')
+ <url scheme: 'bundle', path: 'foo'>
+ >>> url('bundle://../foo')
+ <url scheme: 'bundle', path: '../foo'>
+ >>> url('c:\\\\foo\\\\bar')
+ <url path: 'c:\\\\foo\\\\bar'>
+
+ Authentication credentials:
+
+ >>> url('ssh://joe:xyz@x/repo')
+ <url scheme: 'ssh', user: 'joe', passwd: 'xyz', host: 'x', path: 'repo'>
+ >>> url('ssh://joe@x/repo')
+ <url scheme: 'ssh', user: 'joe', host: 'x', path: 'repo'>
+
+ Query strings and fragments:
+
+ >>> url('http://host/a?b#c')
+ <url scheme: 'http', host: 'host', path: 'a', query: 'b', fragment: 'c'>
+ >>> url('http://host/a?b#c', parsequery=False, parsefragment=False)
+ <url scheme: 'http', host: 'host', path: 'a?b#c'>
+ """
+
+ _safechars = "!~*'()+"
+ _safepchars = "/!~*'()+"
+ _matchscheme = re.compile(r'^[a-zA-Z0-9+.\-]+:').match
+
+ def __init__(self, path, parsequery=True, parsefragment=True):
+ # We slowly chomp away at path until we have only the path left
+ self.scheme = self.user = self.passwd = self.host = None
+ self.port = self.path = self.query = self.fragment = None
+ self._localpath = True
+ self._hostport = ''
+ self._origpath = path
+
+ # special case for Windows drive letters
+ if hasdriveletter(path):
+ self.path = path
+ return
+
+ # For compatibility reasons, we can't handle bundle paths as
+ # normal URLS
+ if path.startswith('bundle:'):
+ self.scheme = 'bundle'
+ path = path[7:]
+ if path.startswith('//'):
+ path = path[2:]
+ self.path = path
+ return
+
+ if self._matchscheme(path):
+ parts = path.split(':', 1)
+ if parts[0]:
+ self.scheme, path = parts
+ self._localpath = False
+
+ if not path:
+ path = None
+ if self._localpath:
+ self.path = ''
+ return
+ else:
+ if parsefragment and '#' in path:
+ path, self.fragment = path.split('#', 1)
+ if not path:
+ path = None
+ if self._localpath:
+ self.path = path
+ return
+
+ if parsequery and '?' in path:
+ path, self.query = path.split('?', 1)
+ if not path:
+ path = None
+ if not self.query:
+ self.query = None
+
+ # // is required to specify a host/authority
+ if path and path.startswith('//'):
+ parts = path[2:].split('/', 1)
+ if len(parts) > 1:
+ self.host, path = parts
+ path = path
+ else:
+ self.host = parts[0]
+ path = None
+ if not self.host:
+ self.host = None
+ if path:
+ path = '/' + path
+
+ if self.host and '@' in self.host:
+ self.user, self.host = self.host.rsplit('@', 1)
+ if ':' in self.user:
+ self.user, self.passwd = self.user.split(':', 1)
+ if not self.host:
+ self.host = None
-def hidepassword(url):
+ # Don't split on colons in IPv6 addresses without ports
+ if (self.host and ':' in self.host and
+ not (self.host.startswith('[') and self.host.endswith(']'))):
+ self._hostport = self.host
+ self.host, self.port = self.host.rsplit(':', 1)
+ if not self.host:
+ self.host = None
+
+ if (self.host and self.scheme == 'file' and
+ self.host not in ('localhost', '127.0.0.1', '[::1]')):
+ raise util.Abort(_('file:// URLs can only refer to localhost'))
+
+ self.path = path
+
+ for a in ('user', 'passwd', 'host', 'port',
+ 'path', 'query', 'fragment'):
+ v = getattr(self, a)
+ if v is not None:
+ setattr(self, a, urllib.unquote(v))
+
+ def __repr__(self):
+ attrs = []
+ for a in ('scheme', 'user', 'passwd', 'host', 'port', 'path',
+ 'query', 'fragment'):
+ v = getattr(self, a)
+ if v is not None:
+ attrs.append('%s: %r' % (a, v))
+ return '<url %s>' % ', '.join(attrs)
+
+ def __str__(self):
+ """Join the URL's components back into a URL string.
+
+ Examples:
+
+ >>> str(url('http://user:pw@host:80/?foo#bar'))
+ 'http://user:pw@host:80/?foo#bar'
+ >>> str(url('ssh://user:pw@[::1]:2200//home/joe#'))
+ 'ssh://user:pw@[::1]:2200//home/joe#'
+ >>> str(url('http://localhost:80//'))
+ 'http://localhost:80//'
+ >>> str(url('http://localhost:80/'))
+ 'http://localhost:80/'
+ >>> str(url('http://localhost:80'))
+ 'http://localhost:80/'
+ >>> str(url('bundle:foo'))
+ 'bundle:foo'
+ >>> str(url('bundle://../foo'))
+ 'bundle:../foo'
+ >>> str(url('path'))
+ 'path'
+ """
+ if self._localpath:
+ s = self.path
+ if self.scheme == 'bundle':
+ s = 'bundle:' + s
+ if self.fragment:
+ s += '#' + self.fragment
+ return s
+
+ s = self.scheme + ':'
+ if (self.user or self.passwd or self.host or
+ self.scheme and not self.path):
+ s += '//'
+ if self.user:
+ s += urllib.quote(self.user, safe=self._safechars)
+ if self.passwd:
+ s += ':' + urllib.quote(self.passwd, safe=self._safechars)
+ if self.user or self.passwd:
+ s += '@'
+ if self.host:
+ if not (self.host.startswith('[') and self.host.endswith(']')):
+ s += urllib.quote(self.host)
+ else:
+ s += self.host
+ if self.port:
+ s += ':' + urllib.quote(self.port)
+ if self.host:
+ s += '/'
+ if self.path:
+ s += urllib.quote(self.path, safe=self._safepchars)
+ if self.query:
+ s += '?' + urllib.quote(self.query, safe=self._safepchars)
+ if self.fragment is not None:
+ s += '#' + urllib.quote(self.fragment, safe=self._safepchars)
+ return s
+
+ def authinfo(self):
+ user, passwd = self.user, self.passwd
+ try:
+ self.user, self.passwd = None, None
+ s = str(self)
+ finally:
+ self.user, self.passwd = user, passwd
+ if not self.user:
+ return (s, None)
+ return (s, (None, (str(self), self.host),
+ self.user, self.passwd or ''))
+
+ def localpath(self):
+ if self.scheme == 'file' or self.scheme == 'bundle':
+ path = self.path or '/'
+ # For Windows, we need to promote hosts containing drive
+ # letters to paths with drive letters.
+ if hasdriveletter(self._hostport):
+ path = self._hostport + '/' + self.path
+ elif self.host is not None and self.path:
+ path = '/' + path
+ # We also need to handle the case of file:///C:/, which
+ # should return C:/, not /C:/.
+ elif hasdriveletter(path):
+ # Strip leading slash from paths with drive names
+ return path[1:]
+ return path
+ return self._origpath
+
+def hasscheme(path):
+ return bool(url(path).scheme)
+
+def hasdriveletter(path):
+ return path[1:2] == ':' and path[0:1].isalpha()
+
+def localpath(path):
+ return url(path, parsequery=False, parsefragment=False).localpath()
+
+def hidepassword(u):
'''hide user credential in a url string'''
- if url.startswith("ssh://"):
- # urllib doesn't know about ssh urls
- return re.sub(r'(ssh://[^/]+):[^/]+(@.*)', r'\1:***\2', url)
- scheme, netloc, path, params, query, fragment = urlparse.urlparse(url)
- netloc = re.sub('([^:]*):([^@]*)@(.*)', r'\1:***@\3', netloc)
- return _urlunparse(scheme, netloc, path, params, query, fragment, url)
+ u = url(u)
+ if u.passwd:
+ u.passwd = '***'
+ return str(u)
-def removeauth(url):
+def removeauth(u):
'''remove all authentication information from a url string'''
- scheme, netloc, path, params, query, fragment = urlparse.urlparse(url)
- netloc = netloc[netloc.find('@')+1:]
- return _urlunparse(scheme, netloc, path, params, query, fragment, url)
+ u = url(u)
+ u.user = u.passwd = None
+ return str(u)
def netlocsplit(netloc):
'''split [user[:passwd]@]host[:port] into 4-tuple.'''
@@ -195,14 +431,10 @@
if not (proxyurl.startswith('http:') or
proxyurl.startswith('https:')):
proxyurl = 'http://' + proxyurl + '/'
- snpqf = urlparse.urlsplit(proxyurl)
- proxyscheme, proxynetloc, proxypath, proxyquery, proxyfrag = snpqf
- hpup = netlocsplit(proxynetloc)
-
- proxyhost, proxyport, proxyuser, proxypasswd = hpup
- if not proxyuser:
- proxyuser = ui.config("http_proxy", "user")
- proxypasswd = ui.config("http_proxy", "passwd")
+ proxy = url(proxyurl)
+ if not proxy.user:
+ proxy.user = ui.config("http_proxy", "user")
+ proxy.passwd = ui.config("http_proxy", "passwd")
# see if we should use a proxy for this url
no_list = ["localhost", "127.0.0.1"]
@@ -217,13 +449,10 @@
else:
self.no_list = no_list
- proxyurl = urlparse.urlunsplit((
- proxyscheme, netlocunsplit(proxyhost, proxyport,
- proxyuser, proxypasswd or ''),
- proxypath, proxyquery, proxyfrag))
+ proxyurl = str(proxy)
proxies = {'http': proxyurl, 'https': proxyurl}
ui.debug('proxying through http://%s:%s\n' %
- (proxyhost, proxyport))
+ (proxy.host, proxy.port))
else:
proxies = {}
@@ -390,13 +619,9 @@
new_tunnel = False
if new_tunnel or tunnel_host == req.get_full_url(): # has proxy
- urlparts = urlparse.urlparse(tunnel_host)
- if new_tunnel or urlparts[0] == 'https': # only use CONNECT for HTTPS
- realhostport = urlparts[1]
- if realhostport[-1] == ']' or ':' not in realhostport:
- realhostport += ':443'
-
- h.realhostport = realhostport
+ u = url(tunnel_host)
+ if new_tunnel or u.scheme == 'https': # only use CONNECT for HTTPS
+ h.realhostport = ':'.join([u.host, (u.port or '443')])
h.headers = req.headers.copy()
h.headers.update(handler.parent.addheaders)
return
@@ -694,31 +919,6 @@
return urllib2.HTTPBasicAuthHandler.http_error_auth_reqed(
self, auth_header, host, req, headers)
-def getauthinfo(path):
- scheme, netloc, urlpath, query, frag = urlparse.urlsplit(path)
- if not urlpath:
- urlpath = '/'
- if scheme != 'file':
- # XXX: why are we quoting the path again with some smart
- # heuristic here? Anyway, it cannot be done with file://
- # urls since path encoding is os/fs dependent (see
- # urllib.pathname2url() for details).
- urlpath = quotepath(urlpath)
- host, port, user, passwd = netlocsplit(netloc)
-
- # urllib cannot handle URLs with embedded user or passwd
- url = urlparse.urlunsplit((scheme, netlocunsplit(host, port),
- urlpath, query, frag))
- if user:
- netloc = host
- if port:
- netloc += ':' + port
- # Python < 2.4.3 uses only the netloc to search for a password
- authinfo = (None, (url, netloc), user, passwd or '')
- else:
- authinfo = None
- return url, authinfo
-
handlerfuncs = []
def opener(ui, authinfo=None):
@@ -749,17 +949,13 @@
opener.addheaders.append(('Accept', 'application/mercurial-0.1'))
return opener
-scheme_re = re.compile(r'^([a-zA-Z0-9+-.]+)://')
-
-def open(ui, url, data=None):
- scheme = None
- m = scheme_re.search(url)
- if m:
- scheme = m.group(1).lower()
- if not scheme:
- path = util.normpath(os.path.abspath(url))
- url = 'file://' + urllib.pathname2url(path)
+def open(ui, url_, data=None):
+ u = url(url_)
+ if u.scheme:
+ u.scheme = u.scheme.lower()
+ url_, authinfo = u.authinfo()
+ else:
+ path = util.normpath(os.path.abspath(url_))
+ url_ = 'file://' + urllib.pathname2url(path)
authinfo = None
- else:
- url, authinfo = getauthinfo(url)
- return opener(ui, authinfo).open(url, data)
+ return opener(ui, authinfo).open(url_, data)
--- a/mercurial/util.py Fri Apr 29 11:04:39 2011 +0200
+++ b/mercurial/util.py Fri Apr 29 11:10:11 2011 +0200
@@ -16,7 +16,7 @@
from i18n import _
import error, osutil, encoding
import errno, re, shutil, sys, tempfile, traceback
-import os, stat, time, calendar, textwrap, unicodedata, signal
+import os, time, calendar, textwrap, unicodedata, signal
import imp, socket
# Python compatibility
@@ -206,12 +206,12 @@
try:
if inname:
os.unlink(inname)
- except:
+ except OSError:
pass
try:
if outname:
os.unlink(outname)
- except:
+ except OSError:
pass
filtertable = {
@@ -295,54 +295,6 @@
b.reverse()
return os.sep.join((['..'] * len(a)) + b) or '.'
-def canonpath(root, cwd, myname, auditor=None):
- """return the canonical path of myname, given cwd and root"""
- if endswithsep(root):
- rootsep = root
- else:
- rootsep = root + os.sep
- name = myname
- if not os.path.isabs(name):
- name = os.path.join(root, cwd, name)
- name = os.path.normpath(name)
- if auditor is None:
- auditor = path_auditor(root)
- if name != rootsep and name.startswith(rootsep):
- name = name[len(rootsep):]
- auditor(name)
- return pconvert(name)
- elif name == root:
- return ''
- else:
- # Determine whether `name' is in the hierarchy at or beneath `root',
- # by iterating name=dirname(name) until that causes no change (can't
- # check name == '/', because that doesn't work on windows). For each
- # `name', compare dev/inode numbers. If they match, the list `rel'
- # holds the reversed list of components making up the relative file
- # name we want.
- root_st = os.stat(root)
- rel = []
- while True:
- try:
- name_st = os.stat(name)
- except OSError:
- break
- if samestat(name_st, root_st):
- if not rel:
- # name was actually the same as root (maybe a symlink)
- return ''
- rel.reverse()
- name = os.path.join(*rel)
- auditor(name)
- return pconvert(name)
- dirname, basename = os.path.split(name)
- rel.append(basename)
- if dirname == name:
- break
- name = dirname
-
- raise Abort('%s not under root' % myname)
-
_hgexecutable = None
def main_is_frozen():
@@ -438,6 +390,9 @@
return check
+def makedir(path, notindexed):
+ os.mkdir(path)
+
def unlinkpath(f):
"""unlink and remove the directory if it is empty"""
os.unlink(f)
@@ -452,7 +407,7 @@
if os.path.islink(src):
try:
os.unlink(dest)
- except:
+ except OSError:
pass
os.symlink(os.readlink(src), dest)
else:
@@ -490,76 +445,47 @@
return hardlink, num
-class path_auditor(object):
- '''ensure that a filesystem path contains no banned components.
- the following properties of a path are checked:
-
- - ends with a directory separator
- - under top-level .hg
- - starts at the root of a windows drive
- - contains ".."
- - traverses a symlink (e.g. a/symlink_here/b)
- - inside a nested repository (a callback can be used to approve
- some nested repositories, e.g., subrepositories)
- '''
-
- def __init__(self, root, callback=None):
- self.audited = set()
- self.auditeddir = set()
- self.root = root
- self.callback = callback
+_windows_reserved_filenames = '''con prn aux nul
+ com1 com2 com3 com4 com5 com6 com7 com8 com9
+ lpt1 lpt2 lpt3 lpt4 lpt5 lpt6 lpt7 lpt8 lpt9'''.split()
+_windows_reserved_chars = ':*?"<>|'
+def checkwinfilename(path):
+ '''Check that the base-relative path is a valid filename on Windows.
+ Returns None if the path is ok, or a UI string describing the problem.
- def __call__(self, path):
- if path in self.audited:
- return
- # AIX ignores "/" at end of path, others raise EISDIR.
- if endswithsep(path):
- raise Abort(_("path ends in directory separator: %s") % path)
- normpath = os.path.normcase(path)
- parts = splitpath(normpath)
- if (os.path.splitdrive(path)[0]
- or parts[0].lower() in ('.hg', '.hg.', '')
- or os.pardir in parts):
- raise Abort(_("path contains illegal component: %s") % path)
- if '.hg' in path.lower():
- lparts = [p.lower() for p in parts]
- for p in '.hg', '.hg.':
- if p in lparts[1:]:
- pos = lparts.index(p)
- base = os.path.join(*parts[:pos])
- raise Abort(_('path %r is inside repo %r') % (path, base))
- def check(prefix):
- curpath = os.path.join(self.root, prefix)
- try:
- st = os.lstat(curpath)
- except OSError, err:
- # EINVAL can be raised as invalid path syntax under win32.
- # They must be ignored for patterns can be checked too.
- if err.errno not in (errno.ENOENT, errno.ENOTDIR, errno.EINVAL):
- raise
- else:
- if stat.S_ISLNK(st.st_mode):
- raise Abort(_('path %r traverses symbolic link %r') %
- (path, prefix))
- elif (stat.S_ISDIR(st.st_mode) and
- os.path.isdir(os.path.join(curpath, '.hg'))):
- if not self.callback or not self.callback(curpath):
- raise Abort(_('path %r is inside repo %r') %
- (path, prefix))
- parts.pop()
- prefixes = []
- while parts:
- prefix = os.sep.join(parts)
- if prefix in self.auditeddir:
- break
- check(prefix)
- prefixes.append(prefix)
- parts.pop()
-
- self.audited.add(path)
- # only add prefixes to the cache after checking everything: we don't
- # want to add "foo/bar/baz" before checking if there's a "foo/.hg"
- self.auditeddir.update(prefixes)
+ >>> checkwinfilename("just/a/normal/path")
+ >>> checkwinfilename("foo/bar/con.xml")
+ "filename contains 'con', which is reserved on Windows"
+ >>> checkwinfilename("foo/con.xml/bar")
+ "filename contains 'con', which is reserved on Windows"
+ >>> checkwinfilename("foo/bar/xml.con")
+ >>> checkwinfilename("foo/bar/AUX/bla.txt")
+ "filename contains 'AUX', which is reserved on Windows"
+ >>> checkwinfilename("foo/bar/bla:.txt")
+ "filename contains ':', which is reserved on Windows"
+ >>> checkwinfilename("foo/bar/b\07la.txt")
+ "filename contains '\\\\x07', which is invalid on Windows"
+ >>> checkwinfilename("foo/bar/bla ")
+ "filename ends with ' ', which is not allowed on Windows"
+ '''
+ for n in path.replace('\\', '/').split('/'):
+ if not n:
+ continue
+ for c in n:
+ if c in _windows_reserved_chars:
+ return _("filename contains '%s', which is reserved "
+ "on Windows") % c
+ if ord(c) <= 31:
+ return _("filename contains %r, which is invalid "
+ "on Windows") % c
+ base = n.split('.')[0]
+ if base and base.lower() in _windows_reserved_filenames:
+ return _("filename contains '%s', which is reserved "
+ "on Windows") % base
+ t = n[-1]
+ if t in '. ':
+ return _("filename ends with '%s', which is not allowed "
+ "on Windows") % t
def lookup_reg(key, name=None, scope=None):
return None
@@ -573,6 +499,7 @@
pass
if os.name == 'nt':
+ checkosfilename = checkwinfilename
from windows import *
else:
from posix import *
@@ -629,7 +556,7 @@
if s2 == s1:
return False
return True
- except:
+ except OSError:
return True
_fspathcache = {}
@@ -680,45 +607,6 @@
return ''.join(result)
-def checkexec(path):
- """
- Check whether the given path is on a filesystem with UNIX-like exec flags
-
- Requires a directory (like /foo/.hg)
- """
-
- # VFAT on some Linux versions can flip mode but it doesn't persist
- # a FS remount. Frequently we can detect it if files are created
- # with exec bit on.
-
- try:
- EXECFLAGS = stat.S_IXUSR | stat.S_IXGRP | stat.S_IXOTH
- fh, fn = tempfile.mkstemp(dir=path, prefix='hg-checkexec-')
- try:
- os.close(fh)
- m = os.stat(fn).st_mode & 0777
- new_file_has_exec = m & EXECFLAGS
- os.chmod(fn, m ^ EXECFLAGS)
- exec_flags_cannot_flip = ((os.stat(fn).st_mode & 0777) == m)
- finally:
- os.unlink(fn)
- except (IOError, OSError):
- # we don't care, the user probably won't be able to commit anyway
- return False
- return not (new_file_has_exec or exec_flags_cannot_flip)
-
-def checklink(path):
- """check whether the given path is on a symlink-capable filesystem"""
- # mktemp is not racy because symlink creation will fail if the
- # file already exists
- name = tempfile.mktemp(dir=path, prefix='hg-checklink-')
- try:
- os.symlink(".", name)
- os.unlink(name)
- return True
- except (OSError, AttributeError):
- return False
-
def checknlink(testfile):
'''check whether hardlink count reporting works properly'''
@@ -769,7 +657,18 @@
def gui():
'''Are we running in a GUI?'''
- return os.name == "nt" or os.name == "mac" or os.environ.get("DISPLAY")
+ if sys.platform == 'darwin':
+ if 'SSH_CONNECTION' in os.environ:
+ # handle SSH access to a box where the user is logged in
+ return False
+ elif getattr(osutil, 'isgui', None):
+ # check if a CoreGraphics session is available
+ return osutil.isgui()
+ else:
+ # pure build; use a safe default
+ return True
+ else:
+ return os.name == "nt" or os.environ.get("DISPLAY")
def mktempcopy(name, emptyok=False, createmode=None):
"""Create a temporary file with the same contents from name
@@ -820,38 +719,41 @@
return temp
class atomictempfile(object):
- """file-like object that atomically updates a file
+ '''writeable file object that atomically updates a file
- All writes will be redirected to a temporary copy of the original
- file. When rename is called, the copy is renamed to the original
- name, making the changes visible.
- """
+ All writes will go to a temporary copy of the original file. Call
+ rename() when you are done writing, and atomictempfile will rename
+ the temporary copy to the original name, making the changes visible.
+
+ Unlike other file-like objects, close() discards your writes by
+ simply deleting the temporary file.
+ '''
def __init__(self, name, mode='w+b', createmode=None):
- self.__name = name
- self._fp = None
- self.temp = mktempcopy(name, emptyok=('w' in mode),
- createmode=createmode)
- self._fp = posixfile(self.temp, mode)
+ self.__name = name # permanent name
+ self._tempname = mktempcopy(name, emptyok=('w' in mode),
+ createmode=createmode)
+ self._fp = posixfile(self._tempname, mode)
- def __getattr__(self, name):
- return getattr(self._fp, name)
+ # delegated methods
+ self.write = self._fp.write
+ self.fileno = self._fp.fileno
def rename(self):
if not self._fp.closed:
self._fp.close()
- rename(self.temp, localpath(self.__name))
+ rename(self._tempname, localpath(self.__name))
def close(self):
- if not self._fp:
- return
if not self._fp.closed:
try:
- os.unlink(self.temp)
- except: pass
+ os.unlink(self._tempname)
+ except OSError:
+ pass
self._fp.close()
def __del__(self):
- self.close()
+ if hasattr(self, '_fp'): # constructor actually did something
+ self.close()
def makedirs(name, mode=None):
"""recursive directory creation with parent mode inheritance"""
@@ -869,98 +771,6 @@
makedirs(parent, mode)
makedirs(name, mode)
-class opener(object):
- """Open files relative to a base directory
-
- This class is used to hide the details of COW semantics and
- remote file access from higher level code.
- """
- def __init__(self, base, audit=True):
- self.base = base
- if audit:
- self.auditor = path_auditor(base)
- else:
- self.auditor = always
- self.createmode = None
- self._trustnlink = None
-
- @propertycache
- def _can_symlink(self):
- return checklink(self.base)
-
- def _fixfilemode(self, name):
- if self.createmode is None:
- return
- os.chmod(name, self.createmode & 0666)
-
- def __call__(self, path, mode="r", text=False, atomictemp=False):
- self.auditor(path)
- f = os.path.join(self.base, path)
-
- if not text and "b" not in mode:
- mode += "b" # for that other OS
-
- nlink = -1
- dirname, basename = os.path.split(f)
- # If basename is empty, then the path is malformed because it points
- # to a directory. Let the posixfile() call below raise IOError.
- if basename and mode not in ('r', 'rb'):
- if atomictemp:
- if not os.path.isdir(dirname):
- makedirs(dirname, self.createmode)
- return atomictempfile(f, mode, self.createmode)
- try:
- if 'w' in mode:
- unlink(f)
- nlink = 0
- else:
- # nlinks() may behave differently for files on Windows
- # shares if the file is open.
- fd = posixfile(f)
- nlink = nlinks(f)
- if nlink < 1:
- nlink = 2 # force mktempcopy (issue1922)
- fd.close()
- except (OSError, IOError), e:
- if e.errno != errno.ENOENT:
- raise
- nlink = 0
- if not os.path.isdir(dirname):
- makedirs(dirname, self.createmode)
- if nlink > 0:
- if self._trustnlink is None:
- self._trustnlink = nlink > 1 or checknlink(f)
- if nlink > 1 or not self._trustnlink:
- rename(mktempcopy(f), f)
- fp = posixfile(f, mode)
- if nlink == 0:
- self._fixfilemode(f)
- return fp
-
- def symlink(self, src, dst):
- self.auditor(dst)
- linkname = os.path.join(self.base, dst)
- try:
- os.unlink(linkname)
- except OSError:
- pass
-
- dirname = os.path.dirname(linkname)
- if not os.path.exists(dirname):
- makedirs(dirname, self.createmode)
-
- if self._can_symlink:
- try:
- os.symlink(src, linkname)
- except OSError, err:
- raise OSError(err.errno, _('could not symlink to %r: %s') %
- (src, err.strerror), linkname)
- else:
- f = self(dst, "w")
- f.write(src)
- f.close()
- self._fixfilemode(dst)
-
class chunkbuffer(object):
"""Allow arbitrary sized chunks of data to be efficiently read from an
iterator over chunks of arbitrary size."""
@@ -1204,10 +1014,17 @@
return parsedate(date, extendeddateformats, d)[0]
date = date.strip()
- if date[0] == "<":
+
+ if not date:
+ raise Abort(_("dates cannot consist entirely of whitespace"))
+ elif date[0] == "<":
+ if not date[1:]:
+ raise Abort(_("invalid day spec, use '<DATE'"))
when = upper(date[1:])
return lambda x: x <= when
elif date[0] == ">":
+ if not date[1:]:
+ raise Abort(_("invalid day spec, use '>DATE'"))
when = lower(date[1:])
return lambda x: x >= when
elif date[0] == "-":
@@ -1215,6 +1032,9 @@
days = int(date[1:])
except ValueError:
raise Abort(_("invalid day spec: %s") % date[1:])
+ if days < 0:
+ raise Abort(_("%s must be nonnegative (see 'hg help dates')")
+ % date[1:])
when = makedate()[0] - days * 3600 * 24
return lambda x: x >= when
elif " to " in date:
@@ -1266,86 +1086,6 @@
except (UnicodeDecodeError, UnicodeEncodeError):
return _ellipsis(text, maxlength)[0]
-def walkrepos(path, followsym=False, seen_dirs=None, recurse=False):
- '''yield every hg repository under path, recursively.'''
- def errhandler(err):
- if err.filename == path:
- raise err
- if followsym and hasattr(os.path, 'samestat'):
- def _add_dir_if_not_there(dirlst, dirname):
- match = False
- samestat = os.path.samestat
- dirstat = os.stat(dirname)
- for lstdirstat in dirlst:
- if samestat(dirstat, lstdirstat):
- match = True
- break
- if not match:
- dirlst.append(dirstat)
- return not match
- else:
- followsym = False
-
- if (seen_dirs is None) and followsym:
- seen_dirs = []
- _add_dir_if_not_there(seen_dirs, path)
- for root, dirs, files in os.walk(path, topdown=True, onerror=errhandler):
- dirs.sort()
- if '.hg' in dirs:
- yield root # found a repository
- qroot = os.path.join(root, '.hg', 'patches')
- if os.path.isdir(os.path.join(qroot, '.hg')):
- yield qroot # we have a patch queue repo here
- if recurse:
- # avoid recursing inside the .hg directory
- dirs.remove('.hg')
- else:
- dirs[:] = [] # don't descend further
- elif followsym:
- newdirs = []
- for d in dirs:
- fname = os.path.join(root, d)
- if _add_dir_if_not_there(seen_dirs, fname):
- if os.path.islink(fname):
- for hgname in walkrepos(fname, True, seen_dirs):
- yield hgname
- else:
- newdirs.append(d)
- dirs[:] = newdirs
-
-_rcpath = None
-
-def os_rcpath():
- '''return default os-specific hgrc search path'''
- path = system_rcpath()
- path.extend(user_rcpath())
- path = [os.path.normpath(f) for f in path]
- return path
-
-def rcpath():
- '''return hgrc search path. if env var HGRCPATH is set, use it.
- for each item in path, if directory, use files ending in .rc,
- else use item.
- make HGRCPATH empty to only look in .hg/hgrc of current repo.
- if no HGRCPATH, use default os-specific path.'''
- global _rcpath
- if _rcpath is None:
- if 'HGRCPATH' in os.environ:
- _rcpath = []
- for p in os.environ['HGRCPATH'].split(os.pathsep):
- if not p:
- continue
- p = expandpath(p)
- if os.path.isdir(p):
- for f, kind in osutil.listdir(p):
- if f.endswith('.rc'):
- _rcpath.append(os.path.join(p, f))
- else:
- _rcpath.append(p)
- else:
- _rcpath = os_rcpath()
- return _rcpath
-
def bytecount(nbytes):
'''return byte count formatted as readable string, with units'''
@@ -1367,26 +1107,6 @@
return format % (nbytes / float(divisor))
return units[-1][2] % nbytes
-def drop_scheme(scheme, path):
- sc = scheme + ':'
- if path.startswith(sc):
- path = path[len(sc):]
- if path.startswith('//'):
- if scheme == 'file':
- i = path.find('/', 2)
- if i == -1:
- return ''
- # On Windows, absolute paths are rooted at the current drive
- # root. On POSIX they are rooted at the file system root.
- if os.name == 'nt':
- droot = os.path.splitdrive(os.getcwd())[0] + '/'
- path = os.path.join(droot, path[i + 1:])
- else:
- path = path[i:]
- else:
- path = path[2:]
- return path
-
def uirepr(s):
# Avoid double backslash in Windows path repr()
return repr(s).replace('\\\\', '\\')
--- a/mercurial/win32.py Fri Apr 29 11:04:39 2011 +0200
+++ b/mercurial/win32.py Fri Apr 29 11:10:11 2011 +0200
@@ -6,7 +6,7 @@
# GNU General Public License version 2 or any later version.
import encoding
-import ctypes, errno, os, struct, subprocess
+import ctypes, errno, os, struct, subprocess, random
_kernel32 = ctypes.windll.kernel32
@@ -56,6 +56,10 @@
_OPEN_EXISTING = 3
+# SetFileAttributes
+_FILE_ATTRIBUTE_NORMAL = 0x80
+_FILE_ATTRIBUTE_NOT_CONTENT_INDEXED = 0x2000
+
# Process Security and Access Rights
_PROCESS_QUERY_INFORMATION = 0x0400
@@ -137,7 +141,10 @@
_kernel32.CloseHandle(fh)
def os_link(src, dst):
- if not _kernel32.CreateHardLinkA(dst, src, None):
+ try:
+ if not _kernel32.CreateHardLinkA(dst, src, None):
+ _raiseoserror(src)
+ except AttributeError: # Wine doesn't support this function
_raiseoserror(src)
def nlinks(name):
@@ -316,3 +323,54 @@
raise ctypes.WinError()
return pi.dwProcessId
+
+def unlink(f):
+ '''try to implement POSIX' unlink semantics on Windows'''
+
+ # POSIX allows to unlink and rename open files. Windows has serious
+ # problems with doing that:
+ # - Calling os.unlink (or os.rename) on a file f fails if f or any
+ # hardlinked copy of f has been opened with Python's open(). There is no
+ # way such a file can be deleted or renamed on Windows (other than
+ # scheduling the delete or rename for the next reboot).
+ # - Calling os.unlink on a file that has been opened with Mercurial's
+ # posixfile (or comparable methods) will delay the actual deletion of
+ # the file for as long as the file is held open. The filename is blocked
+ # during that time and cannot be used for recreating a new file under
+ # that same name ("zombie file"). Directories containing such zombie files
+ # cannot be removed or moved.
+ # A file that has been opened with posixfile can be renamed, so we rename
+ # f to a random temporary name before calling os.unlink on it. This allows
+ # callers to recreate f immediately while having other readers do their
+ # implicit zombie filename blocking on a temporary name.
+
+ for tries in xrange(10):
+ temp = '%s-%08x' % (f, random.randint(0, 0xffffffff))
+ try:
+ os.rename(f, temp) # raises OSError EEXIST if temp exists
+ break
+ except OSError, e:
+ if e.errno != errno.EEXIST:
+ raise
+ else:
+ raise IOError, (errno.EEXIST, "No usable temporary filename found")
+
+ try:
+ os.unlink(temp)
+ except OSError:
+ # The unlink might have failed because the READONLY attribute may heave
+ # been set on the original file. Rename works fine with READONLY set,
+ # but not os.unlink. Reset all attributes and try again.
+ _kernel32.SetFileAttributesA(temp, _FILE_ATTRIBUTE_NORMAL)
+ try:
+ os.unlink(temp)
+ except OSError:
+ # The unlink might have failed due to some very rude AV-Scanners.
+ # Leaking a tempfile is the lesser evil than aborting here and
+ # leaving some potentially serious inconsistencies.
+ pass
+
+def makedir(path, notindexed):
+ os.mkdir(path)
+ if notindexed:
+ _kernel32.SetFileAttributesA(path, _FILE_ATTRIBUTE_NOT_CONTENT_INDEXED)
--- a/mercurial/windows.py Fri Apr 29 11:04:39 2011 +0200
+++ b/mercurial/windows.py Fri Apr 29 11:10:11 2011 +0200
@@ -7,7 +7,7 @@
from i18n import _
import osutil, error
-import errno, msvcrt, os, re, sys, random, subprocess
+import errno, msvcrt, os, re, sys, subprocess
nulldev = 'NUL:'
umask = 002
@@ -32,7 +32,8 @@
def close(self):
try:
self.fp.close()
- except: pass
+ except IOError:
+ pass
def write(self, s):
try:
@@ -73,49 +74,6 @@
def openhardlinks():
return not _is_win_9x()
-_HKEY_LOCAL_MACHINE = 0x80000002L
-
-def system_rcpath():
- '''return default os-specific hgrc search path'''
- rcpath = []
- filename = executable_path()
- # Use mercurial.ini found in directory with hg.exe
- progrc = os.path.join(os.path.dirname(filename), 'mercurial.ini')
- if os.path.isfile(progrc):
- rcpath.append(progrc)
- return rcpath
- # Use hgrc.d found in directory with hg.exe
- progrcd = os.path.join(os.path.dirname(filename), 'hgrc.d')
- if os.path.isdir(progrcd):
- for f, kind in osutil.listdir(progrcd):
- if f.endswith('.rc'):
- rcpath.append(os.path.join(progrcd, f))
- return rcpath
- # else look for a system rcpath in the registry
- value = lookup_reg('SOFTWARE\\Mercurial', None, _HKEY_LOCAL_MACHINE)
- if not isinstance(value, str) or not value:
- return rcpath
- value = value.replace('/', os.sep)
- for p in value.split(os.pathsep):
- if p.lower().endswith('mercurial.ini'):
- rcpath.append(p)
- elif os.path.isdir(p):
- for f, kind in osutil.listdir(p):
- if f.endswith('.rc'):
- rcpath.append(os.path.join(p, f))
- return rcpath
-
-def user_rcpath():
- '''return os-specific hgrc search path to the user dir'''
- home = os.path.expanduser('~')
- path = [os.path.join(home, 'mercurial.ini'),
- os.path.join(home, '.hgrc')]
- userprofile = os.environ.get('USERPROFILE')
- if userprofile:
- path.append(os.path.join(userprofile, 'mercurial.ini'))
- path.append(os.path.join(userprofile, '.hgrc'))
- return path
-
def parse_patch_output(output_line):
"""parses the output produced by patch and returns the filename"""
pf = output_line[14:]
@@ -132,6 +90,12 @@
def set_flags(f, l, x):
pass
+def checkexec(path):
+ return False
+
+def checklink(path):
+ return False
+
def set_binary(fd):
# When run without console, pipes may expose invalid
# fileno(), usually set to -1.
@@ -280,59 +244,19 @@
if osutil.listdir(head):
return
os.rmdir(head)
- except:
+ except (ValueError, OSError):
break
head, tail = os.path.split(head)
def unlinkpath(f):
"""unlink and remove the directory if it is empty"""
- os.unlink(f)
+ unlink(f)
# try removing directories that might now be empty
try:
_removedirs(os.path.dirname(f))
except OSError:
pass
-def unlink(f):
- '''try to implement POSIX' unlink semantics on Windows'''
-
- # POSIX allows to unlink and rename open files. Windows has serious
- # problems with doing that:
- # - Calling os.unlink (or os.rename) on a file f fails if f or any
- # hardlinked copy of f has been opened with Python's open(). There is no
- # way such a file can be deleted or renamed on Windows (other than
- # scheduling the delete or rename for the next reboot).
- # - Calling os.unlink on a file that has been opened with Mercurial's
- # posixfile (or comparable methods) will delay the actual deletion of
- # the file for as long as the file is held open. The filename is blocked
- # during that time and cannot be used for recreating a new file under
- # that same name ("zombie file"). Directories containing such zombie files
- # cannot be removed or moved.
- # A file that has been opened with posixfile can be renamed, so we rename
- # f to a random temporary name before calling os.unlink on it. This allows
- # callers to recreate f immediately while having other readers do their
- # implicit zombie filename blocking on a temporary name.
-
- for tries in xrange(10):
- temp = '%s-%08x' % (f, random.randint(0, 0xffffffff))
- try:
- os.rename(f, temp) # raises OSError EEXIST if temp exists
- break
- except OSError, e:
- if e.errno != errno.EEXIST:
- raise
- else:
- raise IOError, (errno.EEXIST, "No usable temporary filename found")
-
- try:
- os.unlink(temp)
- except:
- # Some very rude AV-scanners on Windows may cause this unlink to fail.
- # Not aborting here just leaks the temp file, whereas aborting at this
- # point may leave serious inconsistencies. Ideally, we would notify
- # the user in this case here.
- pass
-
def rename(src, dst):
'''atomically rename file src to dst, replacing dst if it exists'''
try:
--- a/mercurial/wireproto.py Fri Apr 29 11:04:39 2011 +0200
+++ b/mercurial/wireproto.py Fri Apr 29 11:10:11 2011 +0200
@@ -15,7 +15,9 @@
# list of nodes encoding / decoding
def decodelist(l, sep=' '):
- return map(bin, l.split(sep))
+ if l:
+ return map(bin, l.split(sep))
+ return []
def encodelist(l, sep=' '):
return sep.join(map(hex, l))
@@ -35,7 +37,15 @@
d = self._call("heads")
try:
return decodelist(d[:-1])
- except:
+ except ValueError:
+ self._abort(error.ResponseError(_("unexpected response:"), d))
+
+ def known(self, nodes):
+ n = encodelist(nodes)
+ d = self._call("known", nodes=n)
+ try:
+ return [bool(int(f)) for f in d]
+ except ValueError:
self._abort(error.ResponseError(_("unexpected response:"), d))
def branchmap(self):
@@ -57,7 +67,7 @@
try:
br = [tuple(decodelist(b)) for b in d.splitlines()]
return br
- except:
+ except ValueError:
self._abort(error.ResponseError(_("unexpected response:"), d))
def between(self, pairs):
@@ -68,7 +78,7 @@
d = self._call("between", pairs=n)
try:
r.extend(l and decodelist(l) or [] for l in d.splitlines())
- except:
+ except ValueError:
self._abort(error.ResponseError(_("unexpected response:"), d))
return r
@@ -113,13 +123,29 @@
bases=bases, heads=heads)
return changegroupmod.unbundle10(self._decompress(f), 'UN')
+ def getbundle(self, source, heads=None, common=None):
+ self.requirecap('getbundle', _('look up remote changes'))
+ opts = {}
+ if heads is not None:
+ opts['heads'] = encodelist(heads)
+ if common is not None:
+ opts['common'] = encodelist(common)
+ f = self._callstream("getbundle", **opts)
+ return changegroupmod.unbundle10(self._decompress(f), 'UN')
+
def unbundle(self, cg, heads, source):
'''Send cg (a readable file-like object representing the
changegroup to push, typically a chunkbuffer object) to the
remote server as a bundle. Return an integer indicating the
result of the push (see localrepository.addchangegroup()).'''
- ret, output = self._callpush("unbundle", cg, heads=encodelist(heads))
+ if self.capable('unbundlehash'):
+ heads = encodelist(['hashed',
+ util.sha1(''.join(sorted(heads))).digest()])
+ else:
+ heads = encodelist(heads)
+
+ ret, output = self._callpush("unbundle", cg, heads=heads)
if ret == "":
raise error.ResponseError(
_('push failed:'), output)
@@ -133,6 +159,15 @@
self.ui.status(_('remote: '), l)
return ret
+ def debugwireargs(self, one, two, three=None, four=None):
+ # don't pass optional arguments left at their default value
+ opts = {}
+ if three is not None:
+ opts['three'] = three
+ if four is not None:
+ opts['four'] = four
+ return self._call('debugwireargs', one=one, two=two, **opts)
+
# server side
class streamres(object):
@@ -152,6 +187,17 @@
args = proto.getargs(spec)
return func(repo, proto, *args)
+def options(cmd, keys, others):
+ opts = {}
+ for k in keys:
+ if k in others:
+ opts[k] = others[k]
+ del others[k]
+ if others:
+ sys.stderr.write("abort: %s got unexpected arguments %s\n"
+ % (cmd, ",".join(others)))
+ return opts
+
def between(repo, proto, pairs):
pairs = [decodelist(p, '-') for p in pairs.split(" ")]
r = []
@@ -176,7 +222,8 @@
return "".join(r)
def capabilities(repo, proto):
- caps = 'lookup changegroupsubset branchmap pushkey'.split()
+ caps = ('lookup changegroupsubset branchmap pushkey known getbundle '
+ 'unbundlehash').split()
if _allowstream(repo.ui):
requiredformats = repo.requirements & repo.supportedformats
# if our local revlogs are just revlogv1, add 'stream' cap
@@ -199,6 +246,18 @@
cg = repo.changegroupsubset(bases, heads, 'serve')
return streamres(proto.groupchunks(cg))
+def debugwireargs(repo, proto, one, two, others):
+ # only accept optional args from the known set
+ opts = options('debugwireargs', ['three', 'four'], others)
+ return repo.debugwireargs(one, two, **opts)
+
+def getbundle(repo, proto, others):
+ opts = options('getbundle', ['heads', 'common'], others)
+ for k, v in opts.iteritems():
+ opts[k] = decodelist(v)
+ cg = repo.getbundle('serve', **opts)
+ return streamres(proto.groupchunks(cg))
+
def heads(repo, proto):
h = repo.heads()
return encodelist(h) + "\n"
@@ -228,6 +287,9 @@
success = 0
return "%s %s\n" % (success, r)
+def known(repo, proto, nodes):
+ return ''.join(b and "1" or "0" for b in repo.known(decodelist(nodes)))
+
def pushkey(repo, proto, namespace, key, old, new):
# compatibility with pre-1.8 clients which were accidentally
# sending raw binary nodes rather than utf-8-encoded hex
@@ -298,7 +360,9 @@
def check_heads():
heads = repo.heads()
- return their_heads == ['force'] or their_heads == heads
+ heads_hash = util.sha1(''.join(sorted(heads))).digest()
+ return (their_heads == ['force'] or their_heads == heads or
+ their_heads == ['hashed', heads_hash])
proto.redirect()
@@ -343,8 +407,11 @@
'capabilities': (capabilities, ''),
'changegroup': (changegroup, 'roots'),
'changegroupsubset': (changegroupsubset, 'bases heads'),
+ 'debugwireargs': (debugwireargs, 'one two *'),
+ 'getbundle': (getbundle, '*'),
'heads': (heads, ''),
'hello': (hello, ''),
+ 'known': (known, 'nodes'),
'listkeys': (listkeys, 'namespace'),
'lookup': (lookup, 'key'),
'pushkey': (pushkey, 'namespace key old new'),
--- a/setup.py Fri Apr 29 11:04:39 2011 +0200
+++ b/setup.py Fri Apr 29 11:10:11 2011 +0200
@@ -98,24 +98,8 @@
try:
import py2exe
py2exeloaded = True
-
- # Help py2exe to find win32com.shell
- try:
- import modulefinder
- import win32com
- for p in win32com.__path__[1:]: # Take the path to win32comext
- modulefinder.AddPackagePath("win32com", p)
- pn = "win32com.shell"
- __import__(pn)
- m = sys.modules[pn]
- for p in m.__path__[1:]:
- modulefinder.AddPackagePath(pn, p)
- except ImportError:
- pass
-
except ImportError:
py2exeloaded = False
- pass
def runcmd(cmd, env):
p = subprocess.Popen(cmd, stdout=subprocess.PIPE,
@@ -330,11 +314,17 @@
Extension('mercurial.parsers', ['mercurial/parsers.c']),
]
+osutil_ldflags = []
+
+if sys.platform == 'darwin':
+ osutil_ldflags += ['-framework', 'ApplicationServices']
+
# disable osutil.c under windows + python 2.4 (issue1364)
if sys.platform == 'win32' and sys.version_info < (2, 5, 0, 'final'):
pymodules.append('mercurial.pure.osutil')
else:
- extmodules.append(Extension('mercurial.osutil', ['mercurial/osutil.c']))
+ extmodules.append(Extension('mercurial.osutil', ['mercurial/osutil.c'],
+ extra_link_args=osutil_ldflags))
if sys.platform == 'linux2' and os.uname()[2] > '2.6':
# The inotify extension is only usable with Linux 2.6 kernels.
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/tests/notcapable Fri Apr 29 11:10:11 2011 +0200
@@ -0,0 +1,19 @@
+# Disable the $CAP wire protocol capability.
+
+if test -z "$CAP"
+then
+ echo "CAP environment variable not set."
+fi
+
+cat > notcapable-$CAP.py << EOF
+from mercurial import extensions, repo
+def extsetup():
+ extensions.wrapfunction(repo.repository, 'capable', wrapper)
+def wrapper(orig, self, name, *args, **kwargs):
+ if name == '$CAP':
+ return False
+ return orig(self, name, *args, **kwargs)
+EOF
+
+echo '[extensions]' >> $HGRCPATH
+echo "notcapable-$CAP = `pwd`/notcapable-$CAP.py" >> $HGRCPATH
--- a/tests/run-tests.py Fri Apr 29 11:04:39 2011 +0200
+++ b/tests/run-tests.py Fri Apr 29 11:10:11 2011 +0200
@@ -53,16 +53,40 @@
import tempfile
import time
import re
+import threading
+
+processlock = threading.Lock()
closefds = os.name == 'posix'
-def Popen4(cmd, bufsize=-1):
- p = subprocess.Popen(cmd, shell=True, bufsize=bufsize,
+def Popen4(cmd, wd, timeout):
+ processlock.acquire()
+ orig = os.getcwd()
+ os.chdir(wd)
+ p = subprocess.Popen(cmd, shell=True, bufsize=-1,
close_fds=closefds,
stdin=subprocess.PIPE, stdout=subprocess.PIPE,
stderr=subprocess.STDOUT)
+ os.chdir(orig)
+ processlock.release()
+
p.fromchild = p.stdout
p.tochild = p.stdin
p.childerr = p.stderr
+
+ if timeout:
+ p.timeout = False
+ def t():
+ start = time.time()
+ while time.time() - start < timeout and p.returncode is None:
+ time.sleep(1)
+ p.timeout = True
+ if p.returncode is None:
+ try:
+ p.terminate()
+ except OSError:
+ pass
+ threading.Thread(target=t).start()
+
return p
# reserved exit code to skip test (used by hghave)
@@ -187,12 +211,14 @@
else:
pid = None
def vlog(*msg):
+ iolock.acquire()
if pid:
print pid,
for m in msg:
print m,
print
sys.stdout.flush()
+ iolock.release()
else:
vlog = lambda *msg: None
@@ -227,8 +253,8 @@
continue
for line in f.readlines():
- line = line.strip()
- if line and not line.startswith('#'):
+ line = line.split('#', 1)[0].strip()
+ if line:
blacklist[line] = filename
f.close()
@@ -439,22 +465,16 @@
os.mkdir(adir)
covrun('-i', '-a', '"--directory=%s"' % adir, '"--omit=%s"' % omit)
-class Timeout(Exception):
- pass
-
-def alarmed(signum, frame):
- raise Timeout
-
-def pytest(test, options, replacements):
+def pytest(test, wd, options, replacements):
py3kswitch = options.py3k_warnings and ' -3' or ''
cmd = '%s%s "%s"' % (PYTHON, py3kswitch, test)
vlog("# Running", cmd)
- return run(cmd, options, replacements)
+ return run(cmd, wd, options, replacements)
-def shtest(test, options, replacements):
+def shtest(test, wd, options, replacements):
cmd = '"%s"' % test
vlog("# Running", cmd)
- return run(cmd, options, replacements)
+ return run(cmd, wd, options, replacements)
needescape = re.compile(r'[\x00-\x08\x0b-\x1f\x7f-\xff]').search
escapesub = re.compile(r'[\x00-\x08\x0b-\x1f\\\x7f-\xff]').sub
@@ -465,7 +485,7 @@
def stringescape(s):
return escapesub(escapef, s)
-def tsttest(test, options, replacements):
+def tsttest(test, wd, options, replacements):
t = open(test)
out = []
script = []
@@ -506,7 +526,7 @@
cmd = '/bin/sh "%s"' % name
vlog("# Running", cmd)
- exitcode, output = run(cmd, options, replacements)
+ exitcode, output = run(cmd, wd, options, replacements)
# do not merge output if skipped, return hghave message instead
# similarly, with --debug, output is None
if exitcode == SKIPPED_STATUS or output is None:
@@ -585,7 +605,7 @@
return exitcode, postout
wifexited = getattr(os, "WIFEXITED", lambda x: False)
-def run(cmd, options, replacements):
+def run(cmd, wd, options, replacements):
"""Run command in a sub-process, capturing the output (stdout and stderr).
Return a tuple (exitcode, output). output is None in debug mode."""
# TODO: Use subprocess.Popen if we're running on Python 2.4
@@ -602,55 +622,121 @@
if ret is None:
ret = 0
else:
- proc = Popen4(cmd)
+ proc = Popen4(cmd, wd, options.timeout)
def cleanup():
- os.kill(proc.pid, signal.SIGTERM)
+ try:
+ proc.terminate()
+ except OSError:
+ pass
ret = proc.wait()
if ret == 0:
ret = signal.SIGTERM << 8
killdaemons()
return ret
+ output = ''
+ proc.tochild.close()
+
try:
- output = ''
- proc.tochild.close()
output = proc.fromchild.read()
- ret = proc.wait()
- if wifexited(ret):
- ret = os.WEXITSTATUS(ret)
- except Timeout:
- vlog('# Process %d timed out - killing it' % proc.pid)
- ret = cleanup()
- output += ("\n### Abort: timeout after %d seconds.\n"
- % options.timeout)
except KeyboardInterrupt:
vlog('# Handling keyboard interrupt')
cleanup()
raise
+ ret = proc.wait()
+ if wifexited(ret):
+ ret = os.WEXITSTATUS(ret)
+
+ if proc.timeout:
+ ret = 'timeout'
+
+ if ret:
+ killdaemons()
+
for s, r in replacements:
output = re.sub(s, r, output)
return ret, splitnewlines(output)
-def runone(options, test, skips, fails):
+def runone(options, test):
'''tristate output:
None -> skipped
True -> passed
False -> failed'''
+ global results, resultslock, iolock
+
+ testpath = os.path.join(TESTDIR, test)
+
+ def result(l, e):
+ resultslock.acquire()
+ results[l].append(e)
+ resultslock.release()
+
def skip(msg):
if not options.verbose:
- skips.append((test, msg))
+ result('s', (test, msg))
else:
+ iolock.acquire()
print "\nSkipping %s: %s" % (testpath, msg)
+ iolock.release()
return None
- def fail(msg):
- fails.append((test, msg))
+ def fail(msg, ret):
if not options.nodiff:
+ iolock.acquire()
print "\nERROR: %s %s" % (testpath, msg)
+ iolock.release()
+ if (not ret and options.interactive
+ and os.path.exists(testpath + ".err")):
+ iolock.acquire()
+ print "Accept this change? [n] ",
+ answer = sys.stdin.readline().strip()
+ iolock.release()
+ if answer.lower() in "y yes".split():
+ if test.endswith(".t"):
+ rename(testpath + ".err", testpath)
+ else:
+ rename(testpath + ".err", testpath + ".out")
+ return
+ result('f', (test, msg))
+
+ def success():
+ result('p', test)
+
+ def ignore(msg):
+ result('i', (test, msg))
+
+ if (test.startswith("test-") and '~' not in test and
+ ('.' not in test or test.endswith('.py') or
+ test.endswith('.bat') or test.endswith('.t'))):
+ if not os.path.exists(test):
+ skip("doesn't exist")
+ return None
+ else:
+ return None # not a supported test, don't record
+
+ if options.blacklist:
+ filename = options.blacklist.get(test)
+ if filename is not None:
+ skip("blacklisted")
+ return None
+
+ if options.retest and not os.path.exists(test + ".err"):
+ ignore("not retesting")
return None
+ if options.keywords:
+ fp = open(test)
+ t = fp.read().lower() + test.lower()
+ fp.close()
+ for k in options.keywords.lower().split():
+ if k in t:
+ break
+ else:
+ ignore("doesn't match keyword")
+ return None
+
vlog("# Test", test)
# create a fresh hgrc
@@ -669,7 +755,6 @@
hgrc.write('appendpid=True\n')
hgrc.close()
- testpath = os.path.join(TESTDIR, test)
ref = os.path.join(TESTDIR, test+".out")
err = os.path.join(TESTDIR, test+".err")
if os.path.exists(err):
@@ -694,14 +779,11 @@
runner = shtest
# Make a tmp subdirectory to work in
- testtmp = os.environ["TESTTMP"] = os.path.join(HGTMP, test)
- os.mkdir(testtmp)
- os.chdir(testtmp)
+ testtmp = os.environ["TESTTMP"] = os.environ["HOME"] = \
+ os.path.join(HGTMP, test)
- if options.timeout > 0:
- signal.alarm(options.timeout)
-
- ret, out = runner(testpath, options, [
+ os.mkdir(testtmp)
+ ret, out = runner(testpath, testtmp, options, [
(re.escape(testtmp), '$TESTTMP'),
(r':%s\b' % options.port, ':$HGPORT'),
(r':%s\b' % (options.port + 1), ':$HGPORT1'),
@@ -709,10 +791,9 @@
])
vlog("# Ret was:", ret)
- if options.timeout > 0:
- signal.alarm(0)
-
mark = '.'
+ if ret == 0:
+ success()
skipped = (ret == SKIPPED_STATUS)
@@ -744,33 +825,39 @@
if not missing:
missing = ['irrelevant']
if failed:
- fail("hghave failed checking for %s" % failed[-1])
+ fail("hghave failed checking for %s" % failed[-1], ret)
skipped = False
else:
skip(missing[-1])
+ elif ret == 'timeout':
+ mark = 't'
+ fail("timed out", ret)
elif out != refout:
mark = '!'
- if ret:
- fail("output changed and returned error code %d" % ret)
- else:
- fail("output changed")
if not options.nodiff:
+ iolock.acquire()
if options.view:
os.system("%s %s %s" % (options.view, ref, err))
else:
showdiff(refout, out, ref, err)
+ iolock.release()
+ if ret:
+ fail("output changed and returned error code %d" % ret, ret)
+ else:
+ fail("output changed", ret)
ret = 1
elif ret:
mark = '!'
- fail("returned error code %d" % ret)
+ fail("returned error code %d" % ret, ret)
if not options.verbose:
+ iolock.acquire()
sys.stdout.write(mark)
sys.stdout.flush()
+ iolock.release()
killdaemons()
- os.chdir(TESTDIR)
if not options.keep_tmpdir:
shutil.rmtree(testtmp, True)
if skipped:
@@ -882,6 +969,16 @@
outputcoverage(options)
sys.exit(failures != 0)
+results = dict(p=[], f=[], s=[], i=[])
+resultslock = threading.Lock()
+iolock = threading.Lock()
+
+def runqueue(options, tests, results):
+ for test in tests:
+ ret = runone(options, test)
+ if options.first and ret is not None and not ret:
+ break
+
def runtests(options, tests):
global DAEMON_PIDS, HGRCPATH
DAEMON_PIDS = os.environ["DAEMON_PIDS"] = os.path.join(HGTMP, 'daemon.pids')
@@ -892,19 +989,6 @@
installhg(options)
_checkhglib("Testing")
- if options.timeout > 0:
- try:
- signal.signal(signal.SIGALRM, alarmed)
- vlog('# Running each test with %d second timeout' %
- options.timeout)
- except AttributeError:
- print 'WARNING: cannot run tests with timeouts'
- options.timeout = 0
-
- tested = 0
- failed = 0
- skipped = 0
-
if options.restart:
orig = list(tests)
while tests:
@@ -915,69 +999,30 @@
print "running all tests"
tests = orig
- skips = []
- fails = []
-
- for test in tests:
- if options.blacklist:
- filename = options.blacklist.get(test)
- if filename is not None:
- skips.append((test, "blacklisted (%s)" % filename))
- skipped += 1
- continue
-
- if options.retest and not os.path.exists(test + ".err"):
- skipped += 1
- continue
+ runqueue(options, tests, results)
- if options.keywords:
- fp = open(test)
- t = fp.read().lower() + test.lower()
- fp.close()
- for k in options.keywords.lower().split():
- if k in t:
- break
- else:
- skipped += 1
- continue
-
- ret = runone(options, test, skips, fails)
- if ret is None:
- skipped += 1
- elif not ret:
- if options.interactive:
- print "Accept this change? [n] ",
- answer = sys.stdin.readline().strip()
- if answer.lower() in "y yes".split():
- if test.endswith(".t"):
- rename(test + ".err", test)
- else:
- rename(test + ".err", test + ".out")
- tested += 1
- fails.pop()
- continue
- failed += 1
- if options.first:
- break
- tested += 1
+ failed = len(results['f'])
+ tested = len(results['p']) + failed
+ skipped = len(results['s'])
+ ignored = len(results['i'])
if options.child:
fp = os.fdopen(options.child, 'w')
fp.write('%d\n%d\n%d\n' % (tested, skipped, failed))
- for s in skips:
+ for s in results['s']:
fp.write("%s %s\n" % s)
- for s in fails:
+ for s in results['f']:
fp.write("%s %s\n" % s)
fp.close()
else:
print
- for s in skips:
+ for s in results['s']:
print "Skipped %s: %s" % s
- for s in fails:
+ for s in results['f']:
print "Failed %s: %s" % s
_checkhglib("Tested")
print "# Ran %d tests, %d skipped, %d failed." % (
- tested, skipped, failed)
+ tested, skipped + ignored, failed)
if options.anycoverage:
outputcoverage(options)
@@ -999,22 +1044,7 @@
args = os.listdir(".")
args.sort()
- tests = []
- skipped = []
- for test in args:
- if (test.startswith("test-") and '~' not in test and
- ('.' not in test or test.endswith('.py') or
- test.endswith('.bat') or test.endswith('.t'))):
- if not os.path.exists(test):
- skipped.append(test)
- else:
- tests.append(test)
- if not tests:
- for test in skipped:
- print 'Skipped %s: does not exist' % test
- print "# Ran 0 tests, %d skipped, 0 failed." % len(skipped)
- return
- tests = tests + skipped
+ tests = args
# Reset some environment variables to well-known values so that
# the tests produce repeatable output.
--- a/tests/test-586.t Fri Apr 29 11:04:39 2011 +0200
+++ b/tests/test-586.t Fri Apr 29 11:10:11 2011 +0200
@@ -17,6 +17,7 @@
pulling from ../a
searching for changes
warning: repository is unrelated
+ requesting all changes
adding changesets
adding manifests
adding file changes
@@ -66,6 +67,7 @@
pulling from ../repob
searching for changes
warning: repository is unrelated
+ requesting all changes
adding changesets
adding manifests
adding file changes
--- a/tests/test-acl.t Fri Apr 29 11:04:39 2011 +0200
+++ b/tests/test-acl.t Fri Apr 29 11:10:11 2011 +0200
@@ -90,37 +90,14 @@
f9cafe1212c8c6fa1120d14a556e18cc44ff8bdd
911600dab2ae7a9baff75958b84fe606851ce955
adding changesets
- bundling: 0 changesets
- bundling: 0 changesets
- bundling: 0 changesets
- bundling: 1 changesets
- bundling: 1 changesets
bundling: 1 changesets
bundling: 2 changesets
- bundling: 2 changesets
- bundling: 2 changesets
bundling: 3 changesets
- bundling: 0/3 manifests (0.00%)
- bundling: 0/3 manifests (0.00%)
- bundling: 0/3 manifests (0.00%)
bundling: 1/3 manifests (33.33%)
- bundling: 1/3 manifests (33.33%)
- bundling: 1/3 manifests (33.33%)
- bundling: 2/3 manifests (66.67%)
- bundling: 2/3 manifests (66.67%)
bundling: 2/3 manifests (66.67%)
bundling: 3/3 manifests (100.00%)
bundling: foo/Bar/file.txt 0/3 files (0.00%)
- bundling: foo/Bar/file.txt 0/3 files (0.00%)
- bundling: foo/Bar/file.txt 0/3 files (0.00%)
- bundling: foo/Bar/file.txt 0/3 files (0.00%)
bundling: foo/file.txt 1/3 files (33.33%)
- bundling: foo/file.txt 1/3 files (33.33%)
- bundling: foo/file.txt 1/3 files (33.33%)
- bundling: foo/file.txt 1/3 files (33.33%)
- bundling: quux/file.py 2/3 files (66.67%)
- bundling: quux/file.py 2/3 files (66.67%)
- bundling: quux/file.py 2/3 files (66.67%)
bundling: quux/file.py 2/3 files (66.67%)
changesets: 1 chunks
add changeset ef1ea85a6374
@@ -168,37 +145,14 @@
f9cafe1212c8c6fa1120d14a556e18cc44ff8bdd
911600dab2ae7a9baff75958b84fe606851ce955
adding changesets
- bundling: 0 changesets
- bundling: 0 changesets
- bundling: 0 changesets
- bundling: 1 changesets
- bundling: 1 changesets
bundling: 1 changesets
bundling: 2 changesets
- bundling: 2 changesets
- bundling: 2 changesets
bundling: 3 changesets
- bundling: 0/3 manifests (0.00%)
- bundling: 0/3 manifests (0.00%)
- bundling: 0/3 manifests (0.00%)
bundling: 1/3 manifests (33.33%)
- bundling: 1/3 manifests (33.33%)
- bundling: 1/3 manifests (33.33%)
- bundling: 2/3 manifests (66.67%)
- bundling: 2/3 manifests (66.67%)
bundling: 2/3 manifests (66.67%)
bundling: 3/3 manifests (100.00%)
bundling: foo/Bar/file.txt 0/3 files (0.00%)
- bundling: foo/Bar/file.txt 0/3 files (0.00%)
- bundling: foo/Bar/file.txt 0/3 files (0.00%)
- bundling: foo/Bar/file.txt 0/3 files (0.00%)
bundling: foo/file.txt 1/3 files (33.33%)
- bundling: foo/file.txt 1/3 files (33.33%)
- bundling: foo/file.txt 1/3 files (33.33%)
- bundling: foo/file.txt 1/3 files (33.33%)
- bundling: quux/file.py 2/3 files (66.67%)
- bundling: quux/file.py 2/3 files (66.67%)
- bundling: quux/file.py 2/3 files (66.67%)
bundling: quux/file.py 2/3 files (66.67%)
changesets: 1 chunks
add changeset ef1ea85a6374
@@ -249,37 +203,14 @@
f9cafe1212c8c6fa1120d14a556e18cc44ff8bdd
911600dab2ae7a9baff75958b84fe606851ce955
adding changesets
- bundling: 0 changesets
- bundling: 0 changesets
- bundling: 0 changesets
- bundling: 1 changesets
- bundling: 1 changesets
bundling: 1 changesets
bundling: 2 changesets
- bundling: 2 changesets
- bundling: 2 changesets
bundling: 3 changesets
- bundling: 0/3 manifests (0.00%)
- bundling: 0/3 manifests (0.00%)
- bundling: 0/3 manifests (0.00%)
bundling: 1/3 manifests (33.33%)
- bundling: 1/3 manifests (33.33%)
- bundling: 1/3 manifests (33.33%)
- bundling: 2/3 manifests (66.67%)
- bundling: 2/3 manifests (66.67%)
bundling: 2/3 manifests (66.67%)
bundling: 3/3 manifests (100.00%)
bundling: foo/Bar/file.txt 0/3 files (0.00%)
- bundling: foo/Bar/file.txt 0/3 files (0.00%)
- bundling: foo/Bar/file.txt 0/3 files (0.00%)
- bundling: foo/Bar/file.txt 0/3 files (0.00%)
bundling: foo/file.txt 1/3 files (33.33%)
- bundling: foo/file.txt 1/3 files (33.33%)
- bundling: foo/file.txt 1/3 files (33.33%)
- bundling: foo/file.txt 1/3 files (33.33%)
- bundling: quux/file.py 2/3 files (66.67%)
- bundling: quux/file.py 2/3 files (66.67%)
- bundling: quux/file.py 2/3 files (66.67%)
bundling: quux/file.py 2/3 files (66.67%)
changesets: 1 chunks
add changeset ef1ea85a6374
@@ -339,37 +270,14 @@
f9cafe1212c8c6fa1120d14a556e18cc44ff8bdd
911600dab2ae7a9baff75958b84fe606851ce955
adding changesets
- bundling: 0 changesets
- bundling: 0 changesets
- bundling: 0 changesets
- bundling: 1 changesets
- bundling: 1 changesets
bundling: 1 changesets
bundling: 2 changesets
- bundling: 2 changesets
- bundling: 2 changesets
bundling: 3 changesets
- bundling: 0/3 manifests (0.00%)
- bundling: 0/3 manifests (0.00%)
- bundling: 0/3 manifests (0.00%)
bundling: 1/3 manifests (33.33%)
- bundling: 1/3 manifests (33.33%)
- bundling: 1/3 manifests (33.33%)
- bundling: 2/3 manifests (66.67%)
- bundling: 2/3 manifests (66.67%)
bundling: 2/3 manifests (66.67%)
bundling: 3/3 manifests (100.00%)
bundling: foo/Bar/file.txt 0/3 files (0.00%)
- bundling: foo/Bar/file.txt 0/3 files (0.00%)
- bundling: foo/Bar/file.txt 0/3 files (0.00%)
- bundling: foo/Bar/file.txt 0/3 files (0.00%)
bundling: foo/file.txt 1/3 files (33.33%)
- bundling: foo/file.txt 1/3 files (33.33%)
- bundling: foo/file.txt 1/3 files (33.33%)
- bundling: foo/file.txt 1/3 files (33.33%)
- bundling: quux/file.py 2/3 files (66.67%)
- bundling: quux/file.py 2/3 files (66.67%)
- bundling: quux/file.py 2/3 files (66.67%)
bundling: quux/file.py 2/3 files (66.67%)
changesets: 1 chunks
add changeset ef1ea85a6374
@@ -426,37 +334,14 @@
f9cafe1212c8c6fa1120d14a556e18cc44ff8bdd
911600dab2ae7a9baff75958b84fe606851ce955
adding changesets
- bundling: 0 changesets
- bundling: 0 changesets
- bundling: 0 changesets
- bundling: 1 changesets
- bundling: 1 changesets
bundling: 1 changesets
bundling: 2 changesets
- bundling: 2 changesets
- bundling: 2 changesets
bundling: 3 changesets
- bundling: 0/3 manifests (0.00%)
- bundling: 0/3 manifests (0.00%)
- bundling: 0/3 manifests (0.00%)
bundling: 1/3 manifests (33.33%)
- bundling: 1/3 manifests (33.33%)
- bundling: 1/3 manifests (33.33%)
- bundling: 2/3 manifests (66.67%)
- bundling: 2/3 manifests (66.67%)
bundling: 2/3 manifests (66.67%)
bundling: 3/3 manifests (100.00%)
bundling: foo/Bar/file.txt 0/3 files (0.00%)
- bundling: foo/Bar/file.txt 0/3 files (0.00%)
- bundling: foo/Bar/file.txt 0/3 files (0.00%)
- bundling: foo/Bar/file.txt 0/3 files (0.00%)
bundling: foo/file.txt 1/3 files (33.33%)
- bundling: foo/file.txt 1/3 files (33.33%)
- bundling: foo/file.txt 1/3 files (33.33%)
- bundling: foo/file.txt 1/3 files (33.33%)
- bundling: quux/file.py 2/3 files (66.67%)
- bundling: quux/file.py 2/3 files (66.67%)
- bundling: quux/file.py 2/3 files (66.67%)
bundling: quux/file.py 2/3 files (66.67%)
changesets: 1 chunks
add changeset ef1ea85a6374
@@ -518,37 +403,14 @@
f9cafe1212c8c6fa1120d14a556e18cc44ff8bdd
911600dab2ae7a9baff75958b84fe606851ce955
adding changesets
- bundling: 0 changesets
- bundling: 0 changesets
- bundling: 0 changesets
- bundling: 1 changesets
- bundling: 1 changesets
bundling: 1 changesets
bundling: 2 changesets
- bundling: 2 changesets
- bundling: 2 changesets
bundling: 3 changesets
- bundling: 0/3 manifests (0.00%)
- bundling: 0/3 manifests (0.00%)
- bundling: 0/3 manifests (0.00%)
bundling: 1/3 manifests (33.33%)
- bundling: 1/3 manifests (33.33%)
- bundling: 1/3 manifests (33.33%)
- bundling: 2/3 manifests (66.67%)
- bundling: 2/3 manifests (66.67%)
bundling: 2/3 manifests (66.67%)
bundling: 3/3 manifests (100.00%)
bundling: foo/Bar/file.txt 0/3 files (0.00%)
- bundling: foo/Bar/file.txt 0/3 files (0.00%)
- bundling: foo/Bar/file.txt 0/3 files (0.00%)
- bundling: foo/Bar/file.txt 0/3 files (0.00%)
bundling: foo/file.txt 1/3 files (33.33%)
- bundling: foo/file.txt 1/3 files (33.33%)
- bundling: foo/file.txt 1/3 files (33.33%)
- bundling: foo/file.txt 1/3 files (33.33%)
- bundling: quux/file.py 2/3 files (66.67%)
- bundling: quux/file.py 2/3 files (66.67%)
- bundling: quux/file.py 2/3 files (66.67%)
bundling: quux/file.py 2/3 files (66.67%)
changesets: 1 chunks
add changeset ef1ea85a6374
@@ -607,37 +469,14 @@
f9cafe1212c8c6fa1120d14a556e18cc44ff8bdd
911600dab2ae7a9baff75958b84fe606851ce955
adding changesets
- bundling: 0 changesets
- bundling: 0 changesets
- bundling: 0 changesets
- bundling: 1 changesets
- bundling: 1 changesets
bundling: 1 changesets
bundling: 2 changesets
- bundling: 2 changesets
- bundling: 2 changesets
bundling: 3 changesets
- bundling: 0/3 manifests (0.00%)
- bundling: 0/3 manifests (0.00%)
- bundling: 0/3 manifests (0.00%)
bundling: 1/3 manifests (33.33%)
- bundling: 1/3 manifests (33.33%)
- bundling: 1/3 manifests (33.33%)
- bundling: 2/3 manifests (66.67%)
- bundling: 2/3 manifests (66.67%)
bundling: 2/3 manifests (66.67%)
bundling: 3/3 manifests (100.00%)
bundling: foo/Bar/file.txt 0/3 files (0.00%)
- bundling: foo/Bar/file.txt 0/3 files (0.00%)
- bundling: foo/Bar/file.txt 0/3 files (0.00%)
- bundling: foo/Bar/file.txt 0/3 files (0.00%)
bundling: foo/file.txt 1/3 files (33.33%)
- bundling: foo/file.txt 1/3 files (33.33%)
- bundling: foo/file.txt 1/3 files (33.33%)
- bundling: foo/file.txt 1/3 files (33.33%)
- bundling: quux/file.py 2/3 files (66.67%)
- bundling: quux/file.py 2/3 files (66.67%)
- bundling: quux/file.py 2/3 files (66.67%)
bundling: quux/file.py 2/3 files (66.67%)
changesets: 1 chunks
add changeset ef1ea85a6374
@@ -701,37 +540,14 @@
f9cafe1212c8c6fa1120d14a556e18cc44ff8bdd
911600dab2ae7a9baff75958b84fe606851ce955
adding changesets
- bundling: 0 changesets
- bundling: 0 changesets
- bundling: 0 changesets
- bundling: 1 changesets
- bundling: 1 changesets
bundling: 1 changesets
bundling: 2 changesets
- bundling: 2 changesets
- bundling: 2 changesets
bundling: 3 changesets
- bundling: 0/3 manifests (0.00%)
- bundling: 0/3 manifests (0.00%)
- bundling: 0/3 manifests (0.00%)
bundling: 1/3 manifests (33.33%)
- bundling: 1/3 manifests (33.33%)
- bundling: 1/3 manifests (33.33%)
- bundling: 2/3 manifests (66.67%)
- bundling: 2/3 manifests (66.67%)
bundling: 2/3 manifests (66.67%)
bundling: 3/3 manifests (100.00%)
bundling: foo/Bar/file.txt 0/3 files (0.00%)
- bundling: foo/Bar/file.txt 0/3 files (0.00%)
- bundling: foo/Bar/file.txt 0/3 files (0.00%)
- bundling: foo/Bar/file.txt 0/3 files (0.00%)
bundling: foo/file.txt 1/3 files (33.33%)
- bundling: foo/file.txt 1/3 files (33.33%)
- bundling: foo/file.txt 1/3 files (33.33%)
- bundling: foo/file.txt 1/3 files (33.33%)
- bundling: quux/file.py 2/3 files (66.67%)
- bundling: quux/file.py 2/3 files (66.67%)
- bundling: quux/file.py 2/3 files (66.67%)
bundling: quux/file.py 2/3 files (66.67%)
changesets: 1 chunks
add changeset ef1ea85a6374
@@ -792,37 +608,14 @@
f9cafe1212c8c6fa1120d14a556e18cc44ff8bdd
911600dab2ae7a9baff75958b84fe606851ce955
adding changesets
- bundling: 0 changesets
- bundling: 0 changesets
- bundling: 0 changesets
- bundling: 1 changesets
- bundling: 1 changesets
bundling: 1 changesets
bundling: 2 changesets
- bundling: 2 changesets
- bundling: 2 changesets
bundling: 3 changesets
- bundling: 0/3 manifests (0.00%)
- bundling: 0/3 manifests (0.00%)
- bundling: 0/3 manifests (0.00%)
bundling: 1/3 manifests (33.33%)
- bundling: 1/3 manifests (33.33%)
- bundling: 1/3 manifests (33.33%)
- bundling: 2/3 manifests (66.67%)
- bundling: 2/3 manifests (66.67%)
bundling: 2/3 manifests (66.67%)
bundling: 3/3 manifests (100.00%)
bundling: foo/Bar/file.txt 0/3 files (0.00%)
- bundling: foo/Bar/file.txt 0/3 files (0.00%)
- bundling: foo/Bar/file.txt 0/3 files (0.00%)
- bundling: foo/Bar/file.txt 0/3 files (0.00%)
bundling: foo/file.txt 1/3 files (33.33%)
- bundling: foo/file.txt 1/3 files (33.33%)
- bundling: foo/file.txt 1/3 files (33.33%)
- bundling: foo/file.txt 1/3 files (33.33%)
- bundling: quux/file.py 2/3 files (66.67%)
- bundling: quux/file.py 2/3 files (66.67%)
- bundling: quux/file.py 2/3 files (66.67%)
bundling: quux/file.py 2/3 files (66.67%)
changesets: 1 chunks
add changeset ef1ea85a6374
@@ -885,37 +678,14 @@
f9cafe1212c8c6fa1120d14a556e18cc44ff8bdd
911600dab2ae7a9baff75958b84fe606851ce955
adding changesets
- bundling: 0 changesets
- bundling: 0 changesets
- bundling: 0 changesets
- bundling: 1 changesets
- bundling: 1 changesets
bundling: 1 changesets
bundling: 2 changesets
- bundling: 2 changesets
- bundling: 2 changesets
bundling: 3 changesets
- bundling: 0/3 manifests (0.00%)
- bundling: 0/3 manifests (0.00%)
- bundling: 0/3 manifests (0.00%)
bundling: 1/3 manifests (33.33%)
- bundling: 1/3 manifests (33.33%)
- bundling: 1/3 manifests (33.33%)
- bundling: 2/3 manifests (66.67%)
- bundling: 2/3 manifests (66.67%)
bundling: 2/3 manifests (66.67%)
bundling: 3/3 manifests (100.00%)
bundling: foo/Bar/file.txt 0/3 files (0.00%)
- bundling: foo/Bar/file.txt 0/3 files (0.00%)
- bundling: foo/Bar/file.txt 0/3 files (0.00%)
- bundling: foo/Bar/file.txt 0/3 files (0.00%)
bundling: foo/file.txt 1/3 files (33.33%)
- bundling: foo/file.txt 1/3 files (33.33%)
- bundling: foo/file.txt 1/3 files (33.33%)
- bundling: foo/file.txt 1/3 files (33.33%)
- bundling: quux/file.py 2/3 files (66.67%)
- bundling: quux/file.py 2/3 files (66.67%)
- bundling: quux/file.py 2/3 files (66.67%)
bundling: quux/file.py 2/3 files (66.67%)
changesets: 1 chunks
add changeset ef1ea85a6374
@@ -982,37 +752,14 @@
f9cafe1212c8c6fa1120d14a556e18cc44ff8bdd
911600dab2ae7a9baff75958b84fe606851ce955
adding changesets
- bundling: 0 changesets
- bundling: 0 changesets
- bundling: 0 changesets
- bundling: 1 changesets
- bundling: 1 changesets
bundling: 1 changesets
bundling: 2 changesets
- bundling: 2 changesets
- bundling: 2 changesets
bundling: 3 changesets
- bundling: 0/3 manifests (0.00%)
- bundling: 0/3 manifests (0.00%)
- bundling: 0/3 manifests (0.00%)
bundling: 1/3 manifests (33.33%)
- bundling: 1/3 manifests (33.33%)
- bundling: 1/3 manifests (33.33%)
- bundling: 2/3 manifests (66.67%)
- bundling: 2/3 manifests (66.67%)
bundling: 2/3 manifests (66.67%)
bundling: 3/3 manifests (100.00%)
bundling: foo/Bar/file.txt 0/3 files (0.00%)
- bundling: foo/Bar/file.txt 0/3 files (0.00%)
- bundling: foo/Bar/file.txt 0/3 files (0.00%)
- bundling: foo/Bar/file.txt 0/3 files (0.00%)
bundling: foo/file.txt 1/3 files (33.33%)
- bundling: foo/file.txt 1/3 files (33.33%)
- bundling: foo/file.txt 1/3 files (33.33%)
- bundling: foo/file.txt 1/3 files (33.33%)
- bundling: quux/file.py 2/3 files (66.67%)
- bundling: quux/file.py 2/3 files (66.67%)
- bundling: quux/file.py 2/3 files (66.67%)
bundling: quux/file.py 2/3 files (66.67%)
changesets: 1 chunks
add changeset ef1ea85a6374
@@ -1082,37 +829,14 @@
f9cafe1212c8c6fa1120d14a556e18cc44ff8bdd
911600dab2ae7a9baff75958b84fe606851ce955
adding changesets
- bundling: 0 changesets
- bundling: 0 changesets
- bundling: 0 changesets
- bundling: 1 changesets
- bundling: 1 changesets
bundling: 1 changesets
bundling: 2 changesets
- bundling: 2 changesets
- bundling: 2 changesets
bundling: 3 changesets
- bundling: 0/3 manifests (0.00%)
- bundling: 0/3 manifests (0.00%)
- bundling: 0/3 manifests (0.00%)
bundling: 1/3 manifests (33.33%)
- bundling: 1/3 manifests (33.33%)
- bundling: 1/3 manifests (33.33%)
- bundling: 2/3 manifests (66.67%)
- bundling: 2/3 manifests (66.67%)
bundling: 2/3 manifests (66.67%)
bundling: 3/3 manifests (100.00%)
bundling: foo/Bar/file.txt 0/3 files (0.00%)
- bundling: foo/Bar/file.txt 0/3 files (0.00%)
- bundling: foo/Bar/file.txt 0/3 files (0.00%)
- bundling: foo/Bar/file.txt 0/3 files (0.00%)
bundling: foo/file.txt 1/3 files (33.33%)
- bundling: foo/file.txt 1/3 files (33.33%)
- bundling: foo/file.txt 1/3 files (33.33%)
- bundling: foo/file.txt 1/3 files (33.33%)
- bundling: quux/file.py 2/3 files (66.67%)
- bundling: quux/file.py 2/3 files (66.67%)
- bundling: quux/file.py 2/3 files (66.67%)
bundling: quux/file.py 2/3 files (66.67%)
changesets: 1 chunks
add changeset ef1ea85a6374
@@ -1176,37 +900,14 @@
f9cafe1212c8c6fa1120d14a556e18cc44ff8bdd
911600dab2ae7a9baff75958b84fe606851ce955
adding changesets
- bundling: 0 changesets
- bundling: 0 changesets
- bundling: 0 changesets
- bundling: 1 changesets
- bundling: 1 changesets
bundling: 1 changesets
bundling: 2 changesets
- bundling: 2 changesets
- bundling: 2 changesets
bundling: 3 changesets
- bundling: 0/3 manifests (0.00%)
- bundling: 0/3 manifests (0.00%)
- bundling: 0/3 manifests (0.00%)
bundling: 1/3 manifests (33.33%)
- bundling: 1/3 manifests (33.33%)
- bundling: 1/3 manifests (33.33%)
- bundling: 2/3 manifests (66.67%)
- bundling: 2/3 manifests (66.67%)
bundling: 2/3 manifests (66.67%)
bundling: 3/3 manifests (100.00%)
bundling: foo/Bar/file.txt 0/3 files (0.00%)
- bundling: foo/Bar/file.txt 0/3 files (0.00%)
- bundling: foo/Bar/file.txt 0/3 files (0.00%)
- bundling: foo/Bar/file.txt 0/3 files (0.00%)
bundling: foo/file.txt 1/3 files (33.33%)
- bundling: foo/file.txt 1/3 files (33.33%)
- bundling: foo/file.txt 1/3 files (33.33%)
- bundling: foo/file.txt 1/3 files (33.33%)
- bundling: quux/file.py 2/3 files (66.67%)
- bundling: quux/file.py 2/3 files (66.67%)
- bundling: quux/file.py 2/3 files (66.67%)
bundling: quux/file.py 2/3 files (66.67%)
changesets: 1 chunks
add changeset ef1ea85a6374
@@ -1282,37 +983,14 @@
f9cafe1212c8c6fa1120d14a556e18cc44ff8bdd
911600dab2ae7a9baff75958b84fe606851ce955
adding changesets
- bundling: 0 changesets
- bundling: 0 changesets
- bundling: 0 changesets
- bundling: 1 changesets
- bundling: 1 changesets
bundling: 1 changesets
bundling: 2 changesets
- bundling: 2 changesets
- bundling: 2 changesets
bundling: 3 changesets
- bundling: 0/3 manifests (0.00%)
- bundling: 0/3 manifests (0.00%)
- bundling: 0/3 manifests (0.00%)
bundling: 1/3 manifests (33.33%)
- bundling: 1/3 manifests (33.33%)
- bundling: 1/3 manifests (33.33%)
- bundling: 2/3 manifests (66.67%)
- bundling: 2/3 manifests (66.67%)
bundling: 2/3 manifests (66.67%)
bundling: 3/3 manifests (100.00%)
bundling: foo/Bar/file.txt 0/3 files (0.00%)
- bundling: foo/Bar/file.txt 0/3 files (0.00%)
- bundling: foo/Bar/file.txt 0/3 files (0.00%)
- bundling: foo/Bar/file.txt 0/3 files (0.00%)
bundling: foo/file.txt 1/3 files (33.33%)
- bundling: foo/file.txt 1/3 files (33.33%)
- bundling: foo/file.txt 1/3 files (33.33%)
- bundling: foo/file.txt 1/3 files (33.33%)
- bundling: quux/file.py 2/3 files (66.67%)
- bundling: quux/file.py 2/3 files (66.67%)
- bundling: quux/file.py 2/3 files (66.67%)
bundling: quux/file.py 2/3 files (66.67%)
changesets: 1 chunks
add changeset ef1ea85a6374
@@ -1380,37 +1058,14 @@
f9cafe1212c8c6fa1120d14a556e18cc44ff8bdd
911600dab2ae7a9baff75958b84fe606851ce955
adding changesets
- bundling: 0 changesets
- bundling: 0 changesets
- bundling: 0 changesets
- bundling: 1 changesets
- bundling: 1 changesets
bundling: 1 changesets
bundling: 2 changesets
- bundling: 2 changesets
- bundling: 2 changesets
bundling: 3 changesets
- bundling: 0/3 manifests (0.00%)
- bundling: 0/3 manifests (0.00%)
- bundling: 0/3 manifests (0.00%)
bundling: 1/3 manifests (33.33%)
- bundling: 1/3 manifests (33.33%)
- bundling: 1/3 manifests (33.33%)
- bundling: 2/3 manifests (66.67%)
- bundling: 2/3 manifests (66.67%)
bundling: 2/3 manifests (66.67%)
bundling: 3/3 manifests (100.00%)
bundling: foo/Bar/file.txt 0/3 files (0.00%)
- bundling: foo/Bar/file.txt 0/3 files (0.00%)
- bundling: foo/Bar/file.txt 0/3 files (0.00%)
- bundling: foo/Bar/file.txt 0/3 files (0.00%)
bundling: foo/file.txt 1/3 files (33.33%)
- bundling: foo/file.txt 1/3 files (33.33%)
- bundling: foo/file.txt 1/3 files (33.33%)
- bundling: foo/file.txt 1/3 files (33.33%)
- bundling: quux/file.py 2/3 files (66.67%)
- bundling: quux/file.py 2/3 files (66.67%)
- bundling: quux/file.py 2/3 files (66.67%)
bundling: quux/file.py 2/3 files (66.67%)
changesets: 1 chunks
add changeset ef1ea85a6374
@@ -1474,37 +1129,14 @@
f9cafe1212c8c6fa1120d14a556e18cc44ff8bdd
911600dab2ae7a9baff75958b84fe606851ce955
adding changesets
- bundling: 0 changesets
- bundling: 0 changesets
- bundling: 0 changesets
- bundling: 1 changesets
- bundling: 1 changesets
bundling: 1 changesets
bundling: 2 changesets
- bundling: 2 changesets
- bundling: 2 changesets
bundling: 3 changesets
- bundling: 0/3 manifests (0.00%)
- bundling: 0/3 manifests (0.00%)
- bundling: 0/3 manifests (0.00%)
bundling: 1/3 manifests (33.33%)
- bundling: 1/3 manifests (33.33%)
- bundling: 1/3 manifests (33.33%)
- bundling: 2/3 manifests (66.67%)
- bundling: 2/3 manifests (66.67%)
bundling: 2/3 manifests (66.67%)
bundling: 3/3 manifests (100.00%)
bundling: foo/Bar/file.txt 0/3 files (0.00%)
- bundling: foo/Bar/file.txt 0/3 files (0.00%)
- bundling: foo/Bar/file.txt 0/3 files (0.00%)
- bundling: foo/Bar/file.txt 0/3 files (0.00%)
bundling: foo/file.txt 1/3 files (33.33%)
- bundling: foo/file.txt 1/3 files (33.33%)
- bundling: foo/file.txt 1/3 files (33.33%)
- bundling: foo/file.txt 1/3 files (33.33%)
- bundling: quux/file.py 2/3 files (66.67%)
- bundling: quux/file.py 2/3 files (66.67%)
- bundling: quux/file.py 2/3 files (66.67%)
bundling: quux/file.py 2/3 files (66.67%)
changesets: 1 chunks
add changeset ef1ea85a6374
@@ -1570,37 +1202,14 @@
f9cafe1212c8c6fa1120d14a556e18cc44ff8bdd
911600dab2ae7a9baff75958b84fe606851ce955
adding changesets
- bundling: 0 changesets
- bundling: 0 changesets
- bundling: 0 changesets
- bundling: 1 changesets
- bundling: 1 changesets
bundling: 1 changesets
bundling: 2 changesets
- bundling: 2 changesets
- bundling: 2 changesets
bundling: 3 changesets
- bundling: 0/3 manifests (0.00%)
- bundling: 0/3 manifests (0.00%)
- bundling: 0/3 manifests (0.00%)
bundling: 1/3 manifests (33.33%)
- bundling: 1/3 manifests (33.33%)
- bundling: 1/3 manifests (33.33%)
- bundling: 2/3 manifests (66.67%)
- bundling: 2/3 manifests (66.67%)
bundling: 2/3 manifests (66.67%)
bundling: 3/3 manifests (100.00%)
bundling: foo/Bar/file.txt 0/3 files (0.00%)
- bundling: foo/Bar/file.txt 0/3 files (0.00%)
- bundling: foo/Bar/file.txt 0/3 files (0.00%)
- bundling: foo/Bar/file.txt 0/3 files (0.00%)
bundling: foo/file.txt 1/3 files (33.33%)
- bundling: foo/file.txt 1/3 files (33.33%)
- bundling: foo/file.txt 1/3 files (33.33%)
- bundling: foo/file.txt 1/3 files (33.33%)
- bundling: quux/file.py 2/3 files (66.67%)
- bundling: quux/file.py 2/3 files (66.67%)
- bundling: quux/file.py 2/3 files (66.67%)
bundling: quux/file.py 2/3 files (66.67%)
changesets: 1 chunks
add changeset ef1ea85a6374
@@ -1665,37 +1274,14 @@
f9cafe1212c8c6fa1120d14a556e18cc44ff8bdd
911600dab2ae7a9baff75958b84fe606851ce955
adding changesets
- bundling: 0 changesets
- bundling: 0 changesets
- bundling: 0 changesets
- bundling: 1 changesets
- bundling: 1 changesets
bundling: 1 changesets
bundling: 2 changesets
- bundling: 2 changesets
- bundling: 2 changesets
bundling: 3 changesets
- bundling: 0/3 manifests (0.00%)
- bundling: 0/3 manifests (0.00%)
- bundling: 0/3 manifests (0.00%)
bundling: 1/3 manifests (33.33%)
- bundling: 1/3 manifests (33.33%)
- bundling: 1/3 manifests (33.33%)
- bundling: 2/3 manifests (66.67%)
- bundling: 2/3 manifests (66.67%)
bundling: 2/3 manifests (66.67%)
bundling: 3/3 manifests (100.00%)
bundling: foo/Bar/file.txt 0/3 files (0.00%)
- bundling: foo/Bar/file.txt 0/3 files (0.00%)
- bundling: foo/Bar/file.txt 0/3 files (0.00%)
- bundling: foo/Bar/file.txt 0/3 files (0.00%)
bundling: foo/file.txt 1/3 files (33.33%)
- bundling: foo/file.txt 1/3 files (33.33%)
- bundling: foo/file.txt 1/3 files (33.33%)
- bundling: foo/file.txt 1/3 files (33.33%)
- bundling: quux/file.py 2/3 files (66.67%)
- bundling: quux/file.py 2/3 files (66.67%)
- bundling: quux/file.py 2/3 files (66.67%)
bundling: quux/file.py 2/3 files (66.67%)
changesets: 1 chunks
add changeset ef1ea85a6374
@@ -1747,3 +1333,563 @@
acl: "unlikelytoexist" not defined in [acl.groups]
error: pretxnchangegroup.acl hook failed: group 'unlikelytoexist' is undefined
abort: group 'unlikelytoexist' is undefined
+
+
+Branch acl tests setup
+
+ $ init_config
+ $ cd b
+ $ hg up
+ 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
+ $ hg branch foobar
+ marked working directory as branch foobar
+ $ hg commit -m 'create foobar'
+ $ echo 'foo contents' > abc.txt
+ $ hg add abc.txt
+ $ hg commit -m 'foobar contents'
+ $ cd ..
+ $ hg --cwd a pull ../b
+ pulling from ../b
+ searching for changes
+ adding changesets
+ adding manifests
+ adding file changes
+ added 2 changesets with 1 changes to 1 files (+1 heads)
+ (run 'hg heads' to see heads)
+
+Create additional changeset on foobar branch
+
+ $ cd a
+ $ hg up -C foobar
+ 4 files updated, 0 files merged, 0 files removed, 0 files unresolved
+ $ echo 'foo contents2' > abc.txt
+ $ hg commit -m 'foobar contents2'
+ $ cd ..
+
+
+No branch acls specified
+
+ $ do_push astro
+ Pushing as user astro
+ hgrc = """
+ [acl]
+ sources = push
+ [extensions]
+ """
+ pushing to ../b
+ searching for changes
+ common changesets up to 07e028174695
+ 4 changesets found
+ list of changesets:
+ ef1ea85a6374b77d6da9dcda9541f498f2d17df7
+ f9cafe1212c8c6fa1120d14a556e18cc44ff8bdd
+ 911600dab2ae7a9baff75958b84fe606851ce955
+ 4ea792ff64284af438188103a0ee8aca1724fb8c
+ adding changesets
+ bundling: 1 changesets
+ bundling: 2 changesets
+ bundling: 3 changesets
+ bundling: 4 changesets
+ bundling: 1/4 manifests (25.00%)
+ bundling: 2/4 manifests (50.00%)
+ bundling: 3/4 manifests (75.00%)
+ bundling: 4/4 manifests (100.00%)
+ bundling: abc.txt 0/4 files (0.00%)
+ bundling: foo/Bar/file.txt 1/4 files (25.00%)
+ bundling: foo/file.txt 2/4 files (50.00%)
+ bundling: quux/file.py 3/4 files (75.00%)
+ changesets: 1 chunks
+ add changeset ef1ea85a6374
+ changesets: 2 chunks
+ add changeset f9cafe1212c8
+ changesets: 3 chunks
+ add changeset 911600dab2ae
+ changesets: 4 chunks
+ add changeset 4ea792ff6428
+ adding manifests
+ manifests: 1/4 chunks (25.00%)
+ manifests: 2/4 chunks (50.00%)
+ manifests: 3/4 chunks (75.00%)
+ manifests: 4/4 chunks (100.00%)
+ adding file changes
+ adding abc.txt revisions
+ files: 1/4 chunks (25.00%)
+ adding foo/Bar/file.txt revisions
+ files: 2/4 chunks (50.00%)
+ adding foo/file.txt revisions
+ files: 3/4 chunks (75.00%)
+ adding quux/file.py revisions
+ files: 4/4 chunks (100.00%)
+ added 4 changesets with 4 changes to 4 files (+1 heads)
+ calling hook pretxnchangegroup.acl: hgext.acl.hook
+ acl: acl.allow.branches not enabled
+ acl: acl.deny.branches not enabled
+ acl: acl.allow not enabled
+ acl: acl.deny not enabled
+ acl: branch access granted: "ef1ea85a6374" on branch "default"
+ acl: allowing changeset ef1ea85a6374
+ acl: branch access granted: "f9cafe1212c8" on branch "default"
+ acl: allowing changeset f9cafe1212c8
+ acl: branch access granted: "911600dab2ae" on branch "default"
+ acl: allowing changeset 911600dab2ae
+ acl: branch access granted: "4ea792ff6428" on branch "foobar"
+ acl: allowing changeset 4ea792ff6428
+ updating the branch cache
+ checking for updated bookmarks
+ repository tip rolled back to revision 2 (undo push)
+ working directory now based on revision 2
+ 2:07e028174695
+
+
+Branch acl deny test
+
+ $ echo "[acl.deny.branches]" >> $config
+ $ echo "foobar = *" >> $config
+ $ do_push astro
+ Pushing as user astro
+ hgrc = """
+ [acl]
+ sources = push
+ [extensions]
+ [acl.deny.branches]
+ foobar = *
+ """
+ pushing to ../b
+ searching for changes
+ common changesets up to 07e028174695
+ invalidating branch cache (tip differs)
+ 4 changesets found
+ list of changesets:
+ ef1ea85a6374b77d6da9dcda9541f498f2d17df7
+ f9cafe1212c8c6fa1120d14a556e18cc44ff8bdd
+ 911600dab2ae7a9baff75958b84fe606851ce955
+ 4ea792ff64284af438188103a0ee8aca1724fb8c
+ adding changesets
+ bundling: 1 changesets
+ bundling: 2 changesets
+ bundling: 3 changesets
+ bundling: 4 changesets
+ bundling: 1/4 manifests (25.00%)
+ bundling: 2/4 manifests (50.00%)
+ bundling: 3/4 manifests (75.00%)
+ bundling: 4/4 manifests (100.00%)
+ bundling: abc.txt 0/4 files (0.00%)
+ bundling: foo/Bar/file.txt 1/4 files (25.00%)
+ bundling: foo/file.txt 2/4 files (50.00%)
+ bundling: quux/file.py 3/4 files (75.00%)
+ changesets: 1 chunks
+ add changeset ef1ea85a6374
+ changesets: 2 chunks
+ add changeset f9cafe1212c8
+ changesets: 3 chunks
+ add changeset 911600dab2ae
+ changesets: 4 chunks
+ add changeset 4ea792ff6428
+ adding manifests
+ manifests: 1/4 chunks (25.00%)
+ manifests: 2/4 chunks (50.00%)
+ manifests: 3/4 chunks (75.00%)
+ manifests: 4/4 chunks (100.00%)
+ adding file changes
+ adding abc.txt revisions
+ files: 1/4 chunks (25.00%)
+ adding foo/Bar/file.txt revisions
+ files: 2/4 chunks (50.00%)
+ adding foo/file.txt revisions
+ files: 3/4 chunks (75.00%)
+ adding quux/file.py revisions
+ files: 4/4 chunks (100.00%)
+ added 4 changesets with 4 changes to 4 files (+1 heads)
+ calling hook pretxnchangegroup.acl: hgext.acl.hook
+ acl: acl.allow.branches not enabled
+ acl: acl.deny.branches enabled, 1 entries for user astro
+ acl: acl.allow not enabled
+ acl: acl.deny not enabled
+ acl: branch access granted: "ef1ea85a6374" on branch "default"
+ acl: allowing changeset ef1ea85a6374
+ acl: branch access granted: "f9cafe1212c8" on branch "default"
+ acl: allowing changeset f9cafe1212c8
+ acl: branch access granted: "911600dab2ae" on branch "default"
+ acl: allowing changeset 911600dab2ae
+ error: pretxnchangegroup.acl hook failed: acl: user "astro" denied on branch "foobar" (changeset "4ea792ff6428")
+ transaction abort!
+ rollback completed
+ abort: acl: user "astro" denied on branch "foobar" (changeset "4ea792ff6428")
+ no rollback information available
+ 2:07e028174695
+
+
+Branch acl empty allow test
+
+ $ init_config
+ $ echo "[acl.allow.branches]" >> $config
+ $ do_push astro
+ Pushing as user astro
+ hgrc = """
+ [acl]
+ sources = push
+ [extensions]
+ [acl.allow.branches]
+ """
+ pushing to ../b
+ searching for changes
+ common changesets up to 07e028174695
+ 4 changesets found
+ list of changesets:
+ ef1ea85a6374b77d6da9dcda9541f498f2d17df7
+ f9cafe1212c8c6fa1120d14a556e18cc44ff8bdd
+ 911600dab2ae7a9baff75958b84fe606851ce955
+ 4ea792ff64284af438188103a0ee8aca1724fb8c
+ adding changesets
+ bundling: 1 changesets
+ bundling: 2 changesets
+ bundling: 3 changesets
+ bundling: 4 changesets
+ bundling: 1/4 manifests (25.00%)
+ bundling: 2/4 manifests (50.00%)
+ bundling: 3/4 manifests (75.00%)
+ bundling: 4/4 manifests (100.00%)
+ bundling: abc.txt 0/4 files (0.00%)
+ bundling: foo/Bar/file.txt 1/4 files (25.00%)
+ bundling: foo/file.txt 2/4 files (50.00%)
+ bundling: quux/file.py 3/4 files (75.00%)
+ changesets: 1 chunks
+ add changeset ef1ea85a6374
+ changesets: 2 chunks
+ add changeset f9cafe1212c8
+ changesets: 3 chunks
+ add changeset 911600dab2ae
+ changesets: 4 chunks
+ add changeset 4ea792ff6428
+ adding manifests
+ manifests: 1/4 chunks (25.00%)
+ manifests: 2/4 chunks (50.00%)
+ manifests: 3/4 chunks (75.00%)
+ manifests: 4/4 chunks (100.00%)
+ adding file changes
+ adding abc.txt revisions
+ files: 1/4 chunks (25.00%)
+ adding foo/Bar/file.txt revisions
+ files: 2/4 chunks (50.00%)
+ adding foo/file.txt revisions
+ files: 3/4 chunks (75.00%)
+ adding quux/file.py revisions
+ files: 4/4 chunks (100.00%)
+ added 4 changesets with 4 changes to 4 files (+1 heads)
+ calling hook pretxnchangegroup.acl: hgext.acl.hook
+ acl: acl.allow.branches enabled, 0 entries for user astro
+ acl: acl.deny.branches not enabled
+ acl: acl.allow not enabled
+ acl: acl.deny not enabled
+ error: pretxnchangegroup.acl hook failed: acl: user "astro" not allowed on branch "default" (changeset "ef1ea85a6374")
+ transaction abort!
+ rollback completed
+ abort: acl: user "astro" not allowed on branch "default" (changeset "ef1ea85a6374")
+ no rollback information available
+ 2:07e028174695
+
+
+Branch acl allow other
+
+ $ init_config
+ $ echo "[acl.allow.branches]" >> $config
+ $ echo "* = george" >> $config
+ $ do_push astro
+ Pushing as user astro
+ hgrc = """
+ [acl]
+ sources = push
+ [extensions]
+ [acl.allow.branches]
+ * = george
+ """
+ pushing to ../b
+ searching for changes
+ common changesets up to 07e028174695
+ 4 changesets found
+ list of changesets:
+ ef1ea85a6374b77d6da9dcda9541f498f2d17df7
+ f9cafe1212c8c6fa1120d14a556e18cc44ff8bdd
+ 911600dab2ae7a9baff75958b84fe606851ce955
+ 4ea792ff64284af438188103a0ee8aca1724fb8c
+ adding changesets
+ bundling: 1 changesets
+ bundling: 2 changesets
+ bundling: 3 changesets
+ bundling: 4 changesets
+ bundling: 1/4 manifests (25.00%)
+ bundling: 2/4 manifests (50.00%)
+ bundling: 3/4 manifests (75.00%)
+ bundling: 4/4 manifests (100.00%)
+ bundling: abc.txt 0/4 files (0.00%)
+ bundling: foo/Bar/file.txt 1/4 files (25.00%)
+ bundling: foo/file.txt 2/4 files (50.00%)
+ bundling: quux/file.py 3/4 files (75.00%)
+ changesets: 1 chunks
+ add changeset ef1ea85a6374
+ changesets: 2 chunks
+ add changeset f9cafe1212c8
+ changesets: 3 chunks
+ add changeset 911600dab2ae
+ changesets: 4 chunks
+ add changeset 4ea792ff6428
+ adding manifests
+ manifests: 1/4 chunks (25.00%)
+ manifests: 2/4 chunks (50.00%)
+ manifests: 3/4 chunks (75.00%)
+ manifests: 4/4 chunks (100.00%)
+ adding file changes
+ adding abc.txt revisions
+ files: 1/4 chunks (25.00%)
+ adding foo/Bar/file.txt revisions
+ files: 2/4 chunks (50.00%)
+ adding foo/file.txt revisions
+ files: 3/4 chunks (75.00%)
+ adding quux/file.py revisions
+ files: 4/4 chunks (100.00%)
+ added 4 changesets with 4 changes to 4 files (+1 heads)
+ calling hook pretxnchangegroup.acl: hgext.acl.hook
+ acl: acl.allow.branches enabled, 0 entries for user astro
+ acl: acl.deny.branches not enabled
+ acl: acl.allow not enabled
+ acl: acl.deny not enabled
+ error: pretxnchangegroup.acl hook failed: acl: user "astro" not allowed on branch "default" (changeset "ef1ea85a6374")
+ transaction abort!
+ rollback completed
+ abort: acl: user "astro" not allowed on branch "default" (changeset "ef1ea85a6374")
+ no rollback information available
+ 2:07e028174695
+
+ $ do_push george
+ Pushing as user george
+ hgrc = """
+ [acl]
+ sources = push
+ [extensions]
+ [acl.allow.branches]
+ * = george
+ """
+ pushing to ../b
+ searching for changes
+ common changesets up to 07e028174695
+ 4 changesets found
+ list of changesets:
+ ef1ea85a6374b77d6da9dcda9541f498f2d17df7
+ f9cafe1212c8c6fa1120d14a556e18cc44ff8bdd
+ 911600dab2ae7a9baff75958b84fe606851ce955
+ 4ea792ff64284af438188103a0ee8aca1724fb8c
+ adding changesets
+ bundling: 1 changesets
+ bundling: 2 changesets
+ bundling: 3 changesets
+ bundling: 4 changesets
+ bundling: 1/4 manifests (25.00%)
+ bundling: 2/4 manifests (50.00%)
+ bundling: 3/4 manifests (75.00%)
+ bundling: 4/4 manifests (100.00%)
+ bundling: abc.txt 0/4 files (0.00%)
+ bundling: foo/Bar/file.txt 1/4 files (25.00%)
+ bundling: foo/file.txt 2/4 files (50.00%)
+ bundling: quux/file.py 3/4 files (75.00%)
+ changesets: 1 chunks
+ add changeset ef1ea85a6374
+ changesets: 2 chunks
+ add changeset f9cafe1212c8
+ changesets: 3 chunks
+ add changeset 911600dab2ae
+ changesets: 4 chunks
+ add changeset 4ea792ff6428
+ adding manifests
+ manifests: 1/4 chunks (25.00%)
+ manifests: 2/4 chunks (50.00%)
+ manifests: 3/4 chunks (75.00%)
+ manifests: 4/4 chunks (100.00%)
+ adding file changes
+ adding abc.txt revisions
+ files: 1/4 chunks (25.00%)
+ adding foo/Bar/file.txt revisions
+ files: 2/4 chunks (50.00%)
+ adding foo/file.txt revisions
+ files: 3/4 chunks (75.00%)
+ adding quux/file.py revisions
+ files: 4/4 chunks (100.00%)
+ added 4 changesets with 4 changes to 4 files (+1 heads)
+ calling hook pretxnchangegroup.acl: hgext.acl.hook
+ acl: acl.allow.branches enabled, 1 entries for user george
+ acl: acl.deny.branches not enabled
+ acl: acl.allow not enabled
+ acl: acl.deny not enabled
+ acl: branch access granted: "ef1ea85a6374" on branch "default"
+ acl: allowing changeset ef1ea85a6374
+ acl: branch access granted: "f9cafe1212c8" on branch "default"
+ acl: allowing changeset f9cafe1212c8
+ acl: branch access granted: "911600dab2ae" on branch "default"
+ acl: allowing changeset 911600dab2ae
+ acl: branch access granted: "4ea792ff6428" on branch "foobar"
+ acl: allowing changeset 4ea792ff6428
+ updating the branch cache
+ checking for updated bookmarks
+ repository tip rolled back to revision 2 (undo push)
+ working directory now based on revision 2
+ 2:07e028174695
+
+
+Branch acl conflicting allow
+asterisk ends up applying to all branches and allowing george to
+push foobar into the remote
+
+ $ init_config
+ $ echo "[acl.allow.branches]" >> $config
+ $ echo "foobar = astro" >> $config
+ $ echo "* = george" >> $config
+ $ do_push george
+ Pushing as user george
+ hgrc = """
+ [acl]
+ sources = push
+ [extensions]
+ [acl.allow.branches]
+ foobar = astro
+ * = george
+ """
+ pushing to ../b
+ searching for changes
+ common changesets up to 07e028174695
+ invalidating branch cache (tip differs)
+ 4 changesets found
+ list of changesets:
+ ef1ea85a6374b77d6da9dcda9541f498f2d17df7
+ f9cafe1212c8c6fa1120d14a556e18cc44ff8bdd
+ 911600dab2ae7a9baff75958b84fe606851ce955
+ 4ea792ff64284af438188103a0ee8aca1724fb8c
+ adding changesets
+ bundling: 1 changesets
+ bundling: 2 changesets
+ bundling: 3 changesets
+ bundling: 4 changesets
+ bundling: 1/4 manifests (25.00%)
+ bundling: 2/4 manifests (50.00%)
+ bundling: 3/4 manifests (75.00%)
+ bundling: 4/4 manifests (100.00%)
+ bundling: abc.txt 0/4 files (0.00%)
+ bundling: foo/Bar/file.txt 1/4 files (25.00%)
+ bundling: foo/file.txt 2/4 files (50.00%)
+ bundling: quux/file.py 3/4 files (75.00%)
+ changesets: 1 chunks
+ add changeset ef1ea85a6374
+ changesets: 2 chunks
+ add changeset f9cafe1212c8
+ changesets: 3 chunks
+ add changeset 911600dab2ae
+ changesets: 4 chunks
+ add changeset 4ea792ff6428
+ adding manifests
+ manifests: 1/4 chunks (25.00%)
+ manifests: 2/4 chunks (50.00%)
+ manifests: 3/4 chunks (75.00%)
+ manifests: 4/4 chunks (100.00%)
+ adding file changes
+ adding abc.txt revisions
+ files: 1/4 chunks (25.00%)
+ adding foo/Bar/file.txt revisions
+ files: 2/4 chunks (50.00%)
+ adding foo/file.txt revisions
+ files: 3/4 chunks (75.00%)
+ adding quux/file.py revisions
+ files: 4/4 chunks (100.00%)
+ added 4 changesets with 4 changes to 4 files (+1 heads)
+ calling hook pretxnchangegroup.acl: hgext.acl.hook
+ acl: acl.allow.branches enabled, 1 entries for user george
+ acl: acl.deny.branches not enabled
+ acl: acl.allow not enabled
+ acl: acl.deny not enabled
+ acl: branch access granted: "ef1ea85a6374" on branch "default"
+ acl: allowing changeset ef1ea85a6374
+ acl: branch access granted: "f9cafe1212c8" on branch "default"
+ acl: allowing changeset f9cafe1212c8
+ acl: branch access granted: "911600dab2ae" on branch "default"
+ acl: allowing changeset 911600dab2ae
+ acl: branch access granted: "4ea792ff6428" on branch "foobar"
+ acl: allowing changeset 4ea792ff6428
+ updating the branch cache
+ checking for updated bookmarks
+ repository tip rolled back to revision 2 (undo push)
+ working directory now based on revision 2
+ 2:07e028174695
+
+Branch acl conflicting deny
+
+ $ init_config
+ $ echo "[acl.deny.branches]" >> $config
+ $ echo "foobar = astro" >> $config
+ $ echo "default = astro" >> $config
+ $ echo "* = george" >> $config
+ $ do_push george
+ Pushing as user george
+ hgrc = """
+ [acl]
+ sources = push
+ [extensions]
+ [acl.deny.branches]
+ foobar = astro
+ default = astro
+ * = george
+ """
+ pushing to ../b
+ searching for changes
+ common changesets up to 07e028174695
+ invalidating branch cache (tip differs)
+ 4 changesets found
+ list of changesets:
+ ef1ea85a6374b77d6da9dcda9541f498f2d17df7
+ f9cafe1212c8c6fa1120d14a556e18cc44ff8bdd
+ 911600dab2ae7a9baff75958b84fe606851ce955
+ 4ea792ff64284af438188103a0ee8aca1724fb8c
+ adding changesets
+ bundling: 1 changesets
+ bundling: 2 changesets
+ bundling: 3 changesets
+ bundling: 4 changesets
+ bundling: 1/4 manifests (25.00%)
+ bundling: 2/4 manifests (50.00%)
+ bundling: 3/4 manifests (75.00%)
+ bundling: 4/4 manifests (100.00%)
+ bundling: abc.txt 0/4 files (0.00%)
+ bundling: foo/Bar/file.txt 1/4 files (25.00%)
+ bundling: foo/file.txt 2/4 files (50.00%)
+ bundling: quux/file.py 3/4 files (75.00%)
+ changesets: 1 chunks
+ add changeset ef1ea85a6374
+ changesets: 2 chunks
+ add changeset f9cafe1212c8
+ changesets: 3 chunks
+ add changeset 911600dab2ae
+ changesets: 4 chunks
+ add changeset 4ea792ff6428
+ adding manifests
+ manifests: 1/4 chunks (25.00%)
+ manifests: 2/4 chunks (50.00%)
+ manifests: 3/4 chunks (75.00%)
+ manifests: 4/4 chunks (100.00%)
+ adding file changes
+ adding abc.txt revisions
+ files: 1/4 chunks (25.00%)
+ adding foo/Bar/file.txt revisions
+ files: 2/4 chunks (50.00%)
+ adding foo/file.txt revisions
+ files: 3/4 chunks (75.00%)
+ adding quux/file.py revisions
+ files: 4/4 chunks (100.00%)
+ added 4 changesets with 4 changes to 4 files (+1 heads)
+ calling hook pretxnchangegroup.acl: hgext.acl.hook
+ acl: acl.allow.branches not enabled
+ acl: acl.deny.branches enabled, 1 entries for user george
+ acl: acl.allow not enabled
+ acl: acl.deny not enabled
+ error: pretxnchangegroup.acl hook failed: acl: user "george" denied on branch "default" (changeset "ef1ea85a6374")
+ transaction abort!
+ rollback completed
+ abort: acl: user "george" denied on branch "default" (changeset "ef1ea85a6374")
+ no rollback information available
+ 2:07e028174695
+
--- a/tests/test-add.t Fri Apr 29 11:04:39 2011 +0200
+++ b/tests/test-add.t Fri Apr 29 11:10:11 2011 +0200
@@ -33,6 +33,41 @@
A a
A b
+ $ echo foo > con.xml
+ $ hg --config ui.portablefilenames=jump add con.xml
+ abort: ui.portablefilenames value is invalid ('jump')
+ [255]
+ $ hg --config ui.portablefilenames=abort add con.xml
+ abort: filename contains 'con', which is reserved on Windows: 'con.xml'
+ [255]
+ $ hg st
+ A a
+ A b
+ ? con.xml
+ $ hg add con.xml
+ warning: filename contains 'con', which is reserved on Windows: 'con.xml'
+ $ hg st
+ A a
+ A b
+ A con.xml
+ $ echo bla > 'hello:world'
+ $ hg --config ui.portablefilenames=abort add
+ adding hello:world
+ abort: filename contains ':', which is reserved on Windows: 'hello:world'
+ [255]
+ $ hg st
+ A a
+ A b
+ A con.xml
+ ? hello:world
+ $ hg --config ui.portablefilenames=ignore add
+ adding hello:world
+ $ hg st
+ A a
+ A b
+ A con.xml
+ A hello:world
+
$ hg ci -m 0 --traceback
should fail
--- a/tests/test-addremove.t Fri Apr 29 11:04:39 2011 +0200
+++ b/tests/test-addremove.t Fri Apr 29 11:10:11 2011 +0200
@@ -10,14 +10,17 @@
foo
committed changeset 0:6f7f953567a2
$ cd dir/
- $ touch ../foo_2 bar_2
+ $ touch ../foo_2 bar_2 con.xml
$ hg -v addremove
adding dir/bar_2
+ adding dir/con.xml
adding foo_2
+ warning: filename contains 'con', which is reserved on Windows: 'dir/con.xml'
$ hg -v commit -m "add 2"
dir/bar_2
+ dir/con.xml
foo_2
- committed changeset 1:e65414bf35c5
+ committed changeset 1:6bb597da00f1
$ cd ..
$ hg init sim
--- a/tests/test-alias.t Fri Apr 29 11:04:39 2011 +0200
+++ b/tests/test-alias.t Fri Apr 29 11:10:11 2011 +0200
@@ -304,13 +304,7 @@
alias for: hg root
- print the root (top) of the current working directory
-
- Print the root directory of the current repository.
-
- Returns 0 on success.
-
- use "hg -v help rt" to show global options
+ use "hg help rt" to show the full help text
[255]
invalid global arguments for normal commands, aliases, and shell aliases
--- a/tests/test-annotate.t Fri Apr 29 11:04:39 2011 +0200
+++ b/tests/test-annotate.t Fri Apr 29 11:10:11 2011 +0200
@@ -228,3 +228,8 @@
$ hg annotate --follow foo
foo: foo
+missing file
+
+ $ hg ann nosuchfile
+ abort: nosuchfile: no such file in rev c8abddb41a00
+ [255]
--- a/tests/test-archive.t Fri Apr 29 11:04:39 2011 +0200
+++ b/tests/test-archive.t Fri Apr 29 11:10:11 2011 +0200
@@ -1,6 +1,5 @@
- $ mkdir test
+ $ hg init test
$ cd test
- $ hg init
$ echo foo>foo
$ hg commit -Am 1 -d '1 0'
adding foo
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/tests/test-atomictempfile.py Fri Apr 29 11:10:11 2011 +0200
@@ -0,0 +1,49 @@
+import os
+import glob
+from mercurial.util import atomictempfile
+
+# basic usage
+def test1_simple():
+ if os.path.exists('foo'):
+ os.remove('foo')
+ file = atomictempfile('foo')
+ (dir, basename) = os.path.split(file._tempname)
+ assert not os.path.isfile('foo')
+ assert basename in glob.glob('.foo-*')
+
+ file.write('argh\n')
+ file.rename()
+
+ assert os.path.isfile('foo')
+ assert basename not in glob.glob('.foo-*')
+ print 'OK'
+
+# close() removes the temp file but does not make the write
+# permanent -- essentially discards your work (WTF?!)
+def test2_close():
+ if os.path.exists('foo'):
+ os.remove('foo')
+ file = atomictempfile('foo')
+ (dir, basename) = os.path.split(file._tempname)
+
+ file.write('yo\n')
+ file.close()
+
+ assert not os.path.isfile('foo')
+ assert basename not in os.listdir('.')
+ print 'OK'
+
+# if a programmer screws up and passes bad args to atomictempfile, they
+# get a plain ordinary TypeError, not infinite recursion
+def test3_oops():
+ try:
+ file = atomictempfile()
+ except TypeError:
+ print "OK"
+ else:
+ print "expected TypeError"
+
+if __name__ == '__main__':
+ test1_simple()
+ test2_close()
+ test3_oops()
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/tests/test-atomictempfile.py.out Fri Apr 29 11:10:11 2011 +0200
@@ -0,0 +1,3 @@
+OK
+OK
+OK
--- a/tests/test-audit-path.t Fri Apr 29 11:04:39 2011 +0200
+++ b/tests/test-audit-path.t Fri Apr 29 11:10:11 2011 +0200
@@ -53,7 +53,7 @@
$ hg manifest -r1
foo/.hg/test
$ hg update -Cr1
- abort: path 'foo/.hg/test' is inside repo 'foo'
+ abort: path 'foo/.hg/test' is inside nested repo 'foo'
[255]
attack back/test where back symlinks to ..
--- a/tests/test-basic.t Fri Apr 29 11:04:39 2011 +0200
+++ b/tests/test-basic.t Fri Apr 29 11:10:11 2011 +0200
@@ -1,8 +1,7 @@
Create a repository:
- $ mkdir t
+ $ hg init t
$ cd t
- $ hg init
Make a changeset:
@@ -20,6 +19,22 @@
summary: test
+Verify that updating to revision 0 via commands.update() works properly
+
+ $ cat <<EOF > update_to_rev0.py
+ > from mercurial import ui, hg, commands
+ > myui = ui.ui()
+ > repo = hg.repository(myui, path='.')
+ > commands.update(myui, repo, rev=0)
+ > EOF
+ $ hg up null
+ 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
+ $ python ./update_to_rev0.py
+ 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
+ $ hg identify -n
+ 0
+
+
Poke around at hashes:
$ hg manifest --debug
--- a/tests/test-bisect.t Fri Apr 29 11:04:39 2011 +0200
+++ b/tests/test-bisect.t Fri Apr 29 11:10:11 2011 +0200
@@ -377,6 +377,44 @@
date: Thu Jan 01 00:00:06 1970 +0000
summary: msg 6
+ $ hg log -r "bisected(good)"
+ changeset: 0:b99c7b9c8e11
+ user: test
+ date: Thu Jan 01 00:00:00 1970 +0000
+ summary: msg 0
+
+ changeset: 5:7874a09ea728
+ user: test
+ date: Thu Jan 01 00:00:05 1970 +0000
+ summary: msg 5
+
+ $ hg log -r "bisected(bad)"
+ changeset: 6:a3d5c6fdf0d3
+ user: test
+ date: Thu Jan 01 00:00:06 1970 +0000
+ summary: msg 6
+
+ $ hg log -r "bisected(skip)"
+ changeset: 1:5cd978ea5149
+ user: test
+ date: Thu Jan 01 00:00:01 1970 +0000
+ summary: msg 1
+
+ changeset: 2:db07c04beaca
+ user: test
+ date: Thu Jan 01 00:00:02 1970 +0000
+ summary: msg 2
+
+ changeset: 3:b53bea5e2fcb
+ user: test
+ date: Thu Jan 01 00:00:03 1970 +0000
+ summary: msg 3
+
+ changeset: 4:9b2ba8336a65
+ user: test
+ date: Thu Jan 01 00:00:04 1970 +0000
+ summary: msg 4
+
$ set +e
--- a/tests/test-bisect2.t Fri Apr 29 11:04:39 2011 +0200
+++ b/tests/test-bisect2.t Fri Apr 29 11:10:11 2011 +0200
@@ -416,10 +416,14 @@
summary: merge 10,13
Not all ancestors of this changeset have been checked.
- To check the other ancestors, start from the common ancestor, dab8161ac8fc.
- $ hg bisect -g 8 # dab8161ac8fc
+ Use bisect --extend to continue the bisection from
+ the common ancestor, dab8161ac8fc.
+ $ hg bisect --extend
+ Extending search to changeset 8:dab8161ac8fc
+ 2 files updated, 0 files merged, 2 files removed, 0 files unresolved
+ $ hg bisect -g # dab8161ac8fc
Testing changeset 9:3c77083deb4a (3 changesets remaining, ~1 tests)
- 1 files updated, 0 files merged, 2 files removed, 0 files unresolved
+ 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
$ hg bisect -b
The first bad revision is:
changeset: 9:3c77083deb4a
--- a/tests/test-bookmarks-pushpull.t Fri Apr 29 11:04:39 2011 +0200
+++ b/tests/test-bookmarks-pushpull.t Fri Apr 29 11:10:11 2011 +0200
@@ -26,6 +26,7 @@
adding manifests
adding file changes
added 1 changesets with 1 changes to 1 files
+ updating bookmark Y
(run 'hg update' to get a working copy)
$ hg bookmarks
Y 0:4e3505fd9583
@@ -176,5 +177,19 @@
no changes found
not updating divergent bookmark X
importing bookmark Z
+ $ hg clone http://localhost:$HGPORT/ cloned-bookmarks
+ requesting all changes
+ adding changesets
+ adding manifests
+ adding file changes
+ added 3 changesets with 3 changes to 3 files (+1 heads)
+ updating to branch default
+ 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
+ $ hg -R cloned-bookmarks bookmarks
+ X 1:9b140be10808
+ Y 0:4e3505fd9583
+ Z 2:0d2164f0ce0d
+ foo -1:000000000000
+ foobar -1:000000000000
$ kill `cat ../hg.pid`
--- a/tests/test-bookmarks.t Fri Apr 29 11:04:39 2011 +0200
+++ b/tests/test-bookmarks.t Fri Apr 29 11:10:11 2011 +0200
@@ -84,6 +84,10 @@
date: Thu Jan 01 00:00:00 1970 +0000
summary: 1
+ $ hg log -r 'bookmark(unknown)'
+ abort: bookmark 'unknown' does not exist
+ [255]
+
$ hg help revsets | grep 'bookmark('
"bookmark([name])"
@@ -116,13 +120,13 @@
rename nonexistent bookmark
$ hg bookmark -m A B
- abort: a bookmark of this name does not exist
+ abort: bookmark 'A' does not exist
[255]
rename to existent bookmark
$ hg bookmark -m X Y
- abort: a bookmark of the same name already exists
+ abort: bookmark 'Y' already exists (use -f to force)
[255]
force rename to existent bookmark
@@ -151,7 +155,7 @@
delete nonexistent bookmark
$ hg bookmark -d A
- abort: a bookmark of this name does not exist
+ abort: bookmark 'A' does not exist
[255]
bookmark name with spaces should be stripped
@@ -189,7 +193,7 @@
bookmark with existing name
$ hg bookmark Z
- abort: a bookmark of the same name already exists
+ abort: bookmark 'Z' already exists (use -f to force)
[255]
force bookmark with existing name
@@ -244,3 +248,80 @@
$ hg id
db815d6d32e6 tip Y/Z/x y
+
+test clone
+
+ $ hg bookmarks
+ X2 1:925d80f479bb
+ Y 2:db815d6d32e6
+ * Z 2:db815d6d32e6
+ x y 2:db815d6d32e6
+ $ hg clone . cloned-bookmarks
+ updating to branch default
+ 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
+ $ hg -R cloned-bookmarks bookmarks
+ X2 1:925d80f479bb
+ Y 2:db815d6d32e6
+ Z 2:db815d6d32e6
+ x y 2:db815d6d32e6
+
+test clone with pull protocol
+
+ $ hg clone --pull . cloned-bookmarks-pull
+ requesting all changes
+ adding changesets
+ adding manifests
+ adding file changes
+ added 3 changesets with 3 changes to 3 files (+1 heads)
+ updating to branch default
+ 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
+ $ hg -R cloned-bookmarks-pull bookmarks
+ X2 1:925d80f479bb
+ Y 2:db815d6d32e6
+ Z 2:db815d6d32e6
+ x y 2:db815d6d32e6
+
+test clone with a specific revision
+
+ $ hg clone -r 925d80 . cloned-bookmarks-rev
+ adding changesets
+ adding manifests
+ adding file changes
+ added 2 changesets with 2 changes to 2 files
+ updating to branch default
+ 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
+ $ hg -R cloned-bookmarks-rev bookmarks
+ X2 1:925d80f479bb
+
+create bundle with two heads
+
+ $ hg clone . tobundle
+ updating to branch default
+ 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
+ $ echo x > tobundle/x
+ $ hg -R tobundle add tobundle/x
+ $ hg -R tobundle commit -m'x'
+ $ hg -R tobundle update -r -2
+ 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
+ $ echo y > tobundle/y
+ $ hg -R tobundle branch test
+ marked working directory as branch test
+ $ hg -R tobundle add tobundle/y
+ $ hg -R tobundle commit -m'y'
+ $ hg -R tobundle bundle tobundle.hg
+ searching for changes
+ 2 changesets found
+ $ hg unbundle tobundle.hg
+ adding changesets
+ adding manifests
+ adding file changes
+ added 2 changesets with 2 changes to 2 files (+1 heads)
+ (run 'hg heads' to see heads, 'hg merge' to merge)
+ $ hg update
+ 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
+ $ hg bookmarks
+ X2 1:925d80f479bb
+ Y 2:db815d6d32e6
+ * Z 3:125c9a1d6df6
+ x y 2:db815d6d32e6
+
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/tests/test-branch-tag-confict.t Fri Apr 29 11:10:11 2011 +0200
@@ -0,0 +1,62 @@
+Initial setup.
+
+ $ hg init repo
+ $ cd repo
+ $ touch thefile
+ $ hg ci -A -m 'Initial commit.'
+ adding thefile
+
+Create a tag.
+
+ $ hg tag branchortag
+
+Create a branch with the same name as the tag.
+
+ $ hg branch branchortag
+ marked working directory as branch branchortag
+ $ hg ci -m 'Create a branch with the same name as a tag.'
+
+This is what we have:
+
+ $ hg log
+ changeset: 2:02b1af9b58c2
+ branch: branchortag
+ tag: tip
+ user: test
+ date: Thu Jan 01 00:00:00 1970 +0000
+ summary: Create a branch with the same name as a tag.
+
+ changeset: 1:2635c45ca99b
+ user: test
+ date: Thu Jan 01 00:00:00 1970 +0000
+ summary: Added tag branchortag for changeset f57387372b5d
+
+ changeset: 0:f57387372b5d
+ tag: branchortag
+ user: test
+ date: Thu Jan 01 00:00:00 1970 +0000
+ summary: Initial commit.
+
+Update to the tag:
+
+ $ hg up 'tag(branchortag)'
+ 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
+ $ hg parents
+ changeset: 0:f57387372b5d
+ tag: branchortag
+ user: test
+ date: Thu Jan 01 00:00:00 1970 +0000
+ summary: Initial commit.
+
+Updating to the branch:
+
+ $ hg up 'branch(branchortag)'
+ 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
+ $ hg parents
+ changeset: 2:02b1af9b58c2
+ branch: branchortag
+ tag: tip
+ user: test
+ date: Thu Jan 01 00:00:00 1970 +0000
+ summary: Create a branch with the same name as a tag.
+
--- a/tests/test-branches.t Fri Apr 29 11:04:39 2011 +0200
+++ b/tests/test-branches.t Fri Apr 29 11:10:11 2011 +0200
@@ -350,6 +350,8 @@
$ echo "[extensions]" >> $HGRCPATH
$ echo "color =" >> $HGRCPATH
+ $ echo "[color]" >> $HGRCPATH
+ $ echo "mode = ansi" >> $HGRCPATH
$ hg up -C c
3 files updated, 0 files merged, 2 files removed, 0 files unresolved
--- a/tests/test-bundle.t Fri Apr 29 11:04:39 2011 +0200
+++ b/tests/test-bundle.t Fri Apr 29 11:10:11 2011 +0200
@@ -206,7 +206,7 @@
hg -R bundle://../full.hg verify
$ hg pull bundle://../full.hg
- pulling from bundle://../full.hg
+ pulling from bundle:../full.hg
requesting all changes
adding changesets
adding manifests
@@ -310,7 +310,7 @@
Incoming full.hg in partial
$ hg incoming bundle://../full.hg
- comparing with bundle://../full.hg
+ comparing with bundle:../full.hg
searching for changes
changeset: 4:095197eb4973
parent: 0:f9ee2f85a263
@@ -470,6 +470,22 @@
$ cd ..
+test bundle with # in the filename (issue2154):
+
+ $ cp bundle.hg 'test#bundle.hg'
+ $ cd orig
+ $ hg incoming '../test#bundle.hg'
+ comparing with ../test
+ abort: unknown revision 'bundle.hg'!
+ [255]
+
+note that percent encoding is not handled:
+
+ $ hg incoming ../test%23bundle.hg
+ abort: repository ../test%23bundle.hg not found!
+ [255]
+ $ cd ..
+
test for http://mercurial.selenic.com/bts/issue1144
test that verify bundle does not traceback
@@ -551,26 +567,10 @@
list of changesets:
d2ae7f538514cd87c17547b0de4cea71fe1af9fb
5ece8e77363e2b5269e27c66828b72da29e4341a
- bundling: 0 changesets
- bundling: 0 changesets
- bundling: 0 changesets
- bundling: 1 changesets
- bundling: 1 changesets
bundling: 1 changesets
bundling: 2 changesets
- bundling: 0/2 manifests (0.00%)
- bundling: 0/2 manifests (0.00%)
- bundling: 0/2 manifests (0.00%)
- bundling: 1/2 manifests (50.00%)
- bundling: 1/2 manifests (50.00%)
bundling: 1/2 manifests (50.00%)
bundling: 2/2 manifests (100.00%)
bundling: b 0/2 files (0.00%)
- bundling: b 0/2 files (0.00%)
- bundling: b 0/2 files (0.00%)
- bundling: b 0/2 files (0.00%)
- bundling: b1 1/2 files (50.00%)
- bundling: b1 1/2 files (50.00%)
- bundling: b1 1/2 files (50.00%)
bundling: b1 1/2 files (50.00%)
--- a/tests/test-clone-failure.t Fri Apr 29 11:04:39 2011 +0200
+++ b/tests/test-clone-failure.t Fri Apr 29 11:10:11 2011 +0200
@@ -21,9 +21,8 @@
Inaccessible destination
- $ mkdir b
+ $ hg init b
$ cd b
- $ hg init
$ hg clone . ../a
abort: Permission denied: ../a
[255]
@@ -44,10 +43,7 @@
Default destination, same directory
- $ mkdir q
- $ cd q
- $ hg init
- $ cd ..
+ $ hg init q
$ hg clone q
destination directory: q
abort: destination 'q' is not empty
--- a/tests/test-clone.t Fri Apr 29 11:04:39 2011 +0200
+++ b/tests/test-clone.t Fri Apr 29 11:10:11 2011 +0200
@@ -1,8 +1,7 @@
Prepare repo a:
- $ mkdir a
+ $ hg init a
$ cd a
- $ hg init
$ echo a > a
$ hg add a
$ hg commit -m test
--- a/tests/test-command-template.t Fri Apr 29 11:04:39 2011 +0200
+++ b/tests/test-command-template.t Fri Apr 29 11:10:11 2011 +0200
@@ -1115,7 +1115,7 @@
$ hg log --template '{date|age}\n' > /dev/null || exit 1
$ hg log -l1 --template '{date|age}\n'
- in the future
+ 8 years from now
$ hg log --template '{date|date}\n'
Wed Jan 01 10:01:00 2020 +0000
Mon Jan 12 13:46:40 1970 +0000
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/tests/test-commit-multiple.t Fri Apr 29 11:10:11 2011 +0200
@@ -0,0 +1,129 @@
+# reproduce issue2264, issue2516
+
+create test repo
+ $ cat <<EOF >> $HGRCPATH
+ > [extensions]
+ > transplant =
+ > graphlog =
+ > EOF
+ $ hg init repo
+ $ cd repo
+ $ template="{rev} {desc|firstline} [{branch}]\n"
+
+# we need to start out with two changesets on the default branch
+# in order to avoid the cute little optimization where transplant
+# pulls rather than transplants
+add initial changesets
+ $ echo feature1 > file1
+ $ hg ci -Am"feature 1"
+ adding file1
+ $ echo feature2 >> file2
+ $ hg ci -Am"feature 2"
+ adding file2
+
+# The changes to 'bugfix' are enough to show the bug: in fact, with only
+# those changes, it's a very noisy crash ("RuntimeError: nothing
+# committed after transplant"). But if we modify a second file in the
+# transplanted changesets, the bug is much more subtle: transplant
+# silently drops the second change to 'bugfix' on the floor, and we only
+# see it when we run 'hg status' after transplanting. Subtle data loss
+# bugs are worse than crashes, so reproduce the subtle case here.
+commit bug fixes on bug fix branch
+ $ hg branch fixes
+ marked working directory as branch fixes
+ $ echo fix1 > bugfix
+ $ echo fix1 >> file1
+ $ hg ci -Am"fix 1"
+ adding bugfix
+ $ echo fix2 > bugfix
+ $ echo fix2 >> file1
+ $ hg ci -Am"fix 2"
+ $ hg glog --template="$template"
+ @ 3 fix 2 [fixes]
+ |
+ o 2 fix 1 [fixes]
+ |
+ o 1 feature 2 [default]
+ |
+ o 0 feature 1 [default]
+
+transplant bug fixes onto release branch
+ $ hg update 0
+ 1 files updated, 0 files merged, 2 files removed, 0 files unresolved
+ $ hg branch release
+ marked working directory as branch release
+ $ hg transplant 2 3
+ applying [0-9a-f]{12} (re)
+ [0-9a-f]{12} transplanted to [0-9a-f]{12} (re)
+ applying [0-9a-f]{12} (re)
+ [0-9a-f]{12} transplanted to [0-9a-f]{12} (re)
+ $ hg glog --template="$template"
+ @ 5 fix 2 [release]
+ |
+ o 4 fix 1 [release]
+ |
+ | o 3 fix 2 [fixes]
+ | |
+ | o 2 fix 1 [fixes]
+ | |
+ | o 1 feature 2 [default]
+ |/
+ o 0 feature 1 [default]
+
+ $ hg status
+ $ hg status --rev 0:4
+ M file1
+ A bugfix
+ $ hg status --rev 4:5
+ M bugfix
+ M file1
+
+now test that we fixed the bug for all scripts/extensions
+ $ cat > $TESTTMP/committwice.py <<__EOF__
+ > from mercurial import ui, hg, match, node
+ > from time import sleep
+ >
+ > def replacebyte(fn, b):
+ > f = open(fn, "rb+")
+ > f.seek(0, 0)
+ > f.write(b)
+ > f.close()
+ >
+ > def printfiles(repo, rev):
+ > print "revision %s files: %s" % (rev, repo[rev].files())
+ >
+ > repo = hg.repository(ui.ui(), '.')
+ > assert len(repo) == 6, \
+ > "initial: len(repo): %d, expected: 6" % len(repo)
+ >
+ > replacebyte("bugfix", "u")
+ > sleep(2)
+ > try:
+ > print "PRE: len(repo): %d" % len(repo)
+ > wlock = repo.wlock()
+ > lock = repo.lock()
+ > replacebyte("file1", "x")
+ > repo.commit(text="x", user="test", date=(0, 0))
+ > replacebyte("file1", "y")
+ > repo.commit(text="y", user="test", date=(0, 0))
+ > print "POST: len(repo): %d" % len(repo)
+ > finally:
+ > lock.release()
+ > wlock.release()
+ > printfiles(repo, 6)
+ > printfiles(repo, 7)
+ > __EOF__
+ $ $PYTHON $TESTTMP/committwice.py
+ PRE: len(repo): 6
+ POST: len(repo): 8
+ revision 6 files: ['bugfix', 'file1']
+ revision 7 files: ['file1']
+
+Do a size-preserving modification outside of that process
+ $ echo abcd > bugfix
+ $ hg status
+ M bugfix
+ $ hg log --template "{rev} {desc} {files}\n" -r5:
+ 5 fix 2 bugfix file1
+ 6 x bugfix file1
+ 7 y file1
--- a/tests/test-commit.t Fri Apr 29 11:04:39 2011 +0200
+++ b/tests/test-commit.t Fri Apr 29 11:10:11 2011 +0200
@@ -32,7 +32,7 @@
$ hg add bar
$ rm bar
$ hg commit -m commit-8
- nothing changed
+ nothing changed (1 missing files, see 'hg status')
[1]
$ hg commit -m commit-8-2 bar
abort: bar: file not found!
--- a/tests/test-convert-baz Fri Apr 29 11:04:39 2011 +0200
+++ b/tests/test-convert-baz Fri Apr 29 11:10:11 2011 +0200
@@ -2,10 +2,6 @@
"$TESTDIR/hghave" baz || exit 80
-mkdir do_not_use_HOME_baz
-cd do_not_use_HOME_baz
-HOME=`pwd`; export HOME
-cd ..
baz my-id "mercurial <mercurial@selenic.com>"
echo "[extensions]" >> $HGRCPATH
--- a/tests/test-convert-darcs.t Fri Apr 29 11:04:39 2011 +0200
+++ b/tests/test-convert-darcs.t Fri Apr 29 11:10:11 2011 +0200
@@ -4,7 +4,6 @@
$ echo "convert=" >> $HGRCPATH
$ echo 'graphlog =' >> $HGRCPATH
$ DARCS_EMAIL='test@example.org'; export DARCS_EMAIL
- $ HOME=`pwd`/do_not_use_HOME_darcs; export HOME
skip if we can't import elementtree
--- a/tests/test-convert-git.t Fri Apr 29 11:04:39 2011 +0200
+++ b/tests/test-convert-git.t Fri Apr 29 11:10:11 2011 +0200
@@ -57,9 +57,11 @@
2 t4.1
1 t4.2
0 Merge branch other
+ updating bookmarks
$ hg up -q -R git-repo-hg
$ hg -R git-repo-hg tip -v
changeset: 5:c78094926be2
+ bookmark: master
tag: tip
parent: 3:f5f5cb45432b
parent: 4:4e174f80c67c
@@ -217,6 +219,7 @@
sorting...
converting...
0 addbinary
+ updating bookmarks
$ cd git-repo3-hg
$ hg up -C
1 files updated, 0 files merged, 0 files removed, 0 files unresolved
@@ -248,8 +251,10 @@
converting...
1 addfoo
0 addfoo2
+ updating bookmarks
$ hg -R git-repo4-hg log -v
changeset: 1:d63e967f93da
+ bookmark: master
tag: tip
user: nottest <test@example.org>
date: Mon Jan 01 00:00:21 2007 +0000
--- a/tests/test-convert-hg-source.t Fri Apr 29 11:04:39 2011 +0200
+++ b/tests/test-convert-hg-source.t Fri Apr 29 11:10:11 2011 +0200
@@ -17,6 +17,7 @@
$ hg copy foo baz
$ hg ci -m 'make bar and baz copies of foo' -d '2 0'
created new head
+ $ hg bookmark premerge1
$ hg merge
merging baz and foo to baz
1 files updated, 1 files merged, 0 files removed, 0 files unresolved
@@ -24,6 +25,7 @@
$ hg ci -m 'merge local copy' -d '3 0'
$ hg up -C 1
1 files updated, 0 files merged, 1 files removed, 0 files unresolved
+ $ hg bookmark premerge2
$ hg merge 2
merging foo and baz to baz
1 files updated, 1 files merged, 0 files removed, 0 files unresolved
@@ -44,12 +46,16 @@
2 merge local copy
1 merge remote copy
0 mark baz executable
+ updating bookmarks
$ cd new
$ hg out ../orig
comparing with ../orig
searching for changes
no changes found
[1]
+ $ hg bookmarks
+ premerge1 3:973ef48a98a4
+ premerge2 5:13d9b87cf8f8
$ cd ..
check shamap LF and CRLF handling
@@ -76,6 +82,7 @@
converting...
1 change foo again again
0 change foo again
+ updating bookmarks
init broken repository
--- a/tests/test-convert-hg-startrev.t Fri Apr 29 11:04:39 2011 +0200
+++ b/tests/test-convert-hg-startrev.t Fri Apr 29 11:10:11 2011 +0200
@@ -1,5 +1,5 @@
- $ cat > $HGRCPATH <<EOF
+ $ cat >> $HGRCPATH <<EOF
> [extensions]
> graphlog =
> convert =
--- a/tests/test-convert-mtn.t Fri Apr 29 11:04:39 2011 +0200
+++ b/tests/test-convert-mtn.t Fri Apr 29 11:10:11 2011 +0200
@@ -8,7 +8,6 @@
$ echo "[extensions]" >> $HGRCPATH
$ echo "convert=" >> $HGRCPATH
$ echo 'graphlog =' >> $HGRCPATH
- $ HOME=`pwd`/do_not_use_HOME_mtn; export HOME
Windows version of monotone home
@@ -208,6 +207,21 @@
$ mtn ci -m divergentdirmove2
mtn: beginning commit on branch 'com.selenic.test'
mtn: committed revision 4a736634505795f17786fffdf2c9cbf5b11df6f6
+
+test large file support (> 32kB)
+
+ $ python -c 'for x in range(10000): print x' > large-file
+ $ $TESTDIR/md5sum.py large-file
+ 5d6de8a95c3b6bf9e0ffb808ba5299c1 large-file
+ $ mtn add large-file
+ mtn: adding large-file to workspace manifest
+ $ mtn ci -m largefile
+ mtn: beginning commit on branch 'com.selenic.test'
+ mtn: committed revision f0a20fecd10dc4392d18fe69a03f1f4919d3387b
+
+test suspending (closing a branch)
+
+ $ mtn suspend f0a20fecd10dc4392d18fe69a03f1f4919d3387b 2> /dev/null
$ cd ..
convert incrementally
@@ -217,27 +231,30 @@
scanning source...
sorting...
converting...
- 11 update2 "with" quotes
- 10 createdir1
- 9 movedir1
- 8 movedir
- 7 emptydir
- 6 dropdirectory
- 5 dirfilemove
- 4 dirfilemove2
- 3 dirdirmove
- 2 dirdirmove2
- 1 divergentdirmove
- 0 divergentdirmove2
+ 12 update2 "with" quotes
+ 11 createdir1
+ 10 movedir1
+ 9 movedir
+ 8 emptydir
+ 7 dropdirectory
+ 6 dirfilemove
+ 5 dirfilemove2
+ 4 dirdirmove
+ 3 dirdirmove2
+ 2 divergentdirmove
+ 1 divergentdirmove2
+ 0 largefile
$ glog()
> {
> hg glog --template '{rev} "{desc|firstline}" files: {files}\n' "$@"
> }
$ cd repo.mtn-hg
$ hg up -C
- 11 files updated, 0 files merged, 0 files removed, 0 files unresolved
+ 12 files updated, 0 files merged, 0 files removed, 0 files unresolved
$ glog
- @ 13 "divergentdirmove2" files: dir7-2/c dir7/c dir7/dir9/b dir7/dir9/dir8/a dir8-2/a dir9-2/b
+ @ 14 "largefile" files: large-file
+ |
+ o 13 "divergentdirmove2" files: dir7-2/c dir7/c dir7/dir9/b dir7/dir9/dir8/a dir8-2/a dir9-2/b
|
o 12 "divergentdirmove" files: dir7/c dir7/dir9/b dir7/dir9/dir8/a
|
@@ -280,6 +297,7 @@
dir8-2/a
dir9-2/b
e
+ large-file
contents
@@ -356,3 +374,15 @@
dir8-2/a
dir9-2/b
e
+
+test large file support (> 32kB)
+
+ $ $TESTDIR/md5sum.py large-file
+ 5d6de8a95c3b6bf9e0ffb808ba5299c1 large-file
+
+check branch closing
+
+ $ hg branches -a
+ $ hg branches -c
+ com.selenic.test 14:* (closed) (glob)
+
--- a/tests/test-convert-svn-branches.t Fri Apr 29 11:04:39 2011 +0200
+++ b/tests/test-convert-svn-branches.t Fri Apr 29 11:10:11 2011 +0200
@@ -1,7 +1,7 @@
$ "$TESTDIR/hghave" svn svn-bindings || exit 80
- $ cat > $HGRCPATH <<EOF
+ $ cat >> $HGRCPATH <<EOF
> [extensions]
> convert =
> graphlog =
@@ -32,6 +32,21 @@
1 move back to old
0 last change to a
+Test template keywords
+
+ $ hg -R A-hg log --template '{rev} {svnuuid}{svnpath}@{svnrev}\n'
+ 10 644ede6c-2b81-4367-9dc8-d786514f2cde/trunk@10
+ 9 644ede6c-2b81-4367-9dc8-d786514f2cde/branches/old@9
+ 8 644ede6c-2b81-4367-9dc8-d786514f2cde/branches/old2@8
+ 7 644ede6c-2b81-4367-9dc8-d786514f2cde/branches/old@7
+ 6 644ede6c-2b81-4367-9dc8-d786514f2cde/trunk@6
+ 5 644ede6c-2b81-4367-9dc8-d786514f2cde/branches/old@6
+ 4 644ede6c-2b81-4367-9dc8-d786514f2cde/branches/old@5
+ 3 644ede6c-2b81-4367-9dc8-d786514f2cde/trunk@4
+ 2 644ede6c-2b81-4367-9dc8-d786514f2cde/branches/old@3
+ 1 644ede6c-2b81-4367-9dc8-d786514f2cde/trunk@2
+ 0 644ede6c-2b81-4367-9dc8-d786514f2cde/trunk@1
+
Convert again
$ hg convert --branchmap=branchmap --datesort svn-repo A-hg
--- a/tests/test-convert-svn-encoding.t Fri Apr 29 11:04:39 2011 +0200
+++ b/tests/test-convert-svn-encoding.t Fri Apr 29 11:10:11 2011 +0200
@@ -1,7 +1,7 @@
$ "$TESTDIR/hghave" svn svn-bindings || exit 80
- $ cat > $HGRCPATH <<EOF
+ $ cat >> $HGRCPATH <<EOF
> [extensions]
> convert =
> graphlog =
--- a/tests/test-convert-svn-move.t Fri Apr 29 11:04:39 2011 +0200
+++ b/tests/test-convert-svn-move.t Fri Apr 29 11:10:11 2011 +0200
@@ -5,7 +5,7 @@
> {
> tr '\\' /
> }
- $ cat > $HGRCPATH <<EOF
+ $ cat >> $HGRCPATH <<EOF
> [extensions]
> convert =
> graphlog =
--- a/tests/test-convert-svn-sink.t Fri Apr 29 11:04:39 2011 +0200
+++ b/tests/test-convert-svn-sink.t Fri Apr 29 11:10:11 2011 +0200
@@ -1,5 +1,5 @@
- $ "$TESTDIR/hghave" svn svn-bindings no-outer-repo || exit 80
+ $ "$TESTDIR/hghave" svn no-outer-repo || exit 80
$ fixpath()
> {
@@ -22,7 +22,7 @@
> )
> }
- $ cat > $HGRCPATH <<EOF
+ $ cat >> $HGRCPATH <<EOF
> [extensions]
> convert =
> graphlog =
--- a/tests/test-convert-svn-source.t Fri Apr 29 11:04:39 2011 +0200
+++ b/tests/test-convert-svn-source.t Fri Apr 29 11:10:11 2011 +0200
@@ -5,7 +5,7 @@
> {
> tr '\\' /
> }
- $ cat > $HGRCPATH <<EOF
+ $ cat >> $HGRCPATH <<EOF
> [extensions]
> convert =
> graphlog =
--- a/tests/test-convert-svn-startrev.t Fri Apr 29 11:04:39 2011 +0200
+++ b/tests/test-convert-svn-startrev.t Fri Apr 29 11:10:11 2011 +0200
@@ -1,7 +1,7 @@
$ "$TESTDIR/hghave" svn svn-bindings || exit 80
- $ cat > $HGRCPATH <<EOF
+ $ cat >> $HGRCPATH <<EOF
> [extensions]
> convert =
> graphlog =
--- a/tests/test-convert-svn-tags.t Fri Apr 29 11:04:39 2011 +0200
+++ b/tests/test-convert-svn-tags.t Fri Apr 29 11:10:11 2011 +0200
@@ -1,7 +1,7 @@
$ "$TESTDIR/hghave" svn svn-bindings || exit 80
- $ cat > $HGRCPATH <<EOF
+ $ cat >> $HGRCPATH <<EOF
> [extensions]
> convert =
> graphlog =
--- a/tests/test-convert-tagsbranch-topology.t Fri Apr 29 11:04:39 2011 +0200
+++ b/tests/test-convert-tagsbranch-topology.t Fri Apr 29 11:10:11 2011 +0200
@@ -49,6 +49,7 @@
converting...
0 rev1
updating tags
+ updating bookmarks
Simulate upstream updates after first conversion
@@ -67,6 +68,7 @@
converting...
0 rev2
updating tags
+ updating bookmarks
Print the log
--- a/tests/test-convert-tla.t Fri Apr 29 11:04:39 2011 +0200
+++ b/tests/test-convert-tla.t Fri Apr 29 11:10:11 2011 +0200
@@ -1,9 +1,5 @@
$ "$TESTDIR/hghave" tla || exit 80
- $ mkdir do_not_use_HOME_tla
- $ cd do_not_use_HOME_tla
- $ HOME=`pwd`; export HOME
- $ cd ..
$ tla my-id "mercurial <mercurial@selenic.com>"
$ echo "[extensions]" >> $HGRCPATH
$ echo "convert=" >> $HGRCPATH
--- a/tests/test-copy-move-merge.t Fri Apr 29 11:04:39 2011 +0200
+++ b/tests/test-copy-move-merge.t Fri Apr 29 11:10:11 2011 +0200
@@ -1,6 +1,5 @@
- $ mkdir t
+ $ hg init t
$ cd t
- $ hg init
$ echo 1 > a
$ hg ci -qAm "first"
--- a/tests/test-copy.t Fri Apr 29 11:04:39 2011 +0200
+++ b/tests/test-copy.t Fri Apr 29 11:10:11 2011 +0200
@@ -4,6 +4,9 @@
$ hg commit -m "1"
$ hg status
$ hg copy a b
+ $ hg --config ui.portablefilenames=abort copy a con.xml
+ abort: filename contains 'con', which is reserved on Windows: 'con.xml'
+ [255]
$ hg status
A b
$ hg sum
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/tests/test-debugbundle.t Fri Apr 29 11:10:11 2011 +0200
@@ -0,0 +1,36 @@
+
+Create a test repository:
+
+ $ hg init repo
+ $ cd repo
+ $ touch a ; hg add a ; hg ci -ma
+ $ touch b ; hg add b ; hg ci -mb
+ $ touch c ; hg add c ; hg ci -mc
+ $ hg bundle --base 0 --rev tip bundle.hg
+ 2 changesets found
+
+Terse output:
+
+ $ hg debugbundle bundle.hg
+ 0e067c57feba1a5694ca4844f05588bb1bf82342
+ 991a3460af53952d10ec8a295d3d2cc2e5fa9690
+
+Verbose output:
+
+ $ hg debugbundle --all bundle.hg
+ format: id, p1, p2, cset, len(delta)
+
+ changelog
+ 0e067c57feba1a5694ca4844f05588bb1bf82342 3903775176ed42b1458a6281db4a0ccf4d9f287a 0000000000000000000000000000000000000000 0e067c57feba1a5694ca4844f05588bb1bf82342 80
+ 991a3460af53952d10ec8a295d3d2cc2e5fa9690 0e067c57feba1a5694ca4844f05588bb1bf82342 0000000000000000000000000000000000000000 991a3460af53952d10ec8a295d3d2cc2e5fa9690 80
+
+ manifest
+ 686dbf0aeca417636fa26a9121c681eabbb15a20 8515d4bfda768e04af4c13a69a72e28c7effbea7 0000000000000000000000000000000000000000 0e067c57feba1a5694ca4844f05588bb1bf82342 55
+ ae25a31b30b3490a981e7b96a3238cc69583fda1 686dbf0aeca417636fa26a9121c681eabbb15a20 0000000000000000000000000000000000000000 991a3460af53952d10ec8a295d3d2cc2e5fa9690 55
+
+ b
+ b80de5d138758541c5f05265ad144ab9fa86d1db 0000000000000000000000000000000000000000 0000000000000000000000000000000000000000 0e067c57feba1a5694ca4844f05588bb1bf82342 12
+
+ c
+ b80de5d138758541c5f05265ad144ab9fa86d1db 0000000000000000000000000000000000000000 0000000000000000000000000000000000000000 991a3460af53952d10ec8a295d3d2cc2e5fa9690 12
+
--- a/tests/test-debugcomplete.t Fri Apr 29 11:04:39 2011 +0200
+++ b/tests/test-debugcomplete.t Fri Apr 29 11:10:11 2011 +0200
@@ -67,6 +67,7 @@
$ hg debugcomplete debug
debugancestor
debugbuilddag
+ debugbundle
debugcheckstate
debugcommands
debugcomplete
@@ -75,10 +76,12 @@
debugdata
debugdate
debugfsinfo
+ debuggetbundle
debugignore
debugindex
debugindexdot
debuginstall
+ debugknown
debugpushkey
debugrebuildstate
debugrename
@@ -87,6 +90,7 @@
debugstate
debugsub
debugwalk
+ debugwireargs
Do not show the alias of a debug command if there are other candidates
(this should hide rawcommit)
@@ -199,7 +203,7 @@
addremove: similarity, include, exclude, dry-run
archive: no-decode, prefix, rev, type, subrepos, include, exclude
backout: merge, parent, tool, rev, include, exclude, message, logfile, date, user
- bisect: reset, good, bad, skip, command, noupdate
+ bisect: reset, good, bad, skip, extend, command, noupdate
bookmarks: force, rev, delete, rename
branch: force, clean
branches: active, closed
@@ -208,6 +212,7 @@
copy: after, force, include, exclude, dry-run
debugancestor:
debugbuilddag: mergeable-file, appended-file, overwritten-file, new-file
+ debugbundle: all
debugcheckstate:
debugcommands:
debugcomplete: options
@@ -215,19 +220,22 @@
debugdata:
debugdate: extended
debugfsinfo:
+ debuggetbundle: head, common, type
debugignore:
debugindex: format
debugindexdot:
debuginstall:
+ debugknown:
debugpushkey:
debugrebuildstate: rev
debugrename: rev
debugrevspec:
debugsetparents:
- debugstate: nodates
+ debugstate: nodates, datesort
debugsub: rev
debugwalk: include, exclude
- grep: print0, all, follow, ignore-case, files-with-matches, line-number, rev, user, date, include, exclude
+ debugwireargs: three, four, ssh, remotecmd, insecure
+ grep: print0, all, text, follow, ignore-case, files-with-matches, line-number, rev, user, date, include, exclude
heads: rev, topo, active, closed, style, template
help:
identify: rev, num, id, branch, tags, bookmarks
--- a/tests/test-diff-color.t Fri Apr 29 11:04:39 2011 +0200
+++ b/tests/test-diff-color.t Fri Apr 29 11:10:11 2011 +0200
@@ -1,5 +1,7 @@
Setup
+ $ echo "[color]" >> $HGRCPATH
+ $ echo "mode = ansi" >> $HGRCPATH
$ echo "[extensions]" >> $HGRCPATH
$ echo "color=" >> $HGRCPATH
$ hg init repo
@@ -81,7 +83,7 @@
\x1b[0;36;1mold mode 100644\x1b[0m (esc)
\x1b[0;36;1mnew mode 100755\x1b[0m (esc)
1 hunks, 1 lines changed
- examine changes to 'a'? [Ynsfdaq?]
+ \x1b[0;33mexamine changes to 'a'? [Ynsfdaq?]\x1b[0m (esc)
\x1b[0;35m@@ -2,7 +2,7 @@\x1b[0m (esc)
c
a
@@ -91,7 +93,7 @@
a
a
c
- record this change to 'a'? [Ynsfdaq?]
+ \x1b[0;33mrecord this change to 'a'? [Ynsfdaq?]\x1b[0m (esc)
$ echo
$ echo "[extensions]" >> $HGRCPATH
@@ -110,7 +112,7 @@
\x1b[0;36;1mold mode 100644\x1b[0m (esc)
\x1b[0;36;1mnew mode 100755\x1b[0m (esc)
1 hunks, 1 lines changed
- examine changes to 'a'? [Ynsfdaq?]
+ \x1b[0;33mexamine changes to 'a'? [Ynsfdaq?]\x1b[0m (esc)
\x1b[0;35m@@ -2,7 +2,7 @@\x1b[0m (esc)
c
a
@@ -120,6 +122,6 @@
a
a
c
- record this change to 'a'? [Ynsfdaq?]
+ \x1b[0;33mrecord this change to 'a'? [Ynsfdaq?]\x1b[0m (esc)
$ echo
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/tests/test-diff-issue2761.t Fri Apr 29 11:10:11 2011 +0200
@@ -0,0 +1,23 @@
+Test issue2761
+
+ $ hg init
+
+ $ touch to-be-deleted
+ $ hg add
+ adding to-be-deleted
+ $ hg ci -m first
+ $ echo a > to-be-deleted
+ $ hg ci -m second
+ $ rm to-be-deleted
+ $ hg diff -r 0
+
+Same issue, different code path
+
+ $ hg up -C
+ 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
+ $ touch doesnt-exist-in-1
+ $ hg add
+ adding doesnt-exist-in-1
+ $ hg ci -m third
+ $ rm doesnt-exist-in-1
+ $ hg diff -r 1
--- a/tests/test-dispatch.t Fri Apr 29 11:04:39 2011 +0200
+++ b/tests/test-dispatch.t Fri Apr 29 11:10:11 2011 +0200
@@ -18,20 +18,6 @@
output the current or given revision of files
- Print the specified files as they were at the given revision. If no
- revision is given, the parent of the working directory is used, or tip if
- no revision is checked out.
-
- Output may be to a file, in which case the name of the file is given using
- a format string. The formatting rules are the same as for the export
- command, with the following additions:
-
- "%s" basename of file being printed
- "%d" dirname of file being printed, or '.' if in repository root
- "%p" root-relative path name of file being printed
-
- Returns 0 on success.
-
options:
-o --output FORMAT print output to file with formatted name
@@ -42,7 +28,7 @@
[+] marked option can be specified multiple times
- use "hg -v help cat" to show global options
+ use "hg help cat" to show the full help text
[255]
[defaults]
@@ -61,6 +47,6 @@
$ cd $dir
$ hg cat
- abort: There is no Mercurial repository here (.hg not found)!
+ abort: no repository found in '$TESTTMP' (.hg not found)!
[255]
--- a/tests/test-doctest.py Fri Apr 29 11:04:39 2011 +0200
+++ b/tests/test-doctest.py Fri Apr 29 11:10:11 2011 +0200
@@ -13,6 +13,9 @@
import mercurial.match
doctest.testmod(mercurial.match)
+import mercurial.store
+doctest.testmod(mercurial.store)
+
import mercurial.url
doctest.testmod(mercurial.url)
--- a/tests/test-dumprevlog.t Fri Apr 29 11:04:39 2011 +0200
+++ b/tests/test-dumprevlog.t Fri Apr 29 11:10:11 2011 +0200
@@ -4,9 +4,8 @@
Prepare repo-a:
- $ mkdir repo-a
+ $ hg init repo-a
$ cd repo-a
- $ hg init
$ echo this is file a > a
$ hg add a
@@ -64,9 +63,8 @@
Undumping into repo-b:
- $ mkdir repo-b
+ $ hg init repo-b
$ cd repo-b
- $ hg init
$ python $CONTRIBDIR/undumprevlog < ../repo.dump
.hg/store/00changelog.i
.hg/store/00manifest.i
--- a/tests/test-eol-add.t Fri Apr 29 11:04:39 2011 +0200
+++ b/tests/test-eol-add.t Fri Apr 29 11:10:11 2011 +0200
@@ -1,6 +1,6 @@
Test adding .hgeol
- $ cat > $HGRCPATH <<EOF
+ $ cat >> $HGRCPATH <<EOF
> [diff]
> git = 1
> EOF
--- a/tests/test-eol-clone.t Fri Apr 29 11:04:39 2011 +0200
+++ b/tests/test-eol-clone.t Fri Apr 29 11:10:11 2011 +0200
@@ -1,9 +1,6 @@
Testing cloning with the EOL extension
- $ cat > $HGRCPATH <<EOF
- > [diff]
- > git = True
- >
+ $ cat >> $HGRCPATH <<EOF
> [extensions]
> eol =
>
--- a/tests/test-eol-hook.t Fri Apr 29 11:04:39 2011 +0200
+++ b/tests/test-eol-hook.t Fri Apr 29 11:10:11 2011 +0200
@@ -1,14 +1,7 @@
Test the EOL hook
- $ cat > $HGRCPATH <<EOF
- > [diff]
- > git = True
- > EOF
$ hg init main
$ cat > main/.hg/hgrc <<EOF
- > [extensions]
- > eol =
- >
> [hooks]
> pretxnchangegroup = python:hgext.eol.hook
> EOF
@@ -47,10 +40,12 @@
adding manifests
adding file changes
added 1 changesets with 1 changes to 1 files
- error: pretxnchangegroup hook failed: a.txt should not have CRLF line endings
+ error: pretxnchangegroup hook failed: end-of-line check failed:
+ a.txt in a8ee6548cd86 should not have CRLF line endings
transaction abort!
rollback completed
- abort: a.txt should not have CRLF line endings
+ abort: end-of-line check failed:
+ a.txt in a8ee6548cd86 should not have CRLF line endings
[255]
$ printf "first\nsecond\nthird\n" > a.txt
@@ -73,10 +68,12 @@
adding manifests
adding file changes
added 1 changesets with 1 changes to 1 files
- error: pretxnchangegroup hook failed: crlf.txt should not have LF line endings
+ error: pretxnchangegroup hook failed: end-of-line check failed:
+ crlf.txt in 004ba2132725 should not have LF line endings
transaction abort!
rollback completed
- abort: crlf.txt should not have LF line endings
+ abort: end-of-line check failed:
+ crlf.txt in 004ba2132725 should not have LF line endings
[255]
$ printf "first\r\nsecond\r\nthird\r\n" > crlf.txt
@@ -88,3 +85,133 @@
adding manifests
adding file changes
added 2 changesets with 2 changes to 1 files
+
+ $ printf "first\r\nsecond" > b.txt
+ $ hg add b.txt
+ $ hg commit -m 'CRLF b.txt'
+ $ hg push ../main
+ pushing to ../main
+ searching for changes
+ adding changesets
+ adding manifests
+ adding file changes
+ added 1 changesets with 1 changes to 1 files
+ error: pretxnchangegroup hook failed: end-of-line check failed:
+ b.txt in fbcf9b1025f5 should not have CRLF line endings
+ transaction abort!
+ rollback completed
+ abort: end-of-line check failed:
+ b.txt in fbcf9b1025f5 should not have CRLF line endings
+ [255]
+
+ $ hg up -r -2
+ 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
+ $ printf "some\nother\nfile" > c.txt
+ $ hg add c.txt
+ $ hg commit -m "LF c.txt, b.txt doesn't exist here"
+ created new head
+ $ hg push -f ../main
+ pushing to ../main
+ searching for changes
+ adding changesets
+ adding manifests
+ adding file changes
+ added 2 changesets with 2 changes to 2 files (+1 heads)
+ error: pretxnchangegroup hook failed: end-of-line check failed:
+ b.txt in fbcf9b1025f5 should not have CRLF line endings
+ transaction abort!
+ rollback completed
+ abort: end-of-line check failed:
+ b.txt in fbcf9b1025f5 should not have CRLF line endings
+ [255]
+
+Test checkheadshook alias
+
+ $ cat > ../main/.hg/hgrc <<EOF
+ > [hooks]
+ > pretxnchangegroup = python:hgext.eol.checkheadshook
+ > EOF
+ $ hg push -f ../main
+ pushing to ../main
+ searching for changes
+ adding changesets
+ adding manifests
+ adding file changes
+ added 2 changesets with 2 changes to 2 files (+1 heads)
+ error: pretxnchangegroup hook failed: end-of-line check failed:
+ b.txt in fbcf9b1025f5 should not have CRLF line endings
+ transaction abort!
+ rollback completed
+ abort: end-of-line check failed:
+ b.txt in fbcf9b1025f5 should not have CRLF line endings
+ [255]
+
+We can fix the head and push again
+
+ $ hg up 6
+ 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
+ $ printf "first\nsecond" > b.txt
+ $ hg ci -m "remove CRLF from b.txt"
+ $ hg push -f ../main
+ pushing to ../main
+ searching for changes
+ adding changesets
+ adding manifests
+ adding file changes
+ added 3 changesets with 3 changes to 2 files (+1 heads)
+ $ hg -R ../main rollback
+ repository tip rolled back to revision 5 (undo push)
+ working directory now based on revision -1
+
+Test it still fails with checkallhook
+
+ $ cat > ../main/.hg/hgrc <<EOF
+ > [hooks]
+ > pretxnchangegroup = python:hgext.eol.checkallhook
+ > EOF
+ $ hg push -f ../main
+ pushing to ../main
+ searching for changes
+ adding changesets
+ adding manifests
+ adding file changes
+ added 3 changesets with 3 changes to 2 files (+1 heads)
+ error: pretxnchangegroup hook failed: end-of-line check failed:
+ b.txt in fbcf9b1025f5 should not have CRLF line endings
+ transaction abort!
+ rollback completed
+ abort: end-of-line check failed:
+ b.txt in fbcf9b1025f5 should not have CRLF line endings
+ [255]
+
+But we can push the clean head
+
+ $ hg push -r7 -f ../main
+ pushing to ../main
+ searching for changes
+ adding changesets
+ adding manifests
+ adding file changes
+ added 1 changesets with 1 changes to 1 files
+
+Test multiple files/revisions output
+
+ $ printf "another\r\nbad\r\none" > d.txt
+ $ hg add d.txt
+ $ hg ci -m "add d.txt"
+ $ hg push -f ../main
+ pushing to ../main
+ searching for changes
+ adding changesets
+ adding manifests
+ adding file changes
+ added 3 changesets with 3 changes to 2 files (+1 heads)
+ error: pretxnchangegroup hook failed: end-of-line check failed:
+ d.txt in a7040e68714f should not have CRLF line endings
+ b.txt in fbcf9b1025f5 should not have CRLF line endings
+ transaction abort!
+ rollback completed
+ abort: end-of-line check failed:
+ d.txt in a7040e68714f should not have CRLF line endings
+ b.txt in fbcf9b1025f5 should not have CRLF line endings
+ [255]
--- a/tests/test-eol-patch.t Fri Apr 29 11:04:39 2011 +0200
+++ b/tests/test-eol-patch.t Fri Apr 29 11:10:11 2011 +0200
@@ -1,6 +1,6 @@
Test EOL patching
- $ cat > $HGRCPATH <<EOF
+ $ cat >> $HGRCPATH <<EOF
> [diff]
> git = 1
> EOF
--- a/tests/test-eol-tag.t Fri Apr 29 11:04:39 2011 +0200
+++ b/tests/test-eol-tag.t Fri Apr 29 11:10:11 2011 +0200
@@ -2,10 +2,7 @@
Testing tagging with the EOL extension
- $ cat > $HGRCPATH <<EOF
- > [diff]
- > git = True
- >
+ $ cat >> $HGRCPATH <<EOF
> [extensions]
> eol =
>
--- a/tests/test-eol-update.t Fri Apr 29 11:04:39 2011 +0200
+++ b/tests/test-eol-update.t Fri Apr 29 11:10:11 2011 +0200
@@ -1,6 +1,6 @@
Test EOL update
- $ cat > $HGRCPATH <<EOF
+ $ cat >> $HGRCPATH <<EOF
> [diff]
> git = 1
> EOF
--- a/tests/test-eol.t Fri Apr 29 11:04:39 2011 +0200
+++ b/tests/test-eol.t Fri Apr 29 11:10:11 2011 +0200
@@ -1,6 +1,6 @@
Test EOL extension
- $ cat > $HGRCPATH <<EOF
+ $ cat >> $HGRCPATH <<EOF
> [diff]
> git = True
> EOF
--- a/tests/test-eolfilename.t Fri Apr 29 11:04:39 2011 +0200
+++ b/tests/test-eolfilename.t Fri Apr 29 11:10:11 2011 +0200
@@ -36,6 +36,17 @@
o hell
o
+ $ echo bla > quickfox
+ $ hg add quickfox
+ $ hg ci -m 2
+ $ A=`printf 'quick\rfox'`
+ $ hg cp quickfox "$A"
+ abort: '\n' and '\r' disallowed in filenames: 'quick\rfox'
+ [255]
+ $ hg mv quickfox "$A"
+ abort: '\n' and '\r' disallowed in filenames: 'quick\rfox'
+ [255]
+
http://mercurial.selenic.com/bts/issue2036
$ cd ..
@@ -46,6 +57,8 @@
$ cd bar
$ echo "[extensions]" >> $HGRCPATH
$ echo "color=" >> $HGRCPATH
+ $ echo "[color]" >> $HGRCPATH
+ $ echo "mode = ansi" >> $HGRCPATH
$ A=`printf 'foo\nbar'`
$ B=`printf 'foo\nbar.baz'`
$ touch "$A"
--- a/tests/test-extdiff.t Fri Apr 29 11:04:39 2011 +0200
+++ b/tests/test-extdiff.t Fri Apr 29 11:10:11 2011 +0200
@@ -57,7 +57,7 @@
Should diff cloned files directly:
$ hg falabala -r 0:1
- diffing a.8a5febb7f867/a a.34eed99112ab/a
+ diffing */extdiff.*/a.8a5febb7f867/a a.34eed99112ab/a (glob)
[1]
Test diff during merge:
@@ -75,7 +75,7 @@
Should diff cloned file against wc file:
$ hg falabala
- diffing a.2a13a4d2da36/a $TESTTMP/a/a
+ diffing */extdiff.*/a.2a13a4d2da36/a */a/a (glob)
[1]
@@ -83,13 +83,13 @@
$ hg ci -d '2 0' -mtest3
$ hg falabala -c 1
- diffing a.8a5febb7f867/a a.34eed99112ab/a
+ diffing */extdiff.*/a.8a5febb7f867/a a.34eed99112ab/a (glob)
[1]
Check diff are made from the first parent:
$ hg falabala -c 3 || echo "diff-like tools yield a non-zero exit code"
- diffing a.2a13a4d2da36/a a.46c0e4daeb72/a
+ diffing */extdiff.*/a.2a13a4d2da36/a a.46c0e4daeb72/a (glob)
diff-like tools yield a non-zero exit code
Test extdiff of multiple files in tmp dir:
@@ -161,14 +161,27 @@
Test extdiff with --option:
$ hg extdiff -p echo -o this -c 1
- this a.8a5febb7f867/a a.34eed99112ab/a
+ this */extdiff.*/a.8a5febb7f867/a a.34eed99112ab/a (glob)
[1]
$ hg falabala -o this -c 1
- diffing this a.8a5febb7f867/a a.34eed99112ab/a
+ diffing this */extdiff.*/a.8a5febb7f867/a a.34eed99112ab/a (glob)
+ [1]
+
+Test with revsets:
+
+ $ hg extdif -p echo -c "rev(1)"
+ */extdiff.*/a.8a5febb7f867/a a.34eed99112ab/a (glob)
[1]
+
+ $ hg extdif -p echo -r "0::1"
+ */extdiff.*/a.8a5febb7f867/a a.34eed99112ab/a (glob)
+ [1]
+
$ cd ..
+Test symlinks handling (issue1909)
+
$ hg init testsymlinks
$ cd testsymlinks
$ echo a > a
@@ -181,4 +194,3 @@
diffing testsymlinks.07f494440405 testsymlinks
[1]
$ cd ..
-
--- a/tests/test-flags.t Fri Apr 29 11:04:39 2011 +0200
+++ b/tests/test-flags.t Fri Apr 29 11:10:11 2011 +0200
@@ -1,8 +1,7 @@
$ umask 027
- $ mkdir test1
+
+ $ hg init test1
$ cd test1
-
- $ hg init
$ touch a b
$ hg add a b
$ hg ci -m "added a b"
@@ -11,10 +10,9 @@
$ hg clone test1 test3
updating to branch default
2 files updated, 0 files merged, 0 files removed, 0 files unresolved
- $ mkdir test2
+
+ $ hg init test2
$ cd test2
-
- $ hg init
$ hg pull ../test1
pulling from ../test1
requesting all changes
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/tests/test-getbundle.t Fri Apr 29 11:10:11 2011 +0200
@@ -0,0 +1,253 @@
+
+= Test the getbundle() protocol function =
+
+Enable graphlog extension:
+
+ $ echo "[extensions]" >> $HGRCPATH
+ $ echo "graphlog=" >> $HGRCPATH
+
+Create a test repository:
+
+ $ hg init repo
+ $ cd repo
+ $ hg debugbuilddag -n -m '+2 :fork +5 :p1 *fork +6 :p2 /p1 :m1 +3' > /dev/null
+ $ hg glog --template '{node}\n'
+ @ 2bba2f40f321484159b395a43f20101d4bb7ead0
+ |
+ o d9e5488323c782fe684573f3043369d199038b6f
+ |
+ o 6e9a5adf5437e49c746288cf95c5ac34fa8f2f72
+ |
+ o 733bf0910832b26b768a09172f325f995b5476e1
+ |\
+ | o b5af5d6ea56d73ce24c40bc3cd19a862f74888ac
+ | |
+ | o 6b57ee934bb2996050540f84cdfc8dcad1e7267d
+ | |
+ | o 2c0ec49482e8abe888b7bd090b5827acfc22b3d7
+ | |
+ | o c1818a9f5977dd4139a48f93f5425c67d44a9368
+ | |
+ | o 6c725a58ad10aea441540bfd06c507f63e8b9cdd
+ | |
+ | o 18063366a155bd56b5618229ae2ac3e91849aa5e
+ | |
+ | o a21d913c992197a2eb60b298521ec0f045a04799
+ | |
+ o | b6b2b682253df2ffedc10e9415e4114202b303c5
+ | |
+ o | 2114148793524fd045998f71a45b0aaf139f752b
+ | |
+ o | 74a573f2ae100f1cedfad9aa7b96f8eaab1dabfc
+ | |
+ o | ea919464b16e003894c48b6cb68df3cd9411b544
+ | |
+ o | 0f82d97ec2778746743fbc996740d409558fda22
+ |/
+ o 6e23b016bc0f0e79c7bd9dd372ccee07055d7fd4
+ |
+ o 10e64d654571f11577745b4d8372e859d9e4df63
+
+ $ cd ..
+
+
+= Test locally =
+
+Get everything:
+
+ $ hg debuggetbundle repo bundle
+ $ hg debugbundle bundle
+ 10e64d654571f11577745b4d8372e859d9e4df63
+ 6e23b016bc0f0e79c7bd9dd372ccee07055d7fd4
+ 0f82d97ec2778746743fbc996740d409558fda22
+ ea919464b16e003894c48b6cb68df3cd9411b544
+ 74a573f2ae100f1cedfad9aa7b96f8eaab1dabfc
+ 2114148793524fd045998f71a45b0aaf139f752b
+ b6b2b682253df2ffedc10e9415e4114202b303c5
+ a21d913c992197a2eb60b298521ec0f045a04799
+ 18063366a155bd56b5618229ae2ac3e91849aa5e
+ 6c725a58ad10aea441540bfd06c507f63e8b9cdd
+ c1818a9f5977dd4139a48f93f5425c67d44a9368
+ 2c0ec49482e8abe888b7bd090b5827acfc22b3d7
+ 6b57ee934bb2996050540f84cdfc8dcad1e7267d
+ b5af5d6ea56d73ce24c40bc3cd19a862f74888ac
+ 733bf0910832b26b768a09172f325f995b5476e1
+ 6e9a5adf5437e49c746288cf95c5ac34fa8f2f72
+ d9e5488323c782fe684573f3043369d199038b6f
+ 2bba2f40f321484159b395a43f20101d4bb7ead0
+
+Get part of linear run:
+
+ $ hg debuggetbundle repo bundle -H d9e5488323c782fe684573f3043369d199038b6f -C 733bf0910832b26b768a09172f325f995b5476e1
+ $ hg debugbundle bundle
+ 6e9a5adf5437e49c746288cf95c5ac34fa8f2f72
+ d9e5488323c782fe684573f3043369d199038b6f
+
+Get missing branch and merge:
+
+ $ hg debuggetbundle repo bundle -H d9e5488323c782fe684573f3043369d199038b6f -C 6b57ee934bb2996050540f84cdfc8dcad1e7267d
+ $ hg debugbundle bundle
+ 0f82d97ec2778746743fbc996740d409558fda22
+ ea919464b16e003894c48b6cb68df3cd9411b544
+ 74a573f2ae100f1cedfad9aa7b96f8eaab1dabfc
+ 2114148793524fd045998f71a45b0aaf139f752b
+ b6b2b682253df2ffedc10e9415e4114202b303c5
+ b5af5d6ea56d73ce24c40bc3cd19a862f74888ac
+ 733bf0910832b26b768a09172f325f995b5476e1
+ 6e9a5adf5437e49c746288cf95c5ac34fa8f2f72
+ d9e5488323c782fe684573f3043369d199038b6f
+
+Get from only one head:
+
+ $ hg debuggetbundle repo bundle -H 6c725a58ad10aea441540bfd06c507f63e8b9cdd -C 6e23b016bc0f0e79c7bd9dd372ccee07055d7fd4
+ $ hg debugbundle bundle
+ a21d913c992197a2eb60b298521ec0f045a04799
+ 18063366a155bd56b5618229ae2ac3e91849aa5e
+ 6c725a58ad10aea441540bfd06c507f63e8b9cdd
+
+Get parts of two branches:
+
+ $ hg debuggetbundle repo bundle -H 6b57ee934bb2996050540f84cdfc8dcad1e7267d -C c1818a9f5977dd4139a48f93f5425c67d44a9368 -H 2114148793524fd045998f71a45b0aaf139f752b -C ea919464b16e003894c48b6cb68df3cd9411b544
+ $ hg debugbundle bundle
+ 74a573f2ae100f1cedfad9aa7b96f8eaab1dabfc
+ 2114148793524fd045998f71a45b0aaf139f752b
+ 2c0ec49482e8abe888b7bd090b5827acfc22b3d7
+ 6b57ee934bb2996050540f84cdfc8dcad1e7267d
+
+Check that we get all needed file changes:
+
+ $ hg debugbundle bundle --all
+ format: id, p1, p2, cset, len(delta)
+
+ changelog
+ 74a573f2ae100f1cedfad9aa7b96f8eaab1dabfc ea919464b16e003894c48b6cb68df3cd9411b544 0000000000000000000000000000000000000000 74a573f2ae100f1cedfad9aa7b96f8eaab1dabfc 99
+ 2114148793524fd045998f71a45b0aaf139f752b 74a573f2ae100f1cedfad9aa7b96f8eaab1dabfc 0000000000000000000000000000000000000000 2114148793524fd045998f71a45b0aaf139f752b 99
+ 2c0ec49482e8abe888b7bd090b5827acfc22b3d7 c1818a9f5977dd4139a48f93f5425c67d44a9368 0000000000000000000000000000000000000000 2c0ec49482e8abe888b7bd090b5827acfc22b3d7 102
+ 6b57ee934bb2996050540f84cdfc8dcad1e7267d 2c0ec49482e8abe888b7bd090b5827acfc22b3d7 0000000000000000000000000000000000000000 6b57ee934bb2996050540f84cdfc8dcad1e7267d 102
+
+ manifest
+ dac7984588fc4eea7acbf39693a9c1b06f5b175d 591f732a3faf1fb903815273f3c199a514a61ccb 0000000000000000000000000000000000000000 74a573f2ae100f1cedfad9aa7b96f8eaab1dabfc 113
+ 0772616e6b48a76afb6c1458e193cbb3dae2e4ff dac7984588fc4eea7acbf39693a9c1b06f5b175d 0000000000000000000000000000000000000000 2114148793524fd045998f71a45b0aaf139f752b 113
+ eb498cd9af6c44108e43041e951ce829e29f6c80 bff2f4817ced57b386caf7c4e3e36a4bc9af7e93 0000000000000000000000000000000000000000 2c0ec49482e8abe888b7bd090b5827acfc22b3d7 295
+ b15709c071ddd2d93188508ba156196ab4f19620 eb498cd9af6c44108e43041e951ce829e29f6c80 0000000000000000000000000000000000000000 6b57ee934bb2996050540f84cdfc8dcad1e7267d 114
+
+ mf
+ 4f73f97080266ab8e0c0561ca8d0da3eaf65b695 301ca08d026bb72cb4258a9d211bdf7ca0bcd810 0000000000000000000000000000000000000000 74a573f2ae100f1cedfad9aa7b96f8eaab1dabfc 17
+ c7b583de053293870e145f45bd2d61643563fd06 4f73f97080266ab8e0c0561ca8d0da3eaf65b695 0000000000000000000000000000000000000000 2114148793524fd045998f71a45b0aaf139f752b 18
+ 266ee3c0302a5a18f1cf96817ac79a51836179e9 edc0f6b8db80d68ae6aff2b19f7e5347ab68fa63 0000000000000000000000000000000000000000 2c0ec49482e8abe888b7bd090b5827acfc22b3d7 149
+ 698c6a36220548cd3903ca7dada27c59aa500c52 266ee3c0302a5a18f1cf96817ac79a51836179e9 0000000000000000000000000000000000000000 6b57ee934bb2996050540f84cdfc8dcad1e7267d 19
+
+ nf11
+ 33fbc651630ffa7ccbebfe4eb91320a873e7291c 0000000000000000000000000000000000000000 0000000000000000000000000000000000000000 2c0ec49482e8abe888b7bd090b5827acfc22b3d7 16
+
+ nf12
+ ddce0544363f037e9fb889faca058f52dc01c0a5 0000000000000000000000000000000000000000 0000000000000000000000000000000000000000 6b57ee934bb2996050540f84cdfc8dcad1e7267d 16
+
+ nf4
+ 3c1407305701051cbed9f9cb9a68bdfb5997c235 0000000000000000000000000000000000000000 0000000000000000000000000000000000000000 74a573f2ae100f1cedfad9aa7b96f8eaab1dabfc 15
+
+ nf5
+ 0dbd89c185f53a1727c54cd1ce256482fa23968e 0000000000000000000000000000000000000000 0000000000000000000000000000000000000000 2114148793524fd045998f71a45b0aaf139f752b 15
+
+Get branch and merge:
+
+ $ hg debuggetbundle repo bundle -C 10e64d654571f11577745b4d8372e859d9e4df63 -H 6e9a5adf5437e49c746288cf95c5ac34fa8f2f72
+ $ hg debugbundle bundle
+ 6e23b016bc0f0e79c7bd9dd372ccee07055d7fd4
+ 0f82d97ec2778746743fbc996740d409558fda22
+ ea919464b16e003894c48b6cb68df3cd9411b544
+ 74a573f2ae100f1cedfad9aa7b96f8eaab1dabfc
+ 2114148793524fd045998f71a45b0aaf139f752b
+ b6b2b682253df2ffedc10e9415e4114202b303c5
+ a21d913c992197a2eb60b298521ec0f045a04799
+ 18063366a155bd56b5618229ae2ac3e91849aa5e
+ 6c725a58ad10aea441540bfd06c507f63e8b9cdd
+ c1818a9f5977dd4139a48f93f5425c67d44a9368
+ 2c0ec49482e8abe888b7bd090b5827acfc22b3d7
+ 6b57ee934bb2996050540f84cdfc8dcad1e7267d
+ b5af5d6ea56d73ce24c40bc3cd19a862f74888ac
+ 733bf0910832b26b768a09172f325f995b5476e1
+ 6e9a5adf5437e49c746288cf95c5ac34fa8f2f72
+
+
+= Test via HTTP =
+
+Get everything:
+
+ $ hg serve -R repo -p $HGPORT -d --pid-file=hg.pid -E error.log -A access.log
+ $ cat hg.pid >> $DAEMON_PIDS
+ $ hg debuggetbundle http://localhost:$HGPORT/ bundle
+ $ hg debugbundle bundle
+ 10e64d654571f11577745b4d8372e859d9e4df63
+ 6e23b016bc0f0e79c7bd9dd372ccee07055d7fd4
+ 0f82d97ec2778746743fbc996740d409558fda22
+ ea919464b16e003894c48b6cb68df3cd9411b544
+ 74a573f2ae100f1cedfad9aa7b96f8eaab1dabfc
+ 2114148793524fd045998f71a45b0aaf139f752b
+ b6b2b682253df2ffedc10e9415e4114202b303c5
+ a21d913c992197a2eb60b298521ec0f045a04799
+ 18063366a155bd56b5618229ae2ac3e91849aa5e
+ 6c725a58ad10aea441540bfd06c507f63e8b9cdd
+ c1818a9f5977dd4139a48f93f5425c67d44a9368
+ 2c0ec49482e8abe888b7bd090b5827acfc22b3d7
+ 6b57ee934bb2996050540f84cdfc8dcad1e7267d
+ b5af5d6ea56d73ce24c40bc3cd19a862f74888ac
+ 733bf0910832b26b768a09172f325f995b5476e1
+ 6e9a5adf5437e49c746288cf95c5ac34fa8f2f72
+ d9e5488323c782fe684573f3043369d199038b6f
+ 2bba2f40f321484159b395a43f20101d4bb7ead0
+
+Get parts of two branches:
+
+ $ hg debuggetbundle http://localhost:$HGPORT/ bundle -H 6b57ee934bb2996050540f84cdfc8dcad1e7267d -C c1818a9f5977dd4139a48f93f5425c67d44a9368 -H 2114148793524fd045998f71a45b0aaf139f752b -C ea919464b16e003894c48b6cb68df3cd9411b544
+ $ hg debugbundle bundle
+ 74a573f2ae100f1cedfad9aa7b96f8eaab1dabfc
+ 2114148793524fd045998f71a45b0aaf139f752b
+ 2c0ec49482e8abe888b7bd090b5827acfc22b3d7
+ 6b57ee934bb2996050540f84cdfc8dcad1e7267d
+
+Check that we get all needed file changes:
+
+ $ hg debugbundle bundle --all
+ format: id, p1, p2, cset, len(delta)
+
+ changelog
+ 74a573f2ae100f1cedfad9aa7b96f8eaab1dabfc ea919464b16e003894c48b6cb68df3cd9411b544 0000000000000000000000000000000000000000 74a573f2ae100f1cedfad9aa7b96f8eaab1dabfc 99
+ 2114148793524fd045998f71a45b0aaf139f752b 74a573f2ae100f1cedfad9aa7b96f8eaab1dabfc 0000000000000000000000000000000000000000 2114148793524fd045998f71a45b0aaf139f752b 99
+ 2c0ec49482e8abe888b7bd090b5827acfc22b3d7 c1818a9f5977dd4139a48f93f5425c67d44a9368 0000000000000000000000000000000000000000 2c0ec49482e8abe888b7bd090b5827acfc22b3d7 102
+ 6b57ee934bb2996050540f84cdfc8dcad1e7267d 2c0ec49482e8abe888b7bd090b5827acfc22b3d7 0000000000000000000000000000000000000000 6b57ee934bb2996050540f84cdfc8dcad1e7267d 102
+
+ manifest
+ dac7984588fc4eea7acbf39693a9c1b06f5b175d 591f732a3faf1fb903815273f3c199a514a61ccb 0000000000000000000000000000000000000000 74a573f2ae100f1cedfad9aa7b96f8eaab1dabfc 113
+ 0772616e6b48a76afb6c1458e193cbb3dae2e4ff dac7984588fc4eea7acbf39693a9c1b06f5b175d 0000000000000000000000000000000000000000 2114148793524fd045998f71a45b0aaf139f752b 113
+ eb498cd9af6c44108e43041e951ce829e29f6c80 bff2f4817ced57b386caf7c4e3e36a4bc9af7e93 0000000000000000000000000000000000000000 2c0ec49482e8abe888b7bd090b5827acfc22b3d7 295
+ b15709c071ddd2d93188508ba156196ab4f19620 eb498cd9af6c44108e43041e951ce829e29f6c80 0000000000000000000000000000000000000000 6b57ee934bb2996050540f84cdfc8dcad1e7267d 114
+
+ mf
+ 4f73f97080266ab8e0c0561ca8d0da3eaf65b695 301ca08d026bb72cb4258a9d211bdf7ca0bcd810 0000000000000000000000000000000000000000 74a573f2ae100f1cedfad9aa7b96f8eaab1dabfc 17
+ c7b583de053293870e145f45bd2d61643563fd06 4f73f97080266ab8e0c0561ca8d0da3eaf65b695 0000000000000000000000000000000000000000 2114148793524fd045998f71a45b0aaf139f752b 18
+ 266ee3c0302a5a18f1cf96817ac79a51836179e9 edc0f6b8db80d68ae6aff2b19f7e5347ab68fa63 0000000000000000000000000000000000000000 2c0ec49482e8abe888b7bd090b5827acfc22b3d7 149
+ 698c6a36220548cd3903ca7dada27c59aa500c52 266ee3c0302a5a18f1cf96817ac79a51836179e9 0000000000000000000000000000000000000000 6b57ee934bb2996050540f84cdfc8dcad1e7267d 19
+
+ nf11
+ 33fbc651630ffa7ccbebfe4eb91320a873e7291c 0000000000000000000000000000000000000000 0000000000000000000000000000000000000000 2c0ec49482e8abe888b7bd090b5827acfc22b3d7 16
+
+ nf12
+ ddce0544363f037e9fb889faca058f52dc01c0a5 0000000000000000000000000000000000000000 0000000000000000000000000000000000000000 6b57ee934bb2996050540f84cdfc8dcad1e7267d 16
+
+ nf4
+ 3c1407305701051cbed9f9cb9a68bdfb5997c235 0000000000000000000000000000000000000000 0000000000000000000000000000000000000000 74a573f2ae100f1cedfad9aa7b96f8eaab1dabfc 15
+
+ nf5
+ 0dbd89c185f53a1727c54cd1ce256482fa23968e 0000000000000000000000000000000000000000 0000000000000000000000000000000000000000 2114148793524fd045998f71a45b0aaf139f752b 15
+
+Verify we hit the HTTP server:
+
+ $ cat access.log
+ * - - [*] "GET /?cmd=capabilities HTTP/1.1" 200 - (glob)
+ * - - [*] "GET /?cmd=getbundle HTTP/1.1" 200 - (glob)
+ * - - [*] "GET /?cmd=capabilities HTTP/1.1" 200 - (glob)
+ * - - [*] "GET /?cmd=getbundle&common=c1818a9f5977dd4139a48f93f5425c67d44a9368+ea919464b16e003894c48b6cb68df3cd9411b544&heads=6b57ee934bb2996050540f84cdfc8dcad1e7267d+2114148793524fd045998f71a45b0aaf139f752b HTTP/1.1" 200 - (glob)
+
+ $ cat error.log
+
--- a/tests/test-globalopts.t Fri Apr 29 11:04:39 2011 +0200
+++ b/tests/test-globalopts.t Fri Apr 29 11:10:11 2011 +0200
@@ -28,6 +28,7 @@
pulling from ../b
searching for changes
warning: repository is unrelated
+ requesting all changes
adding changesets
adding manifests
adding file changes
@@ -83,13 +84,13 @@
$ hg ann a/a a/a
0: a
$ hg ann a/a b/b
- abort: There is no Mercurial repository here (.hg not found)!
+ abort: no repository found in '$TESTTMP' (.hg not found)!
[255]
$ hg -R b ann a/a
abort: a/a not under root
[255]
$ hg log
- abort: There is no Mercurial repository here (.hg not found)!
+ abort: no repository found in '$TESTTMP' (.hg not found)!
[255]
Abbreviation of long option:
@@ -334,22 +335,24 @@
config Configuration Files
dates Date Formats
- patterns File Name Patterns
+ diffs Diff Formats
environment Environment Variables
- revisions Specifying Single Revisions
+ extensions Using additional features
+ glossary Glossary
+ hgweb Configuring hgweb
+ merge-tools Merge Tools
multirevs Specifying Multiple Revisions
+ patterns File Name Patterns
+ revisions Specifying Single Revisions
revsets Specifying Revision Sets
- diffs Diff Formats
- merge-tools Merge Tools
+ subrepos Subrepositories
templating Template Usage
urls URL Paths
- extensions Using additional features
- subrepos Subrepositories
- hgweb Configuring hgweb
- glossary Glossary
use "hg -v help" to show builtin aliases and global options
+
+
$ hg --help
Mercurial Distributed SCM
@@ -411,19 +414,19 @@
config Configuration Files
dates Date Formats
- patterns File Name Patterns
+ diffs Diff Formats
environment Environment Variables
- revisions Specifying Single Revisions
+ extensions Using additional features
+ glossary Glossary
+ hgweb Configuring hgweb
+ merge-tools Merge Tools
multirevs Specifying Multiple Revisions
+ patterns File Name Patterns
+ revisions Specifying Single Revisions
revsets Specifying Revision Sets
- diffs Diff Formats
- merge-tools Merge Tools
+ subrepos Subrepositories
templating Template Usage
urls URL Paths
- extensions Using additional features
- subrepos Subrepositories
- hgweb Configuring hgweb
- glossary Glossary
use "hg -v help" to show builtin aliases and global options
--- a/tests/test-grep.t Fri Apr 29 11:04:39 2011 +0200
+++ b/tests/test-grep.t Fri Apr 29 11:10:11 2011 +0200
@@ -1,6 +1,5 @@
- $ mkdir t
+ $ hg init t
$ cd t
- $ hg init
$ echo import > port
$ hg add port
$ hg commit -m 0 -u spam -d '0 0'
@@ -166,3 +165,11 @@
$ hg grep --all red
color:3:-:red
color:1:+:red
+
+ $ hg init a
+ $ cd a
+ $ cp $TESTDIR/binfile.bin .
+ $ hg add binfile.bin
+ $ hg ci -m 'add binfile.bin'
+ $ hg grep "MaCam" --all
+ binfile.bin:0:+: Binary file matches
--- a/tests/test-hardlinks.t Fri Apr 29 11:04:39 2011 +0200
+++ b/tests/test-hardlinks.t Fri Apr 29 11:10:11 2011 +0200
@@ -25,9 +25,8 @@
Prepare repo r1:
- $ mkdir r1
+ $ hg init r1
$ cd r1
- $ hg init
$ echo c1 > f1
$ hg add f1
--- a/tests/test-help.t Fri Apr 29 11:04:39 2011 +0200
+++ b/tests/test-help.t Fri Apr 29 11:10:11 2011 +0200
@@ -105,19 +105,19 @@
config Configuration Files
dates Date Formats
- patterns File Name Patterns
+ diffs Diff Formats
environment Environment Variables
- revisions Specifying Single Revisions
+ extensions Using additional features
+ glossary Glossary
+ hgweb Configuring hgweb
+ merge-tools Merge Tools
multirevs Specifying Multiple Revisions
+ patterns File Name Patterns
+ revisions Specifying Single Revisions
revsets Specifying Revision Sets
- diffs Diff Formats
- merge-tools Merge Tools
+ subrepos Subrepositories
templating Template Usage
urls URL Paths
- extensions Using additional features
- subrepos Subrepositories
- hgweb Configuring hgweb
- glossary Glossary
use "hg -v help" to show builtin aliases and global options
@@ -178,19 +178,19 @@
config Configuration Files
dates Date Formats
- patterns File Name Patterns
+ diffs Diff Formats
environment Environment Variables
- revisions Specifying Single Revisions
+ extensions Using additional features
+ glossary Glossary
+ hgweb Configuring hgweb
+ merge-tools Merge Tools
multirevs Specifying Multiple Revisions
+ patterns File Name Patterns
+ revisions Specifying Single Revisions
revsets Specifying Revision Sets
- diffs Diff Formats
- merge-tools Merge Tools
+ subrepos Subrepositories
templating Template Usage
urls URL Paths
- extensions Using additional features
- subrepos Subrepositories
- hgweb Configuring hgweb
- glossary Glossary
Test short command list with verbose option
@@ -387,17 +387,6 @@
add the specified files on the next commit
- Schedule files to be version controlled and added to the repository.
-
- The files will be added to the repository at the next commit. To undo an
- add before that, see "hg forget".
-
- If no names are given, add all files to the repository.
-
- Returns 0 if all files are successfully added.
-
- use "hg -v help add" to show verbose help
-
options:
-I --include PATTERN [+] include names matching the given patterns
@@ -407,7 +396,7 @@
[+] marked option can be specified multiple times
- use "hg -v help add" to show global options
+ use "hg help add" to show the full help text
[255]
Test ambiguous command help
@@ -705,22 +694,24 @@
config Configuration Files
dates Date Formats
- patterns File Name Patterns
+ diffs Diff Formats
environment Environment Variables
- revisions Specifying Single Revisions
+ extensions Using additional features
+ glossary Glossary
+ hgweb Configuring hgweb
+ merge-tools Merge Tools
multirevs Specifying Multiple Revisions
+ patterns File Name Patterns
+ revisions Specifying Single Revisions
revsets Specifying Revision Sets
- diffs Diff Formats
- merge-tools Merge Tools
+ subrepos Subrepositories
templating Template Usage
urls URL Paths
- extensions Using additional features
- subrepos Subrepositories
- hgweb Configuring hgweb
- glossary Glossary
use "hg -v help" to show builtin aliases and global options
+
+
Test list of commands with command with no help text
$ hg help helpext
@@ -765,6 +756,14 @@
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 templating help
+
+ $ hg help templating | egrep '(desc|diffstat|firstline|nonempty) '
+ desc String. The text of the changeset description.
+ diffstat String. Statistics of changes with the following format:
+ firstline Any text. Returns the first line of text.
+ nonempty Any text. Returns '(none)' if the string is empty.
+
Test help hooks
$ cat > helphook1.py <<EOF
--- a/tests/test-hg-parseurl.py Fri Apr 29 11:04:39 2011 +0200
+++ b/tests/test-hg-parseurl.py Fri Apr 29 11:10:11 2011 +0200
@@ -8,3 +8,6 @@
testparse('http://example.com/no/anchor/branches', branch=['foo'])
testparse('http://example.com/an/anchor/branches#bar', branch=['foo'])
testparse('http://example.com/an/anchor/branches-None#foo', branch=None)
+testparse('http://example.com/')
+testparse('http://example.com')
+testparse('http://example.com#foo')
--- a/tests/test-hg-parseurl.py.out Fri Apr 29 11:04:39 2011 +0200
+++ b/tests/test-hg-parseurl.py.out Fri Apr 29 11:10:11 2011 +0200
@@ -3,3 +3,6 @@
http://example.com/no/anchor/branches, branches: (None, ['foo'])
http://example.com/an/anchor/branches, branches: ('bar', ['foo'])
http://example.com/an/anchor/branches-None, branches: ('foo', [])
+http://example.com/, branches: (None, [])
+http://example.com/, branches: (None, [])
+http://example.com/, branches: ('foo', [])
--- a/tests/test-hgrc.t Fri Apr 29 11:04:39 2011 +0200
+++ b/tests/test-hgrc.t Fri Apr 29 11:10:11 2011 +0200
@@ -20,12 +20,12 @@
$ cd foobar
$ cat .hg/hgrc
[paths]
- default = */foo%bar (glob)
+ default = $TESTTMP/foo%bar
$ hg paths
- default = */foo%bar (glob)
+ default = $TESTTMP/foo%bar
$ hg showconfig
- bundle.mainreporoot=*/foobar (glob)
- paths.default=*/foo%bar (glob)
+ bundle.mainreporoot=$TESTTMP/foobar
+ paths.default=$TESTTMP/foo%bar
$ cd ..
issue1829: wrong indentation
@@ -133,3 +133,39 @@
none: ui.verbose=False
none: ui.debug=True
none: ui.quiet=False
+
+plain mode with exceptions
+
+ $ cat > plain.py <<EOF
+ > def uisetup(ui):
+ > ui.write('plain: %r\n' % ui.plain())
+ > EOF
+ $ echo "[extensions]" >> $HGRCPATH
+ $ echo "plain=./plain.py" >> $HGRCPATH
+ $ HGPLAINEXCEPT=; export HGPLAINEXCEPT
+ $ hg showconfig --config ui.traceback=True --debug
+ plain: ['']
+ read config from: $TESTTMP/hgrc
+ $TESTTMP/hgrc:15: extensions.plain=./plain.py
+ none: ui.traceback=True
+ none: ui.verbose=False
+ none: ui.debug=True
+ none: ui.quiet=False
+ $ unset HGPLAIN
+ $ hg showconfig --config ui.traceback=True --debug
+ plain: ['']
+ read config from: $TESTTMP/hgrc
+ $TESTTMP/hgrc:15: extensions.plain=./plain.py
+ none: ui.traceback=True
+ none: ui.verbose=False
+ none: ui.debug=True
+ none: ui.quiet=False
+ $ HGPLAINEXCEPT=i18n; export HGPLAINEXCEPT
+ $ hg showconfig --config ui.traceback=True --debug
+ plain: ['i18n']
+ read config from: $TESTTMP/hgrc
+ $TESTTMP/hgrc:15: extensions.plain=./plain.py
+ none: ui.traceback=True
+ none: ui.verbose=False
+ none: ui.debug=True
+ none: ui.quiet=False
--- a/tests/test-hgweb-commands.t Fri Apr 29 11:04:39 2011 +0200
+++ b/tests/test-hgweb-commands.t Fri Apr 29 11:10:11 2011 +0200
@@ -16,6 +16,7 @@
adding foo
$ hg tag 1.0
$ hg bookmark something
+ $ hg bookmark -r0 anotherthing
$ echo another > foo
$ hg branch stable
marked working directory as branch stable
@@ -256,7 +257,7 @@
<tr class="parity0">
<td class="age">1970-01-01</td>
<td class="author">test</td>
- <td class="description"><a href="/rev/2ef0ac749a14">base</a><span class="tag">1.0</span> </td>
+ <td class="description"><a href="/rev/2ef0ac749a14">base</a><span class="tag">1.0</span> <span class="tag">anotherthing</span> </td>
</tr>
</table>
@@ -317,7 +318,7 @@
<div class="main">
<h2><a href="/">test</a></h2>
- <h3>changeset 0:2ef0ac749a14 <span class="tag">1.0</span> </h3>
+ <h3>changeset 0:2ef0ac749a14 <span class="tag">1.0</span> <span class="tag">anotherthing</span> </h3>
<form class="search" action="/log">
@@ -443,7 +444,7 @@
<tr class="parity0">
<td class="age">1970-01-01</td>
<td class="author">test</td>
- <td class="description"><a href="/rev/2ef0ac749a14">base</a><span class="tag">1.0</span> </td>
+ <td class="description"><a href="/rev/2ef0ac749a14">base</a><span class="tag">1.0</span> <span class="tag">anotherthing</span> </td>
</tr>
</table>
@@ -603,6 +604,11 @@
stable 1d22e65f027e5a0609357e7d8e7508cd2ba5d2fe open
default a4f92ed23982be056b9852de5dfe873eaac7f0de inactive
+ $ "$TESTDIR/get-with-headers.py" 127.0.0.1:$HGPORT '/raw-bookmarks'
+ 200 Script output follows
+
+ anotherthing 2ef0ac749a14e4f57a5a822464a0902c6f7f448f
+ something 1d22e65f027e5a0609357e7d8e7508cd2ba5d2fe
$ "$TESTDIR/get-with-headers.py" 127.0.0.1:$HGPORT '/summary/?style=gitweb'
200 Script output follows
@@ -691,7 +697,7 @@
<td>
<a class="list" href="/rev/2ef0ac749a14?style=gitweb">
<b>base</b>
- <span class="logtags"><span class="tagtag" title="1.0">1.0</span> </span>
+ <span class="logtags"><span class="tagtag" title="1.0">1.0</span> <span class="bookmarktag" title="anotherthing">anotherthing</span> </span>
</a>
</td>
<td class="link" nowrap>
@@ -717,6 +723,30 @@
<tr class="light"><td colspan="3"><a class="list" href="/tags?style=gitweb">...</a></td></tr>
</table>
+ <div><a class="title" href="/bookmarks?style=gitweb">bookmarks</a></div>
+ <table cellspacing="0">
+
+ <tr class="parity0">
+ <td class="age"><i>1970-01-01</i></td>
+ <td><a class="list" href="/rev/2ef0ac749a14?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> |
+ <a href="/file/2ef0ac749a14?style=gitweb">files</a>
+ </td>
+ </tr>
+ <tr class="parity1">
+ <td class="age"><i>1970-01-01</i></td>
+ <td><a class="list" href="/rev/1d22e65f027e?style=gitweb"><b>something</b></a></td>
+ <td class="link">
+ <a href="/rev/1d22e65f027e?style=gitweb">changeset</a> |
+ <a href="/log/1d22e65f027e?style=gitweb">changelog</a> |
+ <a href="/file/1d22e65f027e?style=gitweb">files</a>
+ </td>
+ </tr>
+ <tr class="light"><td colspan="3"><a class="list" href="/bookmarks?style=gitweb">...</a></td></tr>
+ </table>
+
<div><a class="title" href="#">branches</a></div>
<table cellspacing="0">
@@ -817,7 +847,7 @@
<script>
<!-- hide script content
- var data = [["1d22e65f027e", [0, 1], [[0, 0, 1]], "branch", "test", "1970-01-01", ["stable", true], ["tip"], ["something"]], ["a4f92ed23982", [0, 1], [[0, 0, 1]], "Added tag 1.0 for changeset 2ef0ac749a14", "test", "1970-01-01", ["default", true], [], []], ["2ef0ac749a14", [0, 1], [], "base", "test", "1970-01-01", ["default", false], ["1.0"], []]];
+ var data = [["1d22e65f027e", [0, 1], [[0, 0, 1]], "branch", "test", "1970-01-01", ["stable", true], ["tip"], ["something"]], ["a4f92ed23982", [0, 1], [[0, 0, 1]], "Added tag 1.0 for changeset 2ef0ac749a14", "test", "1970-01-01", ["default", true], [], []], ["2ef0ac749a14", [0, 1], [], "base", "test", "1970-01-01", ["default", false], ["1.0"], ["anotherthing"]]];
var graph = new Graph();
graph.scale(39);
@@ -913,7 +943,7 @@
$ "$TESTDIR/get-with-headers.py" 127.0.0.1:$HGPORT '?cmd=capabilities'; echo
200 Script output follows
- lookup changegroupsubset branchmap pushkey unbundle=HG10GZ,HG10BZ,HG10UN
+ lookup changegroupsubset branchmap pushkey known getbundle unbundlehash unbundle=HG10GZ,HG10BZ,HG10UN
heads
@@ -1082,7 +1112,7 @@
$ "$TESTDIR/get-with-headers.py" 127.0.0.1:$HGPORT '/graph/' \
> | grep '^var data ='
- var data = [["40b4d6888e92", [0, 1], [[0, 0, 1]], "\u80fd", "test", "1970-01-01", ["stable", true], ["tip"], ["something"]], ["1d22e65f027e", [0, 1], [[0, 0, 1]], "branch", "test", "1970-01-01", ["stable", false], [], []], ["a4f92ed23982", [0, 1], [[0, 0, 1]], "Added tag 1.0 for changeset 2ef0ac749a14", "test", "1970-01-01", ["default", true], [], []], ["2ef0ac749a14", [0, 1], [], "base", "test", "1970-01-01", ["default", false], ["1.0"], []]];
+ var data = [["40b4d6888e92", [0, 1], [[0, 0, 1]], "\u80fd", "test", "1970-01-01", ["stable", true], ["tip"], ["something"]], ["1d22e65f027e", [0, 1], [[0, 0, 1]], "branch", "test", "1970-01-01", ["stable", false], [], []], ["a4f92ed23982", [0, 1], [[0, 0, 1]], "Added tag 1.0 for changeset 2ef0ac749a14", "test", "1970-01-01", ["default", true], [], []], ["2ef0ac749a14", [0, 1], [], "base", "test", "1970-01-01", ["default", false], ["1.0"], ["anotherthing"]]];
ERRORS ENCOUNTERED
--- a/tests/test-hgweb-no-path-info.t Fri Apr 29 11:04:39 2011 +0200
+++ b/tests/test-hgweb-no-path-info.t Fri Apr 29 11:10:11 2011 +0200
@@ -2,9 +2,8 @@
no longer passed with the request. Instead, SCRIPT_NAME and PATH_INFO
should be used from d74fc8dec2b4 onward to route the request.
- $ mkdir repo
+ $ hg init repo
$ cd repo
- $ hg init
$ echo foo > bar
$ hg add bar
$ hg commit -m "test"
--- a/tests/test-hgweb-no-request-uri.t Fri Apr 29 11:04:39 2011 +0200
+++ b/tests/test-hgweb-no-request-uri.t Fri Apr 29 11:10:11 2011 +0200
@@ -2,9 +2,8 @@
no longer passed with the request. Instead, SCRIPT_NAME and PATH_INFO
should be used from d74fc8dec2b4 onward to route the request.
- $ mkdir repo
+ $ hg init repo
$ cd repo
- $ hg init
$ echo foo > bar
$ hg add bar
$ hg commit -m "test"
--- a/tests/test-hgweb-non-interactive.t Fri Apr 29 11:04:39 2011 +0200
+++ b/tests/test-hgweb-non-interactive.t Fri Apr 29 11:10:11 2011 +0200
@@ -1,9 +1,8 @@
Tests if hgweb can run without touching sys.stdin, as is required
by the WSGI standard and strictly implemented by mod_wsgi.
- $ mkdir repo
+ $ hg init repo
$ cd repo
- $ hg init
$ echo foo > bar
$ hg add bar
$ hg commit -m "test"
--- a/tests/test-hgweb-raw.t Fri Apr 29 11:04:39 2011 +0200
+++ b/tests/test-hgweb-raw.t Fri Apr 29 11:10:11 2011 +0200
@@ -10,6 +10,7 @@
> care about things like that.
> ENDSOME
$ hg add 'sub/some "text".txt'
+ warning: filename contains '"', which is reserved on Windows: 'sub/some "text".txt'
$ hg commit -d "1 0" -m "Just some text"
$ hg serve -p $HGPORT -A access.log -E error.log -d --pid-file=hg.pid
--- a/tests/test-http-branchmap.t Fri Apr 29 11:04:39 2011 +0200
+++ b/tests/test-http-branchmap.t Fri Apr 29 11:10:11 2011 +0200
@@ -30,7 +30,7 @@
$ echo bar >> b/foo
$ hg -R b ci -m bar
$ hg --encoding utf-8 -R b push
- pushing to http://localhost:$HGPORT1
+ pushing to http://localhost:$HGPORT1/
searching for changes
remote: adding changesets
remote: adding manifests
--- a/tests/test-http-clone-r.t Fri Apr 29 11:04:39 2011 +0200
+++ b/tests/test-http-clone-r.t Fri Apr 29 11:10:11 2011 +0200
@@ -214,7 +214,7 @@
adding changesets
adding manifests
adding file changes
- added 1 changesets with 0 changes to 1 files (+1 heads)
+ added 1 changesets with 0 changes to 0 files (+1 heads)
(run 'hg heads' to see heads, 'hg merge' to merge)
$ hg verify
checking changesets
@@ -238,7 +238,7 @@
adding changesets
adding manifests
adding file changes
- added 2 changesets with 0 changes to 1 files (+1 heads)
+ added 2 changesets with 0 changes to 0 files (+1 heads)
(run 'hg heads' to see heads, 'hg merge' to merge)
$ hg verify
checking changesets
--- a/tests/test-http-proxy.t Fri Apr 29 11:04:39 2011 +0200
+++ b/tests/test-http-proxy.t Fri Apr 29 11:10:11 2011 +0200
@@ -98,27 +98,23 @@
updating to branch default
1 files updated, 0 files merged, 0 files removed, 0 files unresolved
$ cat proxy.log
- * - - [*] "GET http://localhost:$HGPORT/?pairs=0000000000000000000000000000000000000000-0000000000000000000000000000000000000000&cmd=between HTTP/1.1" - - (glob)
* - - [*] "GET http://localhost:$HGPORT/?cmd=capabilities HTTP/1.1" - - (glob)
* - - [*] "GET http://localhost:$HGPORT/?cmd=stream_out HTTP/1.1" - - (glob)
- * - - [*] "GET http://localhost:$HGPORT/?pairs=0000000000000000000000000000000000000000-0000000000000000000000000000000000000000&cmd=between HTTP/1.1" - - (glob)
- * - - [*] "GET http://localhost:$HGPORT/?cmd=heads HTTP/1.1" - - (glob)
- * - - [*] "GET http://localhost:$HGPORT/?cmd=changegroup&roots=0000000000000000000000000000000000000000 HTTP/1.1" - - (glob)
+ * - - [*] "GET http://localhost:$HGPORT/?cmd=listkeys&namespace=bookmarks HTTP/1.1" - - (glob)
* - - [*] "GET http://localhost:$HGPORT/?cmd=capabilities HTTP/1.1" - - (glob)
- * - - [*] "GET http://localhost:$HGPORT/?cmd=listkeys&namespace=bookmarks HTTP/1.1" - - (glob)
- * - - [*] "GET http://localhost:$HGPORT/?pairs=0000000000000000000000000000000000000000-0000000000000000000000000000000000000000&cmd=between HTTP/1.1" - - (glob)
* - - [*] "GET http://localhost:$HGPORT/?cmd=heads HTTP/1.1" - - (glob)
- * - - [*] "GET http://localhost:$HGPORT/?cmd=changegroup&roots=0000000000000000000000000000000000000000 HTTP/1.1" - - (glob)
- * - - [*] "GET http://localhost:$HGPORT/?cmd=capabilities HTTP/1.1" - - (glob)
+ * - - [*] "GET http://localhost:$HGPORT/?cmd=getbundle&common=0000000000000000000000000000000000000000&heads=83180e7845de420a1bb46896fd5fe05294f8d629 HTTP/1.1" - - (glob)
* - - [*] "GET http://localhost:$HGPORT/?cmd=listkeys&namespace=bookmarks HTTP/1.1" - - (glob)
- * - - [*] "GET http://localhost:$HGPORT/?pairs=0000000000000000000000000000000000000000-0000000000000000000000000000000000000000&cmd=between HTTP/1.1" - - (glob)
+ * - - [*] "GET http://localhost:$HGPORT/?cmd=capabilities HTTP/1.1" - - (glob)
* - - [*] "GET http://localhost:$HGPORT/?cmd=heads HTTP/1.1" - - (glob)
- * - - [*] "GET http://localhost:$HGPORT/?cmd=changegroup&roots=0000000000000000000000000000000000000000 HTTP/1.1" - - (glob)
- * - - [*] "GET http://localhost:$HGPORT/?cmd=capabilities HTTP/1.1" - - (glob)
+ * - - [*] "GET http://localhost:$HGPORT/?cmd=getbundle&common=0000000000000000000000000000000000000000&heads=83180e7845de420a1bb46896fd5fe05294f8d629 HTTP/1.1" - - (glob)
* - - [*] "GET http://localhost:$HGPORT/?cmd=listkeys&namespace=bookmarks HTTP/1.1" - - (glob)
- * - - [*] "GET http://localhost:$HGPORT/?pairs=0000000000000000000000000000000000000000-0000000000000000000000000000000000000000&cmd=between HTTP/1.1" - - (glob)
+ * - - [*] "GET http://localhost:$HGPORT/?cmd=capabilities HTTP/1.1" - - (glob)
* - - [*] "GET http://localhost:$HGPORT/?cmd=heads HTTP/1.1" - - (glob)
- * - - [*] "GET http://localhost:$HGPORT/?cmd=changegroup&roots=0000000000000000000000000000000000000000 HTTP/1.1" - - (glob)
+ * - - [*] "GET http://localhost:$HGPORT/?cmd=getbundle&common=0000000000000000000000000000000000000000&heads=83180e7845de420a1bb46896fd5fe05294f8d629 HTTP/1.1" - - (glob)
+ * - - [*] "GET http://localhost:$HGPORT/?cmd=listkeys&namespace=bookmarks HTTP/1.1" - - (glob)
* - - [*] "GET http://localhost:$HGPORT/?cmd=capabilities HTTP/1.1" - - (glob)
+ * - - [*] "GET http://localhost:$HGPORT/?cmd=heads HTTP/1.1" - - (glob)
+ * - - [*] "GET http://localhost:$HGPORT/?cmd=getbundle&common=0000000000000000000000000000000000000000&heads=83180e7845de420a1bb46896fd5fe05294f8d629 HTTP/1.1" - - (glob)
* - - [*] "GET http://localhost:$HGPORT/?cmd=listkeys&namespace=bookmarks HTTP/1.1" - - (glob)
--- a/tests/test-https.t Fri Apr 29 11:04:39 2011 +0200
+++ b/tests/test-https.t Fri Apr 29 11:10:11 2011 +0200
@@ -118,9 +118,9 @@
adding manifests
adding file changes
added 1 changesets with 4 changes to 4 files
- warning: localhost certificate with fingerprint 91:4f:1a:ff:87:24:9c:09:b6:85:9b:88:b1:90:6d:30:75:64:91:ca not verified (check hostfingerprints or web.cacerts config setting)
updating to branch default
4 files updated, 0 files merged, 0 files removed, 0 files unresolved
+ warning: localhost certificate with fingerprint 91:4f:1a:ff:87:24:9c:09:b6:85:9b:88:b1:90:6d:30:75:64:91:ca not verified (check hostfingerprints or web.cacerts config setting)
$ hg verify -R copy-pull
checking changesets
checking manifests
--- a/tests/test-i18n.t Fri Apr 29 11:04:39 2011 +0200
+++ b/tests/test-i18n.t Fri Apr 29 11:10:11 2011 +0200
@@ -8,17 +8,17 @@
using the "replace" error handler:
$ LANGUAGE=pt_BR hg tip
- abortado: N?o h? um reposit?rio do Mercurial aqui (.hg n?o encontrado)!
+ abortado: no repository found in '$TESTTMP' (.hg not found)!
[255]
Using a more accomodating encoding:
$ HGENCODING=UTF-8 LANGUAGE=pt_BR hg tip
- abortado: N\xc3\xa3o h\xc3\xa1 um reposit\xc3\xb3rio do Mercurial aqui (.hg n\xc3\xa3o encontrado)! (esc)
+ abortado: no repository found in '$TESTTMP' (.hg not found)!
[255]
Different encoding:
$ HGENCODING=Latin-1 LANGUAGE=pt_BR hg tip
- abortado: N\xe3o h\xe1 um reposit\xf3rio do Mercurial aqui (.hg n\xe3o encontrado)! (esc)
+ abortado: no repository found in '$TESTTMP' (.hg not found)!
[255]
--- a/tests/test-identify.t Fri Apr 29 11:04:39 2011 +0200
+++ b/tests/test-identify.t Fri Apr 29 11:10:11 2011 +0200
@@ -65,26 +65,43 @@
remote with rev number?
$ hg id -n http://localhost:$HGPORT1/
- abort: can't query remote revision number, branch, tags, or bookmarks
+ abort: can't query remote revision number, branch, or tags
[255]
remote with tags?
$ hg id -t http://localhost:$HGPORT1/
- abort: can't query remote revision number, branch, tags, or bookmarks
+ abort: can't query remote revision number, branch, or tags
[255]
remote with branch?
$ hg id -b http://localhost:$HGPORT1/
- abort: can't query remote revision number, branch, tags, or bookmarks
+ abort: can't query remote revision number, branch, or tags
[255]
-remote with bookmarks?
+test bookmark support
- $ hg id -B http://localhost:$HGPORT1/
- abort: can't query remote revision number, branch, tags, or bookmarks
- [255]
+ $ hg bookmark Y
+ $ hg bookmark Z
+ $ hg bookmarks
+ Y 0:cb9a9f314b8b
+ * Z 0:cb9a9f314b8b
+ $ hg id
+ cb9a9f314b8b+ tip Y/Z
+ $ hg id --bookmarks
+ Y Z
+
+test remote identify with bookmarks
+
+ $ hg id http://localhost:$HGPORT1/
+ cb9a9f314b8b Y/Z
+ $ hg id --bookmarks http://localhost:$HGPORT1/
+ Y Z
+ $ hg id -r . http://localhost:$HGPORT1/
+ cb9a9f314b8b Y/Z
+ $ hg id --bookmarks -r . http://localhost:$HGPORT1/
+ Y Z
Make sure we do not obscure unknown requires file entries (issue2649)
--- a/tests/test-incoming-outgoing.t Fri Apr 29 11:04:39 2011 +0200
+++ b/tests/test-incoming-outgoing.t Fri Apr 29 11:10:11 2011 +0200
@@ -1,6 +1,5 @@
- $ mkdir test
+ $ hg init test
$ cd test
- $ hg init
$ for i in 0 1 2 3 4 5 6 7 8; do
> echo $i >> foo
> hg commit -A -m $i
--- a/tests/test-init.t Fri Apr 29 11:04:39 2011 +0200
+++ b/tests/test-init.t Fri Apr 29 11:10:11 2011 +0200
@@ -199,3 +199,17 @@
store
fncache
dotencode
+
+clone bookmarks
+
+ $ hg -R local bookmark test
+ $ hg -R local bookmarks
+ * test 0:08b9e9f63b32
+ $ hg clone -e "python ./dummyssh" local ssh://user@dummy/remote-bookmarks
+ searching for changes
+ remote: adding changesets
+ remote: adding manifests
+ remote: adding file changes
+ remote: added 1 changesets with 1 changes to 1 files
+ $ hg -R remote-bookmarks bookmarks
+ test 0:08b9e9f63b32
--- a/tests/test-inotify-dirty-dirstate.t Fri Apr 29 11:04:39 2011 +0200
+++ b/tests/test-inotify-dirty-dirstate.t Fri Apr 29 11:10:11 2011 +0200
@@ -7,8 +7,7 @@
issue1810: inotify and fetch
- $ mkdir test; cd test
- $ hg init
+ $ hg init test; cd test
$ hg inserve -d --pid-file=../hg.pid
$ cat ../hg.pid >> "$DAEMON_PIDS"
$ echo foo > foo
--- a/tests/test-install.t Fri Apr 29 11:04:39 2011 +0200
+++ b/tests/test-install.t Fri Apr 29 11:10:11 2011 +0200
@@ -3,7 +3,6 @@
Checking encoding (ascii)...
Checking installed modules (*/mercurial)... (glob)
Checking templates...
- Checking patch...
Checking commit editor...
Checking username...
No problems detected
@@ -13,7 +12,6 @@
Checking encoding (ascii)...
Checking installed modules (*/mercurial)... (glob)
Checking templates...
- Checking patch...
Checking commit editor...
Checking username...
no username supplied (see "hg help config")
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/tests/test-issue1877.t Fri Apr 29 11:10:11 2011 +0200
@@ -0,0 +1,45 @@
+http://mercurial.selenic.com/bts/issue1877
+
+ $ hg init a
+ $ cd a
+ $ echo a > a
+ $ hg add a
+ $ hg ci -m 'a'
+ $ echo b > a
+ $ hg ci -m'b'
+ $ hg up 0
+ 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
+ $ hg book main
+ $ hg book
+ * main 0:cb9a9f314b8b
+ $ echo c > c
+ $ hg add c
+ $ hg ci -m'c'
+ created new head
+ $ hg book
+ * main 2:d36c0562f908
+ $ hg heads
+ changeset: 2:d36c0562f908
+ bookmark: main
+ tag: tip
+ parent: 0:cb9a9f314b8b
+ user: test
+ date: Thu Jan 01 00:00:00 1970 +0000
+ summary: c
+
+ changeset: 1:1e6c11564562
+ user: test
+ date: Thu Jan 01 00:00:00 1970 +0000
+ summary: b
+
+ $ hg up 1e6c11564562
+ 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
+ $ hg merge main
+ 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
+ (branch merge, don't forget to commit)
+ $ hg book
+ main 2:d36c0562f908
+ $ hg ci -m'merge'
+ $ hg book
+ main 2:d36c0562f908
+
--- a/tests/test-keyword.t Fri Apr 29 11:04:39 2011 +0200
+++ b/tests/test-keyword.t Fri Apr 29 11:10:11 2011 +0200
@@ -209,7 +209,7 @@
To: Test
changeset a2392c293916 in $TESTTMP/Test
- details: *cmd=changeset;node=a2392c293916 (glob)
+ details: $TESTTMP/Test?cmd=changeset;node=a2392c293916
description:
addsym
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/tests/test-known.t Fri Apr 29 11:10:11 2011 +0200
@@ -0,0 +1,37 @@
+
+= Test the known() protocol function =
+
+Create a test repository:
+
+ $ hg init repo
+ $ cd repo
+ $ touch a ; hg add a ; hg ci -ma
+ $ touch b ; hg add b ; hg ci -mb
+ $ touch c ; hg add c ; hg ci -mc
+ $ hg log --template '{node}\n'
+ 991a3460af53952d10ec8a295d3d2cc2e5fa9690
+ 0e067c57feba1a5694ca4844f05588bb1bf82342
+ 3903775176ed42b1458a6281db4a0ccf4d9f287a
+ $ cd ..
+
+Test locally:
+
+ $ hg debugknown repo 991a3460af53952d10ec8a295d3d2cc2e5fa9690 0e067c57feba1a5694ca4844f05588bb1bf82342 3903775176ed42b1458a6281db4a0ccf4d9f287a
+ 111
+ $ hg debugknown repo 000a3460af53952d10ec8a295d3d2cc2e5fa9690 0e067c57feba1a5694ca4844f05588bb1bf82342 0003775176ed42b1458a6281db4a0ccf4d9f287a
+ 010
+ $ hg debugknown repo
+
+
+Test via HTTP:
+
+ $ hg serve -R repo -p $HGPORT -d --pid-file=hg.pid -E error.log -A access.log
+ $ cat hg.pid >> $DAEMON_PIDS
+ $ hg debugknown http://localhost:$HGPORT/ 991a3460af53952d10ec8a295d3d2cc2e5fa9690 0e067c57feba1a5694ca4844f05588bb1bf82342 3903775176ed42b1458a6281db4a0ccf4d9f287a
+ 111
+ $ hg debugknown http://localhost:$HGPORT/ 000a3460af53952d10ec8a295d3d2cc2e5fa9690 0e067c57feba1a5694ca4844f05588bb1bf82342 0003775176ed42b1458a6281db4a0ccf4d9f287a
+ 010
+ $ hg debugknown http://localhost:$HGPORT/
+
+ $ cat error.log
+
--- a/tests/test-locate.t Fri Apr 29 11:04:39 2011 +0200
+++ b/tests/test-locate.t Fri Apr 29 11:10:11 2011 +0200
@@ -1,6 +1,5 @@
- $ mkdir t
+ $ hg init t
$ cd t
- $ hg init
$ echo 0 > a
$ echo 0 > b
$ echo 0 > t.h
--- a/tests/test-log.t Fri Apr 29 11:04:39 2011 +0200
+++ b/tests/test-log.t Fri Apr 29 11:10:11 2011 +0200
@@ -512,12 +512,33 @@
date: Thu Jan 01 00:00:01 1970 +0000
summary: r1
+log -d " " (whitespaces only)
+ $ hg log -d " "
+ abort: dates cannot consist entirely of whitespace
+ [255]
log -d -1
$ hg log -d -1
+log -d ">"
+
+ $ hg log -d ">"
+ abort: invalid day spec, use '>DATE'
+ [255]
+
+log -d "<"
+
+ $ hg log -d "<"
+ abort: invalid day spec, use '<DATE'
+ [255]
+
+Negative ranges
+ $ hg log -d "--2"
+ abort: -2 must be nonnegative (see 'hg help dates')
+ [255]
+
log -p -l2 --color=always
--- a/tests/test-merge1.t Fri Apr 29 11:04:39 2011 +0200
+++ b/tests/test-merge1.t Fri Apr 29 11:10:11 2011 +0200
@@ -12,9 +12,8 @@
> EOF
$ HGMERGE="python ../merge"; export HGMERGE
- $ mkdir t
+ $ hg init t
$ cd t
- $ hg init
$ echo This is file a1 > a
$ hg add a
$ hg commit -m "commit #0"
@@ -49,9 +48,8 @@
M b
$ cd ..; rm -r t
- $ mkdir t
+ $ hg init t
$ cd t
- $ hg init
$ echo This is file a1 > a
$ hg add a
$ hg commit -m "commit #0"
@@ -86,9 +84,8 @@
M b
$ cd ..; rm -r t
- $ mkdir t
+ $ hg init t
$ cd t
- $ hg init
$ echo This is file a1 > a
$ hg add a
$ hg commit -m "commit #0"
@@ -129,9 +126,8 @@
M b
$ cd ..; rm -r t
- $ mkdir t
+ $ hg init t
$ cd t
- $ hg init
$ echo This is file a1 > a
$ hg add a
$ hg commit -m "commit #0"
--- a/tests/test-merge2.t Fri Apr 29 11:04:39 2011 +0200
+++ b/tests/test-merge2.t Fri Apr 29 11:10:11 2011 +0200
@@ -1,6 +1,5 @@
- $ mkdir t
+ $ hg init t
$ cd t
- $ hg init
$ echo This is file a1 > a
$ hg add a
$ hg commit -m "commit #0"
@@ -34,9 +33,8 @@
created new head
$ cd ..; rm -r t
- $ mkdir t
+ $ hg init t
$ cd t
- $ hg init
$ echo This is file a1 > a
$ hg add a
$ hg commit -m "commit #0"
--- a/tests/test-merge6.t Fri Apr 29 11:04:39 2011 +0200
+++ b/tests/test-merge6.t Fri Apr 29 11:10:11 2011 +0200
@@ -4,9 +4,8 @@
> EOF
$ HGMERGE="python ../merge"; export HGMERGE
- $ mkdir A1
+ $ hg init A1
$ cd A1
- $ hg init
$ echo This is file foo1 > foo
$ echo This is file bar1 > bar
$ hg add foo bar
--- a/tests/test-mq-guards.t Fri Apr 29 11:04:39 2011 +0200
+++ b/tests/test-mq-guards.t Fri Apr 29 11:10:11 2011 +0200
@@ -309,7 +309,7 @@
qseries again, but with color
- $ hg --config extensions.color= qseries -v --color=always
+ $ hg --config extensions.color= --config color.mode=ansi qseries -v --color=always
0 G \x1b[0;30;1mnew.patch\x1b[0m (esc)
1 G \x1b[0;30;1mb.patch\x1b[0m (esc)
2 A \x1b[0;34;1;4mc.patch\x1b[0m (esc)
@@ -432,5 +432,5 @@
hg qseries -m with color
- $ hg --config extensions.color= qseries -m --color=always
+ $ hg --config extensions.color= --config color.mode=ansi qseries -m --color=always
\x1b[0;31;1mb.patch\x1b[0m (esc)
--- a/tests/test-mq-qdelete.t Fri Apr 29 11:04:39 2011 +0200
+++ b/tests/test-mq-qdelete.t Fri Apr 29 11:10:11 2011 +0200
@@ -162,3 +162,33 @@
adding 3.diff to series file
$ hg qfinish -a
no patches applied
+
+
+resilience to inconsistency: qfinish -a with applied patches not in series
+
+ $ hg qser
+ 3.diff
+ $ hg qapplied
+ $ hg qpush
+ applying 3.diff
+ patch 3.diff is empty
+ now at: 3.diff
+ $ echo next >> base
+ $ hg qrefresh -d '1 0'
+ $ echo > .hg/patches/series # remove 3.diff from series to confuse mq
+ $ hg qfinish -a
+ revision c4dd2b624061 refers to unknown patches: 3.diff
+
+more complex state 'both known and unknown patches
+
+ $ echo hip >> base
+ $ hg qnew -f -d '1 0' -m 4 4.diff
+ $ echo hop >> base
+ $ hg qnew -f -d '1 0' -m 5 5.diff
+ $ echo > .hg/patches/series # remove 4.diff and 5.diff from series to confuse mq
+ $ echo hup >> base
+ $ hg qnew -f -d '1 0' -m 6 6.diff
+ $ hg qfinish -a
+ revision 6fdec4b20ec3 refers to unknown patches: 5.diff
+ revision 2ba51db7ba24 refers to unknown patches: 4.diff
+
--- a/tests/test-mq-qdiff.t Fri Apr 29 11:04:39 2011 +0200
+++ b/tests/test-mq-qdiff.t Fri Apr 29 11:10:11 2011 +0200
@@ -163,4 +163,13 @@
$ hg qdiff --stat
lines | 7 +++++--
1 files changed, 5 insertions(+), 2 deletions(-)
+ $ hg qrefresh
+qdiff when file deleted (but not removed) in working dir:
+
+ $ hg qnew deleted-file
+ $ echo a > newfile
+ $ hg add newfile
+ $ hg qrefresh
+ $ rm newfile
+ $ hg qdiff
--- a/tests/test-mq-strip.t Fri Apr 29 11:04:39 2011 +0200
+++ b/tests/test-mq-strip.t Fri Apr 29 11:10:11 2011 +0200
@@ -410,7 +410,7 @@
abort: local changes found
[255]
$ hg strip tip --keep
- saved backup bundle to * (glob)
+ saved backup bundle to $TESTTMP/test/.hg/strip-backup/*-backup.hg (glob)
$ hg log --graph
@ changeset: 0:9ab35a2d17cb
tag: tip
--- a/tests/test-mq.t Fri Apr 29 11:04:39 2011 +0200
+++ b/tests/test-mq.t Fri Apr 29 11:10:11 2011 +0200
@@ -177,7 +177,7 @@
status --mq with color (issue2096)
- $ hg status --mq --config extensions.color= --color=always
+ $ hg status --mq --config extensions.color= --config color.mode=ansi --color=always
\x1b[0;32;1mA .hgignore\x1b[0m (esc)
\x1b[0;32;1mA A\x1b[0m (esc)
\x1b[0;32;1mA B\x1b[0m (esc)
--- a/tests/test-nested-repo.t Fri Apr 29 11:04:39 2011 +0200
+++ b/tests/test-nested-repo.t Fri Apr 29 11:10:11 2011 +0200
@@ -11,16 +11,16 @@
Should fail:
$ hg st b/x
- abort: path 'b/x' is inside repo 'b'
+ abort: path 'b/x' is inside nested repo 'b'
[255]
$ hg add b/x
- abort: path 'b/x' is inside repo 'b'
+ abort: path 'b/x' is inside nested repo 'b'
[255]
Should fail:
$ hg add b b/x
- abort: path 'b/x' is inside repo 'b'
+ abort: path 'b/x' is inside nested repo 'b'
[255]
$ hg st
@@ -34,7 +34,7 @@
Should fail:
$ hg mv a b
- abort: path 'b/a' is inside repo 'b'
+ abort: path 'b/a' is inside nested repo 'b'
[255]
$ hg st
--- a/tests/test-parse-date.t Fri Apr 29 11:04:39 2011 +0200
+++ b/tests/test-parse-date.t Fri Apr 29 11:10:11 2011 +0200
@@ -96,52 +96,32 @@
Test date formats with '>' or '<' accompanied by space characters
$ hg log -d '>' --template '{date|date}\n'
- Sun Jan 15 13:30:00 2006 +0500
- Sun Jan 15 13:30:00 2006 -0800
- Sat Jul 15 13:30:00 2006 +0500
- Sat Jul 15 13:30:00 2006 -0700
- Sun Jun 11 00:26:40 2006 -0400
- Sat Apr 15 13:30:00 2006 +0200
- Sat Apr 15 13:30:00 2006 +0000
- Wed Feb 01 13:00:30 2006 -0500
- Wed Feb 01 13:00:30 2006 +0000
+ abort: invalid day spec, use '>DATE'
+ [255]
$ hg log -d '<' hg log -d '>' --template '{date|date}\n'
+ abort: invalid day spec, use '>DATE'
+ [255]
$ hg log -d ' >' --template '{date|date}\n'
- Sun Jan 15 13:30:00 2006 +0500
- Sun Jan 15 13:30:00 2006 -0800
- Sat Jul 15 13:30:00 2006 +0500
- Sat Jul 15 13:30:00 2006 -0700
- Sun Jun 11 00:26:40 2006 -0400
- Sat Apr 15 13:30:00 2006 +0200
- Sat Apr 15 13:30:00 2006 +0000
- Wed Feb 01 13:00:30 2006 -0500
- Wed Feb 01 13:00:30 2006 +0000
+ abort: invalid day spec, use '>DATE'
+ [255]
$ hg log -d ' <' --template '{date|date}\n'
+ abort: invalid day spec, use '<DATE'
+ [255]
$ hg log -d '> ' --template '{date|date}\n'
- Sun Jan 15 13:30:00 2006 +0500
- Sun Jan 15 13:30:00 2006 -0800
- Sat Jul 15 13:30:00 2006 +0500
- Sat Jul 15 13:30:00 2006 -0700
- Sun Jun 11 00:26:40 2006 -0400
- Sat Apr 15 13:30:00 2006 +0200
- Sat Apr 15 13:30:00 2006 +0000
- Wed Feb 01 13:00:30 2006 -0500
- Wed Feb 01 13:00:30 2006 +0000
+ abort: invalid day spec, use '>DATE'
+ [255]
$ hg log -d '< ' --template '{date|date}\n'
+ abort: invalid day spec, use '<DATE'
+ [255]
$ hg log -d ' > ' --template '{date|date}\n'
- Sun Jan 15 13:30:00 2006 +0500
- Sun Jan 15 13:30:00 2006 -0800
- Sat Jul 15 13:30:00 2006 +0500
- Sat Jul 15 13:30:00 2006 -0700
- Sun Jun 11 00:26:40 2006 -0400
- Sat Apr 15 13:30:00 2006 +0200
- Sat Apr 15 13:30:00 2006 +0000
- Wed Feb 01 13:00:30 2006 -0500
- Wed Feb 01 13:00:30 2006 +0000
+ abort: invalid day spec, use '>DATE'
+ [255]
$ hg log -d ' < ' --template '{date|date}\n'
+ abort: invalid day spec, use '<DATE'
+ [255]
$ hg log -d '>02/01' --template '{date|date}\n'
$ hg log -d '<02/01' --template '{date|date}\n'
--- a/tests/test-parseindex.t Fri Apr 29 11:04:39 2011 +0200
+++ b/tests/test-parseindex.t Fri Apr 29 11:10:11 2011 +0200
@@ -26,7 +26,7 @@
summary: change foo
$ cat >> test.py << EOF
- > from mercurial import changelog, util
+ > from mercurial import changelog, scmutil
> from mercurial.node import *
>
> class singlebyteread(object):
@@ -42,7 +42,7 @@
> return getattr(self.real, key)
>
> def opener(*args):
- > o = util.opener(*args)
+ > o = scmutil.opener(*args)
> def wrapper(*a):
> f = o(*a)
> return singlebyteread(f)
--- a/tests/test-paths.t Fri Apr 29 11:04:39 2011 +0200
+++ b/tests/test-paths.t Fri Apr 29 11:10:11 2011 +0200
@@ -25,3 +25,23 @@
$ SOMETHING=/foo hg paths
dupe = $TESTTMP/b
expand = /foo/bar
+ $ cd ..
+
+'file:' disables [paths] entries for clone destination
+
+ $ cat >> $HGRCPATH <<EOF
+ > [paths]
+ > gpath1 = http://hg.example.com
+ > EOF
+
+ $ hg clone a gpath1
+ abort: cannot create new http repository
+ [255]
+
+ $ hg clone a file:gpath1
+ updating to branch default
+ 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
+ $ cd gpath1
+ $ hg -q id
+ 000000000000
+
--- a/tests/test-pull-branch.t Fri Apr 29 11:04:39 2011 +0200
+++ b/tests/test-pull-branch.t Fri Apr 29 11:10:11 2011 +0200
@@ -134,3 +134,77 @@
not updating, since new heads added
(run 'hg heads' to see heads, 'hg merge' to merge)
+Make changes on new branch on tt
+
+ $ hg branch branchC
+ marked working directory as branch branchC
+ $ echo b1 > bar
+ $ hg ci -Am "commit on branchC on tt"
+ adding bar
+
+Make changes on default branch on t
+
+ $ cd ../t
+ $ hg up -C default
+ 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
+ $ echo a1 > bar
+ $ hg ci -Am "commit on default on t"
+ adding bar
+
+Pull branchC from tt
+
+ $ hg pull ../tt
+ pulling from ../tt
+ searching for changes
+ adding changesets
+ adding manifests
+ adding file changes
+ added 1 changesets with 1 changes to 1 files (+1 heads)
+ (run 'hg heads' to see heads)
+
+Make changes on default and branchC on tt
+
+ $ cd ../tt
+ $ hg pull ../t
+ pulling from ../t
+ searching for changes
+ adding changesets
+ adding manifests
+ adding file changes
+ added 1 changesets with 1 changes to 1 files (+1 heads)
+ (run 'hg heads' to see heads)
+ $ hg up -C default
+ 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
+ $ echo a1 > bar1
+ $ hg ci -Am "commit on default on tt"
+ adding bar1
+ $ hg up branchC
+ 2 files updated, 0 files merged, 1 files removed, 0 files unresolved
+ $ echo a1 > bar2
+ $ hg ci -Am "commit on branchC on tt"
+ adding bar2
+
+Make changes on default and branchC on t
+
+ $ cd ../t
+ $ hg up default
+ 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
+ $ echo a1 > bar3
+ $ hg ci -Am "commit on default on t"
+ adding bar3
+ $ hg up branchC
+ 2 files updated, 0 files merged, 1 files removed, 0 files unresolved
+ $ echo a1 > bar4
+ $ hg ci -Am "commit on branchC on tt"
+ adding bar4
+
+Pull from tt
+
+ $ hg pull ../tt
+ pulling from ../tt
+ searching for changes
+ adding changesets
+ adding manifests
+ adding file changes
+ added 2 changesets with 2 changes to 2 files (+2 heads)
+ (run 'hg heads .' to see heads, 'hg merge' to merge)
--- a/tests/test-pull-permission.t Fri Apr 29 11:04:39 2011 +0200
+++ b/tests/test-pull-permission.t Fri Apr 29 11:10:11 2011 +0200
@@ -1,6 +1,5 @@
- $ mkdir a
+ $ hg init a
$ cd a
- $ hg init
$ echo foo > b
$ hg add b
$ hg ci -m "b"
--- a/tests/test-pull.t Fri Apr 29 11:04:39 2011 +0200
+++ b/tests/test-pull.t Fri Apr 29 11:10:11 2011 +0200
@@ -1,8 +1,7 @@
- $ mkdir test
+ $ hg init test
$ cd test
$ echo foo>foo
- $ hg init
$ hg addremove
adding foo
$ hg commit -m 1
@@ -68,7 +67,11 @@
Test 'file:' uri handling:
$ hg pull -q file://../test-doesnt-exist
- abort: repository /test-doesnt-exist not found!
+ abort: file:// URLs can only refer to localhost
+ [255]
+
+ $ hg pull -q file://../test
+ abort: file:// URLs can only refer to localhost
[255]
$ hg pull -q file:../test
@@ -78,4 +81,8 @@
$ URL=`python -c "import os; print 'file://foobar' + ('/' + os.getcwd().replace(os.sep, '/')).replace('//', '/') + '/../test'"`
$ hg pull -q "$URL"
+ abort: file:// URLs can only refer to localhost
+ [255]
+ $ URL=`python -c "import os; print 'file://localhost' + ('/' + os.getcwd().replace(os.sep, '/')).replace('//', '/') + '/../test'"`
+ $ hg pull -q "$URL"
--- a/tests/test-push-cgi.t Fri Apr 29 11:04:39 2011 +0200
+++ b/tests/test-push-cgi.t Fri Apr 29 11:10:11 2011 +0200
@@ -59,7 +59,7 @@
adding file changes
added 0 changesets with 0 changes to 1 files
-successful push
+successful push, list of heads
$ QUERY_STRING="cmd=unbundle&heads=f7b1eb17ad24730a1651fccd46c43826d1bbc2ac"; export QUERY_STRING
$ python hgweb.cgi <bundle.hg >page3 2>&1
@@ -72,3 +72,17 @@
adding manifests
adding file changes
added 0 changesets with 0 changes to 1 files
+
+successful push, SHA1 hash of heads (unbundlehash capability)
+
+ $ QUERY_STRING="cmd=unbundle&heads=686173686564 5a785a5f9e0d433b88ed862b206b011b0c3a9d13"; export QUERY_STRING
+ $ python hgweb.cgi <bundle.hg >page4 2>&1
+ $ cat page4
+ Status: 200 Script output follows\r (esc)
+ Content-Type: application/mercurial-0.1\r (esc)
+ \r (esc)
+ 1
+ adding changesets
+ adding manifests
+ adding file changes
+ added 0 changesets with 0 changes to 1 files
--- a/tests/test-push-http.t Fri Apr 29 11:04:39 2011 +0200
+++ b/tests/test-push-http.t Fri Apr 29 11:10:11 2011 +0200
@@ -66,6 +66,23 @@
repository tip rolled back to revision 0 (undo serve)
working directory now based on revision 0
+expect success, server lacks the unbundlehash capability
+
+ $ CAP=unbundlehash
+ $ . "$TESTDIR/notcapable"
+ $ 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: HG_NODE=ba677d0156c1196c1a699fa53f390dcfc3ce3872 HG_SOURCE=serve HG_URL=remote:http:*: (glob)
+ % serve errors
+ $ hg rollback
+ repository tip rolled back to revision 0 (undo serve)
+ working directory now based on revision 0
+
expect authorization error: all users denied
$ echo '[web]' > .hg/hgrc
--- a/tests/test-push-warn.t Fri Apr 29 11:04:39 2011 +0200
+++ b/tests/test-push-warn.t Fri Apr 29 11:10:11 2011 +0200
@@ -1,9 +1,8 @@
$ echo "[extensions]" >> $HGRCPATH
$ echo "graphlog=" >> $HGRCPATH
- $ mkdir a
+ $ hg init a
$ cd a
- $ hg init
$ echo foo > t1
$ hg add t1
$ hg commit -m "1"
--- a/tests/test-qrecord.t Fri Apr 29 11:04:39 2011 +0200
+++ b/tests/test-qrecord.t Fri Apr 29 11:10:11 2011 +0200
@@ -2,36 +2,101 @@
$ echo "[ui]" >> $HGRCPATH
$ echo "interactive=true" >> $HGRCPATH
+
+help record (no record)
+
+ $ hg help record
+ record extension - commands to interactively select changes for commit/qrefresh
+
+ use "hg help extensions" for information on enabling extensions
+
+help qrecord (no record)
+
+ $ hg help qrecord
+ 'qrecord' is provided by the following extension:
+
+ record commands to interactively select changes for commit/qrefresh
+
+ use "hg help extensions" for information on enabling extensions
+
$ echo "[extensions]" >> $HGRCPATH
$ echo "record=" >> $HGRCPATH
+help record (record)
+
+ $ hg help record
+ hg record [OPTION]... [FILE]...
+
+ interactively select changes to commit
+
+ If a list of files is omitted, all changes reported by "hg status" will be
+ candidates for recording.
+
+ See "hg help dates" for a list of formats valid for -d/--date.
+
+ You will be prompted for whether to record changes to each modified file,
+ and for files with multiple changes, for each change to use. For each
+ query, the following responses are possible:
+
+ y - record this change
+ n - skip this change
+
+ s - skip remaining changes to this file
+ f - record remaining changes to this file
+
+ d - done, skip remaining changes and files
+ a - record all changes to all remaining files
+ q - quit, recording no changes
+
+ ? - display help
+
+ This command is not available when committing a merge.
+
+ options:
+
+ -A --addremove mark new/missing files as added/removed before
+ committing
+ --close-branch mark a branch as closed, hiding it from the branch
+ list
+ -I --include PATTERN [+] include names matching the given patterns
+ -X --exclude PATTERN [+] exclude names matching the given patterns
+ -m --message TEXT use text as commit message
+ -l --logfile FILE read commit message from file
+ -d --date DATE record the specified date as commit date
+ -u --user USER record the specified user as committer
+
+ [+] marked option can be specified multiple times
+
+ use "hg -v help record" to show global options
+
help (no mq, so no qrecord)
$ hg help qrecord
- hg: unknown command 'qrecord'
- Mercurial Distributed SCM
+ hg qrecord [OPTION]... PATCH [FILE]...
- basic commands:
+ interactively record a new patch
+
+ See "hg help qnew" & "hg help record" for more information and usage.
- add add the specified files on the next commit
- annotate show changeset information by line for each file
- clone make a copy of an existing repository
- commit commit the specified files or all outstanding changes
- diff diff repository (or selected files)
- export dump the header and diffs for one or more changesets
- forget forget the specified files on the next commit
- init create a new repository in the given directory
- log show revision history of entire repository or files
- merge merge working directory with another revision
- pull pull changes from the specified source
- push push changes to the specified destination
- remove remove the specified files on the next commit
- serve start stand-alone webserver
- status show changed files in the working directory
- summary summarize working directory state
- update update working directory (or switch revisions)
+ use "hg -v help qrecord" to show global options
+
+ $ hg init a
+
+qrecord (mq not present)
+
+ $ hg -R a qrecord
+ hg qrecord: invalid arguments
+ hg qrecord [OPTION]... PATCH [FILE]...
- use "hg help" for the full list of commands or "hg -v" for details
+ interactively record a new patch
+
+ use "hg help qrecord" to show the full help text
+ [255]
+
+qrecord patch (mq not present)
+
+ $ hg -R a qrecord patch
+ abort: 'mq' extension not loaded
[255]
help (mq present)
@@ -61,7 +126,6 @@
use "hg -v help qrecord" to show global options
- $ hg init a
$ cd a
Base commit
--- a/tests/test-rebase-collapse.t Fri Apr 29 11:04:39 2011 +0200
+++ b/tests/test-rebase-collapse.t Fri Apr 29 11:10:11 2011 +0200
@@ -137,6 +137,40 @@
$ cd ..
+Rebasing G onto H with custom message:
+
+ $ hg clone -q -u . a a3
+ $ cd a3
+
+ $ hg rebase --base 6 -m 'custom message'
+ abort: message can only be specified with collapse
+ [255]
+
+ $ hg rebase --base 6 --collapse -m 'custom message'
+ saved backup bundle to $TESTTMP/a3/.hg/strip-backup/*-backup.hg (glob)
+
+ $ hg tglog
+ @ 6: 'custom message'
+ |
+ o 5: 'H'
+ |
+ o 4: 'F'
+ |
+ | o 3: 'D'
+ | |
+ | o 2: 'C'
+ | |
+ | o 1: 'B'
+ |/
+ o 0: 'A'
+
+ $ hg manifest
+ A
+ E
+ F
+ H
+
+ $ cd ..
Create repo b:
--- a/tests/test-rebase-mq.t Fri Apr 29 11:04:39 2011 +0200
+++ b/tests/test-rebase-mq.t Fri Apr 29 11:10:11 2011 +0200
@@ -235,3 +235,73 @@
-mq1
+mq2
+
+Rebase with guards
+
+ $ hg init foo
+ $ cd foo
+ $ echo a > a
+ $ hg ci -Am a
+ adding a
+
+Create mq repo with guarded patches foo and bar:
+
+ $ hg qinit
+ $ hg qnew foo
+ $ hg qguard foo +baz
+ $ echo foo > foo
+ $ hg qref
+ $ hg qpop
+ popping foo
+ patch queue now empty
+
+ $ hg qnew bar
+ $ hg qguard bar +baz
+ $ echo bar > bar
+ $ hg qref
+
+ $ hg qguard -l
+ bar: +baz
+ foo: +baz
+
+ $ hg tglog
+ @ 1:* '[mq]: bar' tags: bar qbase qtip tip (glob)
+ |
+ o 0:* 'a' tags: qparent (glob)
+
+Create new head to rebase bar onto:
+
+ $ hg up -C 0
+ 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
+ $ echo b > b
+ $ hg add b
+ $ hg ci -m b
+ created new head
+ $ hg up -C 1
+ 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
+ $ echo a >> a
+ $ hg qref
+
+ $ hg tglog
+ @ 2:* '[mq]: bar' tags: bar qbase qtip tip (glob)
+ |
+ | o 1:* 'b' tags: (glob)
+ |/
+ o 0:* 'a' tags: qparent (glob)
+
+
+Rebase bar:
+
+ $ hg -q rebase -d 1
+
+ $ hg qguard -l
+ foo: +baz
+ bar: +baz
+
+ $ hg tglog
+ @ 2:* '[mq]: bar' tags: bar qbase qtip tip (glob)
+ |
+ o 1:* 'b' tags: qparent (glob)
+ |
+ o 0:* 'a' tags: (glob)
+
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/tests/test-rebase-named-branches.t Fri Apr 29 11:10:11 2011 +0200
@@ -0,0 +1,171 @@
+ $ cat >> $HGRCPATH <<EOF
+ > [extensions]
+ > graphlog=
+ > rebase=
+ >
+ > [alias]
+ > tglog = log -G --template "{rev}: '{desc}' {branches}\n"
+ > EOF
+
+
+ $ hg init a
+ $ cd a
+
+ $ echo A > A
+ $ hg ci -Am A
+ adding A
+
+ $ echo B > B
+ $ hg ci -Am B
+ adding B
+
+ $ hg up -q -C 0
+
+ $ echo C > C
+ $ hg ci -Am C
+ adding C
+ created new head
+
+ $ hg up -q -C 0
+
+ $ echo D > D
+ $ hg ci -Am D
+ adding D
+ created new head
+
+ $ hg merge -r 2
+ 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
+ (branch merge, don't forget to commit)
+
+ $ hg ci -m E
+
+ $ hg up -q -C 3
+
+ $ echo F > F
+ $ hg ci -Am F
+ adding F
+ created new head
+
+ $ cd ..
+
+
+Rebasing descendant onto ancestor across different named branches
+
+ $ hg clone -q -u . a a1
+
+ $ cd a1
+
+ $ hg branch dev
+ marked working directory as branch dev
+
+ $ echo x > x
+
+ $ hg add x
+
+ $ hg ci -m 'extra named branch'
+
+ $ hg tglog
+ @ 6: 'extra named branch' dev
+ |
+ o 5: 'F'
+ |
+ | o 4: 'E'
+ |/|
+ o | 3: 'D'
+ | |
+ | o 2: 'C'
+ |/
+ | o 1: 'B'
+ |/
+ o 0: 'A'
+
+ $ hg rebase -s 6 -d 5
+ saved backup bundle to $TESTTMP/a1/.hg/strip-backup/*-backup.hg (glob)
+
+ $ hg tglog
+ @ 6: 'extra named branch'
+ |
+ o 5: 'F'
+ |
+ | o 4: 'E'
+ |/|
+ o | 3: 'D'
+ | |
+ | o 2: 'C'
+ |/
+ | o 1: 'B'
+ |/
+ o 0: 'A'
+
+ $ cd ..
+
+Rebasing descendant onto ancestor across the same named branches
+
+ $ hg clone -q -u . a a2
+
+ $ cd a2
+
+ $ echo x > x
+
+ $ hg add x
+
+ $ hg ci -m 'G'
+
+ $ hg tglog
+ @ 6: 'G'
+ |
+ o 5: 'F'
+ |
+ | o 4: 'E'
+ |/|
+ o | 3: 'D'
+ | |
+ | o 2: 'C'
+ |/
+ | o 1: 'B'
+ |/
+ o 0: 'A'
+
+ $ hg rebase -s 6 -d 5
+ abort: source is descendant of destination
+ [255]
+
+ $ cd ..
+
+Rebasing ancestor onto descendant across different named branches
+
+ $ hg clone -q -u . a a3
+
+ $ cd a3
+
+ $ hg branch dev
+ marked working directory as branch dev
+
+ $ echo x > x
+
+ $ hg add x
+
+ $ hg ci -m 'extra named branch'
+
+ $ hg tglog
+ @ 6: 'extra named branch' dev
+ |
+ o 5: 'F'
+ |
+ | o 4: 'E'
+ |/|
+ o | 3: 'D'
+ | |
+ | o 2: 'C'
+ |/
+ | o 1: 'B'
+ |/
+ o 0: 'A'
+
+ $ hg rebase -s 5 -d 6
+ abort: source is ancestor of destination
+ [255]
+
+ $ cd ..
+
+
--- a/tests/test-rebase-parameters.t Fri Apr 29 11:04:39 2011 +0200
+++ b/tests/test-rebase-parameters.t Fri Apr 29 11:10:11 2011 +0200
@@ -330,3 +330,63 @@
$ cd ..
+Test --tool parameter:
+
+ $ hg init b
+ $ cd b
+
+ $ echo c1 > c1
+ $ hg ci -Am c1
+ adding c1
+
+ $ echo c2 > c2
+ $ hg ci -Am c2
+ adding c2
+
+ $ hg up -q 0
+ $ echo c2b > c2
+ $ hg ci -Am c2b
+ adding c2
+ created new head
+
+ $ cd ..
+
+ $ hg clone -q -u . b b1
+ $ cd b1
+
+ $ hg rebase -s 2 -d 1 --tool internal:local
+ saved backup bundle to $TESTTMP/b1/.hg/strip-backup/*-backup.hg (glob)
+
+ $ hg cat c2
+ c2
+
+ $ cd ..
+
+
+ $ hg clone -q -u . b b2
+ $ cd b2
+
+ $ hg rebase -s 2 -d 1 --tool internal:other
+ saved backup bundle to $TESTTMP/b2/.hg/strip-backup/*-backup.hg (glob)
+
+ $ hg cat c2
+ c2b
+
+ $ cd ..
+
+
+ $ hg clone -q -u . b b3
+ $ cd b3
+
+ $ hg rebase -s 2 -d 1 --tool internal:fail
+ abort: unresolved conflicts (see hg resolve, then hg rebase --continue)
+ [255]
+
+ $ hg resolve -l
+ U c2
+
+ $ hg resolve -m c2
+ $ hg rebase -c --tool internal:fail
+ tool option will be ignored
+ saved backup bundle to $TESTTMP/b3/.hg/strip-backup/*-backup.hg (glob)
+
--- a/tests/test-relink.t Fri Apr 29 11:04:39 2011 +0200
+++ b/tests/test-relink.t Fri Apr 29 11:10:11 2011 +0200
@@ -20,23 +20,29 @@
$ hg init repo
$ cd repo
- $ echo '[ui]' > .hg/hgrc
- $ echo 'username= A. Foo <a.foo@bar.com>' >> .hg/hgrc
$ echo a > a
$ echo b > b
$ hg ci -Am addfile
adding a
adding b
- $ echo a >> a
- $ echo a >> b
+ $ cat $TESTDIR/binfile.bin >> a
+ $ cat $TESTDIR/binfile.bin >> b
$ hg ci -Am changefiles
+make another commit to create files larger than 1 KB to test
+formatting of final byte count
+
+ $ cat $TESTDIR/binfile.bin >> a
+ $ cat $TESTDIR/binfile.bin >> b
+ $ hg ci -m anotherchange
+
don't sit forever trying to double-lock the source repo
$ hg relink .
relinking $TESTTMP/repo/.hg/store to $TESTTMP/repo/.hg/store
there is nothing to relink
+
Test files are read in binary mode
$ python -c "file('.hg/store/data/dummy.i', 'wb').write('a\r\nb\n')"
@@ -53,8 +59,6 @@
updating to branch default
2 files updated, 0 files merged, 0 files removed, 0 files unresolved
$ cd clone
- $ echo '[ui]' >> .hg/hgrc
- $ echo 'username= A. Baz <a.baz@bar.com>' >> .hg/hgrc
$ hg pull -q
$ echo b >> b
$ hg ci -m changeb
@@ -81,7 +85,7 @@
pruned down to 2 probably relinkable files
relinking: data/a.i 1/2 files (50.00%)
not linkable: data/dummy.i
- relinked 1 files (136 bytes reclaimed)
+ relinked 1 files (1.37 KB reclaimed)
$ cd ..
--- a/tests/test-rename-dir-merge.t Fri Apr 29 11:04:39 2011 +0200
+++ b/tests/test-rename-dir-merge.t Fri Apr 29 11:10:11 2011 +0200
@@ -1,6 +1,5 @@
- $ mkdir t
+ $ hg init t
$ cd t
- $ hg init
$ mkdir a
$ echo foo > a/a
@@ -118,9 +117,8 @@
Second scenario with two repos:
$ cd ..
- $ mkdir r1
+ $ hg init r1
$ cd r1
- $ hg init
$ mkdir a
$ echo foo > a/f
$ hg add a
--- a/tests/test-rename-merge1.t Fri Apr 29 11:04:39 2011 +0200
+++ b/tests/test-rename-merge1.t Fri Apr 29 11:10:11 2011 +0200
@@ -131,27 +131,27 @@
$ hg init repo2089
$ cd repo2089
- $ echo 0 > A
- $ hg -q ci -Am 0
+ $ echo c0 > f1
+ $ hg ci -Aqm0
- $ hg -q up -C null
- $ echo 1 > A
- $ hg -q ci -Am 1
+ $ hg up null -q
+ $ echo c1 > f1
+ $ hg ci -Aqm1
- $ hg -q up -C 0
+ $ hg up 0 -q
$ hg merge 1 -q --tool internal:local
- $ echo 2 > A
- $ hg -q ci -m 2
+ $ echo c2 > f1
+ $ hg ci -qm2
- $ hg -q up -C 1
- $ hg mv A a
- $ hg -q ci -Am 3
+ $ hg up 1 -q
+ $ hg mv f1 f2
+ $ hg ci -Aqm3
- $ hg -q up -C 2
+ $ hg up 2 -q
$ hg merge 3
- merging A and a to a
+ merging f1 and f2 to f2
0 files updated, 1 files merged, 0 files removed, 0 files unresolved
(branch merge, don't forget to commit)
- $ cat a
- 2
+ $ cat f2
+ c2
--- a/tests/test-rename-merge2.t Fri Apr 29 11:04:39 2011 +0200
+++ b/tests/test-rename-merge2.t Fri Apr 29 11:10:11 2011 +0200
@@ -17,9 +17,8 @@
$ tm()
> {
- > mkdir t
+ > hg init t
> cd t
- > hg init
> echo "[merge]" >> .hg/hgrc
> echo "followcopies = 1" >> .hg/hgrc
>
--- a/tests/test-rename.t Fri Apr 29 11:04:39 2011 +0200
+++ b/tests/test-rename.t Fri Apr 29 11:10:11 2011 +0200
@@ -11,6 +11,9 @@
rename a single file
$ hg rename d1/d11/a1 d2/c
+ $ hg --config ui.portablefilenames=abort rename d1/a d1/con.xml
+ abort: filename contains 'con', which is reserved on Windows: 'd1/con.xml'
+ [255]
$ hg sum
parent: 0:9b4b6e7b2c26 tip
1
--- a/tests/test-repair-strip Fri Apr 29 11:04:39 2011 +0200
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,44 +0,0 @@
-#!/bin/sh
-
-echo "[extensions]" >> $HGRCPATH
-echo "mq=">> $HGRCPATH
-
-teststrip() {
- hg -q up -C $1
- echo % before update $1, strip $2
- hg parents
- chmod -$3 $4
- hg strip $2 2>&1 | sed 's/\(bundle\).*/\1/' | sed 's/Permission denied.*\.hg\/store\/\(.*\)/Permission denied \.hg\/store\/\1/'
- echo % after update $1, strip $2
- chmod +$3 $4
- hg verify
- echo % journal contents
- if [ -f .hg/store/journal ]; then
- sed -e 's/\.i[^\n]*/\.i/' .hg/store/journal
- else
- echo "(no journal)"
- fi
- ls .hg/store/journal >/dev/null 2>&1 && hg recover
- ls .hg/strip-backup/* >/dev/null 2>&1 && hg unbundle -q .hg/strip-backup/*
- rm -rf .hg/strip-backup
-}
-
-hg init test
-cd test
-
-echo a > a
-hg -q ci -m "a" -A
-
-echo b > b
-hg -q ci -m "b" -A
-
-echo b2 >> b
-hg -q ci -m "b2" -A
-
-echo c > c
-hg -q ci -m "c" -A
-
-teststrip 0 2 w .hg/store/data/b.i
-teststrip 0 2 r .hg/store/data/b.i
-teststrip 0 2 w .hg/store/00manifest.i
-
--- a/tests/test-repair-strip.out Fri Apr 29 11:04:39 2011 +0200
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,92 +0,0 @@
-% before update 0, strip 2
-changeset: 0:cb9a9f314b8b
-user: test
-date: Thu Jan 01 00:00:00 1970 +0000
-summary: a
-
-saved backup bundle
-transaction abort!
-failed to truncate data/b.i
-rollback failed - please run hg recover
-strip failed, full bundle
-abort: Permission denied .hg/store/data/b.i
-% after update 0, strip 2
-abandoned transaction found - run hg recover
-checking changesets
-checking manifests
-crosschecking files in changesets and manifests
-checking files
- b@?: rev 1 points to nonexistent changeset 2
- (expected 1)
- b@?: 736c29771fba not in manifests
-warning: orphan revlog 'data/c.i'
-2 files, 2 changesets, 3 total revisions
-2 warnings encountered!
-2 integrity errors encountered!
-% journal contents
-00changelog.i
-00manifest.i
-data/b.i
-data/c.i
-rolling back interrupted transaction
-checking changesets
-checking manifests
-crosschecking files in changesets and manifests
-checking files
-2 files, 2 changesets, 2 total revisions
-% before update 0, strip 2
-changeset: 0:cb9a9f314b8b
-user: test
-date: Thu Jan 01 00:00:00 1970 +0000
-summary: a
-
-abort: Permission denied .hg/store/data/b.i
-% after update 0, strip 2
-checking changesets
-checking manifests
-crosschecking files in changesets and manifests
-checking files
-3 files, 4 changesets, 4 total revisions
-% journal contents
-(no journal)
-% before update 0, strip 2
-changeset: 0:cb9a9f314b8b
-user: test
-date: Thu Jan 01 00:00:00 1970 +0000
-summary: a
-
-saved backup bundle
-transaction abort!
-failed to truncate 00manifest.i
-rollback failed - please run hg recover
-strip failed, full bundle
-abort: Permission denied .hg/store/00manifest.i
-% after update 0, strip 2
-abandoned transaction found - run hg recover
-checking changesets
-checking manifests
- manifest@?: rev 2 points to nonexistent changeset 2
- manifest@?: 3362547cdf64 not in changesets
- manifest@?: rev 3 points to nonexistent changeset 3
- manifest@?: 265a85892ecb not in changesets
-crosschecking files in changesets and manifests
- c@3: in manifest but not in changeset
-checking files
- b@?: rev 1 points to nonexistent changeset 2
- (expected 1)
- c@?: rev 0 points to nonexistent changeset 3
-3 files, 2 changesets, 4 total revisions
-1 warnings encountered!
-7 integrity errors encountered!
-(first damaged changeset appears to be 3)
-% journal contents
-00changelog.i
-00manifest.i
-data/b.i
-data/c.i
-rolling back interrupted transaction
-checking changesets
-checking manifests
-crosschecking files in changesets and manifests
-checking files
-2 files, 2 changesets, 2 total revisions
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/tests/test-repair-strip.t Fri Apr 29 11:10:11 2011 +0200
@@ -0,0 +1,130 @@
+
+ $ echo "[extensions]" >> $HGRCPATH
+ $ echo "mq=">> $HGRCPATH
+
+ $ teststrip() {
+ > hg -q up -C $1
+ > echo % before update $1, strip $2
+ > hg parents
+ > chmod -$3 $4
+ > hg strip $2 2>&1 | sed 's/\(bundle\).*/\1/' | sed 's/Permission denied.*\.hg\/store\/\(.*\)/Permission denied \.hg\/store\/\1/'
+ > echo % after update $1, strip $2
+ > chmod +$3 $4
+ > hg verify
+ > echo % journal contents
+ > if [ -f .hg/store/journal ]; then
+ > sed -e 's/\.i[^\n]*/\.i/' .hg/store/journal
+ > else
+ > echo "(no journal)"
+ > fi
+ > ls .hg/store/journal >/dev/null 2>&1 && hg recover
+ > ls .hg/strip-backup/* >/dev/null 2>&1 && hg unbundle -q .hg/strip-backup/*
+ > rm -rf .hg/strip-backup
+ > }
+
+ $ hg init test
+ $ cd test
+ $ echo a > a
+ $ hg -q ci -m "a" -A
+ $ echo b > b
+ $ hg -q ci -m "b" -A
+ $ echo b2 >> b
+ $ hg -q ci -m "b2" -A
+ $ echo c > c
+ $ hg -q ci -m "c" -A
+ $ teststrip 0 2 w .hg/store/data/b.i
+ % before update 0, strip 2
+ changeset: 0:cb9a9f314b8b
+ user: test
+ date: Thu Jan 01 00:00:00 1970 +0000
+ summary: a
+
+ saved backup bundle
+ transaction abort!
+ failed to truncate data/b.i
+ rollback failed - please run hg recover
+ strip failed, full bundle
+ abort: Permission denied .hg/store/data/b.i
+ % after update 0, strip 2
+ abandoned transaction found - run hg recover
+ checking changesets
+ checking manifests
+ crosschecking files in changesets and manifests
+ checking files
+ b@?: rev 1 points to nonexistent changeset 2
+ (expected 1)
+ b@?: 736c29771fba not in manifests
+ warning: orphan revlog 'data/c.i'
+ 2 files, 2 changesets, 3 total revisions
+ 2 warnings encountered!
+ 2 integrity errors encountered!
+ % journal contents
+ 00changelog.i
+ 00manifest.i
+ data/b.i
+ data/c.i
+ rolling back interrupted transaction
+ checking changesets
+ checking manifests
+ crosschecking files in changesets and manifests
+ checking files
+ 2 files, 2 changesets, 2 total revisions
+ $ teststrip 0 2 r .hg/store/data/b.i
+ % before update 0, strip 2
+ changeset: 0:cb9a9f314b8b
+ user: test
+ date: Thu Jan 01 00:00:00 1970 +0000
+ summary: a
+
+ abort: Permission denied .hg/store/data/b.i
+ % after update 0, strip 2
+ checking changesets
+ checking manifests
+ crosschecking files in changesets and manifests
+ checking files
+ 3 files, 4 changesets, 4 total revisions
+ % journal contents
+ (no journal)
+ $ teststrip 0 2 w .hg/store/00manifest.i
+ % before update 0, strip 2
+ changeset: 0:cb9a9f314b8b
+ user: test
+ date: Thu Jan 01 00:00:00 1970 +0000
+ summary: a
+
+ saved backup bundle
+ transaction abort!
+ failed to truncate 00manifest.i
+ rollback failed - please run hg recover
+ strip failed, full bundle
+ abort: Permission denied .hg/store/00manifest.i
+ % after update 0, strip 2
+ abandoned transaction found - run hg recover
+ checking changesets
+ checking manifests
+ manifest@?: rev 2 points to nonexistent changeset 2
+ manifest@?: 3362547cdf64 not in changesets
+ manifest@?: rev 3 points to nonexistent changeset 3
+ manifest@?: 265a85892ecb not in changesets
+ crosschecking files in changesets and manifests
+ c@3: in manifest but not in changeset
+ checking files
+ b@?: rev 1 points to nonexistent changeset 2
+ (expected 1)
+ c@?: rev 0 points to nonexistent changeset 3
+ 3 files, 2 changesets, 4 total revisions
+ 1 warnings encountered!
+ 7 integrity errors encountered!
+ (first damaged changeset appears to be 3)
+ % journal contents
+ 00changelog.i
+ 00manifest.i
+ data/b.i
+ data/c.i
+ rolling back interrupted transaction
+ checking changesets
+ checking manifests
+ crosschecking files in changesets and manifests
+ checking files
+ 2 files, 2 changesets, 2 total revisions
+
--- a/tests/test-requires.t Fri Apr 29 11:04:39 2011 +0200
+++ b/tests/test-requires.t Fri Apr 29 11:10:11 2011 +0200
@@ -1,6 +1,5 @@
- $ mkdir t
+ $ hg init t
$ cd t
- $ hg init
$ echo a > a
$ hg add a
$ hg commit -m test
--- a/tests/test-revset-outgoing.t Fri Apr 29 11:04:39 2011 +0200
+++ b/tests/test-revset-outgoing.t Fri Apr 29 11:10:11 2011 +0200
@@ -39,7 +39,7 @@
$ cd b
$ cat .hg/hgrc
[paths]
- default = */a#stable (glob)
+ default = $TESTTMP/a#stable
$ echo red >> a
$ hg ci -qm3
@@ -60,7 +60,7 @@
$ hg tout
- comparing with */a (glob)
+ comparing with $TESTTMP/a
searching for changes
2:1d4099801a4e: '3' stable
@@ -79,11 +79,11 @@
$ cat .hg/hgrc
[paths]
- default = */a#stable (glob)
+ default = $TESTTMP/a#stable
green = ../a#default
$ hg tout green
- comparing with */a (glob)
+ comparing with $TESTTMP/a
searching for changes
3:f0461977a3db: '4'
--- a/tests/test-revset.t Fri Apr 29 11:04:39 2011 +0200
+++ b/tests/test-revset.t Fri Apr 29 11:10:11 2011 +0200
@@ -297,6 +297,12 @@
6
$ log 'tag(tip)'
9
+ $ log 'tag(unknown)'
+ abort: tag 'unknown' does not exist
+ [255]
+ $ log 'branch(unknown)'
+ abort: unknown revision 'unknown'!
+ [255]
$ log 'user(bob)'
2
@@ -356,3 +362,15 @@
9
$ log 'ancestors(8) and (heads(branch("-a-b-c-")) or heads(branch(é)))'
4
+
+issue2654: report a parse error if the revset was not completely parsed
+
+ $ log '1 OR 2'
+ hg: parse error at 2: invalid token
+ [255]
+
+or operator should preserve ordering:
+ $ log 'reverse(2::4) or tip'
+ 4
+ 2
+ 9
--- a/tests/test-rollback.t Fri Apr 29 11:04:39 2011 +0200
+++ b/tests/test-rollback.t Fri Apr 29 11:10:11 2011 +0200
@@ -1,7 +1,6 @@
- $ mkdir t
+ $ hg init t
$ cd t
- $ hg init
$ echo a > a
$ hg add a
$ hg commit -m "test"
--- a/tests/test-schemes.t Fri Apr 29 11:04:39 2011 +0200
+++ b/tests/test-schemes.t Fri Apr 29 11:10:11 2011 +0200
@@ -25,10 +25,11 @@
$ hg incoming --debug parts://localhost
using http://localhost:$HGPORT/
- sending between command
- comparing with parts://localhost
+ sending capabilities command
+ comparing with parts://localhost/
sending heads command
searching for changes
+ sending known command
no changes found
[1]
--- a/tests/test-serve Fri Apr 29 11:04:39 2011 +0200
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,58 +0,0 @@
-#!/bin/sh
-
-hgserve()
-{
- hg serve -a localhost -d --pid-file=hg.pid -E errors.log -v $@ \
- | sed -e "s/:$HGPORT1\\([^0-9]\\)/:HGPORT1\1/g" \
- -e "s/:$HGPORT2\\([^0-9]\\)/:HGPORT2\1/g" \
- -e 's/http:\/\/[^/]*\//http:\/\/localhost\//'
- cat hg.pid >> "$DAEMON_PIDS"
- echo % errors
- cat errors.log
- sleep 1
- if [ "$KILLQUIETLY" = "Y" ]; then
- kill `cat hg.pid` 2>/dev/null
- else
- kill `cat hg.pid`
- fi
- sleep 1
-}
-
-hg init test
-cd test
-
-echo '[web]' > .hg/hgrc
-echo 'accesslog = access.log' >> .hg/hgrc
-echo "port = $HGPORT1" >> .hg/hgrc
-
-echo % Without -v
-hg serve -a localhost -p $HGPORT -d --pid-file=hg.pid -E errors.log
-cat hg.pid >> "$DAEMON_PIDS"
-if [ -f access.log ]; then
- echo 'access log created - .hg/hgrc respected'
-fi
-echo % errors
-cat errors.log
-
-echo % With -v
-hgserve
-
-echo % With -v and -p HGPORT2
-hgserve -p "$HGPORT2"
-
-echo '% With -v and -p daytime (should fail because low port)'
-KILLQUIETLY=Y
-hgserve -p daytime
-KILLQUIETLY=N
-
-echo % With --prefix foo
-hgserve --prefix foo
-
-echo % With --prefix /foo
-hgserve --prefix /foo
-
-echo % With --prefix foo/
-hgserve --prefix foo/
-
-echo % With --prefix /foo/
-hgserve --prefix /foo/
--- a/tests/test-serve.out Fri Apr 29 11:04:39 2011 +0200
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,25 +0,0 @@
-% Without -v
-access log created - .hg/hgrc respected
-% errors
-% With -v
-listening at http://localhost/ (bound to 127.0.0.1:HGPORT1)
-% errors
-% With -v and -p HGPORT2
-listening at http://localhost/ (bound to 127.0.0.1:HGPORT2)
-% errors
-% With -v and -p daytime (should fail because low port)
-abort: cannot start server at 'localhost:13': Permission denied
-abort: child process failed to start
-% errors
-% With --prefix foo
-listening at http://localhost/foo/ (bound to 127.0.0.1:HGPORT1)
-% errors
-% With --prefix /foo
-listening at http://localhost/foo/ (bound to 127.0.0.1:HGPORT1)
-% errors
-% With --prefix foo/
-listening at http://localhost/foo/ (bound to 127.0.0.1:HGPORT1)
-% errors
-% With --prefix /foo/
-listening at http://localhost/foo/ (bound to 127.0.0.1:HGPORT1)
-% errors
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/tests/test-serve.t Fri Apr 29 11:10:11 2011 +0200
@@ -0,0 +1,82 @@
+
+ $ hgserve()
+ > {
+ > hg serve -a localhost -d --pid-file=hg.pid -E errors.log -v $@ \
+ > | sed -e "s/:$HGPORT1\\([^0-9]\\)/:HGPORT1\1/g" \
+ > -e "s/:$HGPORT2\\([^0-9]\\)/:HGPORT2\1/g" \
+ > -e 's/http:\/\/[^/]*\//http:\/\/localhost\//'
+ > cat hg.pid >> "$DAEMON_PIDS"
+ > echo % errors
+ > cat errors.log
+ > sleep 1
+ > if [ "$KILLQUIETLY" = "Y" ]; then
+ > kill `cat hg.pid` 2>/dev/null
+ > else
+ > kill `cat hg.pid`
+ > fi
+ > sleep 1
+ > }
+
+ $ hg init test
+ $ cd test
+ $ echo '[web]' > .hg/hgrc
+ $ echo 'accesslog = access.log' >> .hg/hgrc
+ $ echo "port = $HGPORT1" >> .hg/hgrc
+
+Without -v
+
+ $ hg serve -a localhost -p $HGPORT -d --pid-file=hg.pid -E errors.log
+ $ cat hg.pid >> "$DAEMON_PIDS"
+ $ if [ -f access.log ]; then
+ $ echo 'access log created - .hg/hgrc respected'
+ access log created - .hg/hgrc respected
+ $ fi
+
+errors
+
+ $ cat errors.log
+
+With -v
+
+ $ hgserve
+ listening at http://localhost/ (bound to 127.0.0.1:HGPORT1)
+ % errors
+
+With -v and -p HGPORT2
+
+ $ hgserve -p "$HGPORT2"
+ listening at http://localhost/ (bound to 127.0.0.1:HGPORT2)
+ % errors
+
+With -v and -p daytime (should fail because low port)
+
+ $ KILLQUIETLY=Y
+ $ hgserve -p daytime
+ abort: cannot start server at 'localhost:13': Permission denied
+ abort: child process failed to start
+ % errors
+ $ KILLQUIETLY=N
+
+With --prefix foo
+
+ $ hgserve --prefix foo
+ listening at http://localhost/foo/ (bound to 127.0.0.1:HGPORT1)
+ % errors
+
+With --prefix /foo
+
+ $ hgserve --prefix /foo
+ listening at http://localhost/foo/ (bound to 127.0.0.1:HGPORT1)
+ % errors
+
+With --prefix foo/
+
+ $ hgserve --prefix foo/
+ listening at http://localhost/foo/ (bound to 127.0.0.1:HGPORT1)
+ % errors
+
+With --prefix /foo/
+
+ $ hgserve --prefix /foo/
+ listening at http://localhost/foo/ (bound to 127.0.0.1:HGPORT1)
+ % errors
--- a/tests/test-simple-update.t Fri Apr 29 11:04:39 2011 +0200
+++ b/tests/test-simple-update.t Fri Apr 29 11:10:11 2011 +0200
@@ -1,7 +1,6 @@
- $ mkdir test
+ $ hg init test
$ cd test
$ echo foo>foo
- $ hg init
$ hg addremove
adding foo
$ hg commit -m "1"
@@ -50,3 +49,8 @@
$ hg manifest --debug
6f4310b00b9a147241b071a60c28a650827fb03d 644 foo
+update to rev 0 with a date
+
+ $ hg upd -d foo 0
+ abort: you can't specify a revision and a date
+ [255]
--- a/tests/test-ssh-clone-r.t Fri Apr 29 11:04:39 2011 +0200
+++ b/tests/test-ssh-clone-r.t Fri Apr 29 11:10:11 2011 +0200
@@ -232,7 +232,7 @@
adding changesets
adding manifests
adding file changes
- added 1 changesets with 0 changes to 1 files (+1 heads)
+ added 1 changesets with 0 changes to 0 files (+1 heads)
(run 'hg heads' to see heads, 'hg merge' to merge)
$ hg verify
checking changesets
@@ -256,7 +256,7 @@
adding changesets
adding manifests
adding file changes
- added 2 changesets with 0 changes to 1 files (+1 heads)
+ added 2 changesets with 0 changes to 0 files (+1 heads)
(run 'hg heads' to see heads, 'hg merge' to merge)
$ hg verify
checking changesets
--- a/tests/test-ssh.t Fri Apr 29 11:04:39 2011 +0200
+++ b/tests/test-ssh.t Fri Apr 29 11:10:11 2011 +0200
@@ -263,10 +263,28 @@
summary: z
+clone bookmarks
+
+ $ hg -R ../remote bookmark test
+ $ hg -R ../remote bookmarks
+ * test 2:6c0482d977a3
+ $ hg clone -e "python ../dummyssh" ssh://user@dummy/remote local-bookmarks
+ requesting all changes
+ adding changesets
+ adding manifests
+ adding file changes
+ added 4 changesets with 5 changes to 4 files (+1 heads)
+ updating to branch default
+ 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
+ $ hg -R local-bookmarks bookmarks
+ test 2:6c0482d977a3
+
passwords in ssh urls are not supported
+(we use a glob here because different Python versions give different
+results here)
$ hg push ssh://user:erroneouspwd@dummy/remote
- pushing to ssh://user:***@dummy/remote
+ pushing to ssh://user:*@dummy/remote (glob)
abort: password in URL not supported!
[255]
@@ -290,3 +308,4 @@
Got arguments 1:user@dummy 2:hg -R remote serve --stdio
Got arguments 1:user@dummy 2:hg -R remote serve --stdio
Got arguments 1:user@dummy 2:hg -R remote serve --stdio
+ Got arguments 1:user@dummy 2:hg -R remote serve --stdio
--- a/tests/test-static-http.t Fri Apr 29 11:04:39 2011 +0200
+++ b/tests/test-static-http.t Fri Apr 29 11:10:11 2011 +0200
@@ -22,9 +22,8 @@
> EOF
$ python dumb.py 2>/dev/null &
$ echo $! >> $DAEMON_PIDS
- $ mkdir remote
+ $ hg init remote
$ cd remote
- $ hg init
$ echo foo > bar
$ echo c2 > '.dotfile with spaces'
$ hg add
--- a/tests/test-status-color.t Fri Apr 29 11:04:39 2011 +0200
+++ b/tests/test-status-color.t Fri Apr 29 11:10:11 2011 +0200
@@ -2,6 +2,8 @@
$ echo "color=" >> $HGRCPATH
$ echo "[color]" >> $HGRCPATH
$ echo "mode=ansi" >> $HGRCPATH
+Terminfo codes compatibility fix
+ $ echo "color.none=0" >> $HGRCPATH
$ hg init repo1
$ cd repo1
@@ -163,6 +165,19 @@
\x1b[0;0mC .hgignore\x1b[0m (esc)
\x1b[0;0mC modified\x1b[0m (esc)
+hg status -A (with terminfo color):
+
+ $ TERM=xterm hg status --config color.mode=terminfo --color=always -A
+ \x1b[30m\x1b[32m\x1b[1mA added\x1b[30m (esc)
+ \x1b[30m\x1b[32m\x1b[1mA copied\x1b[30m (esc)
+ \x1b[30m\x1b[30m modified\x1b[30m (esc)
+ \x1b[30m\x1b[31m\x1b[1mR removed\x1b[30m (esc)
+ \x1b[30m\x1b[36m\x1b[1m\x1b[4m! deleted\x1b[30m (esc)
+ \x1b[30m\x1b[35m\x1b[1m\x1b[4m? unknown\x1b[30m (esc)
+ \x1b[30m\x1b[30m\x1b[1mI ignored\x1b[30m (esc)
+ \x1b[30m\x1b[30mC .hgignore\x1b[30m (esc)
+ \x1b[30m\x1b[30mC modified\x1b[30m (esc)
+
$ echo "^ignoreddir$" > .hgignore
$ mkdir ignoreddir
--- a/tests/test-subrepo-git.t Fri Apr 29 11:04:39 2011 +0200
+++ b/tests/test-subrepo-git.t Fri Apr 29 11:10:11 2011 +0200
@@ -73,7 +73,7 @@
$ cd t
$ hg clone . ../tc
updating to branch default
- cloning subrepo s
+ cloning subrepo s from $TESTTMP/gitroot
3 files updated, 0 files merged, 0 files removed, 0 files unresolved
$ cd ../tc
$ hg debugsub
@@ -96,7 +96,7 @@
$ cd ../t
$ hg clone . ../ta
updating to branch default
- cloning subrepo s
+ cloning subrepo s from $TESTTMP/gitroot
3 files updated, 0 files merged, 0 files removed, 0 files unresolved
$ cd ../ta
@@ -115,7 +115,7 @@
$ cd ../t
$ hg clone . ../tb
updating to branch default
- cloning subrepo s
+ cloning subrepo s from $TESTTMP/gitroot
3 files updated, 0 files merged, 0 files removed, 0 files unresolved
$ cd ../tb/s
@@ -155,7 +155,7 @@
added 1 changesets with 1 changes to 1 files (+1 heads)
(run 'hg heads' to see heads, 'hg merge' to merge)
$ hg merge 2>/dev/null
- pulling subrepo s
+ pulling subrepo s from $TESTTMP/gitroot
0 files updated, 0 files merged, 0 files removed, 0 files unresolved
(branch merge, don't forget to commit)
$ cat s/f
@@ -199,7 +199,7 @@
$ cd ../t
$ hg clone . ../td
updating to branch default
- cloning subrepo s
+ cloning subrepo s from $TESTTMP/gitroot
checking out detached HEAD in subrepo s
check out a git branch if you intend to make changes
3 files updated, 0 files merged, 0 files removed, 0 files unresolved
@@ -232,7 +232,7 @@
$ cd ../tb
$ hg pull -q
$ hg update 2>/dev/null
- pulling subrepo s
+ pulling subrepo s from $TESTTMP/gitroot
2 files updated, 0 files merged, 0 files removed, 0 files unresolved
$ hg debugsub
path s
@@ -262,7 +262,7 @@
$ cd ../tc
$ hg pull -q
$ hg archive --subrepos -r 5 ../archive 2>/dev/null
- pulling subrepo s
+ pulling subrepo s from $TESTTMP/gitroot
$ cd ../archive
$ cat s/f
f
@@ -282,7 +282,7 @@
$ hg clone ../t inner
updating to branch default
- cloning subrepo s
+ cloning subrepo s from $TESTTMP/gitroot
3 files updated, 0 files merged, 0 files removed, 0 files unresolved
$ echo inner = inner > .hgsub
$ hg add .hgsub
@@ -311,7 +311,7 @@
$ mkdir d
$ hg clone t d/t
updating to branch default
- cloning subrepo s
+ cloning subrepo s from $TESTTMP/gitroot
3 files updated, 0 files merged, 0 files removed, 0 files unresolved
Don't crash if the subrepo is missing
@@ -329,7 +329,7 @@
abort: subrepo s is missing
[255]
$ hg update -C
- cloning subrepo s
+ cloning subrepo s from $TESTTMP/gitroot
1 files updated, 0 files merged, 0 files removed, 0 files unresolved
$ hg sum | grep commit
commit: (clean)
--- a/tests/test-subrepo-paths.t Fri Apr 29 11:04:39 2011 +0200
+++ b/tests/test-subrepo-paths.t Fri Apr 29 11:10:11 2011 +0200
@@ -21,6 +21,15 @@
source C:\libs\foo-lib\
revision
+test cumulative remapping, the $HGRCPATH file is loaded first
+
+ $ echo '[subpaths]' >> $HGRCPATH
+ $ echo 'libfoo = libbar' >> $HGRCPATH
+ $ hg debugsub
+ path sub
+ source C:\libs\bar-lib\
+ revision
+
test bad subpaths pattern
$ cat > .hg/hgrc <<EOF
--- a/tests/test-subrepo-svn.t Fri Apr 29 11:04:39 2011 +0200
+++ b/tests/test-subrepo-svn.t Fri Apr 29 11:10:11 2011 +0200
@@ -225,6 +225,14 @@
$ cd ..
$ hg clone t tc | fix_path
updating to branch default
+ A tc/s/alpha
+ U tc/s
+
+ Fetching external item into 'tc/s/externals'
+ A tc/s/externals/other
+ Checked out external at revision 1.
+
+ Checked out revision 3.
A tc/subdir/s/alpha
U tc/subdir/s
@@ -233,14 +241,6 @@
Checked out external at revision 1.
Checked out revision 2.
- A tc/s/alpha
- U tc/s
-
- Fetching external item into 'tc/s/externals'
- A tc/s/externals/other
- Checked out external at revision 1.
-
- Checked out revision 3.
3 files updated, 0 files merged, 0 files removed, 0 files unresolved
$ cd tc
--- a/tests/test-transplant.t Fri Apr 29 11:04:39 2011 +0200
+++ b/tests/test-transplant.t Fri Apr 29 11:10:11 2011 +0200
@@ -68,6 +68,18 @@
$ hg help revsets | grep transplanted
"transplanted(set)"
+test tranplanted keyword
+
+ $ hg log --template '{rev} {transplanted}\n'
+ 7 a53251cdf717679d1907b289f991534be05c997a
+ 6 722f4667af767100cb15b6a79324bf8abbfe1ef4
+ 5 37a1297eb21b3ef5c5d2ffac22121a0988ed9f21
+ 4
+ 3
+ 2
+ 1
+ 0
+
$ hg clone ../t ../prune
updating to branch default
4 files updated, 0 files merged, 0 files removed, 0 files unresolved
@@ -330,6 +342,40 @@
[255]
$ cd ..
+test environment passed to filter
+
+ $ hg init filter-environment
+ $ cd filter-environment
+ $ cat <<'EOF' >test-filter-environment
+ > #!/bin/sh
+ > echo "Transplant by $HGUSER" >> $1
+ > echo "Transplant from rev $HGREVISION" >> $1
+ > EOF
+ $ chmod +x test-filter-environment
+ $ hg transplant -s ../t --filter ./test-filter-environment 0
+ filtering * (glob)
+ applying 17ab29e464c6
+ 17ab29e464c6 transplanted to 5190e68026a0
+
+ $ hg log --template '{rev} {parents} {desc}\n'
+ 0 r1
+ Transplant by test
+ Transplant from rev 17ab29e464c6ca53e329470efe2a9918ac617a6f
+ $ cd ..
+
+test transplant with filter handles invalid changelog
+
+ $ hg init filter-invalid-log
+ $ cd filter-invalid-log
+ $ cat <<'EOF' >test-filter-invalid-log
+ > #!/bin/sh
+ > echo "" > $1
+ > EOF
+ $ chmod +x test-filter-invalid-log
+ $ hg transplant -s ../t --filter ./test-filter-invalid-log 0
+ filtering * (glob)
+ abort: filter corrupted changeset (no user or date)
+ [255]
test with a win32ext like setup (differing EOLs)
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/tests/test-unbundlehash.t Fri Apr 29 11:10:11 2011 +0200
@@ -0,0 +1,31 @@
+
+Test wire protocol unbundle with hashed heads (capability: unbundlehash)
+
+Create a remote repository.
+
+ $ hg init remote
+ $ hg serve -R remote --config web.push_ssl=False --config web.allow_push=* -p $HGPORT -d --pid-file=hg1.pid -E error.log -A access.log
+ $ cat hg1.pid >> $DAEMON_PIDS
+
+Clone the repository and push a change.
+
+ $ hg clone http://localhost:$HGPORT/ local
+ no changes found
+ updating to branch default
+ 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
+ $ touch local/README
+ $ hg ci -R local -A -m hoge
+ adding README
+ $ hg push -R local
+ 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
+
+Ensure hashed heads format is used.
+The hash here is always the same since the remote repository only has the null head.
+
+ $ cat access.log | grep unbundle
+ * - - [*] "POST /?cmd=unbundle&heads=686173686564+6768033e216468247bd031a0a2d9876d79818f8f HTTP/1.1" 200 - (glob)
--- a/tests/test-unrelated-pull.t Fri Apr 29 11:04:39 2011 +0200
+++ b/tests/test-unrelated-pull.t Fri Apr 29 11:10:11 2011 +0200
@@ -1,14 +1,12 @@
- $ mkdir a
+ $ hg init a
$ cd a
- $ hg init
$ echo 123 > a
$ hg add a
$ hg commit -m "a" -u a
$ cd ..
- $ mkdir b
+ $ hg init b
$ cd b
- $ hg init
$ echo 321 > b
$ hg add b
$ hg commit -m "b" -u b
@@ -23,6 +21,7 @@
pulling from ../a
searching for changes
warning: repository is unrelated
+ requesting all changes
adding changesets
adding manifests
adding file changes
--- a/tests/test-up-local-change.t Fri Apr 29 11:04:39 2011 +0200
+++ b/tests/test-up-local-change.t Fri Apr 29 11:10:11 2011 +0200
@@ -1,8 +1,7 @@
$ HGMERGE=true; export HGMERGE
- $ mkdir r1
+ $ hg init r1
$ cd r1
- $ hg init
$ echo a > a
$ hg addremove
adding a
--- a/tests/test-url.py Fri Apr 29 11:04:39 2011 +0200
+++ b/tests/test-url.py Fri Apr 29 11:10:11 2011 +0200
@@ -49,6 +49,157 @@
check(_verifycert(None, 'example.com'),
'no certificate received')
+import doctest
+
+def test_url():
+ """
+ >>> from mercurial.url import url
+
+ This tests for edge cases in url.URL's parsing algorithm. Most of
+ these aren't useful for documentation purposes, so they aren't
+ part of the class's doc tests.
+
+ Query strings and fragments:
+
+ >>> url('http://host/a?b#c')
+ <url scheme: 'http', host: 'host', path: 'a', query: 'b', fragment: 'c'>
+ >>> url('http://host/a?')
+ <url scheme: 'http', host: 'host', path: 'a'>
+ >>> url('http://host/a#b#c')
+ <url scheme: 'http', host: 'host', path: 'a', fragment: 'b#c'>
+ >>> url('http://host/a#b?c')
+ <url scheme: 'http', host: 'host', path: 'a', fragment: 'b?c'>
+ >>> url('http://host/?a#b')
+ <url scheme: 'http', host: 'host', path: '', query: 'a', fragment: 'b'>
+ >>> url('http://host/?a#b', parsequery=False)
+ <url scheme: 'http', host: 'host', path: '?a', fragment: 'b'>
+ >>> url('http://host/?a#b', parsefragment=False)
+ <url scheme: 'http', host: 'host', path: '', query: 'a#b'>
+ >>> url('http://host/?a#b', parsequery=False, parsefragment=False)
+ <url scheme: 'http', host: 'host', path: '?a#b'>
+
+ IPv6 addresses:
+
+ >>> url('ldap://[2001:db8::7]/c=GB?objectClass?one')
+ <url scheme: 'ldap', host: '[2001:db8::7]', path: 'c=GB',
+ query: 'objectClass?one'>
+ >>> url('ldap://joe:xxx@[2001:db8::7]:80/c=GB?objectClass?one')
+ <url scheme: 'ldap', user: 'joe', passwd: 'xxx', host: '[2001:db8::7]',
+ port: '80', path: 'c=GB', query: 'objectClass?one'>
+
+ Missing scheme, host, etc.:
+
+ >>> url('://192.0.2.16:80/')
+ <url path: '://192.0.2.16:80/'>
+ >>> url('http://mercurial.selenic.com')
+ <url scheme: 'http', host: 'mercurial.selenic.com'>
+ >>> url('/foo')
+ <url path: '/foo'>
+ >>> url('bundle:/foo')
+ <url scheme: 'bundle', path: '/foo'>
+ >>> url('a?b#c')
+ <url path: 'a?b', fragment: 'c'>
+ >>> url('http://x.com?arg=/foo')
+ <url scheme: 'http', host: 'x.com', query: 'arg=/foo'>
+ >>> url('http://joe:xxx@/foo')
+ <url scheme: 'http', user: 'joe', passwd: 'xxx', path: 'foo'>
+
+ Just a scheme and a path:
+
+ >>> url('mailto:John.Doe@example.com')
+ <url scheme: 'mailto', path: 'John.Doe@example.com'>
+ >>> url('a:b:c:d')
+ <url path: 'a:b:c:d'>
+ >>> url('aa:bb:cc:dd')
+ <url scheme: 'aa', path: 'bb:cc:dd'>
+
+ SSH examples:
+
+ >>> url('ssh://joe@host//home/joe')
+ <url scheme: 'ssh', user: 'joe', host: 'host', path: '/home/joe'>
+ >>> url('ssh://joe:xxx@host/src')
+ <url scheme: 'ssh', user: 'joe', passwd: 'xxx', host: 'host', path: 'src'>
+ >>> url('ssh://joe:xxx@host')
+ <url scheme: 'ssh', user: 'joe', passwd: 'xxx', host: 'host'>
+ >>> url('ssh://joe@host')
+ <url scheme: 'ssh', user: 'joe', host: 'host'>
+ >>> url('ssh://host')
+ <url scheme: 'ssh', host: 'host'>
+ >>> url('ssh://')
+ <url scheme: 'ssh'>
+ >>> url('ssh:')
+ <url scheme: 'ssh'>
+
+ Non-numeric port:
+
+ >>> url('http://example.com:dd')
+ <url scheme: 'http', host: 'example.com', port: 'dd'>
+ >>> url('ssh://joe:xxx@host:ssh/foo')
+ <url scheme: 'ssh', user: 'joe', passwd: 'xxx', host: 'host', port: 'ssh',
+ path: 'foo'>
+
+ Bad authentication credentials:
+
+ >>> url('http://joe@joeville:123@4:@host/a?b#c')
+ <url scheme: 'http', user: 'joe@joeville', passwd: '123@4:',
+ host: 'host', path: 'a', query: 'b', fragment: 'c'>
+ >>> url('http://!*#?/@!*#?/:@host/a?b#c')
+ <url scheme: 'http', host: '!*', fragment: '?/@!*#?/:@host/a?b#c'>
+ >>> url('http://!*#?@!*#?:@host/a?b#c')
+ <url scheme: 'http', host: '!*', fragment: '?@!*#?:@host/a?b#c'>
+ >>> url('http://!*@:!*@@host/a?b#c')
+ <url scheme: 'http', user: '!*@', passwd: '!*@', host: 'host',
+ path: 'a', query: 'b', fragment: 'c'>
+
+ File paths:
+
+ >>> url('a/b/c/d.g.f')
+ <url path: 'a/b/c/d.g.f'>
+ >>> url('/x///z/y/')
+ <url path: '/x///z/y/'>
+ >>> url('/foo:bar')
+ <url path: '/foo:bar'>
+ >>> url('\\\\foo:bar')
+ <url path: '\\\\foo:bar'>
+ >>> url('./foo:bar')
+ <url path: './foo:bar'>
+
+ Non-localhost file URL:
+
+ >>> u = url('file://mercurial.selenic.com/foo')
+ Traceback (most recent call last):
+ File "<stdin>", line 1, in ?
+ Abort: file:// URLs can only refer to localhost
+
+ Empty URL:
+
+ >>> u = url('')
+ >>> u
+ <url path: ''>
+ >>> str(u)
+ ''
+
+ Empty path with query string:
+
+ >>> str(url('http://foo/?bar'))
+ 'http://foo/?bar'
+
+ Invalid path:
+
+ >>> u = url('http://foo/bar')
+ >>> u.path = 'bar'
+ >>> str(u)
+ 'http://foo/bar'
+
+ >>> u = url('file:///foo/bar/baz')
+ >>> u
+ <url scheme: 'file', path: '/foo/bar/baz'>
+ >>> str(u)
+ 'file:/foo/bar/baz'
+ """
+
+doctest.testmod(optionflags=doctest.NORMALIZE_WHITESPACE)
+
# Unicode (IDN) certname isn't supported
check(_verifycert(cert(u'\u4f8b.jp'), 'example.jp'),
'IDN in certificate not supported')
--- a/tests/test-walk.t Fri Apr 29 11:04:39 2011 +0200
+++ b/tests/test-walk.t Fri Apr 29 11:10:11 2011 +0200
@@ -1,6 +1,5 @@
- $ mkdir t
+ $ hg init t
$ cd t
- $ hg init
$ mkdir -p beans
$ for b in kidney navy turtle borlotti black pinto; do
> echo $b > beans/$b
@@ -29,6 +28,7 @@
adding mammals/Procyonidae/coatimundi
adding mammals/Procyonidae/raccoon
adding mammals/skunk
+ warning: filename contains ':', which is reserved on Windows: 'glob:glob'
$ hg commit -m "commit #0"
$ hg debugwalk
@@ -159,7 +159,7 @@
f mammals/Procyonidae/raccoon Procyonidae/raccoon
f mammals/skunk skunk
$ hg debugwalk .hg
- abort: path 'mammals/.hg' is inside repo 'mammals'
+ abort: path 'mammals/.hg' is inside nested repo 'mammals'
[255]
$ hg debugwalk ../.hg
abort: path contains illegal component: .hg
@@ -203,7 +203,7 @@
abort: path contains illegal component: .hg/data
[255]
$ hg debugwalk beans/.hg
- abort: path 'beans/.hg' is inside repo 'beans'
+ abort: path 'beans/.hg' is inside nested repo 'beans'
[255]
Test absolute paths:
--- a/tests/test-walkrepo.py Fri Apr 29 11:04:39 2011 +0200
+++ b/tests/test-walkrepo.py Fri Apr 29 11:10:11 2011 +0200
@@ -1,6 +1,6 @@
import os
from mercurial import hg, ui
-from mercurial.util import walkrepos
+from mercurial.scmutil import walkrepos
from os import mkdir, chdir
from os.path import join as pjoin
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/tests/test-wireproto.t Fri Apr 29 11:10:11 2011 +0200
@@ -0,0 +1,60 @@
+
+Test wire protocol argument passing
+
+Setup repo:
+
+ $ hg init repo
+
+Local:
+
+ $ hg debugwireargs repo eins zwei --three drei --four vier
+ eins zwei drei vier
+ $ hg debugwireargs repo eins zwei --four vier
+ eins zwei None vier
+ $ hg debugwireargs repo eins zwei
+ eins zwei None None
+
+HTTP:
+
+ $ hg serve -R repo -p $HGPORT -d --pid-file=hg1.pid -E error.log -A access.log
+ $ cat hg1.pid >> $DAEMON_PIDS
+
+ $ hg debugwireargs http://localhost:$HGPORT/ un deux trois quatre
+ un deux trois quatre
+ $ hg debugwireargs http://localhost:$HGPORT/ eins zwei --four vier
+ eins zwei None vier
+ $ hg debugwireargs http://localhost:$HGPORT/ eins zwei
+ eins zwei None None
+ $ cat access.log
+ * - - [*] "GET /?cmd=capabilities HTTP/1.1" 200 - (glob)
+ * - - [*] "GET /?cmd=debugwireargs&four=quatre&one=un&three=trois&two=deux HTTP/1.1" 200 - (glob)
+ * - - [*] "GET /?cmd=debugwireargs&four=quatre&one=un&three=trois&two=deux HTTP/1.1" 200 - (glob)
+ * - - [*] "GET /?cmd=capabilities HTTP/1.1" 200 - (glob)
+ * - - [*] "GET /?cmd=debugwireargs&four=vier&one=eins&two=zwei HTTP/1.1" 200 - (glob)
+ * - - [*] "GET /?cmd=debugwireargs&four=vier&one=eins&two=zwei HTTP/1.1" 200 - (glob)
+ * - - [*] "GET /?cmd=capabilities HTTP/1.1" 200 - (glob)
+ * - - [*] "GET /?cmd=debugwireargs&one=eins&two=zwei HTTP/1.1" 200 - (glob)
+ * - - [*] "GET /?cmd=debugwireargs&one=eins&two=zwei HTTP/1.1" 200 - (glob)
+
+SSH (try to exercise the ssh functionality with a dummy script):
+
+ $ cat <<EOF > dummyssh
+ > import sys
+ > import os
+ > os.chdir(os.path.dirname(sys.argv[0]))
+ > if sys.argv[1] != "user@dummy":
+ > sys.exit(-1)
+ > if not os.path.exists("dummyssh"):
+ > sys.exit(-1)
+ > os.environ["SSH_CLIENT"] = "127.0.0.1 1 2"
+ > r = os.system(sys.argv[2])
+ > sys.exit(bool(r))
+ > EOF
+
+ $ hg debugwireargs --ssh "python ./dummyssh" ssh://user@dummy/repo uno due tre quattro
+ uno due tre quattro
+ $ hg debugwireargs --ssh "python ./dummyssh" ssh://user@dummy/repo eins zwei --four vier
+ eins zwei None vier
+ $ hg debugwireargs --ssh "python ./dummyssh" ssh://user@dummy/repo eins zwei
+ eins zwei None None
+