changeset 13607:2151703e7f84

merge with stable
author Martin Geisler <mg@lazybytes.net>
date Sun, 13 Mar 2011 13:05:16 +0100
parents 3f6a4579f803 (diff) 1532ed1e50ca (current diff)
children 63ab6b0ccedc
files hgext/transplant.py
diffstat 62 files changed, 775 insertions(+), 441 deletions(-) [+]
line wrap: on
line diff
--- a/contrib/check-code.py	Sun Mar 13 12:44:35 2011 +0100
+++ b/contrib/check-code.py	Sun Mar 13 13:05:16 2011 +0100
@@ -66,6 +66,7 @@
     (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"),
 ]
 
 testfilters = [
--- a/contrib/zsh_completion	Sun Mar 13 12:44:35 2011 +0100
+++ b/contrib/zsh_completion	Sun Mar 13 13:05:16 2011 +0100
@@ -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/hgext/convert/subversion.py	Sun Mar 13 12:44:35 2011 +0100
+++ b/hgext/convert/subversion.py	Sun Mar 13 13:05:16 2011 +0100
@@ -259,6 +259,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)
@@ -761,9 +762,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
 
@@ -942,6 +942,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:
@@ -962,8 +963,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 = []
@@ -1010,8 +1009,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/keyword.py	Sun Mar 13 12:44:35 2011 +0100
+++ b/hgext/keyword.py	Sun Mar 13 13:05:16 2011 +0100
@@ -109,11 +109,14 @@
 }
 
 # date like in cvs' $Date
-utcdate = lambda x: util.datestr((x[0], 0), '%Y/%m/%d %H:%M:%S')
+def utcdate(text):
+    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):
+    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):
+    return util.datestr((text[0], 0), '%Y-%m-%d %H:%M:%SZ')
 
 # make keyword tools accessible
 kwtools = {'templater': None, 'hgcmd': ''}
--- a/hgext/mq.py	Sun Mar 13 12:44:35 2011 +0100
+++ b/hgext/mq.py	Sun Mar 13 13:05:16 2011 +0100
@@ -1898,7 +1898,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/transplant.py	Sun Mar 13 12:44:35 2011 +0100
+++ b/hgext/transplant.py	Sun Mar 13 13:05:16 2011 +0100
@@ -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
--- a/mercurial/ancestor.py	Sun Mar 13 12:44:35 2011 +0100
+++ b/mercurial/ancestor.py	Sun Mar 13 13:05:16 2011 +0100
@@ -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/commands.py	Sun Mar 13 12:44:35 2011 +0100
+++ b/mercurial/commands.py	Sun Mar 13 13:05:16 2011 +0100
@@ -13,7 +13,7 @@
 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
@@ -303,7 +303,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 +327,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 +348,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 +386,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 +450,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:
@@ -1175,6 +1197,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:
@@ -2141,6 +2164,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
@@ -4053,7 +4078,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
@@ -4269,6 +4294,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'))],
--- a/mercurial/help.py	Sun Mar 13 12:44:35 2011 +0100
+++ b/mercurial/help.py	Sun Mar 13 13:05:16 2011 +0100
@@ -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/templates.txt	Sun Mar 13 12:44:35 2011 +0100
+++ b/mercurial/help/templates.txt	Sun Mar 13 13:05:16 2011 +0100
@@ -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	Sun Mar 13 12:44:35 2011 +0100
+++ b/mercurial/hg.py	Sun Mar 13 13:05:16 2011 +0100
@@ -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
@@ -366,6 +366,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:
+                    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)
--- a/mercurial/hgweb/common.py	Sun Mar 13 12:44:35 2011 +0100
+++ b/mercurial/hgweb/common.py	Sun Mar 13 13:05:16 2011 +0100
@@ -73,10 +73,29 @@
     def __init__(self, code, message=None, headers=[]):
         if message is None:
             message = _statusmessage(code)
-        Exception.__init__(self, code, message)
+        super(Exception, self).__init__()
         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	Sun Mar 13 12:44:35 2011 +0100
+++ b/mercurial/hgweb/hgweb_mod.py	Sun Mar 13 13:05:16 2011 +0100
@@ -121,7 +121,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
--- a/mercurial/hgweb/hgwebdir_mod.py	Sun Mar 13 12:44:35 2011 +0100
+++ b/mercurial/hgweb/hgwebdir_mod.py	Sun Mar 13 13:05:16 2011 +0100
@@ -40,9 +40,10 @@
 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:
--- a/mercurial/hgweb/server.py	Sun Mar 13 12:44:35 2011 +0100
+++ b/mercurial/hgweb/server.py	Sun Mar 13 13:05:16 2011 +0100
@@ -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/wsgicgi.py	Sun Mar 13 12:44:35 2011 +0100
+++ b/mercurial/hgweb/wsgicgi.py	Sun Mar 13 13:05:16 2011 +0100
@@ -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	Sun Mar 13 12:44:35 2011 +0100
+++ b/mercurial/httprepo.py	Sun Mar 13 13:05:16 2011 +0100
@@ -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/merge.py	Sun Mar 13 12:44:35 2011 +0100
+++ b/mercurial/merge.py	Sun Mar 13 13:05:16 2011 +0100
@@ -493,7 +493,6 @@
         p1, p2 = pl[0], repo[node]
         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:
@@ -503,9 +502,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()):
@@ -550,7 +547,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/revset.py	Sun Mar 13 12:44:35 2011 +0100
+++ b/mercurial/revset.py	Sun Mar 13 13:05:16 2011 +0100
@@ -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, ")")),
@@ -683,12 +683,27 @@
                for r in bookmarksmod.listbookmarks(repo).values()])
     return [r for r in subset if r in bms]
 
+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])
+    l = []
+    for r in subset:
+        if r in marked:
+            l.append(r)
+    return l
+
 symbols = {
     "adds": adds,
     "all": getall,
     "ancestor": ancestor,
     "ancestors": ancestors,
     "author": author,
+    "bisected": bisected,
     "bookmark": bookmark,
     "branch": branch,
     "children": children,
@@ -815,19 +830,7 @@
     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()
--- a/mercurial/statichttprepo.py	Sun Mar 13 12:44:35 2011 +0100
+++ b/mercurial/statichttprepo.py	Sun Mar 13 13:05:16 2011 +0100
@@ -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)
--- a/mercurial/subrepo.py	Sun Mar 13 12:44:35 2011 +0100
+++ b/mercurial/subrepo.py	Sun Mar 13 13:05:16 2011 +0100
@@ -714,6 +714,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
@@ -762,11 +768,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):
--- a/mercurial/templatefilters.py	Sun Mar 13 12:44:35 2011 +0100
+++ b/mercurial/templatefilters.py	Sun Mar 13 13:05:16 2011 +0100
@@ -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:
@@ -46,6 +48,47 @@
         if n >= 2 or s == 1:
             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 +117,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 +172,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('&', '&amp;')
-            .replace('<', '&lt;')
-            .replace('>', '&gt;')
-            .replace('"', '&quot;')
-            .replace("'", '&#39;')) # &apos; 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 +196,163 @@
     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 util.shortuser(author)
+    return author[:f].rstrip()
+
+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('&', '&amp;')
+            .replace('<', '&lt;')
+            .replace('>', '&gt;')
+            .replace('"', '&quot;')
+            .replace("'", '&#39;')) # &apos; 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	Sun Mar 13 12:44:35 2011 +0100
+++ b/mercurial/templatekw.py	Sun Mar 13 13:05:16 2011 +0100
@@ -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.
@@ -143,32 +143,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 +201,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 +228,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 +269,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 +314,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/ui.py	Sun Mar 13 12:44:35 2011 +0100
+++ b/mercurial/ui.py	Sun Mar 13 13:05:16 2011 +0100
@@ -273,7 +273,7 @@
         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?
--- a/tests/run-tests.py	Sun Mar 13 12:44:35 2011 +0100
+++ b/tests/run-tests.py	Sun Mar 13 13:05:16 2011 +0100
@@ -227,8 +227,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()
--- a/tests/test-basic.t	Sun Mar 13 12:44:35 2011 +0100
+++ b/tests/test-basic.t	Sun Mar 13 13:05:16 2011 +0100
@@ -20,6 +20,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	Sun Mar 13 12:44:35 2011 +0100
+++ b/tests/test-bisect.t	Sun Mar 13 13:05:16 2011 +0100
@@ -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	Sun Mar 13 12:44:35 2011 +0100
+++ b/tests/test-bisect2.t	Sun Mar 13 13:05:16 2011 +0100
@@ -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	Sun Mar 13 12:44:35 2011 +0100
+++ b/tests/test-bookmarks-pushpull.t	Sun Mar 13 13:05:16 2011 +0100
@@ -176,5 +176,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	Sun Mar 13 12:44:35 2011 +0100
+++ b/tests/test-bookmarks.t	Sun Mar 13 13:05:16 2011 +0100
@@ -244,3 +244,47 @@
 
   $ 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
--- a/tests/test-convert-hg-startrev.t	Sun Mar 13 12:44:35 2011 +0100
+++ b/tests/test-convert-hg-startrev.t	Sun Mar 13 13:05:16 2011 +0100
@@ -1,5 +1,5 @@
 
-  $ cat > $HGRCPATH <<EOF
+  $ cat >> $HGRCPATH <<EOF
   > [extensions]
   > graphlog =
   > convert =
--- a/tests/test-convert-svn-branches.t	Sun Mar 13 12:44:35 2011 +0100
+++ b/tests/test-convert-svn-branches.t	Sun Mar 13 13:05:16 2011 +0100
@@ -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-encoding.t	Sun Mar 13 12:44:35 2011 +0100
+++ b/tests/test-convert-svn-encoding.t	Sun Mar 13 13:05:16 2011 +0100
@@ -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	Sun Mar 13 12:44:35 2011 +0100
+++ b/tests/test-convert-svn-move.t	Sun Mar 13 13:05:16 2011 +0100
@@ -5,7 +5,7 @@
   > {
   >     tr '\\' /
   > }
-  $ cat > $HGRCPATH <<EOF
+  $ cat >> $HGRCPATH <<EOF
   > [extensions]
   > convert = 
   > graphlog =
--- a/tests/test-convert-svn-sink.t	Sun Mar 13 12:44:35 2011 +0100
+++ b/tests/test-convert-svn-sink.t	Sun Mar 13 13:05:16 2011 +0100
@@ -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	Sun Mar 13 12:44:35 2011 +0100
+++ b/tests/test-convert-svn-source.t	Sun Mar 13 13:05:16 2011 +0100
@@ -5,7 +5,7 @@
   > {
   >     tr '\\' /
   > }
-  $ cat > $HGRCPATH <<EOF
+  $ cat >> $HGRCPATH <<EOF
   > [extensions]
   > convert = 
   > graphlog =
--- a/tests/test-convert-svn-startrev.t	Sun Mar 13 12:44:35 2011 +0100
+++ b/tests/test-convert-svn-startrev.t	Sun Mar 13 13:05:16 2011 +0100
@@ -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	Sun Mar 13 12:44:35 2011 +0100
+++ b/tests/test-convert-svn-tags.t	Sun Mar 13 13:05:16 2011 +0100
@@ -1,7 +1,7 @@
 
   $ "$TESTDIR/hghave" svn svn-bindings || exit 80
 
-  $ cat > $HGRCPATH <<EOF
+  $ cat >> $HGRCPATH <<EOF
   > [extensions]
   > convert = 
   > graphlog =
--- a/tests/test-debugcomplete.t	Sun Mar 13 12:44:35 2011 +0100
+++ b/tests/test-debugcomplete.t	Sun Mar 13 13:05:16 2011 +0100
@@ -199,7 +199,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
--- a/tests/test-eol-add.t	Sun Mar 13 12:44:35 2011 +0100
+++ b/tests/test-eol-add.t	Sun Mar 13 13:05:16 2011 +0100
@@ -1,6 +1,6 @@
 Test adding .hgeol
 
-  $ cat > $HGRCPATH <<EOF
+  $ cat >> $HGRCPATH <<EOF
   > [diff]
   > git = 1
   > EOF
--- a/tests/test-eol-clone.t	Sun Mar 13 12:44:35 2011 +0100
+++ b/tests/test-eol-clone.t	Sun Mar 13 13:05:16 2011 +0100
@@ -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	Sun Mar 13 12:44:35 2011 +0100
+++ b/tests/test-eol-hook.t	Sun Mar 13 13:05:16 2011 +0100
@@ -1,9 +1,5 @@
 Test the EOL hook
 
-  $ cat > $HGRCPATH <<EOF
-  > [diff]
-  > git = True
-  > EOF
   $ hg init main
   $ cat > main/.hg/hgrc <<EOF
   > [extensions]
--- a/tests/test-eol-patch.t	Sun Mar 13 12:44:35 2011 +0100
+++ b/tests/test-eol-patch.t	Sun Mar 13 13:05:16 2011 +0100
@@ -1,6 +1,6 @@
 Test EOL patching
 
-  $ cat > $HGRCPATH <<EOF
+  $ cat >> $HGRCPATH <<EOF
   > [diff]
   > git = 1
   > EOF
--- a/tests/test-eol-tag.t	Sun Mar 13 12:44:35 2011 +0100
+++ b/tests/test-eol-tag.t	Sun Mar 13 13:05:16 2011 +0100
@@ -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	Sun Mar 13 12:44:35 2011 +0100
+++ b/tests/test-eol-update.t	Sun Mar 13 13:05:16 2011 +0100
@@ -1,6 +1,6 @@
 Test EOL update
 
-  $ cat > $HGRCPATH <<EOF
+  $ cat >> $HGRCPATH <<EOF
   > [diff]
   > git = 1
   > EOF
--- a/tests/test-eol.t	Sun Mar 13 12:44:35 2011 +0100
+++ b/tests/test-eol.t	Sun Mar 13 13:05:16 2011 +0100
@@ -1,6 +1,6 @@
 Test EOL extension
 
-  $ cat > $HGRCPATH <<EOF
+  $ cat >> $HGRCPATH <<EOF
   > [diff]
   > git = True
   > EOF
@@ -398,6 +398,7 @@
   $ touch .hgeol
   $ hg status --traceback
   ? .hgeol
+  $ chmod -R u+w .hg
   $ cd ..
 
 Test cleverencode: and cleverdecode: aliases for win32text extension
--- a/tests/test-help.t	Sun Mar 13 12:44:35 2011 +0100
+++ b/tests/test-help.t	Sun Mar 13 13:05:16 2011 +0100
@@ -765,6 +765,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-hgrc.t	Sun Mar 13 12:44:35 2011 +0100
+++ b/tests/test-hgrc.t	Sun Mar 13 13:05:16 2011 +0100
@@ -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
--- a/tests/test-http-proxy.t	Sun Mar 13 12:44:35 2011 +0100
+++ b/tests/test-http-proxy.t	Sun Mar 13 13:05:16 2011 +0100
@@ -98,27 +98,22 @@
   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=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=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=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=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=listkeys&namespace=bookmarks HTTP/1.1" - - (glob)
 
--- a/tests/test-init.t	Sun Mar 13 12:44:35 2011 +0100
+++ b/tests/test-init.t	Sun Mar 13 13:05:16 2011 +0100
@@ -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-keyword.t	Sun Mar 13 12:44:35 2011 +0100
+++ b/tests/test-keyword.t	Sun Mar 13 13:05:16 2011 +0100
@@ -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
   
--- a/tests/test-mq-strip.t	Sun Mar 13 12:44:35 2011 +0100
+++ b/tests/test-mq-strip.t	Sun Mar 13 13:05:16 2011 +0100
@@ -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-rename-merge1.t	Sun Mar 13 12:44:35 2011 +0100
+++ b/tests/test-rename-merge1.t	Sun Mar 13 13:05:16 2011 +0100
@@ -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-revset-outgoing.t	Sun Mar 13 12:44:35 2011 +0100
+++ b/tests/test-revset-outgoing.t	Sun Mar 13 13:05:16 2011 +0100
@@ -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-schemes.t	Sun Mar 13 12:44:35 2011 +0100
+++ b/tests/test-schemes.t	Sun Mar 13 13:05:16 2011 +0100
@@ -25,7 +25,7 @@
 
   $ hg incoming --debug parts://localhost
   using http://localhost:$HGPORT/
-  sending between command
+  sending capabilities command
   comparing with parts://localhost
   sending heads command
   searching for changes
--- a/tests/test-serve	Sun Mar 13 12:44:35 2011 +0100
+++ /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	Sun Mar 13 12:44:35 2011 +0100
+++ /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	Sun Mar 13 13:05:16 2011 +0100
@@ -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-ssh.t	Sun Mar 13 12:44:35 2011 +0100
+++ b/tests/test-ssh.t	Sun Mar 13 13:05:16 2011 +0100
@@ -263,6 +263,22 @@
   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
 
   $ hg push ssh://user:erroneouspwd@dummy/remote
@@ -289,3 +305,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-subrepo-git.t	Sun Mar 13 12:44:35 2011 +0100
+++ b/tests/test-subrepo-git.t	Sun Mar 13 13:05:16 2011 +0100
@@ -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	Sun Mar 13 12:44:35 2011 +0100
+++ b/tests/test-subrepo-paths.t	Sun Mar 13 13:05:16 2011 +0100
@@ -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-transplant.t	Sun Mar 13 12:44:35 2011 +0100
+++ b/tests/test-transplant.t	Sun Mar 13 13:05:16 2011 +0100
@@ -330,6 +330,27 @@
   [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 with a win32ext like setup (differing EOLs)