changeset 14127:f45c8db21749

merge with nicdumz
author Matt Mackall <mpm@selenic.com>
date Sun, 01 May 2011 06:04:08 -0500
parents a36e8c99d51c (diff) fbbe9239574a (current diff)
children 0386b51dd749
files tests/darcs1.hg tests/legacy-encoding.hg tests/tampered.hg tests/test-bundle-r.t tests/test-issue322.t tests/test-issue433.t tests/test-issue436.t tests/test-keyword.hg tests/test-manifest.hg tests/test-merge-symlinks.hg tests/test-no-symlinks.hg tests/test-rebase-keep-branch.t
diffstat 66 files changed, 1160 insertions(+), 892 deletions(-) [+]
line wrap: on
line diff
--- a/contrib/wix/guids.wxi	Sun May 01 10:51:10 2011 +0200
+++ b/contrib/wix/guids.wxi	Sun May 01 06:04:08 2011 -0500
@@ -33,7 +33,7 @@
   <?define templates.raw.guid = {04DE03A2-FBFD-4c5f-8DEA-5436DDF4689D} ?>
   <?define templates.rss.guid = {A7D608DE-0CF6-44f4-AF1E-EE30CC237FDA} ?>
   <?define templates.spartan.guid = {80222625-FA8F-44b1-86CE-1781EF375D09} ?>
-  <?define templates.static.guid = {68C9F843-DE7E-480f-9DA2-D220B19D02C3} ?>
+  <?define templates.static.guid = {B27D7311-050A-4A96-9971-B674A0EA21D0} ?>
 
   <!-- mercurial.wxs -->
   <?define ProductUpgradeCode = {A1CC6134-E945-4399-BE36-EB0017FDF7CF} ?>
--- a/contrib/wix/templates.wxs	Sun May 01 10:51:10 2011 +0200
+++ b/contrib/wix/templates.wxs	Sun May 01 06:04:08 2011 -0500
@@ -195,7 +195,7 @@
             <File Id="static.coal.file.png"      Name="coal-file.png" />
             <File Id="static.coal.folder.png"    Name="coal-folder.png" />
             <File Id="static.excanvas.js"        Name="excanvas.js" />
-            <File Id="static.graph.js"           Name="graph.js" />
+            <File Id="static.mercurial.js"       Name="mercurial.js" />
             <File Id="static.hgicon.png"         Name="hgicon.png" />
             <File Id="static.hglogo.png"         Name="hglogo.png" />
             <File Id="static.style.coal.css"     Name="style-coal.css" />
--- a/doc/hgrc.5.txt	Sun May 01 10:51:10 2011 +0200
+++ b/doc/hgrc.5.txt	Sun May 01 06:04:08 2011 -0500
@@ -634,6 +634,10 @@
   Run before starting a local commit. Exit status 0 allows the
   commit to proceed. Non-zero status will cause the commit to fail.
   Parent changeset IDs are in ``$HG_PARENT1`` and ``$HG_PARENT2``.
+``prelistkeys``
+  Run before listing pushkeys (like bookmarks) in the
+  repository. Non-zero status will cause failure. The key namespace is
+  in ``$HG_NAMESPACE``.
 ``preoutgoing``
   Run before collecting changes to send from the local repository to
   another. Non-zero status will cause failure. This lets you prevent
@@ -643,6 +647,12 @@
   ``$HG_SOURCE``. If "serve", operation is happening on behalf of remote
   SSH or HTTP repository. If "push", "pull" or "bundle", operation
   is happening on behalf of repository on same system.
+``prepushkey``
+  Run before a pushkey (like a bookmark) is added to the
+  repository. Non-zero status will cause the key to be rejected. The
+  key namespace is in ``$HG_NAMESPACE``, the key is in ``$HG_KEY``,
+  the old value (if any) is in ``$HG_OLD``, and the new value is in
+  ``$HG_NEW``.
 ``pretag``
   Run before creating a tag. Exit status 0 allows the tag to be
   created. Non-zero status will cause the tag to fail. ID of
@@ -669,6 +679,15 @@
   the update to proceed. Non-zero status will prevent the update.
   Changeset ID of first new parent is in ``$HG_PARENT1``. If merge, ID
   of second new parent is in ``$HG_PARENT2``.
+``listkeys``
+  Run after listing pushkeys (like bookmarks) in the repository. The
+  key namespace is in ``$HG_NAMESPACE``. ``$HG_VALUES`` is a
+  dictionary containing the keys and values.
+``pushkey``
+  Run after a pushkey (like a bookmark) is added to the
+  repository. The key namespace is in ``$HG_NAMESPACE``, the key is in
+  ``$HG_KEY``, the old value (if any) is in ``$HG_OLD``, and the new
+  value is in ``$HG_NEW``.
 ``tag``
   Run after a tag is created. ID of tagged changeset is in ``$HG_NODE``.
   Name of tag is in ``$HG_TAG``. Tag is local if ``$HG_LOCAL=1``, in
--- a/hgext/color.py	Sun May 01 10:51:10 2011 +0200
+++ b/hgext/color.py	Sun May 01 06:04:08 2011 -0500
@@ -100,7 +100,7 @@
 
 '''
 
-import os
+import os, sys
 
 from mercurial import commands, dispatch, extensions, ui as uimod, util
 from mercurial.i18n import _
@@ -301,17 +301,22 @@
     global _terminfo_params
     if ui.plain():
         return
+
+    formatted = (os.environ.get('TERM') != 'dumb' and ui.formatted())
     mode = ui.config('color', 'mode', 'auto')
     if mode == 'auto':
         if os.name == 'nt' and 'TERM' not in os.environ:
             # looks line a cmd.exe console, use win32 API or nothing
             mode = w32effects and 'win32' or 'none'
         else:
-            _terminfosetup(ui)
-            if not _terminfo_params:
-                mode = 'ansi'
+            if not formatted:
+                _terminfo_params = False
             else:
-                mode = 'terminfo'
+                _terminfosetup(ui)
+                if not _terminfo_params:
+                    mode = 'ansi'
+                else:
+                    mode = 'terminfo'
     if mode == 'win32':
         if w32effects is None:
             # only warn if color.mode is explicitly set to win32
@@ -329,8 +334,7 @@
         auto = coloropt == 'auto'
         always = util.parsebool(coloropt)
         if (always or
-            (always is None and
-             (auto and (os.environ.get('TERM') != 'dumb' and ui_.formatted())))):
+            (always is None and auto and formatted)):
             colorui._colormode = mode
             colorui.__bases__ = (ui_.__class__,)
             ui_.__class__ = colorui
--- a/hgext/fetch.py	Sun May 01 10:51:10 2011 +0200
+++ b/hgext/fetch.py	Sun May 01 06:04:08 2011 -0500
@@ -9,7 +9,7 @@
 
 from mercurial.i18n import _
 from mercurial.node import nullid, short
-from mercurial import commands, cmdutil, hg, util, url, error
+from mercurial import commands, cmdutil, hg, util, error
 from mercurial.lock import release
 
 def fetch(ui, repo, source='default', **opts):
@@ -66,7 +66,7 @@
         other = hg.repository(hg.remoteui(repo, opts),
                               ui.expandpath(source))
         ui.status(_('pulling from %s\n') %
-                  url.hidepassword(ui.expandpath(source)))
+                  util.hidepassword(ui.expandpath(source)))
         revs = None
         if opts['rev']:
             try:
@@ -125,7 +125,7 @@
             # we don't translate commit messages
             message = (cmdutil.logmessage(opts) or
                        ('Automated merge with %s' %
-                        url.removeauth(other.url())))
+                        util.removeauth(other.url())))
             editor = cmdutil.commiteditor
             if opts.get('force_editor') or opts.get('edit'):
                 editor = cmdutil.commitforceeditor
--- a/hgext/graphlog.py	Sun May 01 10:51:10 2011 +0200
+++ b/hgext/graphlog.py	Sun May 01 06:04:08 2011 -0500
@@ -214,36 +214,50 @@
     else:
         return (len(repo) - 1, 0)
 
-def check_unsupported_flags(opts):
+def check_unsupported_flags(pats, opts):
     for op in ["follow_first", "copies", "newest_first"]:
         if op in opts and opts[op]:
             raise util.Abort(_("-G/--graph option is incompatible with --%s")
                              % op.replace("_", "-"))
+    if pats and opts.get('follow'):
+        raise util.Abort(_("-G/--graph option is incompatible with --follow "
+                           "with file argument"))
 
 def revset(pats, opts):
     """Return revset str built of revisions, log options and file patterns.
     """
-    opt2revset = dict(only_merges='merge',
-                      only_branch='branch',
-                      no_merges='not merge',
-                      include='file',
-                      exclude='not file',
-                      prune='not follow')
+    opt2revset = {
+        'follow': (0, 'follow()'),
+        'no_merges': (0, 'not merge()'),
+        'only_merges': (0, 'merge()'),
+        'removed': (0, 'removes("*")'),
+        'date': (1, 'date($)'),
+        'branch': (2, 'branch($)'),
+        'exclude': (2, 'not file($)'),
+        'include': (2, 'file($)'),
+        'keyword': (2, 'keyword($)'),
+        'only_branch': (2, 'branch($)'),
+        'prune': (2, 'not ($ or ancestors($))'),
+        'user': (2, 'user($)'),
+        }
     revset = []
     for op, val in opts.iteritems():
         if not val:
             continue
-        revop = opt2revset.get(op, op)
-        if op in ('follow', 'only_merges', 'no_merges'):
-            revset.append('%s()' % revop)
-        elif op in ("date", "keyword", "remove", "user", "branch",
-                    "only_branch", "prune"):
-            revset.append('%s(%s)' % (op, val))
-        elif op in ('include', 'exclude'):
+        if op == 'rev':
+            # Already a revset
+            revset.extend(val)
+        if op not in opt2revset:
+            continue
+        arity, revop = opt2revset[op]
+        revop = revop.replace('$', '%(val)r')
+        if arity == 0:
+            revset.append(revop)
+        elif arity == 1:
+            revset.append(revop % {'val': val})
+        else:
             for f in val:
-                revset.append('%s(%r)' % (op, f))
-        elif op == 'rev':
-            revset.extend(val)
+                revset.append(revop % {'val': f})
 
     for path in pats:
         revset.append('file(%r)' % path)
@@ -275,7 +289,7 @@
     directory.
     """
 
-    check_unsupported_flags(opts)
+    check_unsupported_flags(pats, opts)
 
     revs = revrange(repo, [revset(pats, opts)])
     revdag = graphmod.dagwalker(repo, revs)
@@ -301,7 +315,7 @@
     directory.
     """
 
-    check_unsupported_flags(opts)
+    check_unsupported_flags([], opts)
     o = hg._outgoing(ui, repo, dest, opts)
     if o is None:
         return
@@ -323,7 +337,7 @@
     def subreporecurse():
         return 1
 
-    check_unsupported_flags(opts)
+    check_unsupported_flags([], opts)
     def display(other, chlist, displayer):
         revdag = graphrevs(other, chlist, opts)
         showparents = [ctx.node() for ctx in repo[None].parents()]
--- a/hgext/parentrevspec.py	Sun May 01 10:51:10 2011 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,96 +0,0 @@
-# Mercurial extension to make it easy to refer to the parent of a revision
-#
-# Copyright (C) 2007 Alexis S. L. Carvalho <alexis@cecm.usp.br>
-#
-# This software may be used and distributed according to the terms of the
-# GNU General Public License version 2 or any later version.
-
-'''interpret suffixes to refer to ancestor revisions
-
-This extension allows you to use git-style suffixes to refer to the
-ancestors of a specific revision.
-
-For example, if you can refer to a revision as "foo", then::
-
-  foo^N = Nth parent of foo
-  foo^0 = foo
-  foo^1 = first parent of foo
-  foo^2 = second parent of foo
-  foo^  = foo^1
-
-  foo~N = Nth first grandparent of foo
-  foo~0 = foo
-  foo~1 = foo^1 = foo^ = first parent of foo
-  foo~2 = foo^1^1 = foo^^ = first parent of first parent of foo
-'''
-from mercurial import error
-
-def reposetup(ui, repo):
-    if not repo.local():
-        return
-
-    class parentrevspecrepo(repo.__class__):
-        def lookup(self, key):
-            try:
-                _super = super(parentrevspecrepo, self)
-                return _super.lookup(key)
-            except error.RepoError:
-                pass
-
-            circ = key.find('^')
-            tilde = key.find('~')
-            if circ < 0 and tilde < 0:
-                raise
-            elif circ >= 0 and tilde >= 0:
-                end = min(circ, tilde)
-            else:
-                end = max(circ, tilde)
-
-            cl = self.changelog
-            base = key[:end]
-            try:
-                node = _super.lookup(base)
-            except error.RepoError:
-                # eek - reraise the first error
-                return _super.lookup(key)
-
-            rev = cl.rev(node)
-            suffix = key[end:]
-            i = 0
-            while i < len(suffix):
-                # foo^N => Nth parent of foo
-                # foo^0 == foo
-                # foo^1 == foo^ == 1st parent of foo
-                # foo^2 == 2nd parent of foo
-                if suffix[i] == '^':
-                    j = i + 1
-                    p = cl.parentrevs(rev)
-                    if j < len(suffix) and suffix[j].isdigit():
-                        j += 1
-                        n = int(suffix[i + 1:j])
-                        if n > 2 or n == 2 and p[1] == -1:
-                            raise
-                    else:
-                        n = 1
-                    if n:
-                        rev = p[n - 1]
-                    i = j
-                # foo~N => Nth first grandparent of foo
-                # foo~0 = foo
-                # foo~1 = foo^1 == foo^ == 1st parent of foo
-                # foo~2 = foo^1^1 == foo^^ == 1st parent of 1st parent of foo
-                elif suffix[i] == '~':
-                    j = i + 1
-                    while j < len(suffix) and suffix[j].isdigit():
-                        j += 1
-                    if j == i + 1:
-                        raise
-                    n = int(suffix[i + 1:j])
-                    for k in xrange(n):
-                        rev = cl.parentrevs(rev)[0]
-                    i = j
-                else:
-                    raise
-            return cl.node(rev)
-
-    repo.__class__ = parentrevspecrepo
--- a/hgext/patchbomb.py	Sun May 01 10:51:10 2011 +0200
+++ b/hgext/patchbomb.py	Sun May 01 06:04:08 2011 -0500
@@ -48,7 +48,7 @@
 import os, errno, socket, tempfile, cStringIO, time
 import email.MIMEMultipart, email.MIMEBase
 import email.Utils, email.Encoders, email.Generator
-from mercurial import cmdutil, commands, hg, mail, patch, util, discovery, url
+from mercurial import cmdutil, commands, hg, mail, patch, util, discovery
 from mercurial.i18n import _
 from mercurial.node import bin
 
@@ -238,15 +238,14 @@
         dest = ui.expandpath(dest or 'default-push', dest or 'default')
         dest, branches = hg.parseurl(dest)
         revs, checkout = hg.addbranchrevs(repo, repo, branches, revs)
-        if revs:
-            revs = [repo.lookup(rev) for rev in revs]
         other = hg.repository(hg.remoteui(repo, opts), dest)
-        ui.status(_('comparing with %s\n') % url.hidepassword(dest))
-        o = discovery.findoutgoing(repo, other)
+        ui.status(_('comparing with %s\n') % util.hidepassword(dest))
+        common, _anyinc, _heads = discovery.findcommonincoming(repo, other)
+        nodes = revs and map(repo.lookup, revs) or revs
+        o = repo.changelog.findmissing(common, heads=nodes)
         if not o:
             ui.status(_("no changes found\n"))
             return []
-        o = repo.changelog.nodesbetween(o, revs)[0]
         return [str(repo.changelog.rev(r)) for r in o]
 
     def getpatches(revs):
--- a/hgext/schemes.py	Sun May 01 10:51:10 2011 +0200
+++ b/hgext/schemes.py	Sun May 01 06:04:08 2011 -0500
@@ -41,7 +41,7 @@
 """
 
 import os, re
-from mercurial import extensions, hg, templater, url as urlmod, util
+from mercurial import extensions, hg, templater, util
 from mercurial.i18n import _
 
 
@@ -95,4 +95,4 @@
                                'letter %s:\\\n') % (scheme, scheme.upper()))
         hg.schemes[scheme] = ShortRepository(url, scheme, t)
 
-    extensions.wrapfunction(urlmod, 'hasdriveletter', hasdriveletter)
+    extensions.wrapfunction(util, 'hasdriveletter', hasdriveletter)
--- a/hgext/transplant.py	Sun May 01 10:51:10 2011 +0200
+++ b/hgext/transplant.py	Sun May 01 06:04:08 2011 -0500
@@ -494,10 +494,10 @@
     and then resume where you left off by calling :hg:`transplant
     --continue/-c`.
     '''
-    def incwalk(repo, incoming, branches, match=util.always):
+    def incwalk(repo, commmon, branches, match=util.always):
         if not branches:
             branches = None
-        for node in repo.changelog.nodesbetween(incoming, branches)[0]:
+        for node in repo.changelog.findmissing(common, branches):
             if match(node):
                 yield node
 
@@ -552,8 +552,8 @@
     if source:
         sourcerepo = ui.expandpath(source)
         source = hg.repository(ui, sourcerepo)
-        source, common, incoming, bundle = bundlerepo.getremotechanges(ui, repo,
-                                            source, force=True)
+        source, common, anyinc, bundle = bundlerepo.getremotechanges(ui, repo,
+                                          source, force=True)
     else:
         source = repo
 
@@ -577,7 +577,7 @@
                 revmap[int(r)] = source.lookup(r)
         elif opts.get('all') or not merges:
             if source != repo:
-                alltransplants = incwalk(source, incoming, branches,
+                alltransplants = incwalk(source, common, branches,
                                          match=matchfn)
             else:
                 alltransplants = transplantwalk(source, p1, branches,
--- a/hgext/zeroconf/Zeroconf.py	Sun May 01 10:51:10 2011 +0200
+++ b/hgext/zeroconf/Zeroconf.py	Sun May 01 06:04:08 2011 -0500
@@ -808,7 +808,7 @@
 		try:
 			list = self.cache[entry.key]
 			return list[list.index(entry)]
-		except KeyError:
+		except (KeyError, ValueError):
 			return None
 
 	def getByDetails(self, name, type, clazz):
--- a/mercurial/bookmarks.py	Sun May 01 10:51:10 2011 +0200
+++ b/mercurial/bookmarks.py	Sun May 01 06:04:08 2011 -0500
@@ -6,7 +6,7 @@
 # GNU General Public License version 2 or any later version.
 
 from mercurial.i18n import _
-from mercurial.node import nullid, nullrev, bin, hex, short
+from mercurial.node import hex
 from mercurial import encoding, error, util
 import errno, os
 
--- a/mercurial/bundlerepo.py	Sun May 01 10:51:10 2011 +0200
+++ b/mercurial/bundlerepo.py	Sun May 01 06:04:08 2011 -0500
@@ -15,7 +15,7 @@
 from i18n import _
 import os, struct, tempfile, shutil
 import changegroup, util, mdiff, discovery
-import localrepo, changelog, manifest, filelog, revlog, error, url
+import localrepo, changelog, manifest, filelog, revlog, error
 
 class bundlerevlog(revlog.revlog):
     def __init__(self, opener, indexfile, bundle,
@@ -274,7 +274,7 @@
             cwd = os.path.join(cwd,'')
             if parentpath.startswith(cwd):
                 parentpath = parentpath[len(cwd):]
-    u = url.url(path)
+    u = util.url(path)
     path = u.localpath()
     if u.scheme == 'bundle':
         s = path.split("+", 1)
@@ -287,9 +287,8 @@
     return bundlerepository(ui, repopath, bundlename)
 
 def getremotechanges(ui, repo, other, revs=None, bundlename=None,
-                     force=False, usecommon=False):
-    tmp = discovery.findcommonincoming(repo, other, heads=revs, force=force,
-                                       commononly=usecommon)
+                     force=False):
+    tmp = discovery.findcommonincoming(repo, other, heads=revs, force=force)
     common, incoming, rheads = tmp
     if not incoming:
         try:
@@ -305,7 +304,7 @@
         if revs is None and other.capable('changegroupsubset'):
             revs = rheads
 
-        if usecommon:
+        if other.capable('getbundle'):
             cg = other.getbundle('incoming', common=common, heads=revs)
         elif revs is None:
             cg = other.changegroup(incoming, "incoming")
--- a/mercurial/changegroup.py	Sun May 01 10:51:10 2011 +0200
+++ b/mercurial/changegroup.py	Sun May 01 06:04:08 2011 -0500
@@ -43,7 +43,9 @@
         return ""
 
 bundletypes = {
-    "": ("", nocompress),
+    "": ("", nocompress), # only when using unbundle on ssh and old http servers
+                          # since the unification ssh accepts a header but there
+                          # is no capability signaling it.
     "HG10UN": ("HG10UN", nocompress),
     "HG10BZ": ("HG10", lambda: bz2.BZ2Compressor()),
     "HG10GZ": ("HG10GZ", lambda: zlib.compressobj()),
--- a/mercurial/cmdutil.py	Sun May 01 10:51:10 2011 +0200
+++ b/mercurial/cmdutil.py	Sun May 01 06:04:08 2011 -0500
@@ -8,7 +8,7 @@
 from node import hex, nullid, nullrev, short
 from i18n import _
 import os, sys, errno, re, glob, tempfile
-import util, scmutil, templater, patch, error, encoding, templatekw
+import util, scmutil, templater, patch, error, templatekw
 import match as matchmod
 import similar, revset, subrepo
 
@@ -174,7 +174,7 @@
             pass
 
         # fall through to new-style queries if old-style fails
-        m = revset.match(spec)
+        m = revset.match(repo.ui, spec)
         for r in m(repo, range(len(repo))):
             if r not in seen:
                 l.append(r)
@@ -1314,9 +1314,16 @@
     match.bad = lambda x, y: bad.append(x) or oldbad(x, y)
     names = []
     wctx = repo[None]
+    wctx.status(clean=True)
+    existing = None
+    if scmutil.showportabilityalert(ui):
+        existing = dict([(fn.lower(), fn) for fn in
+                         wctx.added() + wctx.clean() + wctx.modified()])
     for f in repo.walk(match):
         exact = match.exact(f)
         if exact or f not in repo.dirstate:
+            if existing:
+                scmutil.checkcasecollision(ui, f, existing)
             names.append(f)
             if ui.verbose or not exact:
                 ui.status(_('adding %s\n') % match.rel(join(f)))
--- a/mercurial/commands.py	Sun May 01 10:51:10 2011 +0200
+++ b/mercurial/commands.py	Sun May 01 06:04:08 2011 -0500
@@ -10,7 +10,7 @@
 from i18n import _, gettext
 import os, re, sys, difflib, time, tempfile
 import hg, scmutil, util, revlog, extensions, copies, error, bookmarks
-import patch, help, mdiff, url, encoding, templatekw, discovery
+import patch, help, url, encoding, templatekw, discovery
 import archival, changegroup, cmdutil, sshserver, hbisect, hgweb, hgweb.server
 import merge as mergemod
 import minirst, revset, templatefilters
@@ -696,49 +696,21 @@
         if dest:
             raise util.Abort(_("--base is incompatible with specifying "
                                "a destination"))
-        base = [repo.lookup(rev) for rev in base]
-        # create the right base
-        # XXX: nodesbetween / changegroup* should be "fixed" instead
-        o = []
-        has = set((nullid,))
-        for n in base:
-            has.update(repo.changelog.reachable(n))
-        if revs:
-            revs = [repo.lookup(rev) for rev in revs]
-            visit = revs[:]
-            has.difference_update(visit)
-        else:
-            visit = repo.changelog.heads()
-        seen = {}
-        while visit:
-            n = visit.pop(0)
-            parents = [p for p in repo.changelog.parents(n) if p not in has]
-            if len(parents) == 0:
-                if n not in has:
-                    o.append(n)
-            else:
-                for p in parents:
-                    if p not in seen:
-                        seen[p] = 1
-                        visit.append(p)
+        common = [repo.lookup(rev) for rev in base]
     else:
         dest = ui.expandpath(dest or 'default-push', dest or 'default')
         dest, branches = hg.parseurl(dest, opts.get('branch'))
         other = hg.repository(hg.remoteui(repo, opts), dest)
         revs, checkout = hg.addbranchrevs(repo, other, branches, revs)
-        if revs:
-            revs = [repo.lookup(rev) for rev in revs]
-        o = discovery.findoutgoing(repo, other, force=opts.get('force'))
-
-    if not o:
+        inc = discovery.findcommonincoming(repo, other, force=opts.get('force'))
+        common, _anyinc, _heads = inc
+
+    nodes = revs and map(repo.lookup, revs) or revs
+    cg = repo.getbundle('bundle', common=common, heads=nodes)
+    if not cg:
         ui.status(_("no changes found\n"))
         return 1
 
-    if revs:
-        cg = repo.changegroupsubset(o, revs, 'bundle')
-    else:
-        cg = repo.changegroup(o, 'bundle')
-
     bundletype = opts.get('type', 'bzip2').lower()
     btypes = {'none': 'HG10UN', 'bzip2': 'HG10BZ', 'gzip': 'HG10GZ'}
     bundletype = btypes.get(bundletype)
@@ -1324,7 +1296,10 @@
     if ui.verbose:
         tree = revset.parse(expr)[0]
         ui.note(tree, "\n")
-    func = revset.match(expr)
+        newtree = revset.findaliases(ui, tree)
+        if newtree != tree:
+            ui.note(newtree, "\n")
+    func = revset.match(ui, expr)
     for c in func(repo, range(len(repo))):
         ui.write("%s\n" % c)
 
@@ -2635,7 +2610,7 @@
         if 'bookmarks' not in other.listkeys('namespaces'):
             ui.warn(_("remote doesn't support bookmarks\n"))
             return 0
-        ui.status(_('comparing with %s\n') % url.hidepassword(source))
+        ui.status(_('comparing with %s\n') % util.hidepassword(source))
         return bookmarks.diff(ui, repo, other)
 
     ret = hg.incoming(ui, repo, source, opts)
@@ -2922,7 +2897,7 @@
         if 'bookmarks' not in other.listkeys('namespaces'):
             ui.warn(_("remote doesn't support bookmarks\n"))
             return 0
-        ui.status(_('comparing with %s\n') % url.hidepassword(dest))
+        ui.status(_('comparing with %s\n') % util.hidepassword(dest))
         return bookmarks.diff(ui, other, repo)
 
     ret = hg.outgoing(ui, repo, dest, opts)
@@ -2996,13 +2971,13 @@
     if search:
         for name, path in ui.configitems("paths"):
             if name == search:
-                ui.write("%s\n" % url.hidepassword(path))
+                ui.write("%s\n" % util.hidepassword(path))
                 return
         ui.warn(_("not found!\n"))
         return 1
     else:
         for name, path in ui.configitems("paths"):
-            ui.write("%s = %s\n" % (name, url.hidepassword(path)))
+            ui.write("%s = %s\n" % (name, util.hidepassword(path)))
 
 def postincoming(ui, repo, modheads, optupdate, checkout):
     if modheads == 0:
@@ -3045,7 +3020,7 @@
     """
     source, branches = hg.parseurl(ui.expandpath(source), opts.get('branch'))
     other = hg.repository(hg.remoteui(repo, opts), source)
-    ui.status(_('pulling from %s\n') % url.hidepassword(source))
+    ui.status(_('pulling from %s\n') % util.hidepassword(source))
     revs, checkout = hg.addbranchrevs(repo, other, branches, opts.get('rev'))
 
     if opts.get('bookmark'):
@@ -3128,7 +3103,7 @@
 
     dest = ui.expandpath(dest or 'default-push', dest or 'default')
     dest, branches = hg.parseurl(dest, opts.get('branch'))
-    ui.status(_('pushing to %s\n') % url.hidepassword(dest))
+    ui.status(_('pushing to %s\n') % util.hidepassword(dest))
     revs, checkout = hg.addbranchrevs(repo, repo, branches, opts.get('rev'))
     other = hg.repository(hg.remoteui(repo, opts), dest)
     if revs:
@@ -3947,7 +3922,7 @@
         source, branches = hg.parseurl(ui.expandpath('default'))
         other = hg.repository(hg.remoteui(repo, {}), source)
         revs, checkout = hg.addbranchrevs(repo, other, branches, opts.get('rev'))
-        ui.debug('comparing with %s\n' % url.hidepassword(source))
+        ui.debug('comparing with %s\n' % util.hidepassword(source))
         repo.ui.pushbuffer()
         common, incoming, rheads = discovery.findcommonincoming(repo, other)
         repo.ui.popbuffer()
@@ -3957,11 +3932,11 @@
         dest, branches = hg.parseurl(ui.expandpath('default-push', 'default'))
         revs, checkout = hg.addbranchrevs(repo, repo, branches, None)
         other = hg.repository(hg.remoteui(repo, {}), dest)
-        ui.debug('comparing with %s\n' % url.hidepassword(dest))
+        ui.debug('comparing with %s\n' % util.hidepassword(dest))
         repo.ui.pushbuffer()
-        o = discovery.findoutgoing(repo, other)
+        common, _anyinc, _heads = discovery.findcommonincoming(repo, other)
         repo.ui.popbuffer()
-        o = repo.changelog.nodesbetween(o, None)[0]
+        o = repo.changelog.findmissing(common=common)
         if o:
             t.append(_('%d outgoing') % len(o))
         if 'bookmarks' in other.listkeys('namespaces'):
--- a/mercurial/discovery.py	Sun May 01 10:51:10 2011 +0200
+++ b/mercurial/discovery.py	Sun May 01 06:04:08 2011 -0500
@@ -9,14 +9,19 @@
 from i18n import _
 import util, error
 
-def findcommonincoming(repo, remote, heads=None, force=False, commononly=False):
-    """Return a tuple (common, missing, heads) used to identify missing nodes
-    from remote. "missing" is either a boolean indicating if any nodes are missing
-    (when commononly=True), or else a list of the root nodes of the missing set.
+def findcommonincoming(repo, remote, heads=None, force=False):
+    """Return a tuple (common, anyincoming, heads) used to identify the common
+    subset of nodes between repo and remote.
 
-    If a list of heads is specified, return only nodes which are heads
-    or ancestors of these heads.
+    "common" is a list of (at least) the heads of the common subset.
+    "anyincoming" is testable as a boolean indicating if any nodes are missing
+      locally. If remote does not support getbundle, this actually is a list of
+      roots of the nodes that would be incoming, to be supplied to
+      changegroupsubset. No code except for pull should be relying on this fact
+      any longer.
+    "heads" is either the supplied heads, or else the remote's heads.
     """
+
     m = repo.changelog.nodemap
     search = []
     fetch = set()
@@ -37,7 +42,7 @@
     # and start by examining the heads
     repo.ui.status(_("searching for changes\n"))
 
-    if commononly:
+    if remote.capable('getbundle'):
         myheads = repo.heads()
         known = remote.known(myheads)
         if util.all(known):
@@ -155,45 +160,6 @@
 
     return base, list(fetch), heads
 
-def findoutgoing(repo, remote, base=None, remoteheads=None, force=False):
-    """Return list of nodes that are roots of subsets not in remote
-
-    If base dict is specified, assume that these nodes and their parents
-    exist on the remote side.
-    If remotehead is specified, assume it is the list of the heads from
-    the remote repository.
-    """
-    if base is None:
-        base = findcommonincoming(repo, remote, heads=remoteheads,
-                                  force=force)[0]
-    else:
-        base = list(base)
-
-    repo.ui.debug("common changesets up to "
-                  + " ".join(map(short, base)) + "\n")
-
-    remain = set(repo.changelog.nodemap)
-
-    # prune everything remote has from the tree
-    remain.remove(nullid)
-    remove = base
-    while remove:
-        n = remove.pop(0)
-        if n in remain:
-            remain.remove(n)
-            for p in repo.changelog.parents(n):
-                remove.append(p)
-
-    # find every node whose parents have been pruned
-    subset = []
-    # find every remote head that will get new children
-    for n in remain:
-        p1, p2 = repo.changelog.parents(n)
-        if p1 not in remain and p2 not in remain:
-            subset.append(n)
-
-    return subset
-
 def prepush(repo, remote, force, revs, newbranch):
     '''Analyze the local and remote repositories and determine which
     changesets need to be pushed to the remote. Return value depends
@@ -209,14 +175,13 @@
     successive changegroup chunks ready to be sent over the wire and
     remoteheads is the list of remote heads.'''
     remoteheads = remote.heads()
-    common, inc, rheads = findcommonincoming(repo, remote, heads=remoteheads,
-                                             force=force)
+    common, inc, _rheads = findcommonincoming(repo, remote, heads=remoteheads,
+                                              force=force)
 
     cl = repo.changelog
-    update = findoutgoing(repo, remote, common, remoteheads)
-    outg, bases, heads = cl.nodesbetween(update, revs)
+    outg = cl.findmissing(common, revs)
 
-    if not bases:
+    if not outg:
         repo.ui.status(_("no changes found\n"))
         return None, 1
 
@@ -317,8 +282,7 @@
 
     if revs is None:
         # use the fast path, no race possible on push
-        nodes = repo.changelog.findmissing(common)
-        cg = repo._changegroup(nodes, 'push')
+        cg = repo._changegroup(outg, 'push')
     else:
-        cg = repo.changegroupsubset(update, revs, 'push')
+        cg = repo.getbundle('push', heads=revs, common=common)
     return cg, remoteheads
--- a/mercurial/dispatch.py	Sun May 01 10:51:10 2011 +0200
+++ b/mercurial/dispatch.py	Sun May 01 06:04:08 2011 -0500
@@ -133,7 +133,8 @@
         elif hasattr(inst, "reason"):
             try: # usually it is in the form (errno, strerror)
                 reason = inst.reason.args[1]
-            except AttributeError: # it might be anything, for example a string
+            except (AttributeError, IndexError):
+                 # it might be anything, for example a string
                 reason = inst.reason
             ui.warn(_("abort: error: %s\n") % reason)
         elif hasattr(inst, "args") and inst.args[0] == errno.EPIPE:
--- a/mercurial/encoding.py	Sun May 01 10:51:10 2011 +0200
+++ b/mercurial/encoding.py	Sun May 01 06:04:08 2011 -0500
@@ -148,3 +148,17 @@
         return sum([w(c) in wide and 2 or 1 for c in d])
     return len(d)
 
+def lower(s):
+    "best-effort encoding-aware case-folding of local string s"
+    try:
+        if isinstance(s, localstr):
+            u = s._utf8.decode("utf-8")
+        else:
+            u = s.decode(encoding, encodingmode)
+
+        lu = u.lower()
+        if u == lu:
+            return s # preserve localstring
+        return lu.encode(encoding)
+    except UnicodeError:
+        return s.lower() # we don't know how to fold this except in ASCII
--- a/mercurial/extensions.py	Sun May 01 10:51:10 2011 +0200
+++ b/mercurial/extensions.py	Sun May 01 06:04:08 2011 -0500
@@ -11,7 +11,7 @@
 
 _extensions = {}
 _order = []
-_ignore = ['hbisect', 'bookmarks']
+_ignore = ['hbisect', 'bookmarks', 'parentrevspec']
 
 def extensions():
     for name in _order:
--- a/mercurial/filelog.py	Sun May 01 10:51:10 2011 +0200
+++ b/mercurial/filelog.py	Sun May 01 06:04:08 2011 -0500
@@ -6,17 +6,28 @@
 # GNU General Public License version 2 or any later version.
 
 import revlog
+import re
 
+_mdre = re.compile('\1\n')
 def _parsemeta(text):
-    if not text.startswith('\1\n'):
-        return {}
-    s = text.index('\1\n', 2)
-    mt = text[2:s]
-    m = {}
-    for l in mt.splitlines():
+    """return (metadatadict, keylist, metadatasize)"""
+    # text can be buffer, so we can't use .startswith or .index
+    if text[:2] != '\1\n':
+        return None, None, None
+    s = _mdre.search(text, 2).start()
+    mtext = text[2:s]
+    meta = {}
+    keys = []
+    for l in mtext.splitlines():
         k, v = l.split(": ", 1)
-        m[k] = v
-    return m
+        meta[k] = v
+        keys.append(k)
+    return meta, keys, (s + 2)
+
+def _packmeta(meta, keys=None):
+    if not keys:
+        keys = sorted(meta.iterkeys())
+    return "".join("%s: %s\n" % (k, meta[k]) for k in keys)
 
 class filelog(revlog.revlog):
     def __init__(self, opener, path):
@@ -32,15 +43,14 @@
 
     def add(self, text, meta, transaction, link, p1=None, p2=None):
         if meta or text.startswith('\1\n'):
-            mt = ["%s: %s\n" % (k, v) for k, v in sorted(meta.iteritems())]
-            text = "\1\n%s\1\n%s" % ("".join(mt), text)
+            text = "\1\n%s\1\n%s" % (_packmeta(meta), text)
         return self.addrevision(text, transaction, link, p1, p2)
 
     def renamed(self, node):
         if self.parents(node)[0] != revlog.nullid:
             return False
         t = self.revision(node)
-        m = _parsemeta(t)
+        m = _parsemeta(t)[0]
         if m and "copy" in m:
             return (m["copy"], revlog.bin(m["copyrev"]))
         return False
--- a/mercurial/graphmod.py	Sun May 01 10:51:10 2011 +0200
+++ b/mercurial/graphmod.py	Sun May 01 06:04:08 2011 -0500
@@ -18,7 +18,6 @@
 """
 
 from mercurial.node import nullrev
-from mercurial.cmdutil import revrange
 
 CHANGESET = 'C'
 
@@ -31,39 +30,29 @@
     returned.
     """
     if not revs:
-        return []
-
-    ns = [repo[r].node() for r in revs]
-    revdag = list(nodes(repo, ns))
+        return
 
     cl = repo.changelog
     lowestrev = min(revs)
     gpcache = {}
-    leafs = {}
 
-    for i, (id, c, ctx, parents) in enumerate(revdag):
+    knownrevs = set(revs)
+    for rev in revs:
+        ctx = repo[rev]
+        parents = sorted(set([p.rev() for p in ctx.parents()
+                              if p.rev() in knownrevs]))
         mpars = [p.rev() for p in ctx.parents() if
                  p.rev() != nullrev and p.rev() not in parents]
-        grandparents = []
 
         for mpar in mpars:
             gp = gpcache.get(mpar) or grandparent(cl, lowestrev, revs, mpar)
             gpcache[mpar] = gp
             if gp is None:
-                leafs.setdefault(mpar, []).append((i, ctx))
-            else:
-                grandparents.append(gp)
+                parents.append(mpar)
+            elif gp not in parents:
+                parents.append(gp)
 
-        if grandparents:
-            for gp in grandparents:
-                if gp not in revdag[i][3]:
-                    revdag[i][3].append(gp)
-
-    for parent, leafs in leafs.iteritems():
-        for i, ctx in leafs:
-            revdag[i][3].append(parent)
-
-    return revdag
+        yield (ctx.rev(), CHANGESET, ctx, parents)
 
 def nodes(repo, nodes):
     """cset DAG generator yielding (id, CHANGESET, ctx, [parentids]) tuples
--- a/mercurial/help/revsets.txt	Sun May 01 10:51:10 2011 +0200
+++ b/mercurial/help/revsets.txt	Sun May 01 06:04:08 2011 -0500
@@ -42,11 +42,45 @@
 
 ``x - y``
   Changesets in x but not in y.
+  
+``x^n``
+  The nth parent of x, n == 0, 1, or 2.
+  For n == 0, x; for n == 1, the first parent of each changeset in x;
+  for n == 2, the second parent of changeset in x.
+
+``x~n``
+  The nth first ancestor of x; ``x~0`` is x; ``x~3`` is ``x^^^``.
+
+There is a single postfix operator:
+
+``x^``
+  Equivalent to ``x^1``, the first parent of each changeset in x.
+
 
 The following predicates are supported:
 
 .. predicatesmarker
 
+New predicates (known as "aliases") can be defined, using any combination of
+existing predicates or other aliases. An alias definition looks like::
+
+  <alias> = <definition>
+
+in the ``revsetalias`` section of ``.hgrc``. Arguments of the form `$1`, `$2`,
+etc. are substituted from the alias into the definition.
+
+For example,
+
+::
+
+  [revsetalias]
+  h = heads()
+  d($1) = sort($1, date)
+  rs($1, $2) = reverse(sort($1, $2))
+
+defines three aliases, ``h``, ``d``, and ``rs``. ``rs(0:tip, author)`` is
+exactly equivalent to ``reverse(sort(0:tip, author))``.
+
 Command line equivalents for :hg:`log`::
 
   -f    ->  ::.
--- a/mercurial/hg.py	Sun May 01 10:51:10 2011 +0200
+++ b/mercurial/hg.py	Sun May 01 06:04:08 2011 -0500
@@ -8,16 +8,16 @@
 
 from i18n import _
 from lock import release
-from node import hex, nullid, nullrev, short
+from node import hex, nullid
 import localrepo, bundlerepo, httprepo, sshrepo, statichttprepo, bookmarks
-import lock, util, extensions, error, encoding, node
-import cmdutil, discovery, url
+import lock, util, extensions, error, node
+import cmdutil, discovery
 import merge as mergemod
 import verify as verifymod
 import errno, os, shutil
 
 def _local(path):
-    path = util.expandpath(url.localpath(path))
+    path = util.expandpath(util.localpath(path))
     return (os.path.isfile(path) and bundlerepo or localrepo)
 
 def addbranchrevs(lrepo, repo, branches, revs):
@@ -54,7 +54,7 @@
 def parseurl(path, branches=None):
     '''parse url#branch, returning (url, (branch, branches))'''
 
-    u = url.url(path)
+    u = util.url(path)
     branch = None
     if u.fragment:
         branch = u.fragment
@@ -71,7 +71,7 @@
 }
 
 def _lookup(path):
-    u = url.url(path)
+    u = util.url(path)
     scheme = u.scheme or 'file'
     thing = schemes.get(scheme) or schemes['file']
     try:
@@ -221,8 +221,8 @@
     else:
         dest = ui.expandpath(dest)
 
-    dest = url.localpath(dest)
-    source = url.localpath(source)
+    dest = util.localpath(dest)
+    source = util.localpath(source)
 
     if os.path.exists(dest):
         if not os.path.isdir(dest):
@@ -248,7 +248,7 @@
         abspath = origsource
         copy = False
         if src_repo.cancopy() and islocal(dest):
-            abspath = os.path.abspath(url.localpath(origsource))
+            abspath = os.path.abspath(util.localpath(origsource))
             copy = not pull and not rev
 
         if copy:
@@ -421,24 +421,19 @@
     """
     source, branches = parseurl(ui.expandpath(source), opts.get('branch'))
     other = repository(remoteui(repo, opts), source)
-    ui.status(_('comparing with %s\n') % url.hidepassword(source))
+    ui.status(_('comparing with %s\n') % util.hidepassword(source))
     revs, checkout = addbranchrevs(repo, other, branches, opts.get('rev'))
 
     if revs:
         revs = [other.lookup(rev) for rev in revs]
-    usecommon = other.capable('getbundle')
-    other, common, incoming, bundle = bundlerepo.getremotechanges(ui, repo, other,
-                                       revs, opts["bundle"], opts["force"],
-                                       usecommon=usecommon)
-    if not incoming:
+    other, common, anyinc, bundle = bundlerepo.getremotechanges(ui, repo, other,
+                                     revs, opts["bundle"], opts["force"])
+    if not anyinc:
         ui.status(_("no changes found\n"))
         return subreporecurse()
 
     try:
-        if usecommon:
-            chlist = other.changelog.findmissing(common, revs)
-        else:
-            chlist = other.changelog.nodesbetween(incoming, revs)[0]
+        chlist = other.changelog.findmissing(common, revs)
         displayer = cmdutil.show_changeset(ui, other, opts, buffered)
 
         # XXX once graphlog extension makes it into core,
@@ -482,18 +477,19 @@
 def _outgoing(ui, repo, dest, opts):
     dest = ui.expandpath(dest or 'default-push', dest or 'default')
     dest, branches = parseurl(dest, opts.get('branch'))
-    ui.status(_('comparing with %s\n') % url.hidepassword(dest))
+    ui.status(_('comparing with %s\n') % util.hidepassword(dest))
     revs, checkout = addbranchrevs(repo, repo, branches, opts.get('rev'))
     if revs:
         revs = [repo.lookup(rev) for rev in revs]
 
     other = repository(remoteui(repo, opts), dest)
-    o = discovery.findoutgoing(repo, other, force=opts.get('force'))
+    inc = discovery.findcommonincoming(repo, other, force=opts.get('force'))
+    common, _anyinc, _heads = inc
+    o = repo.changelog.findmissing(common, revs)
     if not o:
         ui.status(_("no changes found\n"))
         return None
-
-    return repo.changelog.nodesbetween(o, revs)[0]
+    return o
 
 def outgoing(ui, repo, dest, opts):
     def recurse():
--- a/mercurial/hgweb/hgwebdir_mod.py	Sun May 01 10:51:10 2011 +0200
+++ b/mercurial/hgweb/hgwebdir_mod.py	Sun May 01 06:04:08 2011 -0500
@@ -9,7 +9,7 @@
 import os, re, time
 from mercurial.i18n import _
 from mercurial import ui, hg, scmutil, util, templater
-from mercurial import error, encoding, url
+from mercurial import error, encoding
 from common import ErrorResponse, get_mtime, staticfile, paritygen, \
                    get_contact, HTTP_OK, HTTP_NOT_FOUND, HTTP_SERVER_ERROR
 from hgweb_mod import hgweb
@@ -364,7 +364,7 @@
 
     def updatereqenv(self, env):
         if self._baseurl is not None:
-            u = url.url(self._baseurl)
+            u = util.url(self._baseurl)
             env['SERVER_NAME'] = u.host
             if u.port:
                 env['SERVER_PORT'] = u.port
--- a/mercurial/hgweb/protocol.py	Sun May 01 10:51:10 2011 +0200
+++ b/mercurial/hgweb/protocol.py	Sun May 01 06:04:08 2011 -0500
@@ -5,7 +5,7 @@
 # This software may be used and distributed according to the terms of the
 # GNU General Public License version 2 or any later version.
 
-import cStringIO, zlib, sys, urllib
+import cgi, cStringIO, zlib, sys, urllib
 from mercurial import util, wireproto
 from common import HTTP_OK
 
@@ -16,18 +16,31 @@
         self.req = req
         self.response = ''
     def getargs(self, args):
+        knownargs = self._args()
         data = {}
         keys = args.split()
         for k in keys:
             if k == '*':
                 star = {}
-                for key in self.req.form.keys():
+                for key in knownargs.keys():
                     if key != 'cmd' and key not in keys:
-                        star[key] = self.req.form[key][0]
+                        star[key] = knownargs[key][0]
                 data['*'] = star
             else:
-                data[k] = self.req.form[k][0]
+                data[k] = knownargs[k][0]
         return [data[k] for k in keys]
+    def _args(self):
+        args = self.req.form.copy()
+        chunks = []
+        i = 1
+        while 1:
+            h = self.req.env.get('HTTP_X_HGARG_' + str(i))
+            if h is None:
+                break
+            chunks += [h]
+            i += 1
+        args.update(cgi.parse_qs(''.join(chunks), keep_blank_values=True))
+        return args
     def getfile(self, fp):
         length = int(self.req.env['CONTENT_LENGTH'])
         for s in util.filechunkiter(self.req, limit=length):
--- a/mercurial/hgweb/server.py	Sun May 01 10:51:10 2011 +0200
+++ b/mercurial/hgweb/server.py	Sun May 01 06:04:08 2011 -0500
@@ -59,6 +59,12 @@
     def log_message(self, format, *args):
         self._log_any(self.server.accesslog, format, *args)
 
+    def log_request(self, code='-', size='-'):
+        xheaders = [h for h in self.headers.items() if h[0].startswith('x-')]
+        self.log_message('"%s" %s %s%s',
+                         self.requestline, str(code), str(size),
+                         ''.join([' %s:%s' % h for h in sorted(xheaders)]))
+
     def do_write(self):
         try:
             self.do_hgweb()
--- a/mercurial/httprepo.py	Sun May 01 10:51:10 2011 +0200
+++ b/mercurial/httprepo.py	Sun May 01 06:04:08 2011 -0500
@@ -28,7 +28,7 @@
         self.path = path
         self.caps = None
         self.handler = None
-        u = url.url(path)
+        u = util.url(path)
         if u.query or u.fragment:
             raise util.Abort(_('unsupported URL component: "%s"') %
                              (u.query or u.fragment))
@@ -76,7 +76,26 @@
         data = args.pop('data', None)
         headers = args.pop('headers', {})
         self.ui.debug("sending %s command\n" % cmd)
-        q = [('cmd', cmd)] + sorted(args.items())
+        q = [('cmd', cmd)]
+        headersize = 0
+        if len(args) > 0:
+            httpheader = self.capable('httpheader')
+            if httpheader:
+                headersize = int(httpheader.split(',')[0])
+        if headersize > 0:
+            # The headers can typically carry more data than the URL.
+            encargs = urllib.urlencode(sorted(args.items()))
+            headerfmt = 'X-HgArg-%s'
+            contentlen = headersize - len(headerfmt % '000' + ': \r\n')
+            headernum = 0
+            for i in xrange(0, len(encargs), contentlen):
+                headernum += 1
+                header = headerfmt % str(headernum)
+                headers[header] = encargs[i:i + contentlen]
+            varyheaders = [headerfmt % str(h) for h in range(1, headernum + 1)]
+            headers['Vary'] = ','.join(varyheaders)
+        else:
+            q += sorted(args.items())
         qs = '?%s' % urllib.urlencode(q)
         cu = "%s%s" % (self._url, qs)
         req = urllib2.Request(cu, data, headers)
@@ -111,12 +130,12 @@
         except AttributeError:
             proto = resp.headers['content-type']
 
-        safeurl = url.hidepassword(self._url)
+        safeurl = util.hidepassword(self._url)
         # accept old "text/plain" and "application/hg-changegroup" for now
         if not (proto.startswith('application/mercurial-') or
                 proto.startswith('text/plain') or
                 proto.startswith('application/hg-changegroup')):
-            self.ui.debug("requested URL: '%s'\n" % url.hidepassword(cu))
+            self.ui.debug("requested URL: '%s'\n" % util.hidepassword(cu))
             raise error.RepoError(
                 _("'%s' does not appear to be an hg repository:\n"
                   "---%%<--- (%s)\n%s\n---%%<---\n")
@@ -147,19 +166,18 @@
         # have to stream bundle to a temp file because we do not have
         # http 1.1 chunked transfer.
 
-        type = ""
         types = self.capable('unbundle')
-        # servers older than d1b16a746db6 will send 'unbundle' as a
-        # boolean capability
         try:
             types = types.split(',')
         except AttributeError:
+            # servers older than d1b16a746db6 will send 'unbundle' as a
+            # boolean capability. They only support headerless/uncompressed
+            # bundles.
             types = [""]
-        if types:
-            for x in types:
-                if x in changegroup.bundletypes:
-                    type = x
-                    break
+        for x in types:
+            if x in changegroup.bundletypes:
+                type = x
+                break
 
         tempname = changegroup.writebundle(cg, None, type)
         fp = url.httpsendfile(self.ui, tempname, "rb")
--- a/mercurial/localrepo.py	Sun May 01 10:51:10 2011 +0200
+++ b/mercurial/localrepo.py	Sun May 01 06:04:08 2011 -0500
@@ -14,7 +14,6 @@
 import match as matchmod
 import merge as mergemod
 import tags as tagsmod
-import url as urlmod
 from lock import release
 import weakref, errno, os, time, inspect
 propertycache = util.propertycache
@@ -190,7 +189,7 @@
         warned = [0]
         def validate(node):
             try:
-                r = self.changelog.rev(node)
+                self.changelog.rev(node)
                 return node
             except error.LookupError:
                 if not warned[0]:
@@ -1327,9 +1326,8 @@
     def pull(self, remote, heads=None, force=False):
         lock = self.lock()
         try:
-            usecommon = remote.capable('getbundle')
             tmp = discovery.findcommonincoming(self, remote, heads=heads,
-                                               force=force, commononly=usecommon)
+                                               force=force)
             common, fetch, rheads = tmp
             if not fetch:
                 self.ui.status(_("no changes found\n"))
@@ -1341,7 +1339,7 @@
                     # issue1320, avoid a race if remote changed after discovery
                     heads = rheads
 
-                if usecommon:
+                if remote.capable('getbundle'):
                     cg = remote.getbundle('pull', common=common,
                                           heads=heads or rheads)
                 elif heads is None:
@@ -1475,6 +1473,8 @@
         if not heads:
             heads = cl.heads()
         common, missing = cl.findcommonmissing(common, heads)
+        if not missing:
+            return None
         return self._changegroupsubset(common, missing, heads, source)
 
     def _changegroupsubset(self, commonrevs, csets, heads, source):
@@ -1694,7 +1694,7 @@
         cl.delayupdate()
         oldheads = cl.heads()
 
-        tr = self.transaction("\n".join([srctype, urlmod.hidepassword(url)]))
+        tr = self.transaction("\n".join([srctype, util.hidepassword(url)]))
         try:
             trp = weakref.proxy(tr)
             # pull off the changeset group
@@ -1918,10 +1918,18 @@
         return self.pull(remote, heads)
 
     def pushkey(self, namespace, key, old, new):
-        return pushkey.push(self, namespace, key, old, new)
+        self.hook('prepushkey', throw=True, namespace=namespace, key=key,
+                  old=old, new=new)
+        ret = pushkey.push(self, namespace, key, old, new)
+        self.hook('pushkey', namespace=namespace, key=key, old=old, new=new,
+                  ret=ret)
+        return ret
 
     def listkeys(self, namespace):
-        return pushkey.list(self, namespace)
+        self.hook('prelistkeys', throw=True, namespace=namespace)
+        values = pushkey.list(self, namespace)
+        self.hook('listkeys', namespace=namespace, values=values)
+        return values
 
     def debugwireargs(self, one, two, three=None, four=None, five=None):
         '''used to test argument passing over the wire'''
@@ -1936,7 +1944,7 @@
     return a
 
 def instance(ui, path, create):
-    return localrepository(ui, urlmod.localpath(path), create)
+    return localrepository(ui, util.localpath(path), create)
 
 def islocal(path):
     return True
--- a/mercurial/merge.py	Sun May 01 10:51:10 2011 +0200
+++ b/mercurial/merge.py	Sun May 01 06:04:08 2011 -0500
@@ -271,7 +271,6 @@
     ms.reset(wctx.p1().node())
     moves = []
     action.sort(key=actionkey)
-    substate = wctx.substate # prime
 
     # prescan for merges
     u = repo.ui
--- a/mercurial/pure/bdiff.py	Sun May 01 10:51:10 2011 +0200
+++ b/mercurial/pure/bdiff.py	Sun May 01 06:04:08 2011 -0500
@@ -19,6 +19,7 @@
 
 def _normalizeblocks(a, b, blocks):
     prev = None
+    r = []
     for curr in blocks:
         if prev is None:
             prev = curr
@@ -40,9 +41,10 @@
             while (b1end + shift < b2end and
                    a[a1end + shift] == b[b1end + shift]):
                 shift += 1
-        yield a1, b1, l1 + shift
+        r.append((a1, b1, l1 + shift))
         prev = a2 + shift, b2 + shift, l2 - shift
-    yield prev
+    r.append(prev)
+    return r
 
 def bdiff(a, b):
     a = str(a).splitlines(True)
--- a/mercurial/pure/mpatch.py	Sun May 01 10:51:10 2011 +0200
+++ b/mercurial/pure/mpatch.py	Sun May 01 06:04:08 2011 -0500
@@ -56,9 +56,9 @@
 
     def pull(dst, src, l): # pull l bytes from src
         while l:
-            f = src.pop(0)
+            f = src.pop()
             if f[0] > l: # do we need to split?
-                src.insert(0, (f[0] - l, f[1] + l))
+                src.append((f[0] - l, f[1] + l))
                 dst.append((l, f[1]))
                 return
             dst.append(f)
@@ -66,7 +66,7 @@
 
     def collect(buf, list):
         start = buf
-        for l, p in list:
+        for l, p in reversed(list):
             move(buf, p, l)
             buf += l
         return (buf - start, start)
@@ -88,7 +88,7 @@
             new.append((l, pos + 12))        # what got added
             pos += l + 12
             last = p2
-        frags = new + frags                    # what was left at the end
+        frags.extend(reversed(new))                    # what was left at the end
 
     t = collect(b2, frags)
 
--- a/mercurial/pure/parsers.py	Sun May 01 10:51:10 2011 +0200
+++ b/mercurial/pure/parsers.py	Sun May 01 06:04:08 2011 -0500
@@ -5,7 +5,7 @@
 # This software may be used and distributed according to the terms of the
 # GNU General Public License version 2 or any later version.
 
-from mercurial.node import bin, nullid, nullrev
+from mercurial.node import bin, nullid
 from mercurial import util
 import struct, zlib
 
--- a/mercurial/repair.py	Sun May 01 10:51:10 2011 +0200
+++ b/mercurial/repair.py	Sun May 01 06:04:08 2011 -0500
@@ -6,9 +6,9 @@
 # This software may be used and distributed according to the terms of the
 # GNU General Public License version 2 or any later version.
 
-import changegroup, bookmarks
-from node import nullrev, short
-from i18n import _
+from mercurial import changegroup, bookmarks
+from mercurial.node import short
+from mercurial.i18n import _
 import os
 
 def _bundle(repo, bases, heads, node, suffix, compress=True):
--- a/mercurial/revlog.py	Sun May 01 10:51:10 2011 +0200
+++ b/mercurial/revlog.py	Sun May 01 06:04:08 2011 -0500
@@ -14,7 +14,7 @@
 # import stuff from node for others to import from revlog
 from node import bin, hex, nullid, nullrev, short #@UnusedImport
 from i18n import _
-import changegroup, ancestor, mdiff, parsers, error, util
+import ancestor, mdiff, parsers, error, util
 import struct, zlib, errno
 
 _pack = struct.pack
@@ -288,7 +288,7 @@
 
     @util.propertycache
     def nodemap(self):
-        n = self.rev(self.node(0))
+        self.rev(self.node(0))
         return self._nodecache
 
     def rev(self, node):
@@ -822,6 +822,9 @@
     def _chunk(self, rev):
         return decompress(self._chunkraw(rev, rev))
 
+    def _chunkbase(self, rev):
+        return self._chunk(rev)
+
     def _chunkclear(self):
         self._chunkcache = (0, '')
 
@@ -884,7 +887,7 @@
 
         self._chunkraw(base, rev)
         if text is None:
-            text = self._chunk(base)
+            text = self._chunkbase(base)
 
         bins = [self._chunk(r) for r in chain]
         text = mdiff.patches(text, bins)
--- a/mercurial/revset.py	Sun May 01 10:51:10 2011 +0200
+++ b/mercurial/revset.py	Sun May 01 06:04:08 2011 -0500
@@ -13,6 +13,8 @@
 
 elements = {
     "(": (20, ("group", 1, ")"), ("func", 1, ")")),
+    "~": (18, None, ("ancestor", 18)),
+    "^": (18, None, ("parent", 18), ("parentpost", 18)),
     "-": (5, ("negate", 19), ("minus", 5)),
     "::": (17, ("dagrangepre", 17), ("dagrange", 17),
            ("dagrangepost", 17)),
@@ -47,7 +49,7 @@
         elif c == '.' and program[pos:pos + 2] == '..': # look ahead carefully
             yield ('..', None, pos)
             pos += 1 # skip ahead
-        elif c in "():,-|&+!": # handle simple operators
+        elif c in "():,-|&+!~^": # handle simple operators
             yield (c, None, pos)
         elif (c in '"\'' or c == 'r' and
               program[pos:pos + 2] in ("r'", 'r"')): # handle quoted strings
@@ -209,6 +211,22 @@
     s = set(repo.changelog.ancestors(*args)) | set(args)
     return [r for r in subset if r in s]
 
+def ancestorspec(repo, subset, x, n):
+    """``set~n``
+    Changesets that are the Nth ancestor (first parents only) of a changeset in set.
+    """
+    try:
+        n = int(n[1])
+    except ValueError:
+        raise error.ParseError(_("~ expects a number"))
+    ps = set()
+    cl = repo.changelog
+    for r in getset(repo, subset, x):
+        for i in range(n):
+            r = cl.parentrevs(r)[0]
+        ps.add(r)
+    return [r for r in subset if r in ps]
+
 def author(repo, subset, x):
     """``author(string)``
     Alias for ``user(string)``.
@@ -452,6 +470,20 @@
         raise error.ParseError(_("limit expects a number"))
     return getset(repo, subset, l[0])[:lim]
 
+def last(repo, subset, x):
+    """``last(set, n)``
+    Last n members of set.
+    """
+    # i18n: "last" is a keyword
+    l = getargs(x, 2, 2, _("last requires two arguments"))
+    try:
+        # i18n: "last" is a keyword
+        lim = int(getstring(l[1], _("last requires a number")))
+    except ValueError:
+        # i18n: "last" is a keyword
+        raise error.ParseError(_("last expects a number"))
+    return getset(repo, subset, l[0])[-lim:]
+
 def maxrev(repo, subset, x):
     """``max(set)``
     Changeset with highest revision number in set.
@@ -522,10 +554,10 @@
         revs = [repo.lookup(rev) for rev in revs]
     other = hg.repository(hg.remoteui(repo, {}), dest)
     repo.ui.pushbuffer()
-    o = discovery.findoutgoing(repo, other)
+    common, _anyinc, _heads = discovery.findcommonincoming(repo, other)
     repo.ui.popbuffer()
     cl = repo.changelog
-    o = set([cl.rev(r) for r in repo.changelog.nodesbetween(o, revs)[0]])
+    o = set([cl.rev(r) for r in repo.changelog.findmissing(common, revs)])
     return [r for r in subset if r in o]
 
 def p1(repo, subset, x):
@@ -574,6 +606,31 @@
         ps.update(cl.parentrevs(r))
     return [r for r in subset if r in ps]
 
+def parentspec(repo, subset, x, n):
+    """``set^0``
+    The set.
+    ``set^1`` (or ``set^``), ``set^2``
+    First or second parent, respectively, of all changesets in set.
+    """
+    try:
+        n = int(n[1])
+        if n not in (0, 1, 2):
+            raise ValueError
+    except ValueError:
+        raise error.ParseError(_("^ expects a number 0, 1, or 2"))
+    ps = set()
+    cl = repo.changelog
+    for r in getset(repo, subset, x):
+        if n == 0:
+            ps.add(r)
+        elif n == 1:
+            ps.add(cl.parentrevs(r)[0])
+        elif n == 2:
+            parents = cl.parentrevs(r)
+            if len(parents) > 1:
+                ps.add(parents[1])
+    return [r for r in subset if r in ps]
+
 def present(repo, subset, x):
     """``present(set)``
     An empty set, if any revision in set isn't found; otherwise,
@@ -724,6 +781,7 @@
     "head": head,
     "heads": heads,
     "keyword": keyword,
+    "last": last,
     "limit": limit,
     "max": maxrev,
     "min": minrev,
@@ -754,6 +812,9 @@
     "not": notset,
     "list": listset,
     "func": func,
+    "ancestor": ancestorspec,
+    "parent": parentspec,
+    "parentpost": p1,
 }
 
 def optimize(x, small):
@@ -799,9 +860,12 @@
     elif op == 'not':
         o = optimize(x[1], not small)
         return o[0], (op, o[1])
+    elif op == 'parentpost':
+        o = optimize(x[1], small)
+        return o[0], (op, o[1])
     elif op == 'group':
         return optimize(x[1], small)
-    elif op in 'range list':
+    elif op in 'range list parent ancestorspec':
         wa, ta = optimize(x[1], small)
         wb, tb = optimize(x[2], small)
         return wa + wb, (op, ta, tb)
@@ -825,14 +889,89 @@
         return w + wa, (op, x[1], ta)
     return 1, x
 
+class revsetalias(object):
+    funcre = re.compile('^([^(]+)\(([^)]+)\)$')
+    args = ()
+
+    def __init__(self, token, value):
+        '''Aliases like:
+
+        h = heads(default)
+        b($1) = ancestors($1) - ancestors(default)
+        '''
+        if isinstance(token, tuple):
+            self.type, self.name = token
+        else:
+            m = self.funcre.search(token)
+            if m:
+                self.type = 'func'
+                self.name = m.group(1)
+                self.args = [x.strip() for x in m.group(2).split(',')]
+            else:
+                self.type = 'symbol'
+                self.name = token
+
+        if isinstance(value, str):
+            for arg in self.args:
+                value = value.replace(arg, repr(arg))
+            self.replacement, pos = parse(value)
+            if pos != len(value):
+                raise error.ParseError('invalid token', pos)
+        else:
+            self.replacement = value
+
+    def match(self, tree):
+        if not tree:
+            return False
+        if tree == (self.type, self.name):
+            return True
+        if tree[0] != self.type:
+            return False
+        if len(tree) > 1 and tree[1] != ('symbol', self.name):
+            return False
+        # 'func' + funcname + args
+        if ((self.args and len(tree) != 3) or
+            (len(self.args) == 1 and tree[2][0] == 'list') or
+            (len(self.args) > 1 and (tree[2][0] != 'list' or
+                                     len(tree[2]) - 1 != len(self.args)))):
+            raise error.ParseError('invalid amount of arguments', len(tree) - 2)
+        return True
+
+    def replace(self, tree):
+        if tree == (self.type, self.name):
+            return self.replacement
+        result = self.replacement
+        def getsubtree(i):
+            if tree[2][0] == 'list':
+                return tree[2][i + 1]
+            return tree[i + 2]
+        for i, v in enumerate(self.args):
+            valalias = revsetalias(('string', v), getsubtree(i))
+            result = valalias.process(result)
+        return result
+
+    def process(self, tree):
+        if self.match(tree):
+            return self.replace(tree)
+        if isinstance(tree, tuple):
+            return tuple(map(self.process, tree))
+        return tree
+
+def findaliases(ui, tree):
+    for k, v in ui.configitems('revsetalias'):
+        alias = revsetalias(k, v)
+        tree = alias.process(tree)
+    return tree
+
 parse = parser.parser(tokenize, elements).parse
 
-def match(spec):
+def match(ui, spec):
     if not spec:
         raise error.ParseError(_("empty query"))
     tree, pos = parse(spec)
     if (pos != len(spec)):
         raise error.ParseError("invalid token", pos)
+    tree = findaliases(ui, tree)
     weight, tree = optimize(tree, True)
     def mfunc(repo, subset):
         return getset(repo, subset, tree)
--- a/mercurial/scmutil.py	Sun May 01 10:51:10 2011 +0200
+++ b/mercurial/scmutil.py	Sun May 01 06:04:08 2011 -0500
@@ -17,19 +17,42 @@
 def checkportable(ui, f):
     '''Check if filename f is portable and warn or abort depending on config'''
     checkfilename(f)
-    val = ui.config('ui', 'portablefilenames', 'warn')
-    lval = val.lower()
-    abort = os.name == 'nt' or lval == 'abort'
-    bval = util.parsebool(val)
-    if abort or lval == 'warn' or bval:
+    if showportabilityalert(ui):
         msg = util.checkwinfilename(f)
         if msg:
-            if abort:
-                raise util.Abort("%s: %r" % (msg, f))
-            ui.warn(_("warning: %s: %r\n") % (msg, f))
-    elif bval is None and lval != 'ignore':
+            portabilityalert(ui, "%s: %r" % (msg, f))
+
+def checkcasecollision(ui, f, files):
+    if f.lower() in files and files[f.lower()] != f:
+        portabilityalert(ui, _('possible case-folding collision for %s') % f)
+    files[f.lower()] = f
+
+def checkportabilityalert(ui):
+    '''check if the user's config requests nothing, a warning, or abort for
+    non-portable filenames'''
+    val = ui.config('ui', 'portablefilenames', 'warn')
+    lval = val.lower()
+    bval = util.parsebool(val)
+    abort = os.name == 'nt' or lval == 'abort'
+    warn = bval or lval == 'warn'
+    if bval is None and not (warn or abort or lval == 'ignore'):
         raise error.ConfigError(
             _("ui.portablefilenames value is invalid ('%s')") % val)
+    return abort, warn
+
+def showportabilityalert(ui):
+    '''check if the user wants any notification of portability problems'''
+    abort, warn = checkportabilityalert(ui)
+    return abort or warn
+
+def portabilityalert(ui, msg):
+    if not msg:
+        return
+    abort, warn = checkportabilityalert(ui)
+    if abort:
+        raise util.Abort("%s" % msg)
+    elif warn:
+        ui.warn(_("warning: %s\n") % msg)
 
 class path_auditor(object):
     '''ensure that a filesystem path contains no banned components.
@@ -106,7 +129,28 @@
         # want to add "foo/bar/baz" before checking if there's a "foo/.hg"
         self.auditeddir.update(prefixes)
 
-class opener(object):
+class abstractopener(object):
+    """Abstract base class; cannot be instantiated"""
+
+    def __init__(self, *args, **kwargs):
+        '''Prevent instantiation; don't call this from subclasses.'''
+        raise NotImplementedError('attempted instantiating ' + str(type(self)))
+
+    def read(self, *args, **kwargs):
+        fp = self(*args, **kwargs)
+        try:
+            return fp.read()
+        finally:
+            fp.close()
+
+    def write(self, data, *args, **kwargs):
+        fp = self(*args, **kwargs)
+        try:
+            return fp.write(data)
+        finally:
+            fp.close()
+
+class opener(abstractopener):
     '''Open files relative to a base directory
 
     This class is used to hide the details of COW semantics and
@@ -201,6 +245,16 @@
             f.close()
             self._fixfilemode(dst)
 
+class filteropener(abstractopener):
+    '''Wrapper opener for filtering filenames with a function.'''
+
+    def __init__(self, opener, filter):
+        self._filter = filter
+        self._orig = opener
+
+    def __call__(self, path, *args, **kwargs):
+        return self._orig(self._filter(path), *args, **kwargs)
+
 def canonpath(root, cwd, myname, auditor=None):
     '''return the canonical path of myname, given cwd and root'''
     if util.endswithsep(root):
--- a/mercurial/sshrepo.py	Sun May 01 10:51:10 2011 +0200
+++ b/mercurial/sshrepo.py	Sun May 01 06:04:08 2011 -0500
@@ -6,7 +6,7 @@
 # GNU General Public License version 2 or any later version.
 
 from i18n import _
-import util, error, wireproto, url
+import util, error, wireproto
 
 class remotelock(object):
     def __init__(self, repo):
@@ -23,7 +23,7 @@
         self._url = path
         self.ui = ui
 
-        u = url.url(path, parsequery=False, parsefragment=False)
+        u = util.url(path, parsequery=False, parsefragment=False)
         if u.scheme != 'ssh' or not u.host or u.path is None:
             self._abort(error.RepoError(_("couldn't parse location %s") % path))
 
--- a/mercurial/statichttprepo.py	Sun May 01 10:51:10 2011 +0200
+++ b/mercurial/statichttprepo.py	Sun May 01 06:04:08 2011 -0500
@@ -9,7 +9,7 @@
 
 from i18n import _
 import changelog, byterange, url, error
-import localrepo, manifest, util, store
+import localrepo, manifest, util, scmutil, store
 import urllib, urllib2, errno
 
 class httprangereader(object):
@@ -67,17 +67,17 @@
     urlopener = url.opener(ui, authinfo)
     urlopener.add_handler(byterange.HTTPRangeHandler())
 
-    def opener(base):
-        """return a function that opens files over http"""
-        p = base
-        def o(path, mode="r", atomictemp=None):
+    class statichttpopener(scmutil.abstractopener):
+        def __init__(self, base):
+            self.base = base
+
+        def __call__(self, path, mode="r", atomictemp=None):
             if mode not in ('r', 'rb'):
                 raise IOError('Permission denied')
-            f = "/".join((p, urllib.quote(path)))
+            f = "/".join((self.base, urllib.quote(path)))
             return httprangereader(f, urlopener)
-        return o
 
-    return opener
+    return statichttpopener
 
 class statichttprepository(localrepo.localrepository):
     def __init__(self, ui, path):
@@ -85,7 +85,7 @@
         self.ui = ui
 
         self.root = path
-        u = url.url(path.rstrip('/') + "/.hg")
+        u = util.url(path.rstrip('/') + "/.hg")
         self.path, authinfo = u.authinfo()
 
         opener = build_opener(ui, authinfo)
--- a/mercurial/store.py	Sun May 01 10:51:10 2011 +0200
+++ b/mercurial/store.py	Sun May 01 06:04:08 2011 -0500
@@ -6,7 +6,7 @@
 # GNU General Public License version 2 or any later version.
 
 from i18n import _
-import osutil, util
+import osutil, scmutil, util
 import os, stat
 
 _sha = util.sha1
@@ -236,12 +236,12 @@
 
 class basicstore(object):
     '''base class for local repository stores'''
-    def __init__(self, path, opener):
+    def __init__(self, path, openertype):
         self.path = path
         self.createmode = _calcmode(path)
-        op = opener(self.path)
+        op = openertype(self.path)
         op.createmode = self.createmode
-        self.opener = lambda f, *args, **kw: op(encodedir(f), *args, **kw)
+        self.opener = scmutil.filteropener(op, encodedir)
 
     def join(self, f):
         return self.path + '/' + encodedir(f)
@@ -285,12 +285,12 @@
         pass
 
 class encodedstore(basicstore):
-    def __init__(self, path, opener):
+    def __init__(self, path, openertype):
         self.path = path + '/store'
         self.createmode = _calcmode(self.path)
-        op = opener(self.path)
+        op = openertype(self.path)
         op.createmode = self.createmode
-        self.opener = lambda f, *args, **kw: op(encodefilename(f), *args, **kw)
+        self.opener = scmutil.filteropener(op, encodefilename)
 
     def datafiles(self):
         for a, b, size in self._walk('data', True):
@@ -366,11 +366,11 @@
         return iter(self.entries)
 
 class fncachestore(basicstore):
-    def __init__(self, path, opener, encode):
+    def __init__(self, path, openertype, encode):
         self.encode = encode
         self.path = path + '/store'
         self.createmode = _calcmode(self.path)
-        op = opener(self.path)
+        op = openertype(self.path)
         op.createmode = self.createmode
         fnc = fncache(op)
         self.fncache = fnc
@@ -411,11 +411,11 @@
     def write(self):
         self.fncache.write()
 
-def store(requirements, path, opener):
+def store(requirements, path, openertype):
     if 'store' in requirements:
         if 'fncache' in requirements:
             auxencode = lambda f: _auxencode(f, 'dotencode' in requirements)
             encode = lambda f: _hybridencode(f, auxencode)
-            return fncachestore(path, opener, encode)
-        return encodedstore(path, opener)
-    return basicstore(path, opener)
+            return fncachestore(path, openertype, encode)
+        return encodedstore(path, openertype)
+    return basicstore(path, openertype)
--- a/mercurial/subrepo.py	Sun May 01 10:51:10 2011 +0200
+++ b/mercurial/subrepo.py	Sun May 01 06:04:08 2011 -0500
@@ -8,7 +8,7 @@
 import errno, os, re, xml.dom.minidom, shutil, posixpath
 import stat, subprocess, tarfile
 from i18n import _
-import config, scmutil, util, node, error, cmdutil, url, bookmarks
+import config, scmutil, util, node, error, cmdutil, bookmarks
 hg = None
 propertycache = util.propertycache
 
@@ -194,13 +194,13 @@
     """return pull/push path of repo - either based on parent repo .hgsub info
     or on the top repo config. Abort or return None if no source found."""
     if hasattr(repo, '_subparent'):
-        source = url.url(repo._subsource)
+        source = util.url(repo._subsource)
         source.path = posixpath.normpath(source.path)
         if posixpath.isabs(source.path) or source.scheme:
             return str(source)
         parent = _abssource(repo._subparent, push, abort=False)
         if parent:
-            parent = url.url(parent)
+            parent = util.url(parent)
             parent.path = posixpath.join(parent.path, source.path)
             parent.path = posixpath.normpath(parent.path)
             return str(parent)
--- a/mercurial/templatekw.py	Sun May 01 10:51:10 2011 +0200
+++ b/mercurial/templatekw.py	Sun May 01 06:04:08 2011 -0500
@@ -6,7 +6,7 @@
 # GNU General Public License version 2 or any later version.
 
 from node import hex
-import encoding, patch, util, error, help
+import patch, util, error, help
 
 def showlist(name, values, plural=None, **args):
     '''expand set of values.
--- a/mercurial/ui.py	Sun May 01 10:51:10 2011 +0200
+++ b/mercurial/ui.py	Sun May 01 06:04:08 2011 -0500
@@ -7,7 +7,7 @@
 
 from i18n import _
 import errno, getpass, os, socket, sys, tempfile, traceback
-import config, scmutil, util, error, url
+import config, scmutil, util, error
 
 class ui(object):
     def __init__(self, src=None):
@@ -111,7 +111,7 @@
                                   % (n, p, self.configsource('paths', n)))
                         p = p.replace('%%', '%')
                     p = util.expandpath(p)
-                    if not url.hasscheme(p) and not os.path.isabs(p):
+                    if not util.hasscheme(p) and not os.path.isabs(p):
                         p = os.path.normpath(os.path.join(root, p))
                     c.set("paths", n, p)
 
@@ -332,7 +332,7 @@
 
     def expandpath(self, loc, default=None):
         """Return repository location relative to cwd or from [paths]"""
-        if url.hasscheme(loc) or os.path.isdir(os.path.join(loc, '.hg')):
+        if util.hasscheme(loc) or os.path.isdir(os.path.join(loc, '.hg')):
             return loc
 
         path = self.config('paths', loc)
--- a/mercurial/url.py	Sun May 01 10:51:10 2011 +0200
+++ b/mercurial/url.py	Sun May 01 06:04:08 2011 -0500
@@ -7,309 +7,11 @@
 # This software may be used and distributed according to the terms of the
 # GNU General Public License version 2 or any later version.
 
-import urllib, urllib2, httplib, os, socket, cStringIO, re
+import urllib, urllib2, httplib, os, socket, cStringIO
 import __builtin__
 from i18n import _
 import keepalive, util
 
-class url(object):
-    """Reliable URL parser.
-
-    This parses URLs and provides attributes for the following
-    components:
-
-    <scheme>://<user>:<passwd>@<host>:<port>/<path>?<query>#<fragment>
-
-    Missing components are set to None. The only exception is
-    fragment, which is set to '' if present but empty.
-
-    If parsefragment is False, fragment is included in query. If
-    parsequery is False, query is included in path. If both are
-    False, both fragment and query are included in path.
-
-    See http://www.ietf.org/rfc/rfc2396.txt for more information.
-
-    Note that for backward compatibility reasons, bundle URLs do not
-    take host names. That means 'bundle://../' has a path of '../'.
-
-    Examples:
-
-    >>> url('http://www.ietf.org/rfc/rfc2396.txt')
-    <url scheme: 'http', host: 'www.ietf.org', path: 'rfc/rfc2396.txt'>
-    >>> url('ssh://[::1]:2200//home/joe/repo')
-    <url scheme: 'ssh', host: '[::1]', port: '2200', path: '/home/joe/repo'>
-    >>> url('file:///home/joe/repo')
-    <url scheme: 'file', path: '/home/joe/repo'>
-    >>> url('bundle:foo')
-    <url scheme: 'bundle', path: 'foo'>
-    >>> url('bundle://../foo')
-    <url scheme: 'bundle', path: '../foo'>
-    >>> url('c:\\\\foo\\\\bar')
-    <url path: 'c:\\\\foo\\\\bar'>
-
-    Authentication credentials:
-
-    >>> url('ssh://joe:xyz@x/repo')
-    <url scheme: 'ssh', user: 'joe', passwd: 'xyz', host: 'x', path: 'repo'>
-    >>> url('ssh://joe@x/repo')
-    <url scheme: 'ssh', user: 'joe', host: 'x', path: 'repo'>
-
-    Query strings and fragments:
-
-    >>> url('http://host/a?b#c')
-    <url scheme: 'http', host: 'host', path: 'a', query: 'b', fragment: 'c'>
-    >>> url('http://host/a?b#c', parsequery=False, parsefragment=False)
-    <url scheme: 'http', host: 'host', path: 'a?b#c'>
-    """
-
-    _safechars = "!~*'()+"
-    _safepchars = "/!~*'()+"
-    _matchscheme = re.compile(r'^[a-zA-Z0-9+.\-]+:').match
-
-    def __init__(self, path, parsequery=True, parsefragment=True):
-        # We slowly chomp away at path until we have only the path left
-        self.scheme = self.user = self.passwd = self.host = None
-        self.port = self.path = self.query = self.fragment = None
-        self._localpath = True
-        self._hostport = ''
-        self._origpath = path
-
-        # special case for Windows drive letters
-        if hasdriveletter(path):
-            self.path = path
-            return
-
-        # For compatibility reasons, we can't handle bundle paths as
-        # normal URLS
-        if path.startswith('bundle:'):
-            self.scheme = 'bundle'
-            path = path[7:]
-            if path.startswith('//'):
-                path = path[2:]
-            self.path = path
-            return
-
-        if self._matchscheme(path):
-            parts = path.split(':', 1)
-            if parts[0]:
-                self.scheme, path = parts
-                self._localpath = False
-
-        if not path:
-            path = None
-            if self._localpath:
-                self.path = ''
-                return
-        else:
-            if parsefragment and '#' in path:
-                path, self.fragment = path.split('#', 1)
-                if not path:
-                    path = None
-            if self._localpath:
-                self.path = path
-                return
-
-            if parsequery and '?' in path:
-                path, self.query = path.split('?', 1)
-                if not path:
-                    path = None
-                if not self.query:
-                    self.query = None
-
-            # // is required to specify a host/authority
-            if path and path.startswith('//'):
-                parts = path[2:].split('/', 1)
-                if len(parts) > 1:
-                    self.host, path = parts
-                    path = path
-                else:
-                    self.host = parts[0]
-                    path = None
-                if not self.host:
-                    self.host = None
-                    if path:
-                        path = '/' + path
-
-            if self.host and '@' in self.host:
-                self.user, self.host = self.host.rsplit('@', 1)
-                if ':' in self.user:
-                    self.user, self.passwd = self.user.split(':', 1)
-                if not self.host:
-                    self.host = None
-
-            # Don't split on colons in IPv6 addresses without ports
-            if (self.host and ':' in self.host and
-                not (self.host.startswith('[') and self.host.endswith(']'))):
-                self._hostport = self.host
-                self.host, self.port = self.host.rsplit(':', 1)
-                if not self.host:
-                    self.host = None
-
-            if (self.host and self.scheme == 'file' and
-                self.host not in ('localhost', '127.0.0.1', '[::1]')):
-                raise util.Abort(_('file:// URLs can only refer to localhost'))
-
-        self.path = path
-
-        for a in ('user', 'passwd', 'host', 'port',
-                  'path', 'query', 'fragment'):
-            v = getattr(self, a)
-            if v is not None:
-                setattr(self, a, urllib.unquote(v))
-
-    def __repr__(self):
-        attrs = []
-        for a in ('scheme', 'user', 'passwd', 'host', 'port', 'path',
-                  'query', 'fragment'):
-            v = getattr(self, a)
-            if v is not None:
-                attrs.append('%s: %r' % (a, v))
-        return '<url %s>' % ', '.join(attrs)
-
-    def __str__(self):
-        """Join the URL's components back into a URL string.
-
-        Examples:
-
-        >>> str(url('http://user:pw@host:80/?foo#bar'))
-        'http://user:pw@host:80/?foo#bar'
-        >>> str(url('ssh://user:pw@[::1]:2200//home/joe#'))
-        'ssh://user:pw@[::1]:2200//home/joe#'
-        >>> str(url('http://localhost:80//'))
-        'http://localhost:80//'
-        >>> str(url('http://localhost:80/'))
-        'http://localhost:80/'
-        >>> str(url('http://localhost:80'))
-        'http://localhost:80/'
-        >>> str(url('bundle:foo'))
-        'bundle:foo'
-        >>> str(url('bundle://../foo'))
-        'bundle:../foo'
-        >>> str(url('path'))
-        'path'
-        """
-        if self._localpath:
-            s = self.path
-            if self.scheme == 'bundle':
-                s = 'bundle:' + s
-            if self.fragment:
-                s += '#' + self.fragment
-            return s
-
-        s = self.scheme + ':'
-        if (self.user or self.passwd or self.host or
-            self.scheme and not self.path):
-            s += '//'
-        if self.user:
-            s += urllib.quote(self.user, safe=self._safechars)
-        if self.passwd:
-            s += ':' + urllib.quote(self.passwd, safe=self._safechars)
-        if self.user or self.passwd:
-            s += '@'
-        if self.host:
-            if not (self.host.startswith('[') and self.host.endswith(']')):
-                s += urllib.quote(self.host)
-            else:
-                s += self.host
-        if self.port:
-            s += ':' + urllib.quote(self.port)
-        if self.host:
-            s += '/'
-        if self.path:
-            s += urllib.quote(self.path, safe=self._safepchars)
-        if self.query:
-            s += '?' + urllib.quote(self.query, safe=self._safepchars)
-        if self.fragment is not None:
-            s += '#' + urllib.quote(self.fragment, safe=self._safepchars)
-        return s
-
-    def authinfo(self):
-        user, passwd = self.user, self.passwd
-        try:
-            self.user, self.passwd = None, None
-            s = str(self)
-        finally:
-            self.user, self.passwd = user, passwd
-        if not self.user:
-            return (s, None)
-        return (s, (None, (str(self), self.host),
-                    self.user, self.passwd or ''))
-
-    def localpath(self):
-        if self.scheme == 'file' or self.scheme == 'bundle':
-            path = self.path or '/'
-            # For Windows, we need to promote hosts containing drive
-            # letters to paths with drive letters.
-            if hasdriveletter(self._hostport):
-                path = self._hostport + '/' + self.path
-            elif self.host is not None and self.path:
-                path = '/' + path
-            # We also need to handle the case of file:///C:/, which
-            # should return C:/, not /C:/.
-            elif hasdriveletter(path):
-                # Strip leading slash from paths with drive names
-                return path[1:]
-            return path
-        return self._origpath
-
-def hasscheme(path):
-    return bool(url(path).scheme)
-
-def hasdriveletter(path):
-    return path[1:2] == ':' and path[0:1].isalpha()
-
-def localpath(path):
-    return url(path, parsequery=False, parsefragment=False).localpath()
-
-def hidepassword(u):
-    '''hide user credential in a url string'''
-    u = url(u)
-    if u.passwd:
-        u.passwd = '***'
-    return str(u)
-
-def removeauth(u):
-    '''remove all authentication information from a url string'''
-    u = url(u)
-    u.user = u.passwd = None
-    return str(u)
-
-def netlocsplit(netloc):
-    '''split [user[:passwd]@]host[:port] into 4-tuple.'''
-
-    a = netloc.find('@')
-    if a == -1:
-        user, passwd = None, None
-    else:
-        userpass, netloc = netloc[:a], netloc[a + 1:]
-        c = userpass.find(':')
-        if c == -1:
-            user, passwd = urllib.unquote(userpass), None
-        else:
-            user = urllib.unquote(userpass[:c])
-            passwd = urllib.unquote(userpass[c + 1:])
-    c = netloc.find(':')
-    if c == -1:
-        host, port = netloc, None
-    else:
-        host, port = netloc[:c], netloc[c + 1:]
-    return host, port, user, passwd
-
-def netlocunsplit(host, port, user=None, passwd=None):
-    '''turn host, port, user, passwd into [user[:passwd]@]host[:port].'''
-    if port:
-        hostport = host + ':' + port
-    else:
-        hostport = host
-    if user:
-        quote = lambda s: urllib.quote(s, safe='')
-        if passwd:
-            userpass = quote(user) + ':' + quote(passwd)
-        else:
-            userpass = quote(user)
-        return userpass + '@' + hostport
-    return hostport
-
 def readauthforuri(ui, uri):
     # Read configuration
     config = dict()
@@ -342,44 +44,6 @@
             bestauth = group, auth
     return bestauth
 
-_safe = ('abcdefghijklmnopqrstuvwxyz'
-         'ABCDEFGHIJKLMNOPQRSTUVWXYZ'
-         '0123456789' '_.-/')
-_safeset = None
-_hex = None
-def quotepath(path):
-    '''quote the path part of a URL
-
-    This is similar to urllib.quote, but it also tries to avoid
-    quoting things twice (inspired by wget):
-
-    >>> quotepath('abc def')
-    'abc%20def'
-    >>> quotepath('abc%20def')
-    'abc%20def'
-    >>> quotepath('abc%20 def')
-    'abc%20%20def'
-    >>> quotepath('abc def%20')
-    'abc%20def%20'
-    >>> quotepath('abc def%2')
-    'abc%20def%252'
-    >>> quotepath('abc def%')
-    'abc%20def%25'
-    '''
-    global _safeset, _hex
-    if _safeset is None:
-        _safeset = set(_safe)
-        _hex = set('abcdefABCDEF0123456789')
-    l = list(path)
-    for i in xrange(len(l)):
-        c = l[i]
-        if (c == '%' and i + 2 < len(l) and
-            l[i + 1] in _hex and l[i + 2] in _hex):
-            pass
-        elif c not in _safeset:
-            l[i] = '%%%02X' % ord(c)
-    return ''.join(l)
-
 class passwordmgr(urllib2.HTTPPasswordMgrWithDefaultRealm):
     def __init__(self, ui):
         urllib2.HTTPPasswordMgrWithDefaultRealm.__init__(self)
@@ -431,7 +95,7 @@
             if not (proxyurl.startswith('http:') or
                     proxyurl.startswith('https:')):
                 proxyurl = 'http://' + proxyurl + '/'
-            proxy = url(proxyurl)
+            proxy = util.url(proxyurl)
             if not proxy.user:
                 proxy.user = ui.config("http_proxy", "user")
                 proxy.passwd = ui.config("http_proxy", "passwd")
@@ -619,7 +283,7 @@
         new_tunnel = False
 
     if new_tunnel or tunnel_host == req.get_full_url(): # has proxy
-        u = url(tunnel_host)
+        u = util.url(tunnel_host)
         if new_tunnel or u.scheme == 'https': # only use CONNECT for HTTPS
             h.realhostport = ':'.join([u.host, (u.port or '443')])
             h.headers = req.headers.copy()
@@ -781,7 +445,7 @@
 
             host = self.host
             if self.realhostport: # use CONNECT proxy
-                something = _generic_proxytunnel(self)
+                _generic_proxytunnel(self)
                 host = self.realhostport.rsplit(':', 1)[0]
 
             cacerts = self.ui.config('web', 'cacerts')
@@ -950,7 +614,7 @@
     return opener
 
 def open(ui, url_, data=None):
-    u = url(url_)
+    u = util.url(url_)
     if u.scheme:
         u.scheme = u.scheme.lower()
         url_, authinfo = u.authinfo()
--- a/mercurial/util.py	Sun May 01 10:51:10 2011 +0200
+++ b/mercurial/util.py	Sun May 01 06:04:08 2011 -0500
@@ -17,7 +17,7 @@
 import error, osutil, encoding
 import errno, re, shutil, sys, tempfile, traceback
 import os, time, calendar, textwrap, unicodedata, signal
-import imp, socket
+import imp, socket, urllib
 
 # Python compatibility
 
@@ -771,6 +771,20 @@
     makedirs(parent, mode)
     makedirs(name, mode)
 
+def readfile(path):
+    fp = open(path)
+    try:
+        return fp.read()
+    finally:
+        fp.close()
+
+def writefile(path, mode, text):
+    fp = open(path, mode)
+    try:
+        fp.write(text)
+    finally:
+        fp.close()
+
 class chunkbuffer(object):
     """Allow arbitrary sized chunks of data to be efficiently read from an
     iterator over chunks of arbitrary size."""
@@ -933,7 +947,6 @@
         # fill out defaults
         now = makedate()
         defaults = {}
-        nowmap = {}
         for part in ("d", "mb", "yY", "HI", "M", "S"):
             # this piece is for rounding the specific end of unknowns
             b = bias.get(part)
@@ -1284,3 +1297,285 @@
     If s is not a valid boolean, returns None.
     """
     return _booleans.get(s.lower(), None)
+
+_hexdig = '0123456789ABCDEFabcdef'
+_hextochr = dict((a + b, chr(int(a + b, 16)))
+                 for a in _hexdig for b in _hexdig)
+
+def _urlunquote(s):
+    """unquote('abc%20def') -> 'abc def'."""
+    res = s.split('%')
+    # fastpath
+    if len(res) == 1:
+        return s
+    s = res[0]
+    for item in res[1:]:
+        try:
+            s += _hextochr[item[:2]] + item[2:]
+        except KeyError:
+            s += '%' + item
+        except UnicodeDecodeError:
+            s += unichr(int(item[:2], 16)) + item[2:]
+    return s
+
+class url(object):
+    """Reliable URL parser.
+
+    This parses URLs and provides attributes for the following
+    components:
+
+    <scheme>://<user>:<passwd>@<host>:<port>/<path>?<query>#<fragment>
+
+    Missing components are set to None. The only exception is
+    fragment, which is set to '' if present but empty.
+
+    If parsefragment is False, fragment is included in query. If
+    parsequery is False, query is included in path. If both are
+    False, both fragment and query are included in path.
+
+    See http://www.ietf.org/rfc/rfc2396.txt for more information.
+
+    Note that for backward compatibility reasons, bundle URLs do not
+    take host names. That means 'bundle://../' has a path of '../'.
+
+    Examples:
+
+    >>> url('http://www.ietf.org/rfc/rfc2396.txt')
+    <url scheme: 'http', host: 'www.ietf.org', path: 'rfc/rfc2396.txt'>
+    >>> url('ssh://[::1]:2200//home/joe/repo')
+    <url scheme: 'ssh', host: '[::1]', port: '2200', path: '/home/joe/repo'>
+    >>> url('file:///home/joe/repo')
+    <url scheme: 'file', path: '/home/joe/repo'>
+    >>> url('bundle:foo')
+    <url scheme: 'bundle', path: 'foo'>
+    >>> url('bundle://../foo')
+    <url scheme: 'bundle', path: '../foo'>
+    >>> url('c:\\\\foo\\\\bar')
+    <url path: 'c:\\\\foo\\\\bar'>
+
+    Authentication credentials:
+
+    >>> url('ssh://joe:xyz@x/repo')
+    <url scheme: 'ssh', user: 'joe', passwd: 'xyz', host: 'x', path: 'repo'>
+    >>> url('ssh://joe@x/repo')
+    <url scheme: 'ssh', user: 'joe', host: 'x', path: 'repo'>
+
+    Query strings and fragments:
+
+    >>> url('http://host/a?b#c')
+    <url scheme: 'http', host: 'host', path: 'a', query: 'b', fragment: 'c'>
+    >>> url('http://host/a?b#c', parsequery=False, parsefragment=False)
+    <url scheme: 'http', host: 'host', path: 'a?b#c'>
+    """
+
+    _safechars = "!~*'()+"
+    _safepchars = "/!~*'()+"
+    _matchscheme = re.compile(r'^[a-zA-Z0-9+.\-]+:').match
+
+    def __init__(self, path, parsequery=True, parsefragment=True):
+        # We slowly chomp away at path until we have only the path left
+        self.scheme = self.user = self.passwd = self.host = None
+        self.port = self.path = self.query = self.fragment = None
+        self._localpath = True
+        self._hostport = ''
+        self._origpath = path
+
+        # special case for Windows drive letters
+        if hasdriveletter(path):
+            self.path = path
+            return
+
+        # For compatibility reasons, we can't handle bundle paths as
+        # normal URLS
+        if path.startswith('bundle:'):
+            self.scheme = 'bundle'
+            path = path[7:]
+            if path.startswith('//'):
+                path = path[2:]
+            self.path = path
+            return
+
+        if self._matchscheme(path):
+            parts = path.split(':', 1)
+            if parts[0]:
+                self.scheme, path = parts
+                self._localpath = False
+
+        if not path:
+            path = None
+            if self._localpath:
+                self.path = ''
+                return
+        else:
+            if parsefragment and '#' in path:
+                path, self.fragment = path.split('#', 1)
+                if not path:
+                    path = None
+            if self._localpath:
+                self.path = path
+                return
+
+            if parsequery and '?' in path:
+                path, self.query = path.split('?', 1)
+                if not path:
+                    path = None
+                if not self.query:
+                    self.query = None
+
+            # // is required to specify a host/authority
+            if path and path.startswith('//'):
+                parts = path[2:].split('/', 1)
+                if len(parts) > 1:
+                    self.host, path = parts
+                    path = path
+                else:
+                    self.host = parts[0]
+                    path = None
+                if not self.host:
+                    self.host = None
+                    if path:
+                        path = '/' + path
+
+            if self.host and '@' in self.host:
+                self.user, self.host = self.host.rsplit('@', 1)
+                if ':' in self.user:
+                    self.user, self.passwd = self.user.split(':', 1)
+                if not self.host:
+                    self.host = None
+
+            # Don't split on colons in IPv6 addresses without ports
+            if (self.host and ':' in self.host and
+                not (self.host.startswith('[') and self.host.endswith(']'))):
+                self._hostport = self.host
+                self.host, self.port = self.host.rsplit(':', 1)
+                if not self.host:
+                    self.host = None
+
+            if (self.host and self.scheme == 'file' and
+                self.host not in ('localhost', '127.0.0.1', '[::1]')):
+                raise Abort(_('file:// URLs can only refer to localhost'))
+
+        self.path = path
+
+        for a in ('user', 'passwd', 'host', 'port',
+                  'path', 'query', 'fragment'):
+            v = getattr(self, a)
+            if v is not None:
+                setattr(self, a, _urlunquote(v))
+
+    def __repr__(self):
+        attrs = []
+        for a in ('scheme', 'user', 'passwd', 'host', 'port', 'path',
+                  'query', 'fragment'):
+            v = getattr(self, a)
+            if v is not None:
+                attrs.append('%s: %r' % (a, v))
+        return '<url %s>' % ', '.join(attrs)
+
+    def __str__(self):
+        """Join the URL's components back into a URL string.
+
+        Examples:
+
+        >>> str(url('http://user:pw@host:80/?foo#bar'))
+        'http://user:pw@host:80/?foo#bar'
+        >>> str(url('ssh://user:pw@[::1]:2200//home/joe#'))
+        'ssh://user:pw@[::1]:2200//home/joe#'
+        >>> str(url('http://localhost:80//'))
+        'http://localhost:80//'
+        >>> str(url('http://localhost:80/'))
+        'http://localhost:80/'
+        >>> str(url('http://localhost:80'))
+        'http://localhost:80/'
+        >>> str(url('bundle:foo'))
+        'bundle:foo'
+        >>> str(url('bundle://../foo'))
+        'bundle:../foo'
+        >>> str(url('path'))
+        'path'
+        """
+        if self._localpath:
+            s = self.path
+            if self.scheme == 'bundle':
+                s = 'bundle:' + s
+            if self.fragment:
+                s += '#' + self.fragment
+            return s
+
+        s = self.scheme + ':'
+        if (self.user or self.passwd or self.host or
+            self.scheme and not self.path):
+            s += '//'
+        if self.user:
+            s += urllib.quote(self.user, safe=self._safechars)
+        if self.passwd:
+            s += ':' + urllib.quote(self.passwd, safe=self._safechars)
+        if self.user or self.passwd:
+            s += '@'
+        if self.host:
+            if not (self.host.startswith('[') and self.host.endswith(']')):
+                s += urllib.quote(self.host)
+            else:
+                s += self.host
+        if self.port:
+            s += ':' + urllib.quote(self.port)
+        if self.host:
+            s += '/'
+        if self.path:
+            s += urllib.quote(self.path, safe=self._safepchars)
+        if self.query:
+            s += '?' + urllib.quote(self.query, safe=self._safepchars)
+        if self.fragment is not None:
+            s += '#' + urllib.quote(self.fragment, safe=self._safepchars)
+        return s
+
+    def authinfo(self):
+        user, passwd = self.user, self.passwd
+        try:
+            self.user, self.passwd = None, None
+            s = str(self)
+        finally:
+            self.user, self.passwd = user, passwd
+        if not self.user:
+            return (s, None)
+        return (s, (None, (str(self), self.host),
+                    self.user, self.passwd or ''))
+
+    def localpath(self):
+        if self.scheme == 'file' or self.scheme == 'bundle':
+            path = self.path or '/'
+            # For Windows, we need to promote hosts containing drive
+            # letters to paths with drive letters.
+            if hasdriveletter(self._hostport):
+                path = self._hostport + '/' + self.path
+            elif self.host is not None and self.path:
+                path = '/' + path
+            # We also need to handle the case of file:///C:/, which
+            # should return C:/, not /C:/.
+            elif hasdriveletter(path):
+                # Strip leading slash from paths with drive names
+                return path[1:]
+            return path
+        return self._origpath
+
+def hasscheme(path):
+    return bool(url(path).scheme)
+
+def hasdriveletter(path):
+    return path[1:2] == ':' and path[0:1].isalpha()
+
+def localpath(path):
+    return url(path, parsequery=False, parsefragment=False).localpath()
+
+def hidepassword(u):
+    '''hide user credential in a url string'''
+    u = url(u)
+    if u.passwd:
+        u.passwd = '***'
+    return str(u)
+
+def removeauth(u):
+    '''remove all authentication information from a url string'''
+    u = url(u)
+    u.user = u.passwd = None
+    return str(u)
--- a/mercurial/windows.py	Sun May 01 10:51:10 2011 +0200
+++ b/mercurial/windows.py	Sun May 01 06:04:08 2011 -0500
@@ -6,8 +6,8 @@
 # GNU General Public License version 2 or any later version.
 
 from i18n import _
-import osutil, error
-import errno, msvcrt, os, re, sys, subprocess
+import osutil
+import errno, msvcrt, os, re, sys
 
 nulldev = 'NUL:'
 umask = 002
--- a/mercurial/wireproto.py	Sun May 01 10:51:10 2011 +0200
+++ b/mercurial/wireproto.py	Sun May 01 06:04:08 2011 -0500
@@ -233,6 +233,7 @@
         else:
             caps.append('streamreqs=%s' % ','.join(requiredformats))
     caps.append('unbundle=%s' % ','.join(changegroupmod.bundlepriority))
+    caps.append('httpheader=1024')
     return ' '.join(caps)
 
 def changegroup(repo, proto, roots):
@@ -296,7 +297,7 @@
     if len(new) == 20 and new.encode('string-escape') != new:
         # looks like it could be a binary node
         try:
-            u = new.decode('utf-8')
+            new.decode('utf-8')
             new = encoding.tolocal(new) # but cleanly decodes as UTF-8
         except UnicodeDecodeError:
             pass # binary, leave unmodified
--- a/tests/run-tests.py	Sun May 01 10:51:10 2011 +0200
+++ b/tests/run-tests.py	Sun May 01 06:04:08 2011 -0500
@@ -303,6 +303,7 @@
     return missing, failed
 
 def showdiff(expected, output, ref, err):
+    print
     for line in difflib.unified_diff(expected, output, ref, err):
         sys.stdout.write(line)
 
--- a/tests/test-acl.t	Sun May 01 10:51:10 2011 +0200
+++ b/tests/test-acl.t	Sun May 01 06:04:08 2011 -0500
@@ -83,7 +83,6 @@
   """
   pushing to ../b
   searching for changes
-  common changesets up to 6675d58eff77
   3 changesets found
   list of changesets:
   ef1ea85a6374b77d6da9dcda9541f498f2d17df7
@@ -137,7 +136,6 @@
   """
   pushing to ../b
   searching for changes
-  common changesets up to 6675d58eff77
   invalidating branch cache (tip differs)
   3 changesets found
   list of changesets:
@@ -195,7 +193,6 @@
   """
   pushing to ../b
   searching for changes
-  common changesets up to 6675d58eff77
   invalidating branch cache (tip differs)
   3 changesets found
   list of changesets:
@@ -262,7 +259,6 @@
   """
   pushing to ../b
   searching for changes
-  common changesets up to 6675d58eff77
   invalidating branch cache (tip differs)
   3 changesets found
   list of changesets:
@@ -327,7 +323,6 @@
   """
   pushing to ../b
   searching for changes
-  common changesets up to 6675d58eff77
   3 changesets found
   list of changesets:
   ef1ea85a6374b77d6da9dcda9541f498f2d17df7
@@ -396,7 +391,6 @@
   """
   pushing to ../b
   searching for changes
-  common changesets up to 6675d58eff77
   3 changesets found
   list of changesets:
   ef1ea85a6374b77d6da9dcda9541f498f2d17df7
@@ -462,7 +456,6 @@
   """
   pushing to ../b
   searching for changes
-  common changesets up to 6675d58eff77
   3 changesets found
   list of changesets:
   ef1ea85a6374b77d6da9dcda9541f498f2d17df7
@@ -533,7 +526,6 @@
   """
   pushing to ../b
   searching for changes
-  common changesets up to 6675d58eff77
   3 changesets found
   list of changesets:
   ef1ea85a6374b77d6da9dcda9541f498f2d17df7
@@ -601,7 +593,6 @@
   """
   pushing to ../b
   searching for changes
-  common changesets up to 6675d58eff77
   3 changesets found
   list of changesets:
   ef1ea85a6374b77d6da9dcda9541f498f2d17df7
@@ -671,7 +662,6 @@
   """
   pushing to ../b
   searching for changes
-  common changesets up to 6675d58eff77
   3 changesets found
   list of changesets:
   ef1ea85a6374b77d6da9dcda9541f498f2d17df7
@@ -744,7 +734,6 @@
   """
   pushing to ../b
   searching for changes
-  common changesets up to 6675d58eff77
   invalidating branch cache (tip differs)
   3 changesets found
   list of changesets:
@@ -822,7 +811,6 @@
   """
   pushing to ../b
   searching for changes
-  common changesets up to 6675d58eff77
   3 changesets found
   list of changesets:
   ef1ea85a6374b77d6da9dcda9541f498f2d17df7
@@ -893,7 +881,6 @@
   """
   pushing to ../b
   searching for changes
-  common changesets up to 6675d58eff77
   3 changesets found
   list of changesets:
   ef1ea85a6374b77d6da9dcda9541f498f2d17df7
@@ -976,7 +963,6 @@
   """
   pushing to ../b
   searching for changes
-  common changesets up to 6675d58eff77
   3 changesets found
   list of changesets:
   ef1ea85a6374b77d6da9dcda9541f498f2d17df7
@@ -1050,7 +1036,6 @@
   """
   pushing to ../b
   searching for changes
-  common changesets up to 6675d58eff77
   invalidating branch cache (tip differs)
   3 changesets found
   list of changesets:
@@ -1121,7 +1106,6 @@
   """
   pushing to ../b
   searching for changes
-  common changesets up to 6675d58eff77
   invalidating branch cache (tip differs)
   3 changesets found
   list of changesets:
@@ -1195,7 +1179,6 @@
   """
   pushing to ../b
   searching for changes
-  common changesets up to 6675d58eff77
   3 changesets found
   list of changesets:
   ef1ea85a6374b77d6da9dcda9541f498f2d17df7
@@ -1266,7 +1249,6 @@
   """
   pushing to ../b
   searching for changes
-  common changesets up to 6675d58eff77
   invalidating branch cache (tip differs)
   3 changesets found
   list of changesets:
@@ -1378,7 +1360,6 @@
   """
   pushing to ../b
   searching for changes
-  common changesets up to 07e028174695
   4 changesets found
   list of changesets:
   ef1ea85a6374b77d6da9dcda9541f498f2d17df7
@@ -1456,7 +1437,6 @@
   """
   pushing to ../b
   searching for changes
-  common changesets up to 07e028174695
   invalidating branch cache (tip differs)
   4 changesets found
   list of changesets:
@@ -1533,7 +1513,6 @@
   """
   pushing to ../b
   searching for changes
-  common changesets up to 07e028174695
   4 changesets found
   list of changesets:
   ef1ea85a6374b77d6da9dcda9541f498f2d17df7
@@ -1605,7 +1584,6 @@
   """
   pushing to ../b
   searching for changes
-  common changesets up to 07e028174695
   4 changesets found
   list of changesets:
   ef1ea85a6374b77d6da9dcda9541f498f2d17df7
@@ -1671,7 +1649,6 @@
   """
   pushing to ../b
   searching for changes
-  common changesets up to 07e028174695
   4 changesets found
   list of changesets:
   ef1ea85a6374b77d6da9dcda9541f498f2d17df7
@@ -1754,7 +1731,6 @@
   """
   pushing to ../b
   searching for changes
-  common changesets up to 07e028174695
   invalidating branch cache (tip differs)
   4 changesets found
   list of changesets:
@@ -1837,7 +1813,6 @@
   """
   pushing to ../b
   searching for changes
-  common changesets up to 07e028174695
   invalidating branch cache (tip differs)
   4 changesets found
   list of changesets:
--- a/tests/test-bundle-r.t	Sun May 01 10:51:10 2011 +0200
+++ b/tests/test-bundle-r.t	Sun May 01 06:04:08 2011 -0500
@@ -184,7 +184,8 @@
 issue76 msg2163
 
   $ hg -R test bundle --base 3 -r 3 -r 3 test-bundle-cset-3.hg
-  1 changesets found
+  no changes found
+  [1]
 
 Issue1910: 'hg bundle --base $head' does not exclude $head from
 result
--- a/tests/test-bundle.t	Sun May 01 10:51:10 2011 +0200
+++ b/tests/test-bundle.t	Sun May 01 06:04:08 2011 -0500
@@ -562,7 +562,6 @@
 
   $ hg bundle bundle.hg part --debug
   searching for changes
-  common changesets up to c0025332f9ed
   2 changesets found
   list of changesets:
   d2ae7f538514cd87c17547b0de4cea71fe1af9fb
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tests/test-casecollision.t	Sun May 01 06:04:08 2011 -0500
@@ -0,0 +1,34 @@
+run only on case-sensitive filesystems
+
+  $ "$TESTDIR/hghave" no-icasefs || exit 80
+
+test file addition with colliding case
+
+  $ hg init repo1
+  $ cd repo1
+  $ echo a > a
+  $ echo A > A
+  $ hg add a
+  $ hg st
+  A a
+  ? A
+  $ hg add --config ui.portablefilenames=abort A
+  abort: possible case-folding collision for A
+  [255]
+  $ hg st
+  A a
+  ? A
+  $ hg add A
+  warning: possible case-folding collision for A
+  $ hg st
+  A A
+  A a
+  $ hg forget A
+  $ hg st
+  A a
+  ? A
+  $ hg add --config ui.portablefilenames=no A
+  $ hg st
+  A A
+  A a
+  $ cd ..
--- a/tests/test-getbundle.t	Sun May 01 10:51:10 2011 +0200
+++ b/tests/test-getbundle.t	Sun May 01 06:04:08 2011 -0500
@@ -247,7 +247,7 @@
   * - - [*] "GET /?cmd=capabilities HTTP/1.1" 200 - (glob)
   * - - [*] "GET /?cmd=getbundle HTTP/1.1" 200 - (glob)
   * - - [*] "GET /?cmd=capabilities HTTP/1.1" 200 - (glob)
-  * - - [*] "GET /?cmd=getbundle&common=c1818a9f5977dd4139a48f93f5425c67d44a9368+ea919464b16e003894c48b6cb68df3cd9411b544&heads=6b57ee934bb2996050540f84cdfc8dcad1e7267d+2114148793524fd045998f71a45b0aaf139f752b HTTP/1.1" 200 - (glob)
+  * - - [*] "GET /?cmd=getbundle HTTP/1.1" 200 - x-hgarg-1:common=c1818a9f5977dd4139a48f93f5425c67d44a9368+ea919464b16e003894c48b6cb68df3cd9411b544&heads=6b57ee934bb2996050540f84cdfc8dcad1e7267d+2114148793524fd045998f71a45b0aaf139f752b (glob)
 
   $ cat error.log
 
--- a/tests/test-glog.t	Sun May 01 10:51:10 2011 +0200
+++ b/tests/test-glog.t	Sun May 01 06:04:08 2011 -0500
@@ -1393,3 +1393,38 @@
   | | |  date:        Thu Jan 01 00:00:32 1970 +0000
   | | |  summary:     (32) expand
   | | |
+
+Test log -G options
+
+  $ hg log -G -u 'something nice'
+  $ hg log -G -b 'something nice'
+  abort: unknown revision 'something nice'!
+  [255]
+  $ hg log -G -k 'something nice'
+  $ hg log -G --only-branch 'something nice'
+  abort: unknown revision 'something nice'!
+  [255]
+  $ hg log -G --include 'some file' --exclude 'another file'
+  $ hg log -G --follow  --template 'nodetag {rev}\n' | grep nodetag | wc -l
+  \s*36 (re)
+  $ hg log -G --removed --template 'nodetag {rev}\n' | grep nodetag | wc -l
+  \s*0 (re)
+  $ hg log -G --only-merges --template 'nodetag {rev}\n' | grep nodetag | wc -l
+  \s*28 (re)
+  $ hg log -G --no-merges --template 'nodetag {rev}\n' | grep nodetag | wc -l
+  \s*9 (re)
+  $ hg log -G -d 'brace ) in a date'
+  abort: invalid date: 'brace ) in a date'
+  [255]
+  $ hg log -G -P 32 --template '{rev}\n'
+  @  36
+  |
+  o  35
+  |
+  o  34
+  |
+  | o  33
+  | |
+  $ hg log -G --follow a
+  abort: -G/--graph option is incompatible with --follow with file argument
+  [255]
--- a/tests/test-hgweb-commands.t	Sun May 01 10:51:10 2011 +0200
+++ b/tests/test-hgweb-commands.t	Sun May 01 06:04:08 2011 -0500
@@ -952,7 +952,7 @@
   $ "$TESTDIR/get-with-headers.py" 127.0.0.1:$HGPORT '?cmd=capabilities'; echo
   200 Script output follows
   
-  lookup changegroupsubset branchmap pushkey known getbundle unbundlehash unbundle=HG10GZ,HG10BZ,HG10UN
+  lookup changegroupsubset branchmap pushkey known getbundle unbundlehash unbundle=HG10GZ,HG10BZ,HG10UN httpheader=1024
 
 heads
 
--- a/tests/test-hook.t	Sun May 01 10:51:10 2011 +0200
+++ b/tests/test-hook.t	Sun May 01 06:04:08 2011 -0500
@@ -168,6 +168,61 @@
   update hook: HG_ERROR=0 HG_PARENT1=539e4b31b6dc 
   2 files updated, 0 files merged, 0 files removed, 0 files unresolved
 
+pushkey hook
+
+  $ echo 'pushkey = python "$TESTDIR"/printenv.py pushkey' >> .hg/hgrc
+  $ cd ../b
+  $ hg bookmark -r null foo
+  $ hg push -B foo ../a
+  pushing to ../a
+  searching for changes
+  no changes found
+  exporting bookmark foo
+  pushkey hook: HG_KEY=foo HG_NAMESPACE=bookmarks HG_NEW=0000000000000000000000000000000000000000 HG_RET=1 
+  $ cd ../a
+
+listkeys hook
+
+  $ echo 'listkeys = python "$TESTDIR"/printenv.py listkeys' >> .hg/hgrc
+  $ hg bookmark -r null bar
+  $ cd ../b
+  $ hg pull -B bar ../a
+  pulling from ../a
+  listkeys hook: HG_NAMESPACE=bookmarks HG_VALUES={'bar': '0000000000000000000000000000000000000000', 'foo': '0000000000000000000000000000000000000000'} 
+  searching for changes
+  listkeys hook: HG_NAMESPACE=bookmarks HG_VALUES={'bar': '0000000000000000000000000000000000000000', 'foo': '0000000000000000000000000000000000000000'} 
+  importing bookmark bar
+  $ cd ../a
+
+test that prepushkey can prevent incoming keys
+
+  $ echo 'prepushkey = python "$TESTDIR"/printenv.py prepushkey.forbid 1' >> .hg/hgrc
+  $ cd ../b
+  $ hg bookmark -r null baz
+  $ hg push -B baz ../a
+  pushing to ../a
+  searching for changes
+  no changes found
+  listkeys hook: HG_NAMESPACE=bookmarks HG_VALUES={'bar': '0000000000000000000000000000000000000000', 'foo': '0000000000000000000000000000000000000000'} 
+  listkeys hook: HG_NAMESPACE=bookmarks HG_VALUES={'bar': '0000000000000000000000000000000000000000', 'foo': '0000000000000000000000000000000000000000'} 
+  exporting bookmark baz
+  prepushkey.forbid hook: HG_KEY=baz HG_NAMESPACE=bookmarks HG_NEW=0000000000000000000000000000000000000000 
+  abort: prepushkey hook exited with status 1
+  [255]
+  $ cd ../a
+
+test that prelistkeys can prevent listing keys
+
+  $ echo 'prelistkeys = python "$TESTDIR"/printenv.py prelistkeys.forbid 1' >> .hg/hgrc
+  $ hg bookmark -r null quux
+  $ cd ../b
+  $ hg pull -B quux ../a
+  pulling from ../a
+  prelistkeys.forbid hook: HG_NAMESPACE=bookmarks 
+  abort: prelistkeys hook exited with status 1
+  [255]
+  $ cd ../a
+
 prechangegroup hook can prevent incoming changes
 
   $ cd ../b
--- a/tests/test-http-proxy.t	Sun May 01 10:51:10 2011 +0200
+++ b/tests/test-http-proxy.t	Sun May 01 06:04:08 2011 -0500
@@ -100,21 +100,21 @@
   $ cat proxy.log
   * - - [*] "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/?cmd=listkeys&namespace=bookmarks HTTP/1.1" - - (glob)
+  * - - [*] "GET http://localhost:$HGPORT/?cmd=listkeys HTTP/1.1" - - x-hgarg-1:namespace=bookmarks (glob)
   * - - [*] "GET http://localhost:$HGPORT/?cmd=capabilities HTTP/1.1" - - (glob)
   * - - [*] "GET http://localhost:$HGPORT/?cmd=heads HTTP/1.1" - - (glob)
-  * - - [*] "GET http://localhost:$HGPORT/?cmd=getbundle&common=0000000000000000000000000000000000000000&heads=83180e7845de420a1bb46896fd5fe05294f8d629 HTTP/1.1" - - (glob)
-  * - - [*] "GET http://localhost:$HGPORT/?cmd=listkeys&namespace=bookmarks HTTP/1.1" - - (glob)
+  * - - [*] "GET http://localhost:$HGPORT/?cmd=getbundle HTTP/1.1" - - x-hgarg-1:common=0000000000000000000000000000000000000000&heads=83180e7845de420a1bb46896fd5fe05294f8d629 (glob)
+  * - - [*] "GET http://localhost:$HGPORT/?cmd=listkeys HTTP/1.1" - - x-hgarg-1:namespace=bookmarks (glob)
   * - - [*] "GET http://localhost:$HGPORT/?cmd=capabilities HTTP/1.1" - - (glob)
   * - - [*] "GET http://localhost:$HGPORT/?cmd=heads HTTP/1.1" - - (glob)
-  * - - [*] "GET http://localhost:$HGPORT/?cmd=getbundle&common=0000000000000000000000000000000000000000&heads=83180e7845de420a1bb46896fd5fe05294f8d629 HTTP/1.1" - - (glob)
-  * - - [*] "GET http://localhost:$HGPORT/?cmd=listkeys&namespace=bookmarks HTTP/1.1" - - (glob)
+  * - - [*] "GET http://localhost:$HGPORT/?cmd=getbundle HTTP/1.1" - - x-hgarg-1:common=0000000000000000000000000000000000000000&heads=83180e7845de420a1bb46896fd5fe05294f8d629 (glob)
+  * - - [*] "GET http://localhost:$HGPORT/?cmd=listkeys HTTP/1.1" - - x-hgarg-1:namespace=bookmarks (glob)
   * - - [*] "GET http://localhost:$HGPORT/?cmd=capabilities HTTP/1.1" - - (glob)
   * - - [*] "GET http://localhost:$HGPORT/?cmd=heads HTTP/1.1" - - (glob)
-  * - - [*] "GET http://localhost:$HGPORT/?cmd=getbundle&common=0000000000000000000000000000000000000000&heads=83180e7845de420a1bb46896fd5fe05294f8d629 HTTP/1.1" - - (glob)
-  * - - [*] "GET http://localhost:$HGPORT/?cmd=listkeys&namespace=bookmarks HTTP/1.1" - - (glob)
+  * - - [*] "GET http://localhost:$HGPORT/?cmd=getbundle HTTP/1.1" - - x-hgarg-1:common=0000000000000000000000000000000000000000&heads=83180e7845de420a1bb46896fd5fe05294f8d629 (glob)
+  * - - [*] "GET http://localhost:$HGPORT/?cmd=listkeys HTTP/1.1" - - x-hgarg-1:namespace=bookmarks (glob)
   * - - [*] "GET http://localhost:$HGPORT/?cmd=capabilities HTTP/1.1" - - (glob)
   * - - [*] "GET http://localhost:$HGPORT/?cmd=heads HTTP/1.1" - - (glob)
-  * - - [*] "GET http://localhost:$HGPORT/?cmd=getbundle&common=0000000000000000000000000000000000000000&heads=83180e7845de420a1bb46896fd5fe05294f8d629 HTTP/1.1" - - (glob)
-  * - - [*] "GET http://localhost:$HGPORT/?cmd=listkeys&namespace=bookmarks HTTP/1.1" - - (glob)
+  * - - [*] "GET http://localhost:$HGPORT/?cmd=getbundle HTTP/1.1" - - x-hgarg-1:common=0000000000000000000000000000000000000000&heads=83180e7845de420a1bb46896fd5fe05294f8d629 (glob)
+  * - - [*] "GET http://localhost:$HGPORT/?cmd=listkeys HTTP/1.1" - - x-hgarg-1:namespace=bookmarks (glob)
 
--- a/tests/test-parentrevspec.t	Sun May 01 10:51:10 2011 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,105 +0,0 @@
-
-  $ commit()
-  > {
-  >     msg=$1
-  >     p1=$2
-  >     p2=$3
-  > 
-  >     if [ "$p1" ]; then
-  >         hg up -qC $p1
-  >     fi
-  > 
-  >     if [ "$p2" ]; then
-  >         HGMERGE=true hg merge -q $p2
-  >     fi
-  > 
-  >     echo >> foo
-  > 
-  >     hg commit -qAm "$msg"
-  > }
-  $ hg init repo
-  $ cd repo
-  $ echo '[extensions]' > .hg/hgrc
-  $ echo 'parentrevspec =' >> .hg/hgrc
-  $ commit '0: add foo'
-  $ commit '1: change foo 1'
-  $ commit '2: change foo 2a'
-  $ commit '3: change foo 3a'
-  $ commit '4: change foo 2b' 1
-  $ commit '5: merge' 3 4
-  $ commit '6: change foo again'
-  $ hg log --template '{rev}:{node|short} {parents}\n'
-  6:755d1e0d79e9 
-  5:9ce2ce29723a 3:a3e00c7dbf11 4:bb4475edb621 
-  4:bb4475edb621 1:5d953a1917d1 
-  3:a3e00c7dbf11 
-  2:befc7d89d081 
-  1:5d953a1917d1 
-  0:837088b6e1d9 
-  $ echo
-  
-  $ lookup()
-  > {
-  >     for rev in "$@"; do
-  >         printf "$rev: "
-  >         hg id -nr $rev
-  >     done
-  >     true
-  > }
-  $ tipnode=`hg id -ir tip`
-
-should work with tag/branch/node/rev
-
-  $ for r in tip default $tipnode 6; do
-  >     lookup "$r^"
-  > done
-  tip^: 5
-  default^: 5
-  755d1e0d79e9^: 5
-  6^: 5
-  $ echo
-  
-
-some random lookups
-
-  $ lookup "6^^" "6^^^" "6^^^^" "6^^^^^" "6^^^^^^" "6^1" "6^2" "6^^2" "6^1^2" "6^^3"
-  6^^: 3
-  6^^^: 2
-  6^^^^: 1
-  6^^^^^: 0
-  6^^^^^^: -1
-  6^1: 5
-  6^2: hg: parse error at 1: syntax error
-  6^^2: 4
-  6^1^2: 4
-  6^^3: hg: parse error at 1: syntax error
-  $ lookup "6~" "6~1" "6~2" "6~3" "6~4" "6~5" "6~42" "6~1^2" "6~1^2~2"
-  6~: hg: parse error at 1: syntax error
-  6~1: 5
-  6~2: 3
-  6~3: 2
-  6~4: 1
-  6~5: 0
-  6~42: -1
-  6~1^2: 4
-  6~1^2~2: 0
-  $ echo
-  
-
-with a tag "6^" pointing to rev 1
-
-  $ hg tag -l -r 1 "6^"
-  $ lookup "6^" "6^1" "6~1" "6^^"
-  6^: 1
-  6^1: 5
-  6~1: 5
-  6^^: 3
-  $ echo
-  
-
-with a tag "foo^bar" pointing to rev 2
-
-  $ hg tag -l -r 2 "foo^bar"
-  $ lookup "foo^bar" "foo^bar^"
-  foo^bar: 2
-  foo^bar^: hg: parse error at 3: syntax error
--- a/tests/test-patchbomb.t	Sun May 01 10:51:10 2011 +0200
+++ b/tests/test-patchbomb.t	Sun May 01 06:04:08 2011 -0500
@@ -158,16 +158,30 @@
 
   $ hg email -m test.mbox -f quux -t foo -c bar -s test 0:tip \
   > --config extensions.progress= --config progress.assume-tty=1 \
-  > --config progress.delay=0 --config progress.refresh=0
+  > --config progress.delay=0 --config progress.refresh=0 \
+  > --config progress.width=60 2>&1 | \
+  > python $TESTDIR/filtercr.py
   This patch series consists of 2 patches.
   
   
   Write the introductory message for the patch series.
   
-  \rwriting [                                                                 ] 0/3\rwriting [                                                                 ] 0/3\r                                                                                \r\r                                                                                \r\rwriting [====================>                                            ] 1/3\rwriting [====================>                                            ] 1/3\r                                                                                \r\r                                                                                \r\rwriting [==========================================>                      ] 2/3\rwriting [==========================================>                      ] 2/3\r                                                                                \r (esc)
+  
+  writing [                                             ] 0/3
+  writing [                                             ] 0/3
+                                                              
+                                                              
+  writing [==============>                              ] 1/3
+  writing [==============>                              ] 1/3
+                                                              
+                                                              
+  writing [=============================>               ] 2/3
+  writing [=============================>               ] 2/3
+                                                              \r (esc)
   Writing [PATCH 0 of 2] test ...
   Writing [PATCH 1 of 2] a ...
   Writing [PATCH 2 of 2] b ...
+  
 
   $ cd ..
 
--- a/tests/test-push-http.t	Sun May 01 10:51:10 2011 +0200
+++ b/tests/test-push-http.t	Sun May 01 06:04:08 2011 -0500
@@ -66,6 +66,23 @@
   repository tip rolled back to revision 0 (undo serve)
   working directory now based on revision 0
 
+expect success, server lacks the httpheader capability
+
+  $ CAP=httpheader
+  $ . "$TESTDIR/notcapable"
+  $ req
+  pushing to http://localhost:$HGPORT/
+  searching for changes
+  remote: adding changesets
+  remote: adding manifests
+  remote: adding file changes
+  remote: added 1 changesets with 1 changes to 1 files
+  remote: changegroup hook: HG_NODE=ba677d0156c1196c1a699fa53f390dcfc3ce3872 HG_SOURCE=serve HG_URL=remote:http:*:  (glob)
+  % serve errors
+  $ hg rollback
+  repository tip rolled back to revision 0 (undo serve)
+  working directory now based on revision 0
+
 expect success, server lacks the unbundlehash capability
 
   $ CAP=unbundlehash
--- a/tests/test-push-warn.t	Sun May 01 10:51:10 2011 +0200
+++ b/tests/test-push-warn.t	Sun May 01 06:04:08 2011 -0500
@@ -39,7 +39,6 @@
   found new branch changeset 1c9246a22a0a
   found new changesets starting at 1c9246a22a0a
   1 total queries
-  common changesets up to d8d565842d04
   new remote heads on branch 'default'
   new remote head 1e108cc5548c
   abort: push creates new remote heads on branch 'default'!
--- a/tests/test-revset.t	Sun May 01 10:51:10 2011 +0200
+++ b/tests/test-revset.t	Sun May 01 06:04:08 2011 -0500
@@ -2,7 +2,7 @@
   $ export HGENCODING
 
   $ try() {
-  >   hg debugrevspec --debug $@
+  >   hg debugrevspec --debug "$@"
   > }
 
   $ log() {
@@ -374,3 +374,64 @@
   4
   2
   9
+
+parentrevspec
+
+  $ log 'merge()^0'
+  6
+  $ log 'merge()^'
+  5
+  $ log 'merge()^1'
+  5
+  $ log 'merge()^2'
+  4
+  $ log 'merge()^^'
+  3
+  $ log 'merge()^1^'
+  3
+  $ log 'merge()^^^'
+  1
+
+  $ log 'merge()~0'
+  6
+  $ log 'merge()~1'
+  5
+  $ log 'merge()~2'
+  3
+  $ log 'merge()~2^1'
+  1
+  $ log 'merge()~3'
+  1
+
+  $ log '(-3:tip)^'
+  4
+  6
+  8
+
+  $ log 'tip^foo'
+  hg: parse error: ^ expects a number 0, 1, or 2
+  [255]
+
+aliases:
+
+  $ echo '[revsetalias]' >> .hg/hgrc
+  $ echo 'm = merge()' >> .hg/hgrc
+  $ echo 'd($1) = reverse(sort($1, date))' >> .hg/hgrc
+  $ echo 'rs(ARG1, ARG2) = reverse(sort(ARG1, ARG2))' >> .hg/hgrc
+
+  $ try m
+  ('symbol', 'm')
+  ('func', ('symbol', 'merge'), None)
+  6
+  $ try 'd(2:5)'
+  ('func', ('symbol', 'd'), ('range', ('symbol', '2'), ('symbol', '5')))
+  ('func', ('symbol', 'reverse'), ('func', ('symbol', 'sort'), ('list', ('range', ('symbol', '2'), ('symbol', '5')), ('symbol', 'date'))))
+  4
+  5
+  3
+  2
+  $ try 'rs(2 or 3, date)'
+  ('func', ('symbol', 'rs'), ('list', ('or', ('symbol', '2'), ('symbol', '3')), ('symbol', 'date')))
+  ('func', ('symbol', 'reverse'), ('func', ('symbol', 'sort'), ('list', ('or', ('symbol', '2'), ('symbol', '3')), ('symbol', 'date'))))
+  3
+  2
--- a/tests/test-unbundlehash.t	Sun May 01 10:51:10 2011 +0200
+++ b/tests/test-unbundlehash.t	Sun May 01 06:04:08 2011 -0500
@@ -28,4 +28,4 @@
 The hash here is always the same since the remote repository only has the null head.
 
   $ cat access.log | grep unbundle
-  * - - [*] "POST /?cmd=unbundle&heads=686173686564+6768033e216468247bd031a0a2d9876d79818f8f HTTP/1.1" 200 - (glob)
+  * - - [*] "POST /?cmd=unbundle HTTP/1.1" 200 - x-hgarg-1:heads=686173686564+6768033e216468247bd031a0a2d9876d79818f8f (glob)
--- a/tests/test-url.py	Sun May 01 10:51:10 2011 +0200
+++ b/tests/test-url.py	Sun May 01 06:04:08 2011 -0500
@@ -53,7 +53,7 @@
 
 def test_url():
     """
-    >>> from mercurial.url import url
+    >>> from mercurial.util import url
 
     This tests for edge cases in url.URL's parsing algorithm. Most of
     these aren't useful for documentation purposes, so they aren't
--- a/tests/test-wireproto.t	Sun May 01 10:51:10 2011 +0200
+++ b/tests/test-wireproto.t	Sun May 01 06:04:08 2011 -0500
@@ -23,14 +23,57 @@
 
   $ hg debugwireargs http://localhost:$HGPORT/ un deux trois quatre
   un deux trois quatre None
+  $ hg debugwireargs http://localhost:$HGPORT/ \ un deux trois\  qu\ \ atre
+   un deux trois  qu  atre None
   $ hg debugwireargs http://localhost:$HGPORT/ eins zwei --four vier
   eins zwei None vier None
   $ hg debugwireargs http://localhost:$HGPORT/ eins zwei
   eins zwei None None None
   $ hg debugwireargs http://localhost:$HGPORT/ eins zwei --five fuenf
   eins zwei None None None
+  $ hg debugwireargs http://localhost:$HGPORT/ un deux trois onethousandcharactersxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
+  un deux trois onethousandcharactersxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx None
+  $ cat error.log
   $ cat access.log
   * - - [*] "GET /?cmd=capabilities HTTP/1.1" 200 - (glob)
+  * - - [*] "GET /?cmd=debugwireargs HTTP/1.1" 200 - x-hgarg-1:four=quatre&one=un&three=trois&two=deux (glob)
+  * - - [*] "GET /?cmd=debugwireargs HTTP/1.1" 200 - x-hgarg-1:four=quatre&one=un&three=trois&two=deux (glob)
+  * - - [*] "GET /?cmd=capabilities HTTP/1.1" 200 - (glob)
+  * - - [*] "GET /?cmd=debugwireargs HTTP/1.1" 200 - x-hgarg-1:four=qu++atre&one=+un&three=trois+&two=deux (glob)
+  * - - [*] "GET /?cmd=debugwireargs HTTP/1.1" 200 - x-hgarg-1:four=qu++atre&one=+un&three=trois+&two=deux (glob)
+  * - - [*] "GET /?cmd=capabilities HTTP/1.1" 200 - (glob)
+  * - - [*] "GET /?cmd=debugwireargs HTTP/1.1" 200 - x-hgarg-1:four=vier&one=eins&two=zwei (glob)
+  * - - [*] "GET /?cmd=debugwireargs HTTP/1.1" 200 - x-hgarg-1:four=vier&one=eins&two=zwei (glob)
+  * - - [*] "GET /?cmd=capabilities HTTP/1.1" 200 - (glob)
+  * - - [*] "GET /?cmd=debugwireargs HTTP/1.1" 200 - x-hgarg-1:one=eins&two=zwei (glob)
+  * - - [*] "GET /?cmd=debugwireargs HTTP/1.1" 200 - x-hgarg-1:one=eins&two=zwei (glob)
+  * - - [*] "GET /?cmd=capabilities HTTP/1.1" 200 - (glob)
+  * - - [*] "GET /?cmd=debugwireargs HTTP/1.1" 200 - x-hgarg-1:one=eins&two=zwei (glob)
+  * - - [*] "GET /?cmd=debugwireargs HTTP/1.1" 200 - x-hgarg-1:one=eins&two=zwei (glob)
+  * - - [*] "GET /?cmd=capabilities HTTP/1.1" 200 - (glob)
+  * - - [*] "GET /?cmd=debugwireargs HTTP/1.1" 200 - x-hgarg-1:four=onethousandcharactersxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx&one x-hgarg-2:=un&three=trois&two=deux (glob)
+  * - - [*] "GET /?cmd=debugwireargs HTTP/1.1" 200 - x-hgarg-1:four=onethousandcharactersxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx&one x-hgarg-2:=un&three=trois&two=deux (glob)
+
+HTTP without the httpheader capability:
+
+  $ HGRCPATH="`pwd`/repo/.hgrc"
+  $ CAP=httpheader
+  $ . "$TESTDIR/notcapable"
+
+  $ hg serve -R repo -p $HGPORT2 -d --pid-file=hg2.pid -E error2.log -A access2.log
+  $ cat hg2.pid >> $DAEMON_PIDS
+
+  $ hg debugwireargs http://localhost:$HGPORT2/ un deux trois quatre
+  un deux trois quatre None
+  $ hg debugwireargs http://localhost:$HGPORT2/ eins zwei --four vier
+  eins zwei None vier None
+  $ hg debugwireargs http://localhost:$HGPORT2/ eins zwei
+  eins zwei None None None
+  $ hg debugwireargs http://localhost:$HGPORT2/ eins zwei --five fuenf
+  eins zwei None None None
+  $ cat error2.log
+  $ cat access2.log
+  * - - [*] "GET /?cmd=capabilities HTTP/1.1" 200 - (glob)
   * - - [*] "GET /?cmd=debugwireargs&four=quatre&one=un&three=trois&two=deux HTTP/1.1" 200 - (glob)
   * - - [*] "GET /?cmd=debugwireargs&four=quatre&one=un&three=trois&two=deux HTTP/1.1" 200 - (glob)
   * - - [*] "GET /?cmd=capabilities HTTP/1.1" 200 - (glob)
--- a/tests/tinyproxy.py	Sun May 01 10:51:10 2011 +0200
+++ b/tests/tinyproxy.py	Sun May 01 06:04:08 2011 -0500
@@ -30,6 +30,12 @@
         else:
             self.__base_handle()
 
+    def log_request(self, code='-', size='-'):
+        xheaders = [h for h in self.headers.items() if h[0].startswith('x-')]
+        self.log_message('"%s" %s %s%s',
+                         self.requestline, str(code), str(size),
+                         ''.join([' %s:%s' % h for h in sorted(xheaders)]))
+
     def _connect_to(self, netloc, soc):
         i = netloc.find(':')
         if i >= 0: