changeset 8916:3df8dbf706b0

merged with crew
author Martin Geisler <mg@lazybytes.net>
date Sun, 21 Jun 2009 19:06:57 +0200
parents c57336de5c12 (current diff) d0a3eadfbdb3 (diff)
children 41ef9ea0ba66
files hgext/interhg.py mercurial/dispatch.py mercurial/localrepo.py
diffstat 59 files changed, 731 insertions(+), 487 deletions(-) [+]
line wrap: on
line diff
--- a/contrib/buildrpm	Sat Jun 20 18:58:34 2009 +0200
+++ b/contrib/buildrpm	Sun Jun 21 19:06:57 2009 +0200
@@ -1,19 +1,19 @@
 #!/bin/sh
 #
 # Build a Mercurial RPM in place.
-# Known to work on:
-# - Fedora 9
-# - Fedora 10
 #
 # Bryan O'Sullivan <bos@serpentine.com>
+#
+# Tested on
+# - Fedora 10
+# - Fedora 11
+# - Centos 5.3 (with Fedora EPEL repo for asciidoc)
 
-if hg --version > /dev/null 2>&1; then :
-else
-    echo 'hg command not available!' 1>&2
-    exit 1
-fi
+HG="`dirname $0`/../hg"
+PYTHONPATH="`dirname $0`/../mercurial/pure"
+export PYTHONPATH
 
-root="`hg root 2>/dev/null`"
+root="`$HG root 2>/dev/null`"
 specfile=contrib/mercurial.spec
 
 if [ -z "$root" ]; then
@@ -26,7 +26,7 @@
 cd "$root"
 rm -rf $rpmdir
 mkdir -p $rpmdir/RPMS
-hg clone "$root" $rpmdir/BUILD
+$HG clone "$root" $rpmdir/BUILD
 
 if [ ! -f $specfile ]; then
     echo "Cannot find $specfile!" 1>&2
@@ -35,11 +35,11 @@
 
 tmpspec=/tmp/`basename "$specfile"`.$$ # FIXME: Insecure /tmp handling
 # Use the most recent tag as the version.
-version=`hg tags | perl -e 'while(<STDIN>){if(/^(\d\S+)/){print$1;exit}}'`
+version=`$HG tags | python -c 'import sys; print [l for l in sys.stdin.readlines() if l[0].isdigit()][0].split()[0]'`
 # Compute the release number as the difference in revision numbers
 # between the tip and the most recent tag.
-release=`hg tags | perl -e 'while(<STDIN>){($tag,$id)=/^(\S+)\s+(\d+)/;if($tag eq "tip"){$tip = $id}elsif($tag=~/^\d/){print $tip-$id+1;exit}}'`
-tip=`hg -q tip`
+release=`$HG tags | python -c 'import sys; l = sys.stdin.readlines(); print int(l[0].split()[1].split(":")[0]) - int([x for x in l if x[0].isdigit()][0].split()[1].split(":")[0])'`
+tip=`$HG -q tip`
 
 # Beat up the spec file
 sed -e 's,^Source:.*,Source: /dev/null,' \
@@ -51,11 +51,11 @@
 
 cat <<EOF >> $tmpspec
 %changelog
-* `date +'%a %b %d %Y'` `hg showconfig ui.username` $version-$release
+* `date +'%a %b %d %Y'` `$HG showconfig ui.username` $version-$release
 - Automatically built via $0
 
 EOF
-hg log \
+$HG log \
      --template '* {date|rfc822date} {author}\n- {desc|firstline}\n\n' \
      .hgtags \
   | sed -e 's/^\(\* [MTWFS][a-z][a-z]\), \([0-3][0-9]\) \([A-Z][a-z][a-z]\) /\1 \3 \2 /' \
--- a/contrib/mercurial.spec	Sat Jun 20 18:58:34 2009 +0200
+++ b/contrib/mercurial.spec	Sun Jun 21 19:06:57 2009 +0200
@@ -71,6 +71,8 @@
 %{_bindir}/hg-viz
 %{_bindir}/git-rev-tree
 %{_bindir}/mercurial-convert-repo
-%{_libdir}/python%{pythonver}/site-packages/%{name}-*-py2.5.egg-info
+%if "%{?pythonver}" != "2.4"
+%{_libdir}/python%{pythonver}/site-packages/%{name}-*-py%{pythonver}.egg-info
+%endif
 %{pythonlib}
 %{hgext}
--- a/contrib/perf.py	Sat Jun 20 18:58:34 2009 +0200
+++ b/contrib/perf.py	Sun Jun 21 19:06:57 2009 +0200
@@ -1,4 +1,5 @@
 # perf.py - performance test routines
+'''helper extension to measure performance'''
 
 from mercurial import cmdutil, match, commands
 import time, os, sys
--- a/doc/hgrc.5.txt	Sat Jun 20 18:58:34 2009 +0200
+++ b/doc/hgrc.5.txt	Sun Jun 21 19:06:57 2009 +0200
@@ -142,6 +142,11 @@
     foo.password = bar
     foo.schemes = http https
 
+    bar.prefix = secure.example.org
+    bar.key = path/to/file.key
+    bar.cert = path/to/file.cert
+    bar.schemes = https
+
 Supported arguments:
 
   prefix;;
@@ -152,10 +157,17 @@
     against the URI with its scheme stripped as well, and the schemes
     argument, q.v., is then subsequently consulted.
   username;;
-    Username to authenticate with.
+    Optional. Username to authenticate with. If not given, and the
+    remote site requires basic or digest authentication, the user
+    will be prompted for it.
   password;;
-    Optional. Password to authenticate with. If not given the user
+    Optional. Password to authenticate with. If not given, and the
+    remote site requires basic or digest authentication, the user
     will be prompted for it.
+  key;;
+    Optional. PEM encoded client certificate key file.
+  cert;;
+    Optional. PEM encoded client certificate chain file.
   schemes;;
     Optional. Space separated list of URI schemes to use this
     authentication entry with. Only used if the prefix doesn't include
--- a/hgext/acl.py	Sat Jun 20 18:58:34 2009 +0200
+++ b/hgext/acl.py	Sun Jun 21 19:06:57 2009 +0200
@@ -5,49 +5,49 @@
 # This software may be used and distributed according to the terms of the
 # GNU General Public License version 2, incorporated herein by reference.
 #
-# this hook allows to allow or deny access to parts of a repo when
-# taking incoming changesets.
-#
-# authorization is against local user name on system where hook is
-# run, not committer of original changeset (since that is easy to
-# spoof).
-#
-# acl hook is best to use if you use hgsh to set up restricted shells
-# for authenticated users to only push to / pull from.  not safe if
-# user has interactive shell access, because they can disable hook.
-# also not safe if remote users share one local account, because then
-# no way to tell remote users apart.
-#
-# to use, configure acl extension in hgrc like this:
-#
-#   [extensions]
-#   hgext.acl =
-#
-#   [hooks]
-#   pretxnchangegroup.acl = python:hgext.acl.hook
-#
-#   [acl]
-#   sources = serve        # check if source of incoming changes in this list
-#                          # ("serve" == ssh or http, "push", "pull", "bundle")
-#
-# allow and deny lists have subtree pattern (default syntax is glob)
-# on left, user names on right. deny list checked before allow list.
-#
-#   [acl.allow]
-#   # if acl.allow not present, all users allowed by default
-#   # empty acl.allow = no users allowed
-#   docs/** = doc_writer
-#   .hgtags = release_engineer
-#
-#   [acl.deny]
-#   # if acl.deny not present, no users denied by default
-#   # empty acl.deny = all users allowed
-#   glob pattern = user4, user5
-#   ** = user6
+
+'''provide simple hooks for access control
+
+Authorization is against local user name on system where hook is run, not
+committer of original changeset (since that is easy to spoof).
+
+The acl hook is best to use if you use hgsh to set up restricted shells for
+authenticated users to only push to / pull from. It's not safe if user has
+interactive shell access, because they can disable the hook. It's also not
+safe if remote users share one local account, because then there's no way to
+tell remote users apart.
+
+To use, configure the acl extension in hgrc like this:
+
+  [extensions]
+  hgext.acl =
+
+  [hooks]
+  pretxnchangegroup.acl = python:hgext.acl.hook
+
+  [acl]
+  sources = serve        # check if source of incoming changes in this list
+                         # ("serve" == ssh or http, "push", "pull", "bundle")
+
+Allow and deny lists have a subtree pattern (default syntax is glob) on the
+left and user names on right. The deny list is checked before the allow list.
+
+  [acl.allow]
+  # if acl.allow not present, all users allowed by default
+  # empty acl.allow = no users allowed
+  docs/** = doc_writer
+  .hgtags = release_engineer
+
+  [acl.deny]
+  # if acl.deny not present, no users denied by default
+  # empty acl.deny = all users allowed
+  glob pattern = user4, user5
+   ** = user6
+'''
 
 from mercurial.i18n import _
 from mercurial import util, match
-import getpass
+import getpass, urllib
 
 def buildmatch(ui, repo, user, key):
     '''return tuple of (match function, list enabled).'''
@@ -72,7 +72,15 @@
         ui.debug(_('acl: changes have source "%s" - skipping\n') % source)
         return
 
-    user = getpass.getuser()
+    user = None
+    if source == 'serve' and 'url' in kwargs:
+        url = kwargs['url'].split(':')
+        if url[0] == 'remote' and url[1].startswith('http'):
+            user = urllib.unquote(url[2])
+
+    if user is None:
+        user = getpass.getuser()
+
     cfg = ui.config('acl', 'config')
     if cfg:
         ui.readconfig(cfg, sections = ['acl.allow', 'acl.deny'])
--- a/hgext/bookmarks.py	Sat Jun 20 18:58:34 2009 +0200
+++ b/hgext/bookmarks.py	Sun Jun 21 19:06:57 2009 +0200
@@ -64,10 +64,14 @@
         util.copyfile(repo.join('bookmarks'), repo.join('undo.bookmarks'))
     if current(repo) not in refs:
         setcurrent(repo, None)
-    file = repo.opener('bookmarks', 'w+')
-    for refspec, node in refs.iteritems():
-        file.write("%s %s\n" % (hex(node), refspec))
-    file.close()
+    wlock = repo.wlock()
+    try:
+        file = repo.opener('bookmarks', 'w', atomictemp=True)
+        for refspec, node in refs.iteritems():
+            file.write("%s %s\n" % (hex(node), refspec))
+        file.rename()
+    finally:
+        wlock.release()
 
 def current(repo):
     '''Get the current bookmark
@@ -106,9 +110,13 @@
         return
     if mark not in refs:
         mark = ''
-    file = repo.opener('bookmarks.current', 'w+')
-    file.write(mark)
-    file.close()
+    wlock = repo.wlock()
+    try:
+        file = repo.opener('bookmarks.current', 'w', atomictemp=True)
+        file.write(mark)
+        file.rename()
+    finally:
+        wlock.release()
     repo._bookmarkcurrent = mark
 
 def bookmark(ui, repo, mark=None, rev=None, force=False, delete=False, rename=None):
@@ -242,26 +250,30 @@
         def commit(self, *k, **kw):
             """Add a revision to the repository and
             move the bookmark"""
-            node  = super(bookmark_repo, self).commit(*k, **kw)
-            if node is None:
-                return None
-            parents = repo.changelog.parents(node)
-            if parents[1] == nullid:
-                parents = (parents[0],)
-            marks = parse(repo)
-            update = False
-            for mark, n in marks.items():
-                if ui.configbool('bookmarks', 'track.current'):
-                    if mark == current(repo) and n in parents:
-                        marks[mark] = node
-                        update = True
-                else:
-                    if n in parents:
-                        marks[mark] = node
-                        update = True
-            if update:
-                write(repo, marks)
-            return node
+            wlock = self.wlock() # do both commit and bookmark with lock held
+            try:
+                node  = super(bookmark_repo, self).commit(*k, **kw)
+                if node is None:
+                    return None
+                parents = repo.changelog.parents(node)
+                if parents[1] == nullid:
+                    parents = (parents[0],)
+                marks = parse(repo)
+                update = False
+                for mark, n in marks.items():
+                    if ui.configbool('bookmarks', 'track.current'):
+                        if mark == current(repo) and n in parents:
+                            marks[mark] = node
+                            update = True
+                    else:
+                        if n in parents:
+                            marks[mark] = node
+                            update = True
+                if update:
+                    write(repo, marks)
+                return node
+            finally:
+                wlock.release()
 
         def addchangegroup(self, source, srctype, url, emptyok=False):
             parents = repo.dirstate.parents()
--- a/hgext/children.py	Sat Jun 20 18:58:34 2009 +0200
+++ b/hgext/children.py	Sun Jun 21 19:06:57 2009 +0200
@@ -8,6 +8,8 @@
 # This software may be used and distributed according to the terms of the
 # GNU General Public License version 2, incorporated herein by reference.
 
+'''provides children command to show children changesets'''
+
 from mercurial import cmdutil
 from mercurial.commands import templateopts
 from mercurial.i18n import _
--- a/hgext/churn.py	Sat Jun 20 18:58:34 2009 +0200
+++ b/hgext/churn.py	Sun Jun 21 19:06:57 2009 +0200
@@ -120,7 +120,7 @@
 
     It is possible to map alternate email addresses to a main address
     by providing a file using the following format:
-    
+
     <alias email> <actual email>
 
     Such a file may be specified with the --aliases option, otherwise a
--- a/hgext/color.py	Sat Jun 20 18:58:34 2009 +0200
+++ b/hgext/color.py	Sun Jun 21 19:06:57 2009 +0200
@@ -29,10 +29,6 @@
 function (aka ANSI escape codes). This module also provides the
 render_text function, which can be used to add effects to any text.
 
-To enable this extension, add this to your .hgrc file:
-[extensions]
-color =
-
 Default effects may be overridden from the .hgrc file:
 
 [color]
--- a/hgext/convert/convcmd.py	Sat Jun 20 18:58:34 2009 +0200
+++ b/hgext/convert/convcmd.py	Sun Jun 21 19:06:57 2009 +0200
@@ -80,7 +80,7 @@
         self.authorfile = None
 
         # Record converted revisions persistently: maps source revision
-        # ID to target revision ID (both strings).  (This is how 
+        # ID to target revision ID (both strings).  (This is how
         # incremental conversions work.)
         self.map = mapfile(ui, revmapfile)
 
@@ -297,7 +297,7 @@
             parents = [self.map.get(p, p) for p in parents]
         except KeyError:
             parents = [b[0] for b in pbranches]
-        newnode = self.dest.putcommit(files, copies, parents, commit, 
+        newnode = self.dest.putcommit(files, copies, parents, commit,
                                       self.source, self.map)
         self.source.converted(rev, newnode)
         self.map[rev] = newnode
--- a/hgext/convert/p4.py	Sat Jun 20 18:58:34 2009 +0200
+++ b/hgext/convert/p4.py	Sun Jun 21 19:06:57 2009 +0200
@@ -159,7 +159,7 @@
 
             if code == "error":
                 raise IOError(d["generic"], data)
-            
+
             elif code == "stat":
                 p4type = self.re_type.match(d["type"])
                 if p4type:
@@ -173,7 +173,7 @@
                         keywords = self.re_keywords_old
                     elif "k" in flags:
                         keywords = self.re_keywords
-            
+
             elif code == "text" or code == "binary":
                 contents += data
 
--- a/hgext/convert/subversion.py	Sat Jun 20 18:58:34 2009 +0200
+++ b/hgext/convert/subversion.py	Sun Jun 21 19:06:57 2009 +0200
@@ -472,7 +472,7 @@
                 # Here/tags/tag.1 discarded as well as its children.
                 # It happens with tools like cvs2svn. Such tags cannot
                 # be represented in mercurial.
-                addeds = dict((p, e.copyfrom_path) for p,e 
+                addeds = dict((p, e.copyfrom_path) for p, e
                               in origpaths.iteritems() if e.action == 'A')
                 badroots = set()
                 for destroot in addeds:
@@ -484,7 +484,7 @@
                         break
 
                 for badroot in badroots:
-                    pendings = [p for p in pendings if p[2] != badroot 
+                    pendings = [p for p in pendings if p[2] != badroot
                                 and not p[2].startswith(badroot + '/')]
 
                 # Tell tag renamings from tag creations
@@ -497,7 +497,7 @@
                     if tagname in tags:
                         # Keep the latest tag value
                         continue
-                    # From revision may be fake, get one with changes                    
+                    # From revision may be fake, get one with changes
                     try:
                         tagid = self.latest(source, sourcerev)
                         if tagid and tagname not in tags:
--- a/hgext/extdiff.py	Sat Jun 20 18:58:34 2009 +0200
+++ b/hgext/extdiff.py	Sun Jun 21 19:06:57 2009 +0200
@@ -5,18 +5,14 @@
 # This software may be used and distributed according to the terms of the
 # GNU General Public License version 2, incorporated herein by reference.
 
-'''
+'''allow external programs to compare revisions
+
 The `extdiff' Mercurial extension allows you to use external programs
 to compare revisions, or revision with working directory. The external diff
 programs are called with a configurable set of options and two
 non-option arguments: paths to directories containing snapshots of
 files to compare.
 
-To enable this extension:
-
-  [extensions]
-  hgext.extdiff =
-
 The `extdiff' extension also allows to configure new diff commands, so
 you do not need to type "hg extdiff -p kdiff3" always.
 
--- a/hgext/gpg.py	Sat Jun 20 18:58:34 2009 +0200
+++ b/hgext/gpg.py	Sun Jun 21 19:06:57 2009 +0200
@@ -1,10 +1,10 @@
-# GnuPG signing extension for Mercurial
-#
 # Copyright 2005, 2006 Benoit Boissinot <benoit.boissinot@ens-lyon.org>
 #
 # This software may be used and distributed according to the terms of the
 # GNU General Public License version 2, incorporated herein by reference.
 
+'''GnuPG signing extension for Mercurial'''
+
 import os, tempfile, binascii
 from mercurial import util, commands, match
 from mercurial import node as hgnode
--- a/hgext/graphlog.py	Sat Jun 20 18:58:34 2009 +0200
+++ b/hgext/graphlog.py	Sun Jun 21 19:06:57 2009 +0200
@@ -12,59 +12,32 @@
 revision graph is also shown.
 '''
 
-import os
+import os, sys
 from mercurial.cmdutil import revrange, show_changeset
 from mercurial.commands import templateopts
 from mercurial.i18n import _
 from mercurial.node import nullrev
 from mercurial import bundlerepo, changegroup, cmdutil, commands, extensions
-from mercurial import hg, url, util
-
-def revisions(repo, start, stop):
-    """cset DAG generator yielding (rev, node, [parents]) tuples
+from mercurial import hg, url, util, graphmod
 
-    This generator function walks through the revision history from revision
-    start to revision stop (which must be less than or equal to start).
-    """
-    assert start >= stop
-    cur = start
-    while cur >= stop:
-        ctx = repo[cur]
-        parents = [p.rev() for p in ctx.parents() if p.rev() != nullrev]
-        parents.sort()
-        yield (ctx, parents)
-        cur -= 1
-
-def filerevs(repo, path, start, stop):
-    """file cset DAG generator yielding (rev, node, [parents]) tuples
+ASCIIDATA = 'ASC'
 
-    This generator function walks through the revision history of a single
-    file from revision start to revision stop (which must be less than or
-    equal to start).
-    """
-    assert start >= stop
-    filerev = len(repo.file(path)) - 1
-    while filerev >= 0:
-        fctx = repo.filectx(path, fileid=filerev)
-        parents = [f.linkrev() for f in fctx.parents() if f.path() == path]
-        parents.sort()
-        if fctx.rev() <= start:
-            yield (fctx, parents)
-        if fctx.rev() <= stop:
-            break
-        filerev -= 1
+def asciiformat(ui, repo, revdag, opts):
+    """formats a changelog DAG walk for ASCII output"""
+    showparents = [ctx.node() for ctx in repo[None].parents()]
+    displayer = show_changeset(ui, repo, opts, buffered=True)
+    for (id, type, ctx, parentids) in revdag:
+        if type != graphmod.CHANGESET:
+            continue
+        displayer.show(ctx)
+        lines = displayer.hunk.pop(ctx.rev()).split('\n')[:-1]
+        char = ctx.node() in showparents and '@' or 'o'
+        yield (id, ASCIIDATA, (char, lines), parentids)
 
-def grapher(nodes):
-    """grapher for asciigraph on a list of nodes and their parents
-
-    nodes must generate tuples (node, parents, char, lines) where
-     - parents must generate the parents of node, in sorted order,
-       and max length 2,
-     - char is the char to print as the node symbol, and
-     - lines are the lines to display next to the node.
-    """
+def asciiedges(nodes):
+    """adds edge info to changelog DAG walk suitable for ascii()"""
     seen = []
