changeset 6812:fdf5980bd010

Merge with main test-remove is still failing for status() does not return removed files in a sorted list. We can live with this for now, a fix is coming soon.
author Patrick Mezard <pmezard@gmail.com>
date Sun, 20 Jul 2008 20:00:02 +0200
parents 2134d6c09432 (diff) 70ecce68df7c (current diff)
children d8159dd15db3
files hgext/hgk.py
diffstat 38 files changed, 512 insertions(+), 189 deletions(-) [+]
line wrap: on
line diff
--- a/contrib/win32/mercurial.iss	Sun Jul 20 19:25:08 2008 +0200
+++ b/contrib/win32/mercurial.iss	Sun Jul 20 20:00:02 2008 +0200
@@ -2,7 +2,7 @@
 ; SEE THE DOCUMENTATION FOR DETAILS ON CREATING INNO SETUP SCRIPT FILES!
 
 [Setup]
-AppCopyright=Copyright 2005-2007 Matt Mackall and others
+AppCopyright=Copyright 2005-2008 Matt Mackall and others
 AppName=Mercurial
 AppVerName=Mercurial snapshot
 InfoAfterFile=contrib/win32/postinstall.txt
@@ -18,7 +18,7 @@
 DefaultDirName={pf}\Mercurial
 SourceDir=..\..
 VersionInfoDescription=Mercurial distributed SCM
-VersionInfoCopyright=Copyright 2005-2007 Matt Mackall and others
+VersionInfoCopyright=Copyright 2005-2008 Matt Mackall and others
 VersionInfoCompany=Matt Mackall and others
 InternalCompressLevel=max
 SolidCompression=true
--- a/contrib/zsh_completion	Sun Jul 20 19:25:08 2008 +0200
+++ b/contrib/zsh_completion	Sun Jul 20 20:00:02 2008 +0200
@@ -205,8 +205,7 @@
 
 _hg_config() {
     typeset -a items
-    local line
-    items=(${${(%f)"$(_hg_cmd showconfig)"}%%\=*})
+    items=(${${(%f)"$(_call_program hg hg showconfig)"}%%\=*})
     (( $#items )) && _describe -t config 'config item' items
 }
 
@@ -291,10 +290,14 @@
     '--cwd[change working directory]:new working directory:_files -/'
     '(--noninteractive -y)'{-y,--noninteractive}'[do not prompt, assume yes for any required answers]'
     '(--verbose -v)'{-v,--verbose}'[enable additional output]'
+    '*--config[set/override config option]:defined config items:_hg_config'
     '(--quiet -q)'{-q,--quiet}'[suppress output]'
     '(--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)]'
+    '--lsprof[print improved command execution profile]'
     '--traceback[print traceback on exception]'
     '--time[time how long the command takes]'
     '--profile[profile]'
--- a/hgext/convert/__init__.py	Sun Jul 20 19:25:08 2008 +0200
+++ b/hgext/convert/__init__.py	Sun Jul 20 20:00:02 2008 +0200
@@ -86,6 +86,50 @@
     --config convert.hg.saverev=True          (boolean)
         allow target to preserve source revision ID
 
+    CVS Source
+    ----------
+
+    CVS source will use a sandbox (i.e. a checked-out copy) from CVS
+    to indicate the starting point of what will be converted. Direct
+    access to the repository files is not needed, unless of course
+    the repository is :local:. The conversion uses the top level
+    directory in the sandbox to find the CVS repository, and then uses
+    CVS rlog commands to find files to convert. This means that unless
+    a filemap is given, all files under the starting directory will be
+    converted, and that any directory reorganisation in the CVS
+    sandbox is ignored.
+
+    Because CVS does not have changesets, it is necessary to collect
+    individual commits to CVS and merge them into changesets. CVS source
+    can use the external 'cvsps' program (this is a legacy option and may
+    be removed in future) or use its internal changeset merging code.
+    External cvsps is default, and options may be passed to it by setting
+        --config convert.cvsps='cvsps -A -u --cvs-direct -q'
+    The options shown are the defaults.
+
+    Internal cvsps is selected by setting
+        --config convert.cvsps=builtin
+    and has a few more configurable options:
+        --config convert.cvsps.fuzz=60   (integer)
+            Specify the maximum time (in seconds) that is allowed between
+            commits with identical user and log message in a single
+            changeset. When very large files were checked in as part
+            of a changeset then the default may not be long enough.
+        --config convert.cvsps.mergeto='{{mergetobranch ([-\w]+)}}'
+            Specify a regular expression to which commit log messages are
+            matched. If a match occurs, then the conversion process will
+            insert a dummy revision merging the branch on which this log
+            message occurs to the branch indicated in the regex.
+        --config convert.cvsps.mergefrom='{{mergefrombranch ([-\w]+)}}'
+            Specify a regular expression to which commit log messages are
+            matched. If a match occurs, then the conversion process will
+            add the most recent revision on the branch indicated in the
+            regex as the second parent of the changeset.
+    
+    The hgext/convert/cvsps wrapper script allows the builtin changeset
+    merging code to be run without doing a conversion. Its parameters and
+    output are similar to that of cvsps 2.1.
+
     Subversion Source
     -----------------
 
--- a/hgext/mq.py	Sun Jul 20 19:25:08 2008 +0200
+++ b/hgext/mq.py	Sun Jul 20 20:00:02 2008 +0200
@@ -1129,7 +1129,7 @@
                         f = repo.file(dst)
                         src = f.renamed(man[dst])
                         if src:
-                            copies[src[0]] = copies.get(dst, [])
+                            copies.setdefault(src[0], []).extend(copies.get(dst, []))
                             if dst in a:
                                 copies[src[0]].append(dst)
                         # we can't copy a file created by the patch itself
--- a/mercurial/commands.py	Sun Jul 20 19:25:08 2008 +0200
+++ b/mercurial/commands.py	Sun Jul 20 20:00:02 2008 +0200
@@ -2026,9 +2026,9 @@
     Valid URLs are of the form:
 
       local/filesystem/path (or file://local/filesystem/path)
-      http://[user@]host[:port]/[path]
-      https://[user@]host[:port]/[path]
-      ssh://[user@]host[:port]/[path]
+      http://[user[:pass]@]host[:port]/[path]
+      https://[user[:pass]@]host[:port]/[path]
+      ssh://[user[:pass]@]host[:port]/[path]
       static-http://host[:port]/[path]
 
     Paths in the local filesystem can either point to Mercurial
@@ -2088,9 +2088,9 @@
     Valid URLs are of the form:
 
       local/filesystem/path (or file://local/filesystem/path)
-      ssh://[user@]host[:port]/[path]
-      http://[user@]host[:port]/[path]
-      https://[user@]host[:port]/[path]
+      ssh://[user[:pass]@]host[:port]/[path]
+      http://[user[:pass]@]host[:port]/[path]
+      https://[user[:pass]@]host[:port]/[path]
 
     An optional identifier after # indicates a particular branch, tag,
     or changeset to push. If -r is used, the named changeset and all its
--- a/mercurial/hgweb/hgweb_mod.py	Sun Jul 20 19:25:08 2008 +0200
+++ b/mercurial/hgweb/hgweb_mod.py	Sun Jul 20 20:00:02 2008 +0200
@@ -10,25 +10,17 @@
 from mercurial.node import hex, nullid
 from mercurial.repo import RepoError
 from mercurial import mdiff, ui, hg, util, patch, hook
-from mercurial import revlog, templater, templatefilters, changegroup
+from mercurial import revlog, templater, templatefilters
 from common import get_mtime, style_map, paritygen, countgen, ErrorResponse
 from common import HTTP_OK, HTTP_BAD_REQUEST, HTTP_NOT_FOUND, HTTP_SERVER_ERROR
 from request import wsgirequest
 import webcommands, protocol, webutil
 
-shortcuts = {
-    'cl': [('cmd', ['changelog']), ('rev', None)],
-    'sl': [('cmd', ['shortlog']), ('rev', None)],
-    'cs': [('cmd', ['changeset']), ('node', None)],
-    'f': [('cmd', ['file']), ('filenode', None)],
-    'fl': [('cmd', ['filelog']), ('filenode', None)],
-    'fd': [('cmd', ['filediff']), ('node', None)],
-    'fa': [('cmd', ['annotate']), ('filenode', None)],
-    'mf': [('cmd', ['manifest']), ('manifest', None)],
-    'ca': [('cmd', ['archive']), ('node', None)],
-    'tags': [('cmd', ['tags'])],
-    'tip': [('cmd', ['changeset']), ('node', ['tip'])],
-    'static': [('cmd', ['static']), ('file', None)]
+perms = {
+    'changegroup': 'pull',
+    'changegroupsubset': 'pull',
+    'unbundle': 'push',
+    'stream_out': 'pull',
 }
 
 class hgweb(object):
@@ -44,7 +36,6 @@
         self.reponame = name
         self.archives = 'zip', 'gz', 'bz2'
         self.stripecount = 1
-        self._capabilities = None
         # a repo owner may set web.templates in .hg/hgrc to get any file
         # readable by the user running the CGI script
         self.templatepath = self.config("web", "templates",
@@ -76,18 +67,6 @@
             self.maxfiles = int(self.config("web", "maxfiles", 10))
             self.allowpull = self.configbool("web", "allowpull", True)
             self.encoding = self.config("web", "encoding", util._encoding)
-            self._capabilities = None
-
-    def capabilities(self):
-        if self._capabilities is not None:
-            return self._capabilities
-        caps = ['lookup', 'changegroupsubset']
-        if self.configbool('server', 'uncompressed'):
-            caps.append('stream=%d' % self.repo.changelog.version)
-        if changegroup.bundlepriority:
-            caps.append('unbundle=%s' % ','.join(changegroup.bundlepriority))
-        self._capabilities = caps
-        return caps
 
     def run(self):
         if not os.environ.get('GATEWAY_INTERFACE', '').startswith("CGI/1."):
@@ -97,22 +76,22 @@
 
     def __call__(self, env, respond):
         req = wsgirequest(env, respond)
-        self.run_wsgi(req)
-        return req
+        return self.run_wsgi(req)
 
     def run_wsgi(self, req):
 
         self.refresh()
 
-        # expand form shortcuts
+        # process this if it's a protocol request
+        # protocol bits don't need to create any URLs
+        # and the clients always use the old URL structure
 
-        for k in shortcuts.iterkeys():
-            if k in req.form:
-                for name, value in shortcuts[k]:
-                    if value is None:
-                        value = req.form[k]
-                    req.form[name] = value
-                del req.form[k]
+        cmd = req.form.get('cmd', [''])[0]
+        if cmd and cmd in protocol.__all__:
+            if cmd in perms and not self.check_perm(req, perms[cmd]):
+                return []
+            method = getattr(protocol, cmd)
+            return method(self.repo, req)
 
         # work with CGI variables to create coherent structure
         # use SCRIPT_NAME, PATH_INFO and QUERY_STRING as well as our REPO_NAME
@@ -145,8 +124,10 @@
                 cmd = cmd[style+1:]
 
             # avoid accepting e.g. style parameter as command
-            if hasattr(webcommands, cmd) or hasattr(protocol, cmd):
+            if hasattr(webcommands, cmd):
                 req.form['cmd'] = [cmd]
+            else:
+                cmd = ''
 
             if args and args[0]:
                 node = args.pop(0)
@@ -164,14 +145,6 @@
                         req.form['node'] = [fn[:-len(ext)]]
                         req.form['type'] = [type_]
 
-        # process this if it's a protocol request
-
-        cmd = req.form.get('cmd', [''])[0]
-        if cmd in protocol.__all__:
-            method = getattr(protocol, cmd)
-            method(self, req)
-            return
-
         # process the web interface request
 
         try:
@@ -196,6 +169,7 @@
 
             req.write(content)
             del tmpl
+            return []
 
         except revlog.LookupError, err:
             req.respond(HTTP_NOT_FOUND, ctype)
@@ -203,12 +177,15 @@
             if 'manifest' not in msg:
                 msg = 'revision not found: %s' % err.name
             req.write(tmpl('error', error=msg))
+            return []
         except (RepoError, revlog.RevlogError), inst:
             req.respond(HTTP_SERVER_ERROR, ctype)
             req.write(tmpl('error', error=str(inst)))
+            return []
         except ErrorResponse, inst:
             req.respond(inst.code, ctype)
             req.write(tmpl('error', error=inst.message))
+            return []
 
     def templater(self, req):
 
@@ -364,16 +341,39 @@
         'zip': ('application/zip', 'zip', '.zip', None),
         }
 
-    def check_perm(self, req, op, default):
-        '''check permission for operation based on user auth.
-        return true if op allowed, else false.
-        default is policy to use if no config given.'''
+    def check_perm(self, req, op):
+        '''Check permission for operation based on request data (including
+        authentication info. Return true if op allowed, else false.'''
+
+        def error(status, message):
+            req.respond(status, protocol.HGTYPE)
+            req.write('0\n%s\n' % message)
+
+        if op == 'pull':
+            return self.allowpull
+
+        # enforce that you can only push using POST requests
+        if req.env['REQUEST_METHOD'] != 'POST':
+            error('405 Method Not Allowed', 'push requires POST request')
+            return False
+
+        # require ssl by default for pushing, auth info cannot be sniffed
+        # and replayed
+        scheme = req.env.get('wsgi.url_scheme')
+        if self.configbool('web', 'push_ssl', True) and scheme != 'https':
+            error(HTTP_OK, 'ssl required')
+            return False
 
         user = req.env.get('REMOTE_USER')
 
-        deny = self.configlist('web', 'deny_' + op)
+        deny = self.configlist('web', 'deny_push')
         if deny and (not user or deny == ['*'] or user in deny):
+            error('401 Unauthorized', 'push not authorized')
             return False
 
-        allow = self.configlist('web', 'allow_' + op)
-        return (allow and (allow == ['*'] or user in allow)) or default
+        allow = self.configlist('web', 'allow_push')
+        result = allow and (allow == ['*'] or user in allow)
+        if not result:
+            error('401 Unauthorized', 'push not authorized')
+
+        return result
--- a/mercurial/hgweb/hgwebdir_mod.py	Sun Jul 20 19:25:08 2008 +0200
+++ b/mercurial/hgweb/hgwebdir_mod.py	Sun Jul 20 20:00:02 2008 +0200
@@ -70,8 +70,7 @@
 
     def __call__(self, env, respond):
         req = wsgirequest(env, respond)
-        self.run_wsgi(req)
-        return req
+        return self.run_wsgi(req)
 
     def run_wsgi(self, req):
 
@@ -91,13 +90,13 @@
                     else:
                         fname = req.form['static'][0]
                     req.write(staticfile(static, fname, req))
-                    return
+                    return []
 
                 # top-level index
                 elif not virtual:
                     req.respond(HTTP_OK, ctype)
                     req.write(self.makeindex(req, tmpl))
-                    return
+                    return []
 
                 # nested indexes and hgwebs
 
@@ -108,8 +107,7 @@
                         req.env['REPO_NAME'] = virtual
                         try:
                             repo = hg.repository(self.parentui, real)
-                            hgweb(repo).run_wsgi(req)
-                            return
+                            return hgweb(repo).run_wsgi(req)
                         except IOError, inst:
                             msg = inst.strerror
                             raise ErrorResponse(HTTP_SERVER_ERROR, msg)
@@ -121,7 +119,7 @@
                     if [r for r in repos if r.startswith(subdir)]:
                         req.respond(HTTP_OK, ctype)
                         req.write(self.makeindex(req, tmpl, subdir))
-                        return
+                        return []
 
                     up = virtual.rfind('/')
                     if up < 0:
@@ -131,10 +129,12 @@
                 # prefixes not found
                 req.respond(HTTP_NOT_FOUND, ctype)
                 req.write(tmpl("notfound", repo=virtual))
+                return []
 
             except ErrorResponse, err:
                 req.respond(err.code, ctype)
                 req.write(tmpl('error', error=err.message or ''))
+                return []
         finally:
             tmpl = None
 
--- a/mercurial/hgweb/protocol.py	Sun Jul 20 19:25:08 2008 +0200
+++ b/mercurial/hgweb/protocol.py	Sun Jul 20 20:00:02 2008 +0200
@@ -21,69 +21,65 @@
 
 HGTYPE = 'application/mercurial-0.1'
 
-def lookup(web, req):
+def lookup(repo, req):
     try:
-        r = hex(web.repo.lookup(req.form['key'][0]))
+        r = hex(repo.lookup(req.form['key'][0]))
         success = 1
     except Exception,inst:
         r = str(inst)
         success = 0
     resp = "%s %s\n" % (success, r)
     req.respond(HTTP_OK, HGTYPE, length=len(resp))
-    req.write(resp)
+    yield resp
 
-def heads(web, req):
-    resp = " ".join(map(hex, web.repo.heads())) + "\n"
+def heads(repo, req):
+    resp = " ".join(map(hex, repo.heads())) + "\n"
     req.respond(HTTP_OK, HGTYPE, length=len(resp))
-    req.write(resp)
+    yield resp
 
-def branches(web, req):
+def branches(repo, req):
     nodes = []
     if 'nodes' in req.form:
         nodes = map(bin, req.form['nodes'][0].split(" "))
     resp = cStringIO.StringIO()
-    for b in web.repo.branches(nodes):
+    for b in repo.branches(nodes):
         resp.write(" ".join(map(hex, b)) + "\n")
     resp = resp.getvalue()
     req.respond(HTTP_OK, HGTYPE, length=len(resp))
-    req.write(resp)
+    yield resp
 
-def between(web, req):
+def between(repo, req):
     if 'pairs' in req.form:
         pairs = [map(bin, p.split("-"))
                  for p in req.form['pairs'][0].split(" ")]
     resp = cStringIO.StringIO()
-    for b in web.repo.between(pairs):
+    for b in repo.between(pairs):
         resp.write(" ".join(map(hex, b)) + "\n")
     resp = resp.getvalue()
     req.respond(HTTP_OK, HGTYPE, length=len(resp))
-    req.write(resp)
+    yield resp
 
-def changegroup(web, req):
+def changegroup(repo, req):
     req.respond(HTTP_OK, HGTYPE)
     nodes = []
-    if not web.allowpull:
-        return
 
     if 'roots' in req.form:
         nodes = map(bin, req.form['roots'][0].split(" "))
 
     z = zlib.compressobj()
-    f = web.repo.changegroup(nodes, 'serve')
+    f = repo.changegroup(nodes, 'serve')
     while 1:
         chunk = f.read(4096)
         if not chunk:
             break
-        req.write(z.compress(chunk))
+        yield z.compress(chunk)
 
-    req.write(z.flush())
+    yield z.flush()
 
-def changegroupsubset(web, req):
+def changegroupsubset(repo, req):
     req.respond(HTTP_OK, HGTYPE)
     bases = []
     heads = []
-    if not web.allowpull:
-        return
 
     if 'bases' in req.form:
         bases = [bin(x) for x in req.form['bases'][0].split(' ')]
@@ -91,67 +87,44 @@
         heads = [bin(x) for x in req.form['heads'][0].split(' ')]
 
     z = zlib.compressobj()
-    f = web.repo.changegroupsubset(bases, heads, 'serve')
+    f = repo.changegroupsubset(bases, heads, 'serve')
     while 1:
         chunk = f.read(4096)
         if not chunk:
             break
-        req.write(z.compress(chunk))
+        yield z.compress(chunk)
 
-    req.write(z.flush())
+    yield z.flush()
 
-def capabilities(web, req):
-    resp = ' '.join(web.capabilities())
-    req.respond(HTTP_OK, HGTYPE, length=len(resp))
-    req.write(resp)
+def capabilities(repo, req):
+    caps = ['lookup', 'changegroupsubset']
+    if repo.ui.configbool('server', 'uncompressed', untrusted=True):
+        caps.append('stream=%d' % repo.changelog.version)
+    if changegroupmod.bundlepriority:
+        caps.append('unbundle=%s' % ','.join(changegroupmod.bundlepriority))
+    rsp = ' '.join(caps)
+    req.respond(HTTP_OK, HGTYPE, length=len(rsp))
+    yield rsp
 
-def unbundle(web, req):
+def unbundle(repo, req):
+
+    errorfmt = '0\n%s\n'
+    proto = req.env.get('wsgi.url_scheme') or 'http'
+    their_heads = req.form['heads'][0].split(' ')
 
-    def bail(response, headers={}):
+    def check_heads():
+        heads = map(hex, repo.heads())
+        return their_heads == [hex('force')] or their_heads == heads
+
+    # fail early if possible
+    if not check_heads():
         length = int(req.env.get('CONTENT_LENGTH', 0))
         for s in util.filechunkiter(req, limit=length):
             # drain incoming bundle, else client will not see
             # response when run outside cgi script
             pass
-
-        status = headers.pop('status', HTTP_OK)
-        req.header(headers.items())
-        req.respond(status, HGTYPE)
-        req.write('0\n')
-        req.write(response)
-
-    # enforce that you can only unbundle with POST requests
-    if req.env['REQUEST_METHOD'] != 'POST':
-        headers = {'status': '405 Method Not Allowed'}
-        bail('unbundle requires POST request\n', headers)
-        return
-
-    # require ssl by default, auth info cannot be sniffed and
-    # replayed
-    ssl_req = web.configbool('web', 'push_ssl', True)
-    if ssl_req:
-        if req.env.get('wsgi.url_scheme') != 'https':
-            bail('ssl required\n')
-            return
-        proto = 'https'
-    else:
-        proto = 'http'
-
-    # do not allow push unless explicitly allowed
-    if not web.check_perm(req, 'push', False):
-        bail('push not authorized\n', headers={'status': '401 Unauthorized'})
-        return
-
-    their_heads = req.form['heads'][0].split(' ')
-
-    def check_heads():
-        heads = map(hex, web.repo.heads())
-        return their_heads == [hex('force')] or their_heads == heads
-
-    # fail early if possible
-    if not check_heads():
-        bail('unsynced changes\n')
-        return
+        req.respond(HTTP_OK, HGTYPE)
+        return errorfmt % 'unsynced changes',
 
     req.respond(HTTP_OK, HGTYPE)
 
@@ -166,12 +139,10 @@
             fp.write(s)
 
         try:
-            lock = web.repo.lock()
+            lock = repo.lock()
             try:
                 if not check_heads():
-                    req.write('0\n')
-                    req.write('unsynced changes\n')
-                    return
+                    return errorfmt % 'unsynced changes',
 
                 fp.seek(0)
                 header = fp.read(6)
@@ -190,26 +161,23 @@
                     url = 'remote:%s:%s' % (proto,
                                             req.env.get('REMOTE_HOST', ''))
                     try:
-                        ret = web.repo.addchangegroup(gen, 'serve', url)
+                        ret = repo.addchangegroup(gen, 'serve', url)
                     except util.Abort, inst:
                         sys.stdout.write("abort: %s\n" % inst)
                         ret = 0
                 finally:
                     val = sys.stdout.getvalue()
                     sys.stdout, sys.stderr = oldio
-                req.write('%d\n' % ret)
-                req.write(val)
+                return '%d\n%s' % (ret, val),
             finally:
                 del lock
         except ValueError, inst:
-            req.write('0\n')
-            req.write(str(inst) + '\n')
+            return errorfmt % inst,
         except (OSError, IOError), inst:
-            req.write('0\n')
             filename = getattr(inst, 'filename', '')
             # Don't send our filesystem layout to the client
-            if filename.startswith(web.repo.root):
-                filename = filename[len(web.repo.root)+1:]
+            if filename.startswith(repo.root):
+                filename = filename[len(repo.root)+1:]
             else:
                 filename = ''
             error = getattr(inst, 'strerror', 'Unknown error')
@@ -218,13 +186,12 @@
             else:
                 code = HTTP_SERVER_ERROR
             req.respond(code)
-            req.write('%s: %s\n' % (error, filename))
+            return '0\n%s: %s\n' % (error, filename),
     finally:
         fp.close()
         os.unlink(tempname)
 
-def stream_out(web, req):
-    if not web.allowpull:
-        return
+def stream_out(repo, req):
     req.respond(HTTP_OK, HGTYPE)
-    streamclone.stream_out(web.repo, req, untrusted=True)
+    streamclone.stream_out(repo, req, untrusted=True)
+    return []
--- a/mercurial/hgweb/request.py	Sun Jul 20 19:25:08 2008 +0200
+++ b/mercurial/hgweb/request.py	Sun Jul 20 20:00:02 2008 +0200
@@ -9,6 +9,31 @@
 import socket, cgi, errno
 from common import ErrorResponse, statusmessage
 
+shortcuts = {
+    'cl': [('cmd', ['changelog']), ('rev', None)],
+    'sl': [('cmd', ['shortlog']), ('rev', None)],
+    'cs': [('cmd', ['changeset']), ('node', None)],
+    'f': [('cmd', ['file']), ('filenode', None)],
+    'fl': [('cmd', ['filelog']), ('filenode', None)],
+    'fd': [('cmd', ['filediff']), ('node', None)],
+    'fa': [('cmd', ['annotate']), ('filenode', None)],
+    'mf': [('cmd', ['manifest']), ('manifest', None)],
+    'ca': [('cmd', ['archive']), ('node', None)],
+    'tags': [('cmd', ['tags'])],
+    'tip': [('cmd', ['changeset']), ('node', ['tip'])],
+    'static': [('cmd', ['static']), ('file', None)]
+}
+
+def expand(form):
+    for k in shortcuts.iterkeys():
+        if k in form:
+            for name, value in shortcuts[k]:
+                if value is None:
+                    value = form[k]
+                form[name] = value
+            del form[k]
+    return form
+
 class wsgirequest(object):
     def __init__(self, wsgienv, start_response):
         version = wsgienv['wsgi.version']
@@ -21,7 +46,7 @@
         self.multiprocess = wsgienv['wsgi.multiprocess']
         self.run_once = wsgienv['wsgi.run_once']
         self.env = wsgienv
-        self.form = cgi.parse(self.inp, self.env, keep_blank_values=1)
+        self.form = expand(cgi.parse(self.inp, self.env, keep_blank_values=1))
         self._start_response = start_response
         self.server_write = None
         self.headers = []
--- a/mercurial/hgweb/server.py	Sun Jul 20 19:25:08 2008 +0200
+++ b/mercurial/hgweb/server.py	Sun Jul 20 20:00:02 2008 +0200
@@ -122,7 +122,8 @@
         self.saved_headers = []
         self.sent_headers = False
         self.length = None
-        self.server.application(env, self._start_response)
+        for chunk in self.server.application(env, self._start_response):
+            self._write(chunk)
 
     def send_headers(self):
         if not self.saved_status:
--- a/mercurial/httprepo.py	Sun Jul 20 19:25:08 2008 +0200
+++ b/mercurial/httprepo.py	Sun Jul 20 20:00:02 2008 +0200
@@ -268,6 +268,7 @@
 
         # 1.0 here is the _protocol_ version
         opener.addheaders = [('User-agent', 'mercurial/proto-1.0')]
+        opener.addheaders.append(('Accept', 'application/mercurial-0.1'))
         urllib2.install_opener(opener)
 
     def url(self):
--- a/mercurial/merge.py	Sun Jul 20 19:25:08 2008 +0200
+++ b/mercurial/merge.py	Sun Jul 20 20:00:02 2008 +0200
@@ -258,6 +258,17 @@
 
     return action
 
+def actioncmp(a1, a2):
+    m1 = a1[1]
+    m2 = a2[1]
+    if m1 == m2:
+        return cmp(a1, a2)
+    if m1 == 'r':
+        return -1
+    if m2 == 'r':
+        return 1
+    return cmp(a1, a2)
+
 def applyupdates(repo, action, wctx, mctx):
     "apply the merge action list to the working directory"
 
@@ -265,7 +276,7 @@
     ms = mergestate(repo)
     ms.reset(wctx.parents()[0].node())
     moves = []
-    action.sort()
+    action.sort(actioncmp)
 
     # prescan for merges
     for a in action:
--- a/mercurial/templater.py	Sun Jul 20 19:25:08 2008 +0200
+++ b/mercurial/templater.py	Sun Jul 20 20:00:02 2008 +0200
@@ -81,18 +81,18 @@
     def __contains__(self, key):
         return key in self.cache or key in self.map
 
-    def __call__(self, t, **map):
-        '''perform expansion.
-        t is name of map element to expand.
-        map is added elements to use during expansion.'''
+    def _template(self, t):
+        '''Get the template for the given template name. Use a local cache.'''
         if not t in self.cache:
             try:
                 self.cache[t] = file(self.map[t]).read()
             except IOError, inst:
                 raise IOError(inst.args[0], _('template file %s: %s') %
                               (self.map[t], inst.args[1]))
-        tmpl = self.cache[t]
+        return self.cache[t]
 
+    def _process(self, tmpl, map):
+        '''Render a template. Returns a generator.'''
         while tmpl:
             m = self.template_re.search(tmpl)
             if not m:
@@ -119,13 +119,34 @@
                 lm = map.copy()
                 for i in v:
                     lm.update(i)
-                    yield self(format, **lm)
+                    t = self._template(format)
+                    yield self._process(t, lm)
             else:
                 if fl:
                     for f in fl.split("|")[1:]:
                         v = self.filters[f](v)
                 yield v
 
+    def __call__(self, t, **map):
+        '''Perform expansion. t is name of map element to expand. map contains
+        added elements for use during expansion. Is a generator.'''
+        tmpl = self._template(t)
+        iters = [self._process(tmpl, map)]
+        while iters:
+            try:
+                item = iters[0].next()
+            except StopIteration:
+                iters.pop(0)
+                continue
+            if isinstance(item, str):
+                yield item
+            elif item is None:
+                yield ''
+            elif hasattr(item, '__iter__'):
+                iters.insert(0, iter(item))
+            else:
+                yield str(item)
+
 def templatepath(name=None):
     '''return location of template file or directory (if no name).
     returns None if not found.'''
--- a/setup.py	Sun Jul 20 19:25:08 2008 +0200
+++ b/setup.py	Sun Jul 20 20:00:02 2008 +0200
@@ -129,7 +129,7 @@
                    [os.path.join(root, file_) for file_ in files])
                   for root, dirs, files in os.walk('templates')],
       cmdclass=cmdclass,
-      options=dict(py2exe=dict(packages=['hgext']),
+      options=dict(py2exe=dict(packages=['hgext', 'email']),
                    bdist_mpkg=dict(zipdist=True,
                                    license='COPYING',
                                    readme='contrib/macosx/Readme.html',
--- a/templates/coal/changeset.tmpl	Sun Jul 20 19:25:08 2008 +0200
+++ b/templates/coal/changeset.tmpl	Sun Jul 20 20:00:02 2008 +0200
@@ -9,8 +9,8 @@
 <img src="{staticurl}hglogo.png" width=75 height=90 border=0 alt="mercurial"></a>
 </div>
 <ul>
- <li><a href="{url}shortlog{sessionvars%urlparameter}">log</a></li>
- <li><a href="{url}graph{sessionvars%urlparameter}">graph</a></li>
+ <li><a href="{url}shortlog/{node|short}{sessionvars%urlparameter}">log</a></li>
+ <li><a href="{url}graph/{node|short}{sessionvars%urlparameter}">graph</a></li>
  <li><a href="{url}tags{sessionvars%urlparameter}">tags</a></li>
 </ul>
 <ul>
--- a/templates/coal/fileannotate.tmpl	Sun Jul 20 19:25:08 2008 +0200
+++ b/templates/coal/fileannotate.tmpl	Sun Jul 20 20:00:02 2008 +0200
@@ -10,8 +10,8 @@
 <img src="{staticurl}hglogo.png" width=75 height=90 border=0 alt="mercurial"></a>
 </div>
 <ul>
-<li><a href="{url}shortlog/{sessionvars%urlparameter}">log</a></li>
-<li><a href="{url}graph/{sessionvars%urlparameter}">graph</a></li>
+<li><a href="{url}shortlog/{node|short}{sessionvars%urlparameter}">log</a></li>
+<li><a href="{url}graph/{node|short}{sessionvars%urlparameter}">graph</a></li>
 <li><a href="{url}tags{sessionvars%urlparameter}">tags</a></li>
 </ul>
 
--- a/templates/coal/filediff.tmpl	Sun Jul 20 19:25:08 2008 +0200
+++ b/templates/coal/filediff.tmpl	Sun Jul 20 20:00:02 2008 +0200
@@ -10,8 +10,8 @@
 <img src="{staticurl}hglogo.png" width=75 height=90 border=0 alt="mercurial"></a>
 </div>
 <ul>
-<li><a href="{url}shortlog/{sessionvars%urlparameter}">log</a></li>
-<li><a href="{url}graph/{sessionvars%urlparameter}">graph</a></li>
+<li><a href="{url}shortlog/{node|short}{sessionvars%urlparameter}">log</a></li>
+<li><a href="{url}graph/{node|short}{sessionvars%urlparameter}">graph</a></li>
 <li><a href="{url}tags{sessionvars%urlparameter}">tags</a></li>
 </ul>
 <ul>
--- a/templates/coal/filelog.tmpl	Sun Jul 20 19:25:08 2008 +0200
+++ b/templates/coal/filelog.tmpl	Sun Jul 20 20:00:02 2008 +0200
@@ -15,8 +15,8 @@
 <img src="{staticurl}hglogo.png" width=75 height=90 border=0 alt="mercurial"></a>
 </div>
 <ul>
-<li><a href="{url}shortlog/{sessionvars%urlparameter}">log</a></li>
-<li><a href="{url}graph/{sessionvars%urlparameter}">graph</a></li>
+<li><a href="{url}shortlog/{node|short}{sessionvars%urlparameter}">log</a></li>
+<li><a href="{url}graph/{node|short}{sessionvars%urlparameter}">graph</a></li>
 <li><a href="{url}tags{sessionvars%urlparameter}">tags</a></li>
 </ul>
 <ul>
--- a/templates/coal/filerevision.tmpl	Sun Jul 20 19:25:08 2008 +0200
+++ b/templates/coal/filerevision.tmpl	Sun Jul 20 20:00:02 2008 +0200
@@ -10,8 +10,8 @@
 <img src="{staticurl}hglogo.png" width=75 height=90 border=0 alt="mercurial"></a>
 </div>
 <ul>
-<li><a href="{url}shortlog/{sessionvars%urlparameter}">log</a></li>
-<li><a href="{url}graph/{sessionvars%urlparameter}">graph</a></li>
+<li><a href="{url}shortlog/{node|short}{sessionvars%urlparameter}">log</a></li>
+<li><a href="{url}graph/{node|short}{sessionvars%urlparameter}">graph</a></li>
 <li><a href="{url}tags{sessionvars%urlparameter}">tags</a></li>
 </ul>
 <ul>
--- a/templates/coal/graph.tmpl	Sun Jul 20 19:25:08 2008 +0200
+++ b/templates/coal/graph.tmpl	Sun Jul 20 20:00:02 2008 +0200
@@ -15,7 +15,7 @@
 <img src="{staticurl}hglogo.png" width=75 height=90 border=0 alt="mercurial"></a>
 </div>
 <ul>
-<li><a href="{url}shortlog{sessionvars%urlparameter}">log</a></li>
+<li><a href="{url}shortlog/{node|short}{sessionvars%urlparameter}">log</a></li>
 <li class="active">graph</li>
 <li><a href="{url}tags{sessionvars%urlparameter}">tags</a></li>
 </ul>
--- a/templates/coal/manifest.tmpl	Sun Jul 20 19:25:08 2008 +0200
+++ b/templates/coal/manifest.tmpl	Sun Jul 20 20:00:02 2008 +0200
@@ -10,8 +10,8 @@
 <img src="{staticurl}hglogo.png" width=75 height=90 border=0 alt="mercurial"></a>
 </div>
 <ul>
-<li><a href="{url}shortlog/{sessionvars%urlparameter}">log</a></li>
-<li><a href="{url}graph/{sessionvars%urlparameter}">graph</a></li>
+<li><a href="{url}shortlog/{node|short}{sessionvars%urlparameter}">log</a></li>
+<li><a href="{url}graph/{node|short}{sessionvars%urlparameter}">graph</a></li>
 <li><a href="{url}tags{sessionvars%urlparameter}">tags</a></li>
 </ul>
 <ul>
--- a/templates/coal/shortlog.tmpl	Sun Jul 20 19:25:08 2008 +0200
+++ b/templates/coal/shortlog.tmpl	Sun Jul 20 20:00:02 2008 +0200
@@ -15,7 +15,7 @@
 </div>
 <ul>
 <li class="active">log</li>
-<li><a href="{url}graph{sessionvars%urlparameter}">graph</a></li>
+<li><a href="{url}graph/{node|short}{sessionvars%urlparameter}">graph</a></li>
 <li><a href="{url}tags{sessionvars%urlparameter}">tags</a></li>
 </ul>
 <ul>
--- a/tests/hghave	Sun Jul 20 19:25:08 2008 +0200
+++ b/tests/hghave	Sun Jul 20 20:00:02 2008 +0200
@@ -57,6 +57,24 @@
     finally:
         os.remove(path)
 
+def has_icasefs():
+    # Stolen from mercurial.util
+    fd, path = tempfile.mkstemp(prefix=tempprefix)
+    os.close(fd)
+    try:
+        s1 = os.stat(path)
+        d, b = os.path.split(path)
+        p2 = os.path.join(d, b.upper())
+        if path == p2:
+            p2 = os.path.join(d, b.lower())
+        try:
+            s2 = os.stat(p2)
+            return s2 == s1
+        except:
+            return False
+    finally:
+        os.remove(path)  
+
 def has_fifo():
     return hasattr(os, "mkfifo")
 
@@ -129,6 +147,7 @@
     "fifo": (has_fifo, "named pipes"),
     "git": (has_git, "git command line client"),
     "hotshot": (has_hotshot, "python hotshot module"),
+    "icasefs": (has_icasefs, "case insensitive file system"),
     "lsprof": (has_lsprof, "python lsprof module"),
     "mtn": (has_mtn, "monotone client (> 0.31)"),
     "svn": (has_svn, "subversion client and admin tools"),
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tests/test-casefolding	Sun Jul 20 20:00:02 2008 +0200
@@ -0,0 +1,41 @@
+#!/bin/sh
+
+"$TESTDIR/hghave" icasefs || exit 80
+
+echo '% test file addition with bad case'
+hg init repo1
+cd repo1
+echo a > a
+hg add A
+hg st
+hg ci -m adda
+hg manifest
+cd ..
+
+echo '% test case collision on rename (issue 750)'
+hg init repo2
+cd repo2
+echo a > a
+hg --debug ci -Am adda
+hg mv a A
+# 'a' used to be removed under windows
+test -f a || echo 'a is missing'
+hg st
+cd ..
+
+echo '% test case collision between revisions (issue 912)'
+hg init repo3
+cd repo3
+echo a > a
+hg ci -Am adda
+hg rm a
+hg ci -Am removea
+echo A > A
+hg ci -Am addA
+# Used to fail under case insensitive fs
+hg up -C 0
+hg up -C
+cd ..
+
+
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tests/test-casefolding.out	Sun Jul 20 20:00:02 2008 +0200
@@ -0,0 +1,13 @@
+% test file addition with bad case
+adding a
+A a
+a
+% test case collision on rename (issue 750)
+adding a
+a
+A: not overwriting - file exists
+% test case collision between revisions (issue 912)
+adding a
+adding A
+1 files updated, 0 files merged, 1 files removed, 0 files unresolved
+1 files updated, 0 files merged, 1 files removed, 0 files unresolved
--- a/tests/test-convert-baz	Sun Jul 20 19:25:08 2008 +0200
+++ b/tests/test-convert-baz	Sun Jul 20 20:00:02 2008 +0200
@@ -69,4 +69,5 @@
 
 echo % show graph log
 glog -R baz-repo-hg
+hg up -q -R baz-repo-hg
 hg -R baz-repo-hg manifest --debug
--- a/tests/test-convert-darcs	Sun Jul 20 19:25:08 2008 +0200
+++ b/tests/test-convert-darcs	Sun Jul 20 20:00:02 2008 +0200
@@ -58,4 +58,5 @@
 # "c" file in p1.1 patch are reverted too.
 # Just to say that manifest not listing "c" here is a bug.
 glog -R darcs-repo-hg
+hg up -q -R darcs-repo-hg
 hg -R darcs-repo-hg manifest --debug
--- a/tests/test-convert-tla	Sun Jul 20 19:25:08 2008 +0200
+++ b/tests/test-convert-tla	Sun Jul 20 20:00:02 2008 +0200
@@ -69,4 +69,5 @@
 
 echo % show graph log
 glog -R tla-repo-hg
+hg up -q -R tla-repo-hg
 hg -R tla-repo-hg manifest --debug
--- a/tests/test-convert.out	Sun Jul 20 19:25:08 2008 +0200
+++ b/tests/test-convert.out	Sun Jul 20 20:00:02 2008 +0200
@@ -74,6 +74,50 @@
     --config convert.hg.saverev=True          (boolean)
         allow target to preserve source revision ID
 
+    CVS Source
+    ----------
+
+    CVS source will use a sandbox (i.e. a checked-out copy) from CVS
+    to indicate the starting point of what will be converted. Direct
+    access to the repository files is not needed, unless of course
+    the repository is :local:. The conversion uses the top level
+    directory in the sandbox to find the CVS repository, and then uses
+    CVS rlog commands to find files to convert. This means that unless
+    a filemap is given, all files under the starting directory will be
+    converted, and that any directory reorganisation in the CVS
+    sandbox is ignored.
+
+    Because CVS does not have changesets, it is necessary to collect
+    individual commits to CVS and merge them into changesets. CVS source
+    can use the external 'cvsps' program (this is a legacy option and may
+    be removed in future) or use its internal changeset merging code.
+    External cvsps is default, and options may be passed to it by setting
+        --config convert.cvsps='cvsps -A -u --cvs-direct -q'
+    The options shown are the defaults.
+
+    Internal cvsps is selected by setting
+        --config convert.cvsps=builtin
+    and has a few more configurable options:
+        --config convert.cvsps.fuzz=60   (integer)
+            Specify the maximum time (in seconds) that is allowed between
+            commits with identical user and log message in a single
+            changeset. When very large files were checked in as part
+            of a changeset then the default may not be long enough.
+        --config convert.cvsps.mergeto='{{mergetobranch ([-\w]+)}}'
+            Specify a regular expression to which commit log messages are
+            matched. If a match occurs, then the conversion process will
+            insert a dummy revision merging the branch on which this log
+            message occurs to the branch indicated in the regex.
+        --config convert.cvsps.mergefrom='{{mergefrombranch ([-\w]+)}}'
+            Specify a regular expression to which commit log messages are
+            matched. If a match occurs, then the conversion process will
+            add the most recent revision on the branch indicated in the
+            regex as the second parent of the changeset.
+    
+    The hgext/convert/cvsps wrapper script allows the builtin changeset
+    merging code to be run without doing a conversion. Its parameters and
+    output are similar to that of cvsps 2.1.
+
     Subversion Source
     -----------------
 
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tests/test-hgk	Sun Jul 20 20:00:02 2008 +0200
@@ -0,0 +1,11 @@
+#!/bin/sh
+
+echo "[extensions]" >> $HGRCPATH
+echo "hgk=" >> $HGRCPATH
+
+hg init repo
+cd repo
+echo a > a
+hg ci -Am adda
+hg debug-cat-file commit 0
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tests/test-hgk.out	Sun Jul 20 20:00:02 2008 +0200
@@ -0,0 +1,9 @@
+adding a
+tree a0c8bcbbb45c
+parent 000000000000
+author test 0 0
+committer test 0 0
+revision 0
+branch default
+
+adda
--- a/tests/test-hgweb-commands	Sun Jul 20 19:25:08 2008 +0200
+++ b/tests/test-hgweb-commands	Sun Jul 20 20:00:02 2008 +0200
@@ -34,21 +34,22 @@
 echo % Overviews
 "$TESTDIR/get-with-headers.py" 127.0.0.1:$HGPORT '/tags/?style=atom' | sed "s/http:\/\/[^/]*\//http:\/\/127.0.0.1\//"
 "$TESTDIR/get-with-headers.py" 127.0.0.1:$HGPORT '/summary/?style=gitweb' | sed "s/[0-9]* years ago/long ago/g"
+"$TESTDIR/get-with-headers.py" 127.0.0.1:$HGPORT '/graph/?style=gitweb' | sed "s/[0-9]* years/long/g"
 
 echo % capabilities
-"$TESTDIR/get-with-headers.py" 127.0.0.1:$HGPORT '/capabilities'
+"$TESTDIR/get-with-headers.py" 127.0.0.1:$HGPORT '?cmd=capabilities'
 echo % heads
-"$TESTDIR/get-with-headers.py" 127.0.0.1:$HGPORT '/heads'
+"$TESTDIR/get-with-headers.py" 127.0.0.1:$HGPORT '?cmd=heads'
 echo % lookup
-"$TESTDIR/get-with-headers.py" 127.0.0.1:$HGPORT '/lookup/1'
+"$TESTDIR/get-with-headers.py" 127.0.0.1:$HGPORT '?cmd=lookup&node=1'
 echo % branches
-"$TESTDIR/get-with-headers.py" 127.0.0.1:$HGPORT '/branches'
+"$TESTDIR/get-with-headers.py" 127.0.0.1:$HGPORT '?cmd=branches'
 echo % changegroup
-"$TESTDIR/get-with-headers.py" 127.0.0.1:$HGPORT '/changegroup'
+"$TESTDIR/get-with-headers.py" 127.0.0.1:$HGPORT '?cmd=changegroup'
 echo % stream_out
-"$TESTDIR/get-with-headers.py" 127.0.0.1:$HGPORT '/stream_out'
+"$TESTDIR/get-with-headers.py" 127.0.0.1:$HGPORT '?cmd=stream_out'
 echo % failing unbundle, requires POST request
-"$TESTDIR/get-with-headers.py" 127.0.0.1:$HGPORT '/unbundle'
+"$TESTDIR/get-with-headers.py" 127.0.0.1:$HGPORT '?cmd=unbundle'
 
 echo % Static files
 "$TESTDIR/get-with-headers.py" 127.0.0.1:$HGPORT '/static/style.css'
Binary file tests/test-hgweb-commands.out has changed
--- a/tests/test-mq-qrefresh	Sun Jul 20 19:25:08 2008 +0200
+++ b/tests/test-mq-qrefresh	Sun Jul 20 20:00:02 2008 +0200
@@ -82,3 +82,28 @@
 cat .hg/patches/mqbase | \
 sed -e "s/\(+++ [a-zA-Z0-9_/.-]*\).*/\1/" \
     -e "s/\(--- [a-zA-Z0-9_/.-]*\).*/\1/"
+cd ..
+
+
+
+echo "[diff]" >> $HGRCPATH
+echo "git=True" >> $HGRCPATH
+
+# Test qrefresh --git losing copy metadata
+echo % create test repo
+hg init repo
+cd repo
+echo a > a
+hg ci -Am adda
+hg copy a ab
+echo b >> ab
+hg copy a ac
+echo c >> ac
+echo % capture changes
+hg qnew -f p1
+hg qdiff
+echo % refresh and check changes again
+hg qref
+hg qdiff
+cd ..
+
--- a/tests/test-mq-qrefresh.out	Sun Jul 20 19:25:08 2008 +0200
+++ b/tests/test-mq-qrefresh.out	Sun Jul 20 20:00:02 2008 +0200
@@ -159,3 +159,39 @@
 @@ -1,1 +1,1 @@
 -base
 +patched
+% create test repo
+adding a
+% capture changes
+diff --git a/a b/ab
+copy from a
+copy to ab
+--- a/a
++++ b/ab
+@@ -1,1 +1,2 @@
+ a
++b
+diff --git a/a b/ac
+copy from a
+copy to ac
+--- a/a
++++ b/ac
+@@ -1,1 +1,2 @@
+ a
++c
+% refresh and check changes again
+diff --git a/a b/ab
+copy from a
+copy to ab
+--- a/a
++++ b/ab
+@@ -1,1 +1,2 @@
+ a
++b
+diff --git a/a b/ac
+copy from a
+copy to ac
+--- a/a
++++ b/ac
+@@ -1,1 +1,2 @@
+ a
++c
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tests/test-pull-http	Sun Jul 20 20:00:02 2008 +0200
@@ -0,0 +1,36 @@
+#!/bin/sh
+
+cp "$TESTDIR"/printenv.py .
+
+hg init test
+cd test
+echo a > a
+hg ci -Ama -d '0 0'
+
+cd ..
+hg clone test test2
+cd test2
+echo a >> a
+hg ci -mb -d '0 0'
+
+echo % expect error, cloning not allowed
+echo '[web]' > .hg/hgrc
+echo 'allowpull = false' >> .hg/hgrc
+hg serve -p $HGPORT -d --pid-file=hg.pid -E errors.log
+cat hg.pid >> $DAEMON_PIDS
+hg clone http://localhost:$HGPORT/ test3 | sed -e 's,:[0-9][0-9]*/,/,'
+kill `cat hg.pid`
+echo % serve errors
+cat errors.log
+
+req() {
+	hg serve -p $HGPORT -d --pid-file=hg.pid -E errors.log
+	cat hg.pid >> $DAEMON_PIDS
+	hg --cwd ../test pull http://localhost:$HGPORT/ | sed -e 's,:[0-9][0-9]*/,/,'
+	kill `cat hg.pid`
+	echo % serve errors
+	cat errors.log
+}
+
+echo % expect error, pulling not allowed
+req
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tests/test-pull-http.out	Sun Jul 20 20:00:02 2008 +0200
@@ -0,0 +1,12 @@
+adding a
+updating working directory
+1 files updated, 0 files merged, 0 files removed, 0 files unresolved
+% expect error, cloning not allowed
+abort: error: 
+requesting all changes
+% serve errors
+% expect error, pulling not allowed
+abort: error: 
+pulling from http://localhost/
+searching for changes
+% serve errors
--- a/tests/test-update-reverse.out	Sun Jul 20 19:25:08 2008 +0200
+++ b/tests/test-update-reverse.out	Sun Jul 20 20:00:02 2008 +0200
@@ -46,9 +46,9 @@
  side2: remote deleted -> r
  side1: remote deleted -> r
  main: remote created -> g
-getting main
 removing side1
 removing side2
+getting main
 1 files updated, 0 files merged, 2 files removed, 0 files unresolved
 Should only show a main
 a