-    for node, parents, char, lines in nodes:
+    for node, type, data, parents in nodes:
         if node not in seen:
             seen.append(node)
         nodeidx = seen.index(node)
@@ -88,7 +61,7 @@
             edges.append((nodeidx, nodeidx + 1))
         nmorecols = len(nextseen) - ncols
         seen = nextseen
-        yield (char, lines, nodeidx, edges, ncols, nmorecols)
+        yield (nodeidx, type, data, edges, ncols, nmorecols)
 
 def fix_long_right_edges(edges):
     for (i, (start, end)) in enumerate(edges):
@@ -142,14 +115,16 @@
     line.extend(["|", " "] * (n_columns - ni - 1))
     return line
 
-def ascii(ui, grapher):
-    """prints an ASCII graph of the DAG returned by the grapher
+def ascii(ui, dag):
+    """prints an ASCII graph of the DAG
+
+    dag is a generator that emits tuples with the following elements:
 
-    grapher is a generator that emits tuples with the following elements:
-
-      - Character to use as node's symbol.
-      - List of lines to display as the node's text.
       - Column of the current node in the set of ongoing edges.
+      - Type indicator of node data == ASCIIDATA.
+      - Payload: (char, lines):
+        - Character to use as node's symbol.
+        - List of lines to display as the node's text.
       - Edges; a list of (col, next_col) indicating the edges between
         the current node and its parents.
       - Number of columns (ongoing edges) in the current revision.
@@ -160,7 +135,7 @@
     """
     prev_n_columns_diff = 0
     prev_node_index = 0
-    for (node_ch, node_lines, node_index, edges, n_columns, n_columns_diff) in grapher:
+    for (node_index, type, (node_ch, node_lines), edges, n_columns, n_columns_diff) in dag:
 
         assert -2 < n_columns_diff < 2
         if n_columns_diff == -1:
@@ -278,34 +253,19 @@
     if path:
         path = util.canonpath(repo.root, os.getcwd(), path)
     if path: # could be reset in canonpath
-        revdag = filerevs(repo, path, start, stop)
+        revdag = graphmod.filerevs(repo, path, start, stop)
     else:
-        revdag = revisions(repo, start, stop)
+        revdag = graphmod.revisions(repo, start, stop)
 
-    graphdag = graphabledag(ui, repo, revdag, opts)
-    ascii(ui, grapher(graphdag))
+    fmtdag = asciiformat(ui, repo, revdag, opts)
+    ascii(ui, asciiedges(fmtdag))
 
 def graphrevs(repo, nodes, opts):
-    include = set(nodes)
     limit = cmdutil.loglimit(opts)
-    count = 0
-    for node in reversed(nodes):
-        if count >= limit:
-            break
-        ctx = repo[node]
-        parents = [p.rev() for p in ctx.parents() if p.node() in include]
-        parents.sort()
-        yield (ctx, parents)
-        count += 1
-
-def graphabledag(ui, repo, revdag, opts):
-    showparents = [ctx.node() for ctx in repo[None].parents()]
-    displayer = show_changeset(ui, repo, opts, buffered=True)
-    for (ctx, parents) in revdag:
-        displayer.show(ctx)
-        lines = displayer.hunk.pop(ctx.rev()).split('\n')[:-1]
-        char = ctx.node() in showparents and '@' or 'o'
-        yield (ctx.rev(), parents, char, lines)
+    nodes.reverse()
+    if limit < sys.maxint:
+        nodes = nodes[:limit]
+    return graphmod.nodes(repo, nodes)
 
 def goutgoing(ui, repo, dest=None, **opts):
     """show the outgoing changesets alongside an ASCII revision graph
@@ -332,8 +292,8 @@
 
     o = repo.changelog.nodesbetween(o, revs)[0]
     revdag = graphrevs(repo, o, opts)
-    graphdag = graphabledag(ui, repo, revdag, opts)
-    ascii(ui, grapher(graphdag))
+    fmtdag = asciiformat(ui, repo, revdag, opts)
+    ascii(ui, asciiedges(fmtdag))
 
 def gincoming(ui, repo, source="default", **opts):
     """show the incoming changesets alongside an ASCII revision graph
@@ -381,8 +341,8 @@
 
         chlist = other.changelog.nodesbetween(incoming, revs)[0]
         revdag = graphrevs(other, chlist, opts)
-        graphdag = graphabledag(ui, repo, revdag, opts)
-        ascii(ui, grapher(graphdag))
+        fmtdag = asciiformat(ui, repo, revdag, opts)
+        ascii(ui, asciiedges(fmtdag))
 
     finally:
         if hasattr(other, 'close'):
--- a/hgext/hgk.py	Sat Jun 20 18:58:34 2009 +0200
+++ b/hgext/hgk.py	Sun Jun 21 19:06:57 2009 +0200
@@ -14,20 +14,8 @@
 hgk consists of two parts: a Tcl script that does the displaying and
 querying of information, and an extension to Mercurial named hgk.py,
 which provides hooks for hgk to get information. hgk can be found in
-the contrib directory, and hgk.py can be found in the hgext directory.
-
-To load the hgext.py extension, add it to your .hgrc file (you have to
-use your global $HOME/.hgrc file, not one in a repository). You can
-specify an absolute path:
-
-  [extensions]
-  hgk=/usr/local/lib/hgk.py
-
-Mercurial can also scan the default python library path for a file
-named 'hgk.py' if you set hgk empty:
-
-  [extensions]
-  hgk=
+the contrib directory, and the extension is shipped in the hgext
+repository, and needs to be enabled.
 
 The hg view command will launch the hgk Tcl script. For this command
 to work, hgk must be in your search path. Alternately, you can specify
--- a/hgext/highlight/__init__.py	Sat Jun 20 18:58:34 2009 +0200
+++ b/hgext/highlight/__init__.py	Sun Jun 21 19:06:57 2009 +0200
@@ -13,11 +13,6 @@
 It depends on the Pygments syntax highlighting library:
 http://pygments.org/
 
-To enable the extension add this to hgrc:
-
-[extensions]
-hgext.highlight =
-
 There is a single configuration option:
 
 [web]
@@ -30,10 +25,10 @@
 
 import highlight
 from mercurial.hgweb import webcommands, webutil, common
-from mercurial import extensions
+from mercurial import extensions, encoding
 
 def filerevision_highlight(orig, web, tmpl, fctx):
-    mt = ''.join(tmpl('mimetype', encoding=web.encoding))
+    mt = ''.join(tmpl('mimetype', encoding=encoding.encoding))
     # only pygmentize for mimetype containing 'html' so we both match
     # 'text/html' and possibly 'application/xhtml+xml' in the future
     # so that we don't have to touch the extension when the mimetype
@@ -47,7 +42,7 @@
     return orig(web, tmpl, fctx)
 
 def annotate_highlight(orig, web, req, tmpl):
-    mt = ''.join(tmpl('mimetype', encoding=web.encoding))
+    mt = ''.join(tmpl('mimetype', encoding=encoding.encoding))
     if 'html' in mt:
         fctx = webutil.filectx(web.repo, req)
         style = web.config('web', 'pygments_style', 'colorful')
--- a/hgext/interhg.py	Sat Jun 20 18:58:34 2009 +0200
+++ b/hgext/interhg.py	Sun Jun 21 19:06:57 2009 +0200
@@ -14,12 +14,8 @@
 which will be automatically expanded into links or any other
 arbitrary expression, much like InterWiki does.
 
-To enable this extension, add the following lines to your hgrc:
-
-  [extensions]
-  interhg =
-
-A few example patterns (link to bug tracking, etc.):
+A few example patterns (link to bug tracking, etc.) that may
+be used in your hgrc:
 
   [interhg]
   issues = s!issue(\\d+)!<a href="http://bts/issue\\1">issue\\1</a>!
--- a/hgext/keyword.py	Sat Jun 20 18:58:34 2009 +0200
+++ b/hgext/keyword.py	Sun Jun 21 19:06:57 2009 +0200
@@ -21,12 +21,6 @@
 #
 # Binary files are not touched.
 #
-# Setup in hgrc:
-#
-#   [extensions]
-#   # enable extension
-#   hgext.keyword =
-#
 # Files to act upon/ignore are specified in the [keyword] section.
 # Customized keyword template mappings in the [keywordmaps] section.
 #
--- a/hgext/mq.py	Sat Jun 20 18:58:34 2009 +0200
+++ b/hgext/mq.py	Sun Jun 21 19:06:57 2009 +0200
@@ -543,6 +543,8 @@
 
     def _apply(self, repo, series, list=False, update_status=True,
                strict=False, patchdir=None, merge=None, all_files={}):
+        '''returns (error, hash)
+        error = 1 for unable to read, 2 for patch failed, 3 for patch fuzz'''
         # TODO unify with commands.py
         if not patchdir:
             patchdir = self.path
@@ -559,7 +561,7 @@
             try:
                 ph = patchheader(self.join(patchname))
             except:
-                self.ui.warn(_("Unable to read %s\n") % patchname)
+                self.ui.warn(_("unable to read %s\n") % patchname)
                 err = 1
                 break
 
@@ -607,46 +609,60 @@
 
             if patcherr:
                 self.ui.warn(_("patch failed, rejects left in working dir\n"))
-                err = 1
+                err = 2
                 break
 
             if fuzz and strict:
                 self.ui.warn(_("fuzz found when applying patch, stopping\n"))
-                err = 1
+                err = 3
                 break
         return (err, n)
 
-    def _clean_series(self, patches):
+    def _cleanup(self, patches, numrevs, keep=False):
+        if not keep:
+            r = self.qrepo()
+            if r:
+                r.remove(patches, True)
+            else:
+                for p in patches:
+                    os.unlink(self.join(p))
+
+        if numrevs:
+            del self.applied[:numrevs]
+            self.applied_dirty = 1
+
         for i in sorted([self.find_series(p) for p in patches], reverse=True):
             del self.full_series[i]
         self.parse_series()
         self.series_dirty = 1
 
-    def finish(self, repo, revs):
+    def _revpatches(self, repo, revs):
         firstrev = repo[self.applied[0].rev].rev()
-        appliedbase = 0
         patches = []
-        for rev in sorted(revs):
+        for i, rev in enumerate(revs):
+
             if rev < firstrev:
                 raise util.Abort(_('revision %d is not managed') % rev)
-            base = bin(self.applied[appliedbase].rev)
-            node = repo.changelog.node(rev)
-            if node != base:
-                raise util.Abort(_('cannot delete revision %d above '
-                                   'applied patches') % rev)
-            patches.append(self.applied[appliedbase].name)
-            appliedbase += 1
+
+            ctx = repo[rev]
+            base = bin(self.applied[i].rev)
+            if ctx.node() != base:
+                msg = _('cannot delete revision %d above applied patches')
+                raise util.Abort(msg % rev)
 
-        r = self.qrepo()
-        if r:
-            r.remove(patches, True)
-        else:
-            for p in patches:
-                os.unlink(self.join(p))
+            patch = self.applied[i].name
+            for fmt in ('[mq]: %s', 'imported patch %s'):
+                if ctx.description() == fmt % patch:
+                    msg = _('patch %s finalized without changeset message\n')
+                    repo.ui.status(msg % patch)
+                    break
 
-        del self.applied[:appliedbase]
-        self.applied_dirty = 1
-        self._clean_series(patches)
+            patches.append(patch)
+        return patches
+
+    def finish(self, repo, revs):
+        patches = self._revpatches(repo, sorted(revs))
+        self._cleanup(patches, len(patches))
 
     def delete(self, repo, patches, opts):
         if not patches and not opts.get('rev'):
@@ -663,37 +679,18 @@
                 raise util.Abort(_("patch %s not in series file") % patch)
             realpatches.append(patch)
 
-        appliedbase = 0
+        numrevs = 0
         if opts.get('rev'):
             if not self.applied:
                 raise util.Abort(_('no patches applied'))
             revs = cmdutil.revrange(repo, opts['rev'])
             if len(revs) > 1 and revs[0] > revs[1]:
                 revs.reverse()
-            for rev in revs:
-                if appliedbase >= len(self.applied):
-                    raise util.Abort(_("revision %d is not managed") % rev)
-
-                base = bin(self.applied[appliedbase].rev)
-                node = repo.changelog.node(rev)
-                if node != base:
-                    raise util.Abort(_("cannot delete revision %d above "
-                                       "applied patches") % rev)
-                realpatches.append(self.applied[appliedbase].name)
-                appliedbase += 1
+            revpatches = self._revpatches(repo, revs)
+            realpatches += revpatches
+            numrevs = len(revpatches)
 
-        if not opts.get('keep'):
-            r = self.qrepo()
-            if r:
-                r.remove(realpatches, True)
-            else:
-                for p in realpatches:
-                    os.unlink(self.join(p))
-
-        if appliedbase:
-            del self.applied[:appliedbase]
-            self.applied_dirty = 1
-        self._clean_series(realpatches)
+        self._cleanup(realpatches, numrevs, opts.get('keep'))
 
     def check_toppatch(self, repo):
         if len(self.applied) > 0:
@@ -958,6 +955,7 @@
                 end = start + 1
             else:
                 end = self.series.index(patch, start) + 1
+
             s = self.series[start:end]
             all_files = {}
             try:
@@ -977,13 +975,15 @@
                         util.unlink(repo.wjoin(f))
                 self.ui.warn(_('done\n'))
                 raise
+
             top = self.applied[-1].name
-            if ret[0]:
-                self.ui.write(_("errors during apply, please fix and "
-                                "refresh %s\n") % top)
+            if ret[0] and ret[0] > 1:
+                msg = _("errors during apply, please fix and refresh %s\n")
+                self.ui.write(msg % top)
             else:
                 self.ui.write(_("now at: %s\n") % top)
             return ret[0]
+
         finally:
             wlock.release()
 
--- a/hgext/parentrevspec.py	Sat Jun 20 18:58:34 2009 +0200
+++ b/hgext/parentrevspec.py	Sun Jun 21 19:06:57 2009 +0200
@@ -5,8 +5,7 @@
 # This software may be used and distributed according to the terms of the
 # GNU General Public License version 2, incorporated herein by reference.
 
-'''\
-use suffixes to refer to ancestor revisions
+'''use suffixes to refer to ancestor revisions
 
 This extension allows you to use git-style suffixes to refer to the
 ancestors of a specific revision.
--- a/hgext/patchbomb.py	Sat Jun 20 18:58:34 2009 +0200
+++ b/hgext/patchbomb.py	Sun Jun 21 19:06:57 2009 +0200
@@ -28,11 +28,6 @@
 with a diffstat summary and the changeset summary, so you can be sure
 you are sending the right changes.
 
-To enable this extension:
-
-  [extensions]
-  hgext.patchbomb =
-
 To configure other defaults, add a section like this to your hgrc
 file:
 
--- a/hgext/purge.py	Sat Jun 20 18:58:34 2009 +0200
+++ b/hgext/purge.py	Sun Jun 21 19:06:57 2009 +0200
@@ -6,10 +6,6 @@
 # This program was inspired by the "cvspurge" script contained in CVS utilities
 # (http://www.red-bean.com/cvsutils/).
 #
-# To enable the "purge" extension put these lines in your ~/.hgrc:
-#  [extensions]
-#  hgext.purge =
-#
 # For help on the usage of "hg purge" use:
 #  hg help purge
 #
@@ -27,6 +23,8 @@
 # along with this program; if not, write to the Free Software
 # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
 
+'''enable removing untracked files only'''
+
 from mercurial import util, commands, cmdutil
 from mercurial.i18n import _
 import os, stat
--- a/hgext/share.py	Sat Jun 20 18:58:34 2009 +0200
+++ b/hgext/share.py	Sun Jun 21 19:06:57 2009 +0200
@@ -1,10 +1,10 @@
-# Mercurial extension to provide the 'hg share' command
-#
 # Copyright 2006, 2007 Matt Mackall <mpm@selenic.com>
 #
 # This software may be used and distributed according to the terms of the
 # GNU General Public License version 2, incorporated herein by reference.
 
+'''provides the hg share command'''
+
 import os
 from mercurial.i18n import _
 from mercurial import hg, commands
--- a/hgext/win32mbcs.py	Sat Jun 20 18:58:34 2009 +0200
+++ b/hgext/win32mbcs.py	Sun Jun 21 19:06:57 2009 +0200
@@ -33,11 +33,6 @@
  * You should set same encoding for the repository by locale or
    HGENCODING.
 
-To use this extension, enable the extension in .hg/hgrc or ~/.hgrc:
-
-  [extensions]
-  hgext.win32mbcs =
-
 Path encoding conversion are done between Unicode and
 encoding.encoding which is decided by Mercurial from current locale
 setting or HGENCODING.
--- a/hgext/win32text.py	Sat Jun 20 18:58:34 2009 +0200
+++ b/hgext/win32text.py	Sun Jun 21 19:06:57 2009 +0200
@@ -4,31 +4,34 @@
 #
 # This software may be used and distributed according to the terms of the
 # GNU General Public License version 2, incorporated herein by reference.
-#
-# To perform automatic newline conversion, use:
-#
-# [extensions]
-# hgext.win32text =
-# [encode]
-# ** = cleverencode:
-# # or ** = macencode:
-# [decode]
-# ** = cleverdecode:
-# # or ** = macdecode:
-#
-# If not doing conversion, to make sure you do not commit CRLF/CR by
-# accident:
-#
-# [hooks]
-# pretxncommit.crlf = python:hgext.win32text.forbidcrlf
-# # or pretxncommit.cr = python:hgext.win32text.forbidcr
-#
-# To do the same check on a server to prevent CRLF/CR from being
-# pushed or pulled:
-#
-# [hooks]
-# pretxnchangegroup.crlf = python:hgext.win32text.forbidcrlf
-# # or pretxnchangegroup.cr = python:hgext.win32text.forbidcr
+
+'''LF <-> CRLF/CR translation utilities
+
+To perform automatic newline conversion, use:
+
+[extensions]
+hgext.win32text =
+[encode]
+** = cleverencode:
+# or ** = macencode:
+
+[decode]
+** = cleverdecode:
+# or ** = macdecode:
+
+If not doing conversion, to make sure you do not commit CRLF/CR by accident:
+
+[hooks]
+pretxncommit.crlf = python:hgext.win32text.forbidcrlf
+# or pretxncommit.cr = python:hgext.win32text.forbidcr
+
+To do the same check on a server to prevent CRLF/CR from being
+pushed or pulled:
+
+[hooks]
+pretxnchangegroup.crlf = python:hgext.win32text.forbidcrlf
+# or pretxnchangegroup.cr = python:hgext.win32text.forbidcr
+'''
 
 from mercurial.i18n import _
 from mercurial.node import short
--- a/hgext/zeroconf/__init__.py	Sat Jun 20 18:58:34 2009 +0200
+++ b/hgext/zeroconf/__init__.py	Sun Jun 21 19:06:57 2009 +0200
@@ -11,12 +11,6 @@
 the need to configure a server or a service. They can be discovered
 without knowing their actual IP address.
 
-To use the zeroconf extension add the following entry to your hgrc
-file:
-
-[extensions]
-hgext.zeroconf =
-
 To allow other people to discover your repository using run "hg serve"
 in your repository.
 
--- a/mercurial/bdiff.c	Sat Jun 20 18:58:34 2009 +0200
+++ b/mercurial/bdiff.c	Sun Jun 21 19:06:57 2009 +0200
@@ -18,6 +18,10 @@
 # define inline
 #endif
 
+#ifdef __linux
+# define inline __inline
+#endif
+
 #ifdef _WIN32
 #ifdef _MSC_VER
 #define inline __inline
--- a/mercurial/commands.py	Sat Jun 20 18:58:34 2009 +0200
+++ b/mercurial/commands.py	Sun Jun 21 19:06:57 2009 +0200
@@ -1230,16 +1230,14 @@
                 for i in xrange(blo, bhi):
                     yield ('+', b[i])
 
-    prev = {}
-    def display(fn, rev, states, prevstates):
+    def display(fn, r, pstates, states):
         datefunc = ui.quiet and util.shortdate or util.datestr
         found = False
         filerevmatches = {}
-        r = prev.get(fn, -1)
         if opts.get('all'):
-            iter = difflinestates(states, prevstates)
+            iter = difflinestates(pstates, states)
         else:
-            iter = [('', l) for l in prevstates]
+            iter = [('', l) for l in states]
         for change, l in iter:
             cols = [fn, str(r)]
             if opts.get('line_number'):
@@ -1261,8 +1259,8 @@
             found = True
         return found
 
-    fstate = {}
     skip = {}
+    revfiles = {}
     get = util.cachefunc(lambda r: repo[r].changeset())
     changeiter, matchfn = cmdutil.walkchangerevs(ui, repo, pats, get, opts)
     found = False
@@ -1270,46 +1268,58 @@
     for st, rev, fns in changeiter:
         if st == 'window':
             matches.clear()
+            revfiles.clear()
         elif st == 'add':
             ctx = repo[rev]
-            matches[rev] = {}
+            pctx = ctx.parents()[0]
+            parent = pctx.rev()
+            matches.setdefault(rev, {})
+            matches.setdefault(parent, {})
+            files = revfiles.setdefault(rev, [])
             for fn in fns:
-                if fn in skip:
-                    continue
+                flog = getfile(fn)
                 try:
-                    grepbody(fn, rev, getfile(fn).read(ctx.filenode(fn)))
-                    fstate.setdefault(fn, [])
-                    if follow:
-                        copied = getfile(fn).renamed(ctx.filenode(fn))
-                        if copied:
-                            copies.setdefault(rev, {})[fn] = copied[0]
+                    fnode = ctx.filenode(fn)
                 except error.LookupError:
-                    pass
+                    continue
+
+                copied = flog.renamed(fnode)
+                copy = follow and copied and copied[0]
+                if copy:
+                    copies.setdefault(rev, {})[fn] = copy
+                if fn in skip:
+                    if copy:
+                        skip[copy] = True
+                    continue
+                files.append(fn)
+
+                if not matches[rev].has_key(fn):
+                    grepbody(fn, rev, flog.read(fnode))
+
+                pfn = copy or fn
+                if not matches[parent].has_key(pfn):
+                    try:
+                        fnode = pctx.filenode(pfn)
+                        grepbody(pfn, parent, flog.read(fnode))
+                    except error.LookupError:
+                        pass
         elif st == 'iter':
-            for fn, m in sorted(matches[rev].items()):
+            parent = repo[rev].parents()[0].rev()
+            for fn in sorted(revfiles.get(rev, [])):
+                states = matches[rev][fn]
                 copy = copies.get(rev, {}).get(fn)
                 if fn in skip:
                     if copy:
                         skip[copy] = True
                     continue
-                if fn in prev or fstate[fn]:
-                    r = display(fn, rev, m, fstate[fn])
+                pstates = matches.get(parent, {}).get(copy or fn, [])
+                if pstates or states:
+                    r = display(fn, rev, pstates, states)
                     found = found or r
                     if r and not opts.get('all'):
                         skip[fn] = True
                         if copy:
                             skip[copy] = True
-                fstate[fn] = m
-                if copy:
-                    fstate[copy] = m
-                prev[fn] = rev
-
-    for fn, state in sorted(fstate.items()):
-        if fn in skip:
-            continue
-        if fn not in copies.get(prev[fn], {}):
-            found = display(fn, rev, {}, state) or found
-    return (not found and 1) or 0
 
 def heads(ui, repo, *branchrevs, **opts):
     """show current repository heads or show branch heads
@@ -1473,18 +1483,9 @@
             else:
                 ui.write(' %-*s   %s\n' % (m, f, h[f]))
 
-        exts = list(extensions.extensions())
-        if exts and name != 'shortlist':
-            ui.write(_('\nenabled extensions:\n\n'))
-            maxlength = 0
-            exthelps = []
-            for ename, ext in exts:
-                doc = (gettext(ext.__doc__) or _('(no help text available)'))
-                ename = ename.split('.')[-1]
-                maxlength = max(len(ename), maxlength)
-                exthelps.append((ename, doc.splitlines(0)[0].strip()))
-            for ename, text in exthelps:
-                ui.write(_(' %s   %s\n') % (ename.ljust(maxlength), text))
+        if name != 'shortlist':
+            exts, maxlength = extensions.enabled()
+            ui.write(help.listexts(_('enabled extensions:'), exts, maxlength))
 
         if not ui.quiet:
             addglobalopts(True)
@@ -2108,7 +2109,7 @@
                                'use "hg update" or merge with an explicit rev'))
         node = parent == bheads[0] and bheads[-1] or bheads[0]
 
-    if opts.get('show'):
+    if opts.get('preview'):
         p1 = repo['.']
         p2 = repo[node]
         common = p1.ancestor(p2)
@@ -2670,7 +2671,8 @@
     This command should be used with care. There is only one level of
     rollback, and there is no way to undo a rollback. It will also
     restore the dirstate at the time of the last transaction, losing
-    any dirstate changes since that time.
+    any dirstate changes since that time. This command does not alter
+    the working directory.
 
     Transactions are used to encapsulate the effects of all commands
     that create new changesets or propagate existing changesets into a
@@ -2718,9 +2720,9 @@
 
     baseui = repo and repo.baseui or ui
     optlist = ("name templates style address port prefix ipv6"
-               " accesslog errorlog webdir_conf certificate")
+               " accesslog errorlog webdir_conf certificate encoding")
     for o in optlist.split():
-        if opts[o]:
+        if opts.get(o, None):
             baseui.setconfig("web", o, str(opts[o]))
             if (repo is not None) and (repo.ui != baseui):
                 repo.ui.setconfig("web", o, str(opts[o]))
@@ -2965,7 +2967,7 @@
 
     return postincoming(ui, repo, modheads, opts.get('update'), None)
 
-def update(ui, repo, node=None, rev=None, clean=False, date=None):
+def update(ui, repo, node=None, rev=None, clean=False, date=None, check=False):
     """update working directory
 
     Update the repository's working directory to the specified
@@ -2981,7 +2983,8 @@
 
     When there are uncommitted changes, use option -C/--clean to
     discard them, forcibly replacing the state of the working
-    directory with the requested revision.
+    directory with the requested revision. Alternately, use -c/--check
+    to abort.
 
     When there are uncommitted changes and option -C/--clean is not
     used, and the parent revision and requested revision are on the
@@ -3001,6 +3004,12 @@
     if not rev:
         rev = node
 
+    if not clean and check:
+        # we could use dirty() but we can ignore merge and branch trivia
+        c = repo[None]
+        if c.modified() or c.added() or c.removed():
+            raise util.Abort(_("uncommitted local changes"))
+
     if date:
         if rev:
             raise util.Abort(_("you can't specify a revision and a date"))
@@ -3358,7 +3367,7 @@
         (merge,
          [('f', 'force', None, _('force a merge with outstanding changes')),
           ('r', 'rev', '', _('revision to merge')),
-          ('S', 'show', None,
+          ('P', 'preview', None,
            _('review revisions to merge (no merge is performed)'))],
          _('[-f] [[-r] REV]')),
     "outgoing|out":
@@ -3492,6 +3501,7 @@
     "^update|up|checkout|co":
         (update,
          [('C', 'clean', None, _('overwrite locally modified files (no backup)')),
+          ('c', 'check', None, _('check for uncommitted changes')),
           ('d', 'date', '', _('tipmost revision matching date')),
           ('r', 'rev', '', _('revision'))],
          _('[-C] [-d DATE] [[-r] REV]')),
--- a/mercurial/dispatch.py	Sat Jun 20 18:58:34 2009 +0200
+++ b/mercurial/dispatch.py	Sun Jun 21 19:06:57 2009 +0200
@@ -224,7 +224,6 @@
     # but only if they have been defined prior to the current definition.
     for alias, definition in ui.configitems('alias'):
         aliasdef = cmdalias(alias, definition, cmdtable)
-        
         cmdtable[alias] = (aliasdef, aliasdef.opts, aliasdef.help)
         if aliasdef.norepo:
             commands.norepo += ' %s' % alias
--- a/mercurial/extensions.py	Sat Jun 20 18:58:34 2009 +0200
+++ b/mercurial/extensions.py	Sun Jun 21 19:06:57 2009 +0200
@@ -5,9 +5,9 @@
 # This software may be used and distributed according to the terms of the
 # GNU General Public License version 2, incorporated herein by reference.
 
-import imp, os
-import util, cmdutil
-from i18n import _
+import imp, os, sys
+import util, cmdutil, help
+from i18n import _, gettext
 
 _extensions = {}
 _order = []
@@ -117,3 +117,61 @@
     origfn = getattr(container, funcname)
     setattr(container, funcname, wrap)
     return origfn
+
+def disabled():
+    '''find disabled extensions from hgext
+    returns a dict of {name: desc}, and the max name length'''
+
+    import hgext
+    extpath = os.path.dirname(os.path.abspath(hgext.__file__))
+
+    exts = {}
+    maxlength = 0
+    for e in os.listdir(extpath):
+
+        if e.endswith('.py'):
+            name = e.rsplit('.', 1)[0]
+            path = os.path.join(extpath, e)
+        else:
+            name = e
+            path = os.path.join(extpath, e, '__init__.py')
+            if not os.path.exists(path):
+                continue
+
+        if name in exts or name in _order or name == '__init__':
+            continue
+
+        try:
+            file = open(path)
+        except IOError:
+            continue
+        else:
+            doc = help.moduledoc(file)
+            file.close()
+
+        if doc: # extracting localized synopsis
+            exts[name] = gettext(doc).splitlines()[0]
+        else:
+            exts[name] = _('(no help text available)')
+
+        if len(name) > maxlength:
+            maxlength = len(name)
+
+    return exts, maxlength
+
+def enabled():
+    '''return a dict of {name: desc} of extensions, and the max name length'''
+
+    if not enabled:
+        return {}, 0
+
+    exts = {}
+    maxlength = 0
+    exthelps = []
+    for ename, ext in extensions():
+        doc = (gettext(ext.__doc__) or _('(no help text available)'))
+        ename = ename.split('.')[-1]
+        maxlength = max(len(ename), maxlength)
+        exts[ename] = doc.splitlines(0)[0].strip()
+
+    return exts, maxlength
--- a/mercurial/filemerge.py	Sat Jun 20 18:58:34 2009 +0200
+++ b/mercurial/filemerge.py	Sun Jun 21 19:06:57 2009 +0200
@@ -195,8 +195,8 @@
     elif tool == 'internal:dump':
         a = repo.wjoin(fd)
         util.copyfile(a, a + ".local")
-        repo.wwrite(a + ".other", fco.data(), fco.flags())
-        repo.wwrite(a + ".base", fca.data(), fca.flags())
+        repo.wwrite(fd + ".other", fco.data(), fco.flags())
+        repo.wwrite(fd + ".base", fca.data(), fca.flags())
         return 1 # unresolved
     else:
         args = _toolstr(ui, tool, "args", '$local $base $other')
--- a/mercurial/graphmod.py	Sat Jun 20 18:58:34 2009 +0200
+++ b/mercurial/graphmod.py	Sun Jun 21 19:06:57 2009 +0200
@@ -6,70 +6,114 @@
 # This software may be used and distributed according to the terms of the
 # GNU General Public License version 2, incorporated herein by reference.
 
-from node import nullrev
+"""supports walking the history as DAGs suitable for graphical output
 
-def graph(repo, start_rev, stop_rev):
-    """incremental revision grapher
+The most basic format we use is that of::
+
+  (id, type, data, [parentids])
 
-    This generator function walks through the revision history from
-    revision start_rev to revision stop_rev (which must be less than
-    or equal to start_rev) and for each revision emits tuples with the
-    following elements:
+The node and parent ids are arbitrary integers which identify a node in the
+context of the graph returned. Type is a constant specifying the node type.
+Data depends on type.
+"""
+
+from mercurial.node import nullrev
+
+CHANGESET = 'C'
 
-      - Current node
-      - Column and color for the current node
-      - Edges; a list of (col, next_col, color) indicating the edges between
-        the current node and its parents.
-      - First line of the changeset description
-      - The changeset author
-      - The changeset date/time
+def revisions(repo, start, stop):
+    """cset DAG generator yielding (id, CHANGESET, ctx, [parentids]) tuples
+
+    This generator function walks through the revision history from revision
+    start to revision stop (which must be less than or equal to start). It
+    returns a tuple for each node. The node and parent ids are arbitrary
+    integers which identify a node in the context of the graph returned.
     """
+    cur = start
+    while cur >= stop:
+        ctx = repo[cur]
+        parents = [p.rev() for p in ctx.parents() if p.rev() != nullrev]
+        yield (cur, CHANGESET, ctx, sorted(parents))
+        cur -= 1
 
-    if start_rev == nullrev and not stop_rev:
-        return
+def filerevs(repo, path, start, stop):
+    """file cset DAG generator yielding (id, CHANGESET, ctx, [parentids]) tuples
 
-    assert start_rev >= stop_rev
-    assert stop_rev >= 0
-    curr_rev = start_rev
-    revs = []
-    cl = repo.changelog
-    colors = {}
-    new_color = 1
+    This generator function walks through the revision history of a single
+    file from revision start down to revision stop.
+    """
+    filerev = len(repo.file(path)) - 1
+    while filerev >= 0:
+        fctx = repo.filectx(path, fileid=filerev)
+        parents = [f.linkrev() for f in fctx.parents() if f.path() == path]
+        rev = fctx.rev()
+        if rev <= start:
+            yield (rev, CHANGESET, fctx, sorted(parents))
+        if rev <= stop:
+            break
+        filerev -= 1
+
+def nodes(repo, nodes):
+    """cset DAG generator yielding (id, CHANGESET, ctx, [parentids]) tuples
+
+    This generator function walks the given nodes. It only returns parents
+    that are in nodes, too.
+    """
+    include = set(nodes)
+    for node in nodes:
+        ctx = repo[node]
+        parents = [p.rev() for p in ctx.parents() if p.node() in include]
+        yield (ctx.rev(), CHANGESET, ctx, sorted(parents))
+
+def colored(dag):
+    """annotates a DAG with colored edge information
 
-    while curr_rev >= stop_rev:
-        # Compute revs and next_revs
-        if curr_rev not in revs:
-            revs.append(curr_rev) # new head
-            colors[curr_rev] = new_color
-            new_color += 1
+    For each DAG node this function emits tuples::
+
+      (id, type, data, (col, color), [(col, nextcol, color)])
+
+    with the following new elements:
 
-        idx = revs.index(curr_rev)
-        color = colors.pop(curr_rev)
-        next = revs[:]
+      - Tuple (col, color) with column and color index for the current node
+      - A list of tuples indicating the edges between the current node and its
+        parents.
+    """
+    seen = []
+    colors = {}
+    newcolor = 1
+    for (cur, type, data, parents) in dag:
 
-        # Add parents to next_revs
-        parents = [x for x in cl.parentrevs(curr_rev) if x != nullrev]
+        # Compute seen and next
+        if cur not in seen:
+            seen.append(cur) # new head
+            colors[cur] = newcolor
+            newcolor += 1
+
+        col = seen.index(cur)
+        color = colors.pop(cur)
+        next = seen[:]
+
+        # Add parents to next
         addparents = [p for p in parents if p not in next]
-        next[idx:idx + 1] = addparents
+        next[col:col + 1] = addparents
 
         # Set colors for the parents
         for i, p in enumerate(addparents):
             if not i:
                 colors[p] = color
             else:
-                colors[p] = new_color
-                new_color += 1
+                colors[p] = newcolor
+                newcolor += 1
 
         # Add edges to the graph
         edges = []
-        for col, r in enumerate(revs):
-            if r in next:
-                edges.append((col, next.index(r), colors[r]))
-            elif r == curr_rev:
+        for ecol, eid in enumerate(seen):
+            if eid in next:
+                edges.append((ecol, next.index(eid), colors[eid]))
+            elif eid == cur:
                 for p in parents:
-                    edges.append((col, next.index(p), colors[p]))
+                    edges.append((ecol, next.index(p), colors[p]))
 
         # Yield and move on
-        yield (repo[curr_rev], (idx, color), edges)
-        revs = next
-        curr_rev -= 1
+        yield (cur, type, data, (col, color), edges)
+        seen = next
--- a/mercurial/help.py	Sat Jun 20 18:58:34 2009 +0200
+++ b/mercurial/help.py	Sun Jun 21 19:06:57 2009 +0200
@@ -6,6 +6,90 @@
 # GNU General Public License version 2, incorporated herein by reference.
 
 from i18n import _
+import extensions
+
+
+def moduledoc(file):
+    '''return the top-level python documentation for the given file
+
+    Loosely inspired by pydoc.source_synopsis(), but rewritten to handle \'''
+    as well as """ and to return the whole text instead of just the synopsis'''
+    result = []
+
+    line = file.readline()
+    while line[:1] == '#' or not line.strip():
+        line = file.readline()
+        if not line: break
+
+    start = line[:3]
+    if start == '"""' or start == "'''":
+        line = line[3:]
+        while line:
+            if line.rstrip().endswith(start):
+                line = line.split(start)[0]
+                if line:
+                    result.append(line)
+                break
+            elif not line:
+                return None # unmatched delimiter
+            result.append(line)
+            line = file.readline()
+    else:
+        return None
+
+    return ''.join(result)
+
+def listexts(header, exts, maxlength):
+    '''return a text listing of the given extensions'''
+    if not exts:
+        return ''
+    result = '\n%s\n\n' % header
+    for name, desc in sorted(exts.iteritems()):
+        result += ' %s   %s\n' % (name.ljust(maxlength), desc)
+    return result
+
+def extshelp():
+    doc = _(r'''
+    Mercurial has a mechanism for adding new features through the
+    use of extensions. Extensions may bring new commands, or new
+    hooks, or change Mercurial's behavior.
+
+    Extensions are not loaded by default for a variety of reasons,
+    they may be meant for advanced users or provide potentially
+    dangerous commands (e.g. mq and rebase allow history to be
+    rewritten), they might not be ready for prime-time yet, or
+    they may alter Mercurial's behavior. It is thus up to the user
+    to activate extensions as desired.
+
+    To enable the "foo" extension, either shipped with Mercurial
+    or in the Python search path, create an entry for it in your
+    hgrc, like this:
+
+      [extensions]
+      foo =
+
+    You may also specify the full path to an extension:
+
+      [extensions]
+      myfeature = ~/.hgext/myfeature.py
+
+    To explicitly disable an extension enabled in an hgrc of broader
+    scope, prepend its path with !:
+
+      [extensions]
+      # disabling extension bar residing in /ext/path
+      hgext.bar = !/path/to/extension/bar.py
+      # ditto, but no path was supplied for extension baz
+      hgext.baz = !
+    ''')
+
+    exts, maxlength = extensions.enabled()
+    doc += listexts(_('enabled extensions:'), exts, maxlength)
+
+    exts, maxlength = extensions.disabled()
+    doc += listexts(_('disabled extensions:'), exts, maxlength)
+
+    return doc
 
 helptable = (
     (["dates"], _("Date Formats"),
@@ -418,4 +502,5 @@
       The push command will look for a path named 'default-push', and
       prefer it over 'default' if both are defined.
     ''')),
+    (["extensions"], _("Using additional features"), extshelp),
 )
--- a/mercurial/hgweb/hgweb_mod.py	Sat Jun 20 18:58:34 2009 +0200
+++ b/mercurial/hgweb/hgweb_mod.py	Sun Jun 21 19:06:57 2009 +0200
@@ -64,7 +64,8 @@
             self.maxshortchanges = int(self.config("web", "maxshortchanges", 60))
             self.maxfiles = int(self.config("web", "maxfiles", 10))
             self.allowpull = self.configbool("web", "allowpull", True)
-            self.encoding = self.config("web", "encoding", encoding.encoding)
+            encoding.encoding = self.config("web", "encoding",
+                                            encoding.encoding)
 
     def run(self):
         if not os.environ.get('GATEWAY_INTERFACE', '').startswith("CGI/1."):
@@ -81,28 +82,6 @@
 
         self.refresh()
 
-        # process this if it's a protocol request
-        # protocol bits don't need to create any URLs
-        # and the clients always use the old URL structure
-
-        cmd = req.form.get('cmd', [''])[0]
-        if cmd and cmd in protocol.__all__:
-            try:
-                if cmd in perms:
-                    try:
-                        self.check_perm(req, perms[cmd])
-                    except ErrorResponse, inst:
-                        if cmd == 'unbundle':
-                            req.drain()
-                        raise
-                method = getattr(protocol, cmd)
-                return method(self.repo, req)
-            except ErrorResponse, inst:
-                req.respond(inst, protocol.HGTYPE)
-                if not inst.message:
-                    return []
-                return '0\n%s\n' % inst.message,
-
         # work with CGI variables to create coherent structure
         # use SCRIPT_NAME, PATH_INFO and QUERY_STRING as well as our REPO_NAME
 
@@ -122,6 +101,30 @@
             query = req.env['QUERY_STRING'].split('&', 1)[0]
             query = query.split(';', 1)[0]
 
+        # process this if it's a protocol request
+        # protocol bits don't need to create any URLs
+        # and the clients always use the old URL structure
+
+        cmd = req.form.get('cmd', [''])[0]
+        if cmd and cmd in protocol.__all__:
+            if query:
+                raise ErrorResponse(HTTP_NOT_FOUND)
+            try:
+                if cmd in perms:
+                    try:
+                        self.check_perm(req, perms[cmd])
+                    except ErrorResponse, inst:
+                        if cmd == 'unbundle':
+                            req.drain()
+                        raise
+                method = getattr(protocol, cmd)
+                return method(self.repo, req)
+            except ErrorResponse, inst:
+                req.respond(inst, protocol.HGTYPE)
+                if not inst.message:
+                    return []
+                return '0\n%s\n' % inst.message,
+
         # translate user-visible url structure to internal structure
 
         args = query.split('/', 2)
@@ -160,7 +163,7 @@
 
         try:
             tmpl = self.templater(req)
-            ctype = tmpl('mimetype', encoding=self.encoding)
+            ctype = tmpl('mimetype', encoding=encoding.encoding)
             ctype = templater.stringify(ctype)
 
             # check read permissions non-static content
@@ -219,7 +222,7 @@
         # some functions for the templater
 
         def header(**map):
-            yield tmpl('header', encoding=self.encoding, **map)
+            yield tmpl('header', encoding=encoding.encoding, **map)
 
         def footer(**map):
             yield tmpl("footer", **map)
--- a/mercurial/hgweb/hgwebdir_mod.py	Sat Jun 20 18:58:34 2009 +0200
+++ b/mercurial/hgweb/hgwebdir_mod.py	Sun Jun 21 19:06:57 2009 +0200
@@ -70,6 +70,8 @@
         elif isinstance(self.conf, dict):
             paths = self.conf.items()
 
+        encoding.encoding = self.ui.config('web', 'encoding',
+                                           encoding.encoding)
         self.motd = self.ui.config('web', 'motd')
         self.style = self.ui.config('web', 'style', 'paper')
         self.stripecount = self.ui.config('web', 'stripes', 1)
--- a/mercurial/hgweb/protocol.py	Sat Jun 20 18:58:34 2009 +0200
+++ b/mercurial/hgweb/protocol.py	Sun Jun 21 19:06:57 2009 +0200
@@ -162,8 +162,10 @@
                 sys.stderr = sys.stdout = cStringIO.StringIO()
 
                 try:
-                    url = 'remote:%s:%s' % (proto,
-                                            req.env.get('REMOTE_HOST', ''))
+                    url = 'remote:%s:%s:%s' % (
+                          proto,
+                          urllib.quote(req.env.get('REMOTE_HOST', '')),
+                          urllib.quote(req.env.get('REMOTE_USER', '')))
                     try:
                         ret = repo.addchangegroup(gen, 'serve', url)
                     except util.Abort, inst:
--- a/mercurial/hgweb/webcommands.py	Sat Jun 20 18:58:34 2009 +0200
+++ b/mercurial/hgweb/webcommands.py	Sun Jun 21 19:06:57 2009 +0200
@@ -668,10 +668,13 @@
     count = len(web.repo)
     changenav = webutil.revnavgen(rev, revcount, count, web.repo.changectx)
 
-    tree = list(graphmod.graph(web.repo, rev, downrev))
+    dag = graphmod.revisions(web.repo, rev, downrev)
+    tree = list(graphmod.colored(dag))
     canvasheight = (len(tree) + 1) * bg_height - 27;
     data = []
-    for (ctx, vtx, edges) in tree:
+    for (id, type, ctx, vtx, edges) in tree:
+        if type != graphmod.CHANGESET:
+            continue
         node = short(ctx.node())
         age = templatefilters.age(ctx.date())
         desc = templatefilters.firstline(ctx.description())
--- a/mercurial/localrepo.py	Sat Jun 20 18:58:34 2009 +0200
+++ b/mercurial/localrepo.py	Sun Jun 21 19:06:57 2009 +0200
@@ -262,7 +262,7 @@
                     warn(_("node '%s' is not well formed") % node)
                     continue
                 if bin_n not in self.changelog.nodemap:
-                    warn(_("tag '%s' refers to unknown node") % key)
+                    # silently ignore as pull -r might cause this
                     continue
 
                 h = []
@@ -290,11 +290,24 @@
                 globaltags[k] = an, ah
                 tagtypes[k] = tagtype
 
-        # read the tags file from each head, ending with the tip
+        seen = set()
         f = None
-        for rev, node, fnode in self._hgtagsnodes():
-            f = (f and f.filectx(fnode) or
-                 self.filectx('.hgtags', fileid=fnode))
+        ctxs = []
+        for node in self.heads():
+            try:
+                fnode = self[node].filenode('.hgtags')
+            except error.LookupError:
+                continue
+            if fnode not in seen:
+                seen.add(fnode)
+                if not f:
+                    f = self.filectx('.hgtags', fileid=fnode)
+                else:
+                    f = f.filectx(fnode)
+                ctxs.append(f)
+
+        # read the tags file from each head, ending with the tip
+        for f in reversed(ctxs):
             readtags(f.data().splitlines(), f, "global")
 
         try:
@@ -328,22 +341,6 @@
 
         return self._tagstypecache.get(tagname)
 
-    def _hgtagsnodes(self):
-        last = {}
-        ret = []
-        for node in reversed(self.heads()):
-            c = self[node]
-            rev = c.rev()
-            try:
-                fnode = c.filenode('.hgtags')
-            except error.LookupError:
-                continue
-            ret.append((rev, node, fnode))
-            if fnode in last:
-                ret[last[fnode]] = None
-            last[fnode] = len(ret) - 1
-        return [item for item in ret if item]
-
     def tagslist(self):
         '''return a list of tags ordered by revision'''
         l = []
--- a/mercurial/patch.py	Sat Jun 20 18:58:34 2009 +0200
+++ b/mercurial/patch.py	Sun Jun 21 19:06:57 2009 +0200
@@ -972,7 +972,7 @@
 def applydiff(ui, fp, changed, strip=1, sourcefile=None, reverse=False,
               eol=None):
     """
-    Reads a patch from fp and tries to apply it. 
+    Reads a patch from fp and tries to apply it.
 
     The dict 'changed' is filled in with all of the filenames changed
     by the patch. Returns 0 for a clean patch, -1 if any rejects were
@@ -1137,7 +1137,7 @@
         eol = {'strict': None, 'crlf': '\r\n', 'lf': '\n'}[eolmode.lower()]
     except KeyError:
         raise util.Abort(_('Unsupported line endings type: %s') % eolmode)
-            
+
     try:
         fp = file(patchobj, 'rb')
     except TypeError:
--- a/mercurial/url.py	Sat Jun 20 18:58:34 2009 +0200
+++ b/mercurial/url.py	Sun Jun 21 19:06:57 2009 +0200
@@ -109,7 +109,9 @@
             return (user, passwd)
 
         if not user:
-            user, passwd = self._readauthtoken(authuri)
+            auth = self.readauthtoken(authuri)
+            if auth:
+                user, passwd = auth.get('username'), auth.get('password')
         if not user or not passwd:
             if not self.ui.interactive():
                 raise util.Abort(_('http authorization required'))
@@ -132,7 +134,7 @@
         msg = _('http auth: user %s, password %s\n')
         self.ui.debug(msg % (user, passwd and '*' * len(passwd) or 'not set'))
 
-    def _readauthtoken(self, uri):
+    def readauthtoken(self, uri):
         # Read configuration
         config = dict()
         for key, val in self.ui.configitems('auth'):
@@ -143,7 +145,7 @@
         # Find the best match
         scheme, hostpath = uri.split('://', 1)
         bestlen = 0
-        bestauth = None, None
+        bestauth = None
         for auth in config.itervalues():
             prefix = auth.get('prefix')
             if not prefix: continue
@@ -155,7 +157,7 @@
             if (prefix == '*' or hostpath.startswith(prefix)) and \
                 len(prefix) > bestlen and scheme in schemes:
                 bestlen = len(prefix)
-                bestauth = auth.get('username'), auth.get('password')
+                bestauth = auth
         return bestauth
 
 class proxyhandler(urllib2.ProxyHandler):
@@ -411,8 +413,38 @@
         send = _gen_sendfile(httplib.HTTPSConnection)
 
     class httpshandler(keepalive.KeepAliveHandler, urllib2.HTTPSHandler):
+        def __init__(self, ui):
+            keepalive.KeepAliveHandler.__init__(self)
+            urllib2.HTTPSHandler.__init__(self)
+            self.ui = ui
+            self.pwmgr = passwordmgr(self.ui)
+
         def https_open(self, req):
-            return self.do_open(httpsconnection, req)
+            self.auth = self.pwmgr.readauthtoken(req.get_full_url())
+            return self.do_open(self._makeconnection, req)
+
+        def _makeconnection(self, host, port=443, *args, **kwargs):
+            keyfile = None
+            certfile = None
+
+            if args: # key_file
+                keyfile = args.pop(0)
+            if args: # cert_file
+                certfile = args.pop(0)
+
+            # if the user has specified different key/cert files in
+            # hgrc, we prefer these
+            if self.auth and 'key' in self.auth and 'cert' in self.auth:
+                keyfile = self.auth['key']
+                certfile = self.auth['cert']
+
+            # let host port take precedence
+            if ':' in host and '[' not in host or ']:' in host:
+                host, port = host.rsplit(':', 1)
+                if '[' in host:
+                    host = host[1:-1]
+
+            return httpsconnection(host, port, keyfile, certfile, *args, **kwargs)
 
 # In python < 2.5 AbstractDigestAuthHandler raises a ValueError if
 # it doesn't know about the auth type requested.  This can happen if
@@ -460,7 +492,7 @@
     '''
     handlers = [httphandler()]
     if has_https:
-        handlers.append(httpshandler())
+        handlers.append(httpshandler(ui))
 
     handlers.append(proxyhandler(ui))
 
--- a/tests/run-tests.py	Sat Jun 20 18:58:34 2009 +0200
+++ b/tests/run-tests.py	Sun Jun 21 19:06:57 2009 +0200
@@ -16,7 +16,7 @@
 # If you change this script, it is recommended that you ensure you
 # haven't broken it by running it in various modes with a representative
 # sample of test scripts.  For example:
-# 
+#
 #  1) serial, no coverage, temp install:
 #      ./run-tests.py test-s*
 #  2) serial, no coverage, local hg:
--- a/tests/test-convert.out	Sat Jun 20 18:58:34 2009 +0200
+++ b/tests/test-convert.out	Sun Jun 21 19:06:57 2009 +0200
@@ -271,5 +271,5 @@
 emptydir does not look like a monotone repo
 emptydir does not look like a GNU Arch repo
 emptydir does not look like a Bazaar repo
-emptydir does not look like a P4 repo
+cannot find required "p4" tool
 abort: emptydir: missing or unsupported repository
--- a/tests/test-debugcomplete.out	Sat Jun 20 18:58:34 2009 +0200
+++ b/tests/test-debugcomplete.out	Sun Jun 21 19:06:57 2009 +0200
@@ -169,14 +169,14 @@
 export: output, switch-parent, text, git, nodates
 init: ssh, remotecmd
 log: follow, follow-first, date, copies, keyword, rev, removed, only-merges, user, only-branch, prune, patch, git, limit, no-merges, style, template, include, exclude
-merge: force, rev, show
+merge: force, rev, preview
 parents: rev, style, template
 pull: update, force, rev, ssh, remotecmd
 push: force, rev, ssh, remotecmd
 remove: after, force, include, exclude
 serve: accesslog, daemon, daemon-pipefds, errorlog, port, address, prefix, name, webdir-conf, pid-file, stdio, templates, style, ipv6, certificate
 status: all, modified, added, removed, deleted, clean, unknown, ignored, no-status, copies, print0, rev, include, exclude
-update: clean, date, rev
+update: clean, check, date, rev
 addremove: similarity, include, exclude, dry-run
 archive: no-decode, prefix, rev, type, include, exclude
 backout: merge, parent, rev, include, exclude, message, logfile, date, user
--- a/tests/test-double-merge	Sat Jun 20 18:58:34 2009 +0200
+++ b/tests/test-double-merge	Sun Jun 21 19:06:57 2009 +0200
@@ -20,7 +20,7 @@
 hg ci -m 'change foo' -d "1000000 0"
 
 # we get conflicts that shouldn't be there
-hg merge -S
+hg merge -P
 hg merge --debug
 
 echo "-- foo --"
--- a/tests/test-globalopts.out	Sat Jun 20 18:58:34 2009 +0200
+++ b/tests/test-globalopts.out	Sun Jun 21 19:06:57 2009 +0200
@@ -208,6 +208,7 @@
  diffs        Diff Formats
  templating   Template Usage
  urls         URL Paths
+ extensions   Using additional features
 
 use "hg -v help" to show aliases and global options
 Mercurial Distributed SCM
@@ -273,6 +274,7 @@
  diffs        Diff Formats
  templating   Template Usage
  urls         URL Paths
+ extensions   Using additional features
 
 use "hg -v help" to show aliases and global options
 %% not tested: --debugger
--- a/tests/test-grep	Sat Jun 20 18:58:34 2009 +0200
+++ b/tests/test-grep	Sun Jun 21 19:06:57 2009 +0200
@@ -73,3 +73,25 @@
 # Used to crash here
 hg grep -r 1 octarine
 
+# Issue337: grep did not compared changesets by their revision numbers
+# instead of following parent-child relationships.
+cd ..
+echo % issue 337
+hg init issue337
+cd issue337
+
+echo white > color
+hg commit -A -m "0 white"
+
+echo red > color
+hg commit -A -m "1 red"
+
+hg update 0
+echo black > color
+hg commit -A -m "2 black"
+
+hg update --clean 1
+echo blue > color
+hg commit -A -m "3 blue"
+
+hg grep --all red
--- a/tests/test-grep.out	Sat Jun 20 18:58:34 2009 +0200
+++ b/tests/test-grep.out	Sun Jun 21 19:06:57 2009 +0200
@@ -38,6 +38,13 @@
 noeol:4:no infinite loo
 % issue 685
 adding color
+colour:1:octarine
 color:0:octarine
 colour:1:octarine
-colour:1:octarine
+% issue 337
+adding color
+1 files updated, 0 files merged, 0 files removed, 0 files unresolved
+created new head
+1 files updated, 0 files merged, 0 files removed, 0 files unresolved
+color:3:-:red
+color:1:+:red
--- a/tests/test-hardlinks-safety.out	Sat Jun 20 18:58:34 2009 +0200
+++ b/tests/test-hardlinks-safety.out	Sun Jun 21 19:06:57 2009 +0200
@@ -15,6 +15,8 @@
 430ed4828a74fa4047bc816a25500f7472ab4bfe:foo
 %
 foo
+patch foo finalized without changeset message
+patch bar finalized without changeset message
 %%%
 4e7abb4840c46a910f6d7b4d3c3fc7e5209e684c foo
 430ed4828a74fa4047bc816a25500f7472ab4bfe bar
--- a/tests/test-help.out	Sat Jun 20 18:58:34 2009 +0200
+++ b/tests/test-help.out	Sun Jun 21 19:06:57 2009 +0200
@@ -99,6 +99,7 @@
  diffs        Diff Formats
  templating   Template Usage
  urls         URL Paths
+ extensions   Using additional features
 
 use "hg -v help" to show aliases and global options
  add          add the specified files on the next commit
@@ -160,6 +161,7 @@
  diffs        Diff Formats
  templating   Template Usage
  urls         URL Paths
+ extensions   Using additional features
 hg add [OPTION]... [FILE]...
 
 add the specified files on the next commit
--- a/tests/test-merge-default	Sat Jun 20 18:58:34 2009 +0200
+++ b/tests/test-merge-default	Sun Jun 21 19:06:57 2009 +0200
@@ -31,7 +31,7 @@
 hg commit -mm1
 
 echo % should succeed - 2 heads
-hg merge -S
+hg merge -P
 hg merge
 hg commit -mm2
 
--- a/tests/test-merge1	Sat Jun 20 18:58:34 2009 +0200
+++ b/tests/test-merge1	Sun Jun 21 19:06:57 2009 +0200
@@ -30,7 +30,7 @@
 hg commit -m "commit #2" -d "1000000 0"
 echo This is file b1 > b
 echo %% no merges expected
-hg merge -S 1
+hg merge -P 1
 hg merge 1
 hg diff --nodates
 hg status
--- a/tests/test-mq-qdelete.out	Sat Jun 20 18:58:34 2009 +0200
+++ b/tests/test-mq-qdelete.out	Sun Jun 21 19:06:57 2009 +0200
@@ -13,9 +13,12 @@
 b
 series
 status
+patch a finalized without changeset message
 1 [mq]: a
 0 base
 abort: cannot delete revision 3 above applied patches
+patch d finalized without changeset message
+patch e finalized without changeset message
 f
 4 [mq]: f
 3 [mq]: e
@@ -32,11 +35,14 @@
 applying c
 patch c is empty
 now at: c
+patch a finalized without changeset message
+patch b finalized without changeset message
 c
 3 imported patch c
 2 [mq]: b
 1 [mq]: a
 0 base
+patch c finalized without changeset message
 3 imported patch c
 2 [mq]: b
 1 [mq]: a
--- a/tests/test-mq-qimport.out	Sat Jun 20 18:58:34 2009 +0200
+++ b/tests/test-mq-qimport.out	Sun Jun 21 19:06:57 2009 +0200
@@ -27,3 +27,5 @@
 adding another.diff to series file
 applying another.diff
 now at: another.diff
+patch b.diff finalized without changeset message
+patch another.diff finalized without changeset message
--- a/tests/test-mq-qpush-fail	Sat Jun 20 18:58:34 2009 +0200
+++ b/tests/test-mq-qpush-fail	Sun Jun 21 19:06:57 2009 +0200
@@ -45,3 +45,12 @@
 
 echo '% bar should be gone; other unknown/ignored files should still be around'
 hg status -A
+
+echo '% preparing qpush of a missing patch'
+hg qpop -a
+hg qpush
+rm .hg/patches/patch2
+echo '% now we expect the push to fail, but it should NOT complain about patch1'
+hg qpush
+
+true # happy ending
--- a/tests/test-mq-qpush-fail.out	Sat Jun 20 18:58:34 2009 +0200
+++ b/tests/test-mq-qpush-fail.out	Sun Jun 21 19:06:57 2009 +0200
@@ -19,3 +19,11 @@
 ? untracked-file
 I .hgignore
 C foo
+% preparing qpush of a missing patch
+no patches applied
+applying patch1
+now at: patch1
+% now we expect the push to fail, but it should NOT complain about patch1
+applying patch2
+unable to read patch2
+now at: patch1
--- a/tests/test-tags.out	Sat Jun 20 18:58:34 2009 +0200
+++ b/tests/test-tags.out	Sun Jun 21 19:06:57 2009 +0200
@@ -26,14 +26,12 @@
 .hgtags@c071f74ab5eb, line 2: cannot parse entry
 .hgtags@c071f74ab5eb, line 4: node 'foo' is not well formed
 .hgtags@4ca6f1b1a68c, line 2: node 'x' is not well formed
-localtags, line 1: tag 'invalid' refers to unknown node
 tip                                8:4ca6f1b1a68c
 first                              0:0acdaf898367
 changeset:   8:4ca6f1b1a68c
 .hgtags@c071f74ab5eb, line 2: cannot parse entry
 .hgtags@c071f74ab5eb, line 4: node 'foo' is not well formed
 .hgtags@4ca6f1b1a68c, line 2: node 'x' is not well formed
-localtags, line 1: tag 'invalid' refers to unknown node
 tag:         tip
 parent:      3:b2ef3841386b
 user:        test
--- a/tests/test-up-issue1456	Sat Jun 20 18:58:34 2009 +0200
+++ b/tests/test-up-issue1456	Sun Jun 21 19:06:57 2009 +0200
@@ -10,7 +10,7 @@
 hg ci -m1
 hg co -q 0
 echo dirty > foo
-sleep 1
+hg up -c
 hg up -q
 cat foo
 hg st -A
--- a/tests/test-up-issue1456.out	Sat Jun 20 18:58:34 2009 +0200
+++ b/tests/test-up-issue1456.out	Sun Jun 21 19:06:57 2009 +0200
@@ -1,3 +1,4 @@
+abort: uncommitted local changes
 dirty
 M foo
 % validate update of standalone execute bit change