changeset 7631:0b2ee57dfdb1

Merge with crew-stable
author Patrick Mezard <pmezard@gmail.com>
date Tue, 13 Jan 2009 23:17:19 +0100
parents 97253bcb44a8 (diff) a679bd371091 (current diff)
children 9626819b2e3d
files mercurial/merge.py
diffstat 97 files changed, 1334 insertions(+), 701 deletions(-) [+]
line wrap: on
line diff
--- a/contrib/zsh_completion	Tue Jan 13 22:41:06 2009 +0100
+++ b/contrib/zsh_completion	Tue Jan 13 23:17:19 2009 +0100
@@ -4,14 +4,13 @@
 # it into your zsh function path (/usr/share/zsh/site-functions for
 # instance)
 #
-# Copyright (C) 2005 Steve Borho
+# Copyright (C) 2005-6 Steve Borho
 # Copyright (C) 2006-8 Brendan Cully <brendan@kublai.com>
 #
 # This is free software; you can redistribute it and/or modify it under
 # the terms of the GNU General Public License as published by the Free
 # Software Foundation; either version 2 of the License, or (at your
 # option) any later version.
-#
 
 emulate -LR zsh
 setopt extendedglob
@@ -118,27 +117,17 @@
   typeset -ga _hg_cmd_list
   typeset -gA _hg_alias_list
   local hline cmd cmdalias
-  _call_program help hg --verbose help | while read -A hline
+
+  _call_program hg hg debugcomplete -v 2>/dev/null | while read -A hline
   do
-    cmd="$hline[1]"
-    case $cmd in
-      *:)
-        cmd=${cmd%:}
-        _hg_cmd_list+=($cmd)
-      ;;
-      *,)
-        cmd=${cmd%,}
-        _hg_cmd_list+=($cmd)
-        integer i=2
-        while (( i <= $#hline ))
-        do
-          cmdalias=${hline[$i]%(:|,)}
-          _hg_cmd_list+=($cmdalias)
-          _hg_alias_list+=($cmdalias $cmd)
-          (( i++ ))
-        done
-      ;;
-    esac
+    cmd=$hline[1]
+    _hg_cmd_list+=($cmd)
+
+    for cmdalias in $hline[2,-1]
+    do
+      _hg_cmd_list+=($cmdalias)
+      _hg_alias_list+=($cmdalias $cmd)
+    done
   done
 }
 
--- a/hgext/bookmarks.py	Tue Jan 13 22:41:06 2009 +0100
+++ b/hgext/bookmarks.py	Tue Jan 13 23:17:19 2009 +0100
@@ -14,9 +14,19 @@
 
 It is possible to use bookmark names in every revision lookup (e.g. hg
 merge, hg update).
+
+The bookmark extension offers the possiblity to have a more git-like experience
+by adding the following configuration option to your .hgrc:
+
+[bookmarks]
+track.current = True
+
+This will cause bookmarks to track the bookmark that you are currently on, and
+just updates it. This is similar to git's approach of branching.
 '''
 
 from mercurial.commands import templateopts, hex, short
+from mercurial import extensions
 from mercurial.i18n import _
 from mercurial import cmdutil, util, commands, changelog
 from mercurial.node import nullid, nullrev
@@ -54,11 +64,54 @@
     '''
     if os.path.exists(repo.join('bookmarks')):
         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.items():
+    for refspec, node in refs.iteritems():
         file.write("%s %s\n" % (hex(node), refspec))
     file.close()
 
+def current(repo):
+    '''Get the current bookmark
+
+    If we use gittishsh branches we have a current bookmark that
+    we are on. This function returns the name of the bookmark. It
+    is stored in .hg/bookmarks.current
+    '''
+    if repo._bookmarkcurrent:
+        return repo._bookmarkcurrent
+    mark = None
+    if os.path.exists(repo.join('bookmarks.current')):
+        file = repo.opener('bookmarks.current')
+        mark = file.readline()
+        if mark == '':
+            mark = None
+        file.close()
+    repo._bookmarkcurrent = mark
+    return mark
+
+def setcurrent(repo, mark):
+    '''Set the name of the bookmark that we are currently on
+
+    Set the name of the bookmark that we are on (hg update <bookmark>).
+    The name is recoreded in .hg/bookmarks.current
+    '''
+    if current(repo) == mark:
+        return
+
+    refs = parse(repo)
+
+    # do not update if we do update to a rev equal to the current bookmark
+    if (mark not in refs and
+        current(repo) and refs[current(repo)] == repo.changectx('.').node()):
+        return
+    if mark not in refs:
+        mark = ''
+    file = repo.opener('bookmarks.current', 'w+')
+    file.write(mark)
+    file.close()
+    repo._bookmarkcurrent = mark
+
 def bookmark(ui, repo, mark=None, rev=None, force=False, delete=False, rename=None):
     '''mercurial bookmarks
 
@@ -85,6 +138,8 @@
             raise util.Abort(_("new bookmark name required"))
         marks[mark] = marks[rename]
         del marks[rename]
+        if current(repo) == rename:
+            setcurrent(repo, mark)
         write(repo, marks)
         return
 
@@ -121,7 +176,11 @@
             ui.status("no bookmarks set\n")
         else:
             for bmark, n in marks.iteritems():
-                prefix = (n == cur) and '*' or ' '
+                if ui.configbool('bookmarks', 'track.current'):
+                    prefix = (bmark == current(repo) and n == cur) and '*' or ' '
+                else:
+                    prefix = (n == cur) and '*' or ' '
+
                 ui.write(" %s %-25s %d:%s\n" % (
                     prefix, bmark, repo.changelog.rev(n), hexfn(n)))
         return
@@ -147,7 +206,7 @@
     revisions = _revstostrip(repo.changelog, node)
     marks = parse(repo)
     update = []
-    for mark, n in marks.items():
+    for mark, n in marks.iteritems():
         if repo.changelog.rev(n) in revisions:
             update.append(mark)
     oldstrip(ui, repo, node, backup)
@@ -166,6 +225,7 @@
     # init a bookmark cache as otherwise we would get a infinite reading
     # in lookup()
     repo._bookmarks = None
+    repo._bookmarkcurrent = None
 
     class bookmark_repo(repo.__class__):
         def rollback(self):
@@ -192,9 +252,14 @@
             marks = parse(repo)
             update = False
             for mark, n in marks.items():
-                if n in parents:
-                    marks[mark] = node
-                    update = True
+                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
@@ -218,8 +283,35 @@
                 write(repo, marks)
             return result
 
+        def tags(self):
+            """Merge bookmarks with normal tags"""
+            if self.tagscache:
+                return self.tagscache
+
+            tagscache = super(bookmark_repo, self).tags()
+            tagscache.update(parse(repo))
+            return tagscache
+
     repo.__class__ = bookmark_repo
 
+def updatecurbookmark(orig, ui, repo, *args, **opts):
+    '''Set the current bookmark
+
+    If the user updates to a bookmark we update the .hg/bookmarks.current
+    file.
+    '''
+    res = orig(ui, repo, *args, **opts)
+    rev = opts['rev']
+    if not rev and len(args) > 0:
+        rev = args[0]
+    setcurrent(repo, rev)
+    return res
+
+def uisetup(ui):
+    'Replace push with a decorator to provide --non-bookmarked option'
+    if ui.configbool('bookmarks', 'track.current'):
+        extensions.wrapcommand(commands.table, 'update', updatecurbookmark)
+
 cmdtable = {
     "bookmarks":
         (bookmark,
--- a/hgext/bugzilla.py	Tue Jan 13 22:41:06 2009 +0100
+++ b/hgext/bugzilla.py	Tue Jan 13 23:17:19 2009 +0100
@@ -4,53 +4,111 @@
 #
 # This software may be used and distributed according to the terms
 # of the GNU General Public License, incorporated herein by reference.
-#
-# hook extension to update comments of bugzilla bugs when changesets
-# that refer to bugs by id are seen.  this hook does not change bug
-# status, only comments.
-#
-# to configure, add items to '[bugzilla]' section of hgrc.
-#
-# to use, configure bugzilla extension and enable like this:
-#
-#   [extensions]
-#   hgext.bugzilla =
-#
-#   [hooks]
-#   # run bugzilla hook on every change pulled or pushed in here
-#   incoming.bugzilla = python:hgext.bugzilla.hook
-#
-# config items:
-#
-# section name is 'bugzilla'.
-#  [bugzilla]
-#
-# REQUIRED:
-#   host = bugzilla # mysql server where bugzilla database lives
-#   password = **   # user's password
-#   version = 2.16  # version of bugzilla installed
-#
-# OPTIONAL:
-#   bzuser = ...    # fallback bugzilla user name to record comments with
-#   db = bugs       # database to connect to
-#   notify = ...    # command to run to get bugzilla to send mail
-#   regexp = ...    # regexp to match bug ids (must contain one "()" group)
-#   strip = 0       # number of slashes to strip for url paths
-#   style = ...     # style file to use when formatting comments
-#   template = ...  # template to use when formatting comments
-#   timeout = 5     # database connection timeout (seconds)
-#   user = bugs     # user to connect to database as
-#   [web]
-#   baseurl = http://hgserver/... # root of hg web site for browsing commits
-#
-# if hg committer names are not same as bugzilla user names, use
-# "usermap" feature to map from committer email to bugzilla user name.
-# usermap can be in hgrc or separate config file.
-#
-#   [bugzilla]
-#   usermap = filename # cfg file with "committer"="bugzilla user" info
-#   [usermap]
-#   committer_email = bugzilla_user_name
+
+'''Bugzilla integration
+
+This hook extension adds comments on bugs in Bugzilla when changesets
+that refer to bugs by Bugzilla ID are seen. The hook does not change bug
+status.
+
+The hook updates the Bugzilla database directly. Only Bugzilla installations
+using MySQL are supported.
+
+The hook relies on a Bugzilla script to send bug change notification emails.
+That script changes between Bugzilla versions; the 'processmail' script used
+prior to 2.18 is replaced in 2.18 and subsequent versions by
+'config/sendbugmail.pl'. Note that these will be run by Mercurial as the user
+pushing the change; you will need to ensure the Bugzilla install file
+permissions are set appropriately.
+
+Configuring the extension:
+
+    [bugzilla]
+    host       Hostname of the MySQL server holding the Bugzilla database.
+    db         Name of the Bugzilla database in MySQL. Default 'bugs'.
+    user       Username to use to access MySQL server. Default 'bugs'.
+    password   Password to use to access MySQL server.
+    timeout    Database connection timeout (seconds). Default 5.
+    version    Bugzilla version. Specify '3.0' for Bugzilla versions 3.0 and
+               later, '2.18' for Bugzilla versions from 2.18 and '2.16' for
+               versions prior to 2.18.
+    bzuser     Fallback Bugzilla user name to record comments with, if
+               changeset committer cannot be found as a Bugzilla user.
+    bzdir      Bugzilla install directory. Used by default notify.
+               Default '/var/www/html/bugzilla'.
+    notify     The command to run to get Bugzilla to send bug change
+               notification emails. Substitutes from a map with 3 keys,
+               'bzdir', 'id' (bug id) and 'user' (committer bugzilla email).
+               Default depends on version; from 2.18 it is
+               "cd %(bzdir)s && perl -T contrib/sendbugmail.pl %(id)s %(user)s".
+    regexp     Regular expression to match bug IDs in changeset commit message.
+               Must contain one "()" group. The default expression matches
+               'Bug 1234', 'Bug no. 1234', 'Bug number 1234',
+               'Bugs 1234,5678', 'Bug 1234 and 5678' and variations thereof.
+               Matching is case insensitive.
+    style      The style file to use when formatting comments.
+    template   Template to use when formatting comments. Overrides
+               style if specified. In addition to the usual Mercurial
+               keywords, the extension specifies:
+                   {bug}       The Bugzilla bug ID.
+                   {root}      The full pathname of the Mercurial repository.
+                   {webroot}   Stripped pathname of the Mercurial repository.
+                   {hgweb}     Base URL for browsing Mercurial repositories.
+               Default 'changeset {node|short} in repo {root} refers '
+                       'to bug {bug}.\\ndetails:\\n\\t{desc|tabindent}'
+    strip      The number of slashes to strip from the front of {root}
+               to produce {webroot}. Default 0.
+    usermap    Path of file containing Mercurial committer ID to Bugzilla user
+               ID mappings. If specified, the file should contain one mapping
+               per line, "committer"="Bugzilla user". See also the
+               [usermap] section.
+
+    [usermap]
+    Any entries in this section specify mappings of Mercurial committer ID
+    to Bugzilla user ID. See also [bugzilla].usermap.
+    "committer"="Bugzilla user"
+
+    [web]
+    baseurl    Base URL for browsing Mercurial repositories. Reference from
+               templates as {hgweb}.
+
+Activating the extension:
+
+    [extensions]
+    hgext.bugzilla =
+
+    [hooks]
+    # run bugzilla hook on every change pulled or pushed in here
+    incoming.bugzilla = python:hgext.bugzilla.hook
+
+Example configuration:
+
+This example configuration is for a collection of Mercurial repositories
+in /var/local/hg/repos/ used with a local Bugzilla 3.2 installation in
+/opt/bugzilla-3.2.
+
+    [bugzilla]
+    host=localhost
+    password=XYZZY
+    version=3.0
+    bzuser=unknown@domain.com
+    bzdir=/opt/bugzilla-3.2
+    template=Changeset {node|short} in {root|basename}.\\n{hgweb}/{webroot}/rev/{node|short}\\n\\n{desc}\\n
+    strip=5
+
+    [web]
+    baseurl=http://dev.domain.com/hg
+
+    [usermap]
+    user@emaildomain.com=user.name@bugzilladomain.com
+
+Commits add a comment to the Bugzilla bug record of the form:
+
+    Changeset 3b16791d6642 in repository-name.
+    http://dev.domain.com/hg/repository-name/rev/3b16791d6642
+
+    Changeset commit comment. Bug 1234.
+'''
 
 from mercurial.i18n import _
 from mercurial.node import short
@@ -82,6 +140,7 @@
         self.cursor = self.conn.cursor()
         self.longdesc_id = self.get_longdesc_id()
         self.user_ids = {}
+        self.default_notify = "cd %(bzdir)s && ./processmail %(id)s %(user)s"
 
     def run(self, *args, **kwargs):
         '''run a query.'''
@@ -118,15 +177,23 @@
             unknown.pop(id, None)
         return util.sort(unknown.keys())
 
-    def notify(self, ids):
+    def notify(self, ids, committer):
         '''tell bugzilla to send mail.'''
 
         self.ui.status(_('telling bugzilla to send mail:\n'))
+        (user, userid) = self.get_bugzilla_user(committer)
         for id in ids:
             self.ui.status(_('  bug %s\n') % id)
-            cmd = self.ui.config('bugzilla', 'notify',
-                               'cd /var/www/html/bugzilla && '
-                               './processmail %s nobody@nowhere.com') % id
+            cmdfmt = self.ui.config('bugzilla', 'notify', self.default_notify)
+            bzdir = self.ui.config('bugzilla', 'bzdir', '/var/www/html/bugzilla')
+            try:
+                # Backwards-compatible with old notify string, which
+                # took one string. This will throw with a new format
+                # string.
+                cmd = cmdfmt % id
+            except TypeError:
+                cmd = cmdfmt % {'bzdir': bzdir, 'id': id, 'user': user}
+            self.ui.note(_('running notify command %s\n') % cmd)
             fp = util.popen('(%s) 2>&1' % cmd)
             out = fp.read()
             ret = fp.close()
@@ -161,9 +228,10 @@
                 return bzuser
         return user
 
-    def add_comment(self, bugid, text, committer):
-        '''add comment to bug. try adding comment as committer of
-        changeset, otherwise as default bugzilla user.'''
+    def get_bugzilla_user(self, committer):
+        '''see if committer is a registered bugzilla user. Return
+        bugzilla username and userid if so. If not, return default
+        bugzilla username and userid.'''
         user = self.map_committer(committer)
         try:
             userid = self.get_user_id(user)
@@ -174,9 +242,16 @@
                     raise util.Abort(_('cannot find bugzilla user id for %s') %
                                      user)
                 userid = self.get_user_id(defaultuser)
+                user = defaultuser
             except KeyError:
                 raise util.Abort(_('cannot find bugzilla user id for %s or %s') %
                                  (user, defaultuser))
+        return (user, userid)
+
+    def add_comment(self, bugid, text, committer):
+        '''add comment to bug. try adding comment as committer of
+        changeset, otherwise as default bugzilla user.'''
+        (user, userid) = self.get_bugzilla_user(committer)
         now = time.strftime('%Y-%m-%d %H:%M:%S')
         self.run('''insert into longdescs
                     (bug_id, who, bug_when, thetext)
@@ -185,12 +260,20 @@
         self.run('''insert into bugs_activity (bug_id, who, bug_when, fieldid)
                     values (%s, %s, %s, %s)''',
                  (bugid, userid, now, self.longdesc_id))
+        self.conn.commit()
 
-class bugzilla_3_0(bugzilla_2_16):
+class bugzilla_2_18(bugzilla_2_16):
+    '''support for bugzilla 2.18 series.'''
+
+    def __init__(self, ui):
+        bugzilla_2_16.__init__(self, ui)
+        self.default_notify = "cd %(bzdir)s && perl -T contrib/sendbugmail.pl %(id)s %(user)s"
+
+class bugzilla_3_0(bugzilla_2_18):
     '''support for bugzilla 3.0 series.'''
 
     def __init__(self, ui):
-        bugzilla_2_16.__init__(self, ui)
+        bugzilla_2_18.__init__(self, ui)
 
     def get_longdesc_id(self):
         '''get identity of longdesc field'''
@@ -205,6 +288,7 @@
     # different schemas.
     _versions = {
         '2.16': bugzilla_2_16,
+        '2.18': bugzilla_2_18,
         '3.0':  bugzilla_3_0
         }
 
@@ -320,7 +404,7 @@
         if ids:
             for id in ids:
                 bz.update(id, ctx)
-            bz.notify(ids)
+            bz.notify(ids, util.email(ctx.user()))
     except MySQLdb.MySQLError, err:
         raise util.Abort(_('database error: %s') % err[1])
 
--- a/hgext/churn.py	Tue Jan 13 22:41:06 2009 +0100
+++ b/hgext/churn.py	Tue Jan 13 23:17:19 2009 +0100
@@ -12,27 +12,6 @@
 import os, sys
 import time, datetime
 
-def get_tty_width():
-    if 'COLUMNS' in os.environ:
-        try:
-            return int(os.environ['COLUMNS'])
-        except ValueError:
-            pass
-    try:
-        import termios, array, fcntl
-        for dev in (sys.stdout, sys.stdin):
-            try:
-                fd = dev.fileno()
-                if not os.isatty(fd):
-                    continue
-                arri = fcntl.ioctl(fd, termios.TIOCGWINSZ, '\0' * 8)
-                return array.array('h', arri)[1]
-            except ValueError:
-                pass
-    except ImportError:
-        pass
-    return 80
-
 def maketemplater(ui, repo, tmpl):
     tmpl = templater.parsestring(tmpl, quoted=False)
     try:
@@ -100,7 +79,7 @@
             newpct = int(100.0 * count / max(len(repo), 1))
             if pct < newpct:
                 pct = newpct
-                ui.write(_("\rGenerating stats: %d%%") % pct)
+                ui.write(_("\rgenerating stats: %d%%") % pct)
                 sys.stdout.flush()
 
     if opts.get('progress'):
@@ -111,7 +90,7 @@
 
 
 def churn(ui, repo, *pats, **opts):
-    '''Graph count of revisions grouped by template
+    '''graph count of revisions grouped by template
 
     Will graph count of changed lines or revisions grouped by template or
     alternatively by date, if dateformat is used. In this case it will override
@@ -157,7 +136,7 @@
     maxcount = float(max([v for k, v in rate]))
     maxname = max([len(k) for k, v in rate])
 
-    ttywidth = get_tty_width()
+    ttywidth = util.termwidth()
     ui.debug(_("assuming %i character terminal\n") % ttywidth)
     width = ttywidth - maxname - 2 - 6 - 2 - 2
 
--- a/hgext/color.py	Tue Jan 13 22:41:06 2009 +0100
+++ b/hgext/color.py	Tue Jan 13 23:17:19 2009 +0100
@@ -204,6 +204,7 @@
 _diff_prefixes = [('diff', 'diffline'),
                   ('copy', 'extended'),
                   ('rename', 'extended'),
+                  ('old', 'extended'),
                   ('new', 'extended'),
                   ('deleted', 'extended'),
                   ('---', 'file_a'),
--- a/hgext/convert/__init__.py	Tue Jan 13 22:41:06 2009 +0100
+++ b/hgext/convert/__init__.py	Tue Jan 13 23:17:19 2009 +0100
@@ -7,13 +7,14 @@
 '''converting foreign VCS repositories to Mercurial'''
 
 import convcmd
+import cvsps
 from mercurial import commands
 from mercurial.i18n import _
 
 # Commands definition was moved elsewhere to ease demandload job.
 
 def convert(ui, src, dest=None, revmapfile=None, **opts):
-    """Convert a foreign SCM repository to a Mercurial one.
+    """convert a foreign SCM repository to a Mercurial one.
 
     Accepted source formats [identifiers]:
     - Mercurial [hg]
@@ -183,7 +184,18 @@
 def debugsvnlog(ui, **opts):
     return convcmd.debugsvnlog(ui, **opts)
 
-commands.norepo += " convert debugsvnlog"
+def debugcvsps(ui, *args, **opts):
+    '''create changeset information from CVS
+
+    This command is intended as a debugging tool for the CVS to Mercurial
+    converter, and can be used as a direct replacement for cvsps.
+
+    Hg debugcvsps reads the CVS rlog for current directory (or any named
+    directory) in the CVS repository, and converts the log to a series of
+    changesets based on matching commit log entries and dates.'''
+    return cvsps.debugcvsps(ui, *args, **opts)
+
+commands.norepo += " convert debugsvnlog debugcvsps"
 
 cmdtable = {
     "convert":
@@ -200,4 +212,22 @@
         (debugsvnlog,
          [],
          'hg debugsvnlog'),
+    "debugcvsps":
+        (debugcvsps,
+         [
+          # Main options shared with cvsps-2.1
+          ('b', 'branches', [], _('only return changes on specified branches')),
+          ('p', 'prefix', '', _('prefix to remove from file names')),
+          ('r', 'revisions', [], _('only return changes after or between specified tags')),
+          ('u', 'update-cache', None, _("update cvs log cache")),
+          ('x', 'new-cache', None, _("create new cvs log cache")),
+          ('z', 'fuzz', 60, _('set commit time fuzz in seconds')),
+          ('', 'root', '', _('specify cvsroot')),
+          # Options specific to builtin cvsps
+          ('', 'parents', '', _('show parent changesets')),
+          ('', 'ancestors', '', _('show current changeset in ancestor branches')),
+          # Options that are ignored for compatibility with cvsps-2.1
+          ('A', 'cvs-direct', None, 'ignored for compatibility'),
+         ],
+         'hg debugcvsps [OPTION]... [PATH]...'),
 }
--- a/hgext/convert/common.py	Tue Jan 13 22:41:06 2009 +0100
+++ b/hgext/convert/common.py	Tue Jan 13 23:17:19 2009 +0100
@@ -228,7 +228,9 @@
             except TypeError:
                 pass
         cmdline = [util.shellquote(arg) for arg in cmdline]
-        cmdline += ['2>', util.nulldev, '<', util.nulldev]
+        if not self.ui.debugflag:
+            cmdline += ['2>', util.nulldev]
+        cmdline += ['<', util.nulldev]
         cmdline = ' '.join(cmdline)
         return cmdline
 
--- a/hgext/convert/convcmd.py	Tue Jan 13 22:41:06 2009 +0100
+++ b/hgext/convert/convcmd.py	Tue Jan 13 23:17:19 2009 +0100
@@ -206,7 +206,7 @@
                         _('Overriding mapping for author %s, was %s, will be %s\n')
                         % (srcauthor, self.authors[srcauthor], dstauthor))
                 else:
-                    self.ui.debug(_('Mapping author %s to %s\n')
+                    self.ui.debug(_('mapping author %s to %s\n')
                                   % (srcauthor, dstauthor))
                     self.authors[srcauthor] = dstauthor
             except IndexError:
--- a/hgext/convert/cvs.py	Tue Jan 13 22:41:06 2009 +0100
+++ b/hgext/convert/cvs.py	Tue Jan 13 23:17:19 2009 +0100
@@ -144,11 +144,11 @@
                             if branch == "HEAD":
                                 branch = ""
                             if branch:
-                                latest = None
+                                latest = 0
                                 # the last changeset that contains a base
                                 # file is our parent
                                 for r in oldrevs:
-                                    latest = max(filerevids.get(r, None), latest)
+                                    latest = max(filerevids.get(r, 0), latest)
                                 if latest:
                                     p = [latest]
 
--- a/hgext/convert/cvsps	Tue Jan 13 22:41:06 2009 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,154 +0,0 @@
-#!/usr/bin/env python
-#
-# Commandline front-end for cvsps.py
-#
-# Copyright 2008, Frank Kingswood <frank@kingswood-consulting.co.uk>
-#
-# This software may be used and distributed according to the terms
-# of the GNU General Public License, incorporated herein by reference.
-
-import sys
-from mercurial import util
-from mercurial.i18n import _
-from optparse import OptionParser, SUPPRESS_HELP
-from hgext.convert.cvsps import createlog, createchangeset, logerror
-
-def main():
-    '''Main program to mimic cvsps.'''
-
-    op = OptionParser(usage='%prog [-bpruvxz] path',
-                      description='Read CVS rlog for current directory or named '
-                                  'path in repository, and convert the log to changesets '
-                                  'based on matching commit log entries and dates.')
-
-    # Options that are ignored for compatibility with cvsps-2.1
-    op.add_option('-A', dest='Ignore', action='store_true', help=SUPPRESS_HELP)
-    op.add_option('--cvs-direct', dest='Ignore', action='store_true', help=SUPPRESS_HELP)
-    op.add_option('-q', dest='Ignore', action='store_true', help=SUPPRESS_HELP)
-
-    # Main options shared with cvsps-2.1
-    op.add_option('-b', dest='Branches', action='append', default=[],
-                  help='Only return changes on specified branches')
-    op.add_option('-p', dest='Prefix', action='store', default='',
-                  help='Prefix to remove from file names')
-    op.add_option('-r', dest='Revisions', action='append', default=[],
-                  help='Only return changes after or between specified tags')
-    op.add_option('-u', dest='Cache', action='store_const', const='update',
-                  help="Update cvs log cache")
-    op.add_option('-v', dest='Verbose', action='count', default=0,
-                  help='Be verbose')
-    op.add_option('-x', dest='Cache', action='store_const', const='write',
-                  help="Create new cvs log cache")
-    op.add_option('-z', dest='Fuzz', action='store', type='int', default=60,
-                  help='Set commit time fuzz', metavar='seconds')
-    op.add_option('--root', dest='Root', action='store', default='',
-                  help='Specify cvsroot', metavar='cvsroot')
-
-    # Options specific to this version
-    op.add_option('--parents', dest='Parents', action='store_true',
-                  help='Show parent changesets')
-    op.add_option('--ancestors', dest='Ancestors', action='store_true',
-                  help='Show current changeset in ancestor branches')
-
-    options, args = op.parse_args()
-
-    # Create a ui object for printing progress messages
-    class UI:
-        def __init__(self, verbose):
-            if verbose:
-                self.status = self.message
-            if verbose>1:
-                self.note = self.message
-            if verbose>2:
-                self.debug = self.message
-        def message(self, msg):
-            sys.stderr.write(msg)
-        def nomessage(self, msg):
-            pass
-        status = nomessage
-        note = nomessage
-        debug = nomessage
-    ui = UI(options.Verbose)
-
-    try:
-        if args:
-            log = []
-            for d in args:
-                log += createlog(ui, d, root=options.Root, cache=options.Cache)
-        else:
-            log = createlog(ui, root=options.Root, cache=options.Cache)
-    except logerror, e:
-        print e
-        return
-
-    changesets = createchangeset(ui, log, options.Fuzz)
-    del log
-
-    # Print changesets (optionally filtered)
-
-    off = len(options.Revisions)
-    branches = {}    # latest version number in each branch
-    ancestors = {}   # parent branch
-    for cs in changesets:
-
-        if options.Ancestors:
-            if cs.branch not in branches and cs.parents and cs.parents[0].id:
-                ancestors[cs.branch] = changesets[cs.parents[0].id-1].branch, cs.parents[0].id
-            branches[cs.branch] = cs.id
-
-        # limit by branches
-        if options.Branches and (cs.branch or 'HEAD') not in options.Branches:
-            continue
-
-        if not off:
-            # Note: trailing spaces on several lines here are needed to have
-            #       bug-for-bug compatibility with cvsps.
-            print '---------------------'
-            print 'PatchSet %d ' % cs.id
-            print 'Date: %s' % util.datestr(cs.date, '%Y/%m/%d %H:%M:%S %1%2')
-            print 'Author: %s' % cs.author
-            print 'Branch: %s' % (cs.branch or 'HEAD')
-            print 'Tag%s: %s ' % (['', 's'][len(cs.tags)>1],
-                                  ','.join(cs.tags) or '(none)')
-            if options.Parents and cs.parents:
-                if len(cs.parents)>1:
-                    print 'Parents: %s' % (','.join([str(p.id) for p in cs.parents]))
-                else:
-                    print 'Parent: %d' % cs.parents[0].id
-
-            if options.Ancestors:
-                b = cs.branch
-                r = []
-                while b:
-                    b, c = ancestors[b]
-                    r.append('%s:%d:%d' % (b or "HEAD", c, branches[b]))
-                if r:
-                    print 'Ancestors: %s' % (','.join(r))
-
-            print 'Log:'
-            print cs.comment
-            print
-            print 'Members: '
-            for f in cs.entries:
-                fn = f.file
-                if fn.startswith(options.Prefix):
-                    fn = fn[len(options.Prefix):]
-                print '\t%s:%s->%s%s ' % (fn, '.'.join([str(x) for x in f.parent]) or 'INITIAL',
-                                          '.'.join([str(x) for x in f.revision]), ['', '(DEAD)'][f.dead])
-            print
-
-        # have we seen the start tag?
-        if options.Revisions and off:
-            if options.Revisions[0] == str(cs.id) or \
-                options.Revisions[0] in cs.tags:
-                off = False
-
-        # see if we reached the end tag
-        if len(options.Revisions)>1 and not off:
-            if options.Revisions[1] == str(cs.id) or \
-                options.Revisions[1] in cs.tags:
-                break
-
-
-if __name__ == '__main__':
-    main()
--- a/hgext/convert/cvsps.py	Tue Jan 13 22:41:06 2009 +0100
+++ b/hgext/convert/cvsps.py	Tue Jan 13 23:17:19 2009 +0100
@@ -191,7 +191,13 @@
     ui.note(_("running %s\n") % (' '.join(cmd)))
     ui.debug(_("prefix=%r directory=%r root=%r\n") % (prefix, directory, root))
 
-    for line in util.popen(' '.join(cmd)):
+    pfp = util.popen(' '.join(cmd))
+    peek = pfp.readline()
+    while True:
+        line = peek
+        if line == '':
+            break
+        peek = pfp.readline()
         if line.endswith('\n'):
             line = line[:-1]
         #ui.debug('state=%d line=%r\n' % (state, line))
@@ -263,7 +269,7 @@
             if re_31.match(line):
                 state = 5
             else:
-                assert not re_32.match(line), _('Must have at least some revisions')
+                assert not re_32.match(line), _('must have at least some revisions')
 
         elif state == 5:
             # expecting revision number and possibly (ignored) lock indication
@@ -312,7 +318,7 @@
                 e.branches = [tuple([int(y) for y in x.strip().split('.')])
                                 for x in m.group(1).split(';')]
                 state = 8
-            elif re_31.match(line):
+            elif re_31.match(line) and re_50.match(peek):
                 state = 5
                 store = True
             elif re_32.match(line):
@@ -584,3 +590,95 @@
     ui.status(_('%d changeset entries\n') % len(changesets))
 
     return changesets
+
+
+def debugcvsps(ui, *args, **opts):
+    '''Read CVS rlog for current directory or named path in repository, and
+    convert the log to changesets based on matching commit log entries and dates.'''
+
+    if opts["new_cache"]:
+        cache = "write"
+    elif opts["update_cache"]:
+        cache = "update"
+    else:
+        cache = None
+
+    revisions = opts["revisions"]
+
+    try:
+        if args:
+            log = []
+            for d in args:
+                log += createlog(ui, d, root=opts["root"], cache=cache)
+        else:
+            log = createlog(ui, root=opts["root"], cache=cache)
+    except logerror, e:
+        ui.write("%r\n"%e)
+        return
+
+    changesets = createchangeset(ui, log, opts["fuzz"])
+    del log
+
+    # Print changesets (optionally filtered)
+
+    off = len(revisions)
+    branches = {}    # latest version number in each branch
+    ancestors = {}   # parent branch
+    for cs in changesets:
+
+        if opts["ancestors"]:
+            if cs.branch not in branches and cs.parents and cs.parents[0].id:
+                ancestors[cs.branch] = changesets[cs.parents[0].id-1].branch, cs.parents[0].id
+            branches[cs.branch] = cs.id
+
+        # limit by branches
+        if opts["branches"] and (cs.branch or 'HEAD') not in opts["branches"]:
+            continue
+
+        if not off:
+            # Note: trailing spaces on several lines here are needed to have
+            #       bug-for-bug compatibility with cvsps.
+            ui.write('---------------------\n')
+            ui.write('PatchSet %d \n' % cs.id)
+            ui.write('Date: %s\n' % util.datestr(cs.date, '%Y/%m/%d %H:%M:%S %1%2'))
+            ui.write('Author: %s\n' % cs.author)
+            ui.write('Branch: %s\n' % (cs.branch or 'HEAD'))
+            ui.write('Tag%s: %s \n' % (['', 's'][len(cs.tags)>1],
+                                  ','.join(cs.tags) or '(none)'))
+            if opts["parents"] and cs.parents:
+                if len(cs.parents)>1:
+                    ui.write('Parents: %s\n' % (','.join([str(p.id) for p in cs.parents])))
+                else:
+                    ui.write('Parent: %d\n' % cs.parents[0].id)
+
+            if opts["ancestors"]:
+                b = cs.branch
+                r = []
+                while b:
+                    b, c = ancestors[b]
+                    r.append('%s:%d:%d' % (b or "HEAD", c, branches[b]))
+                if r:
+                    ui.write('Ancestors: %s\n' % (','.join(r)))
+
+            ui.write('Log:\n')
+            ui.write('%s\n\n' % cs.comment)
+            ui.write('Members: \n')
+            for f in cs.entries:
+                fn = f.file
+                if fn.startswith(opts["prefix"]):
+                    fn = fn[len(opts["prefix"]):]
+                ui.write('\t%s:%s->%s%s \n' % (fn, '.'.join([str(x) for x in f.parent]) or 'INITIAL',
+                                          '.'.join([str(x) for x in f.revision]), ['', '(DEAD)'][f.dead]))
+            ui.write('\n')
+
+        # have we seen the start tag?
+        if revisions and off:
+            if revisions[0] == str(cs.id) or \
+                revisions[0] in cs.tags:
+                off = False
+
+        # see if we reached the end tag
+        if len(revisions)>1 and not off:
+            if revisions[1] == str(cs.id) or \
+                revisions[1] in cs.tags:
+                break
--- a/hgext/convert/darcs.py	Tue Jan 13 22:41:06 2009 +0100
+++ b/hgext/convert/darcs.py	Tue Jan 13 23:17:19 2009 +0100
@@ -32,7 +32,7 @@
         if ElementTree is None:
             raise util.Abort(_("Python ElementTree module is not available"))
 
-        if not os.path.exists(os.path.join(path, '_darcs', 'inventory')):
+        if not os.path.exists(os.path.join(path, '_darcs', 'inventories')):
             raise NoRepo("%s does not look like a darcs repo" % path)
 
         self.path = os.path.realpath(path)
--- a/hgext/convert/gnuarch.py	Tue Jan 13 22:41:06 2009 +0100
+++ b/hgext/convert/gnuarch.py	Tue Jan 13 23:17:19 2009 +0100
@@ -3,7 +3,8 @@
 from common import NoRepo, commandline, commit, converter_source
 from mercurial.i18n import _
 from mercurial import util
-import os, shutil, tempfile, stat
+import os, shutil, tempfile, stat, locale
+from email.Parser import Parser
 
 class gnuarch_source(converter_source, commandline):
 
@@ -13,6 +14,7 @@
             self.summary = ''
             self.date = None
             self.author = ''
+            self.continuationof = None
             self.add_files = []
             self.mod_files = []
             self.del_files = []
@@ -46,38 +48,74 @@
         self.parents = {}
         self.tags = {}
         self.modecache = {}
+        self.catlogparser = Parser()
+        self.locale = locale.getpreferredencoding()
+        self.archives = []
 
     def before(self):
+        # Get registered archives
+        self.archives = [i.rstrip('\n')
+                         for i in self.runlines0('archives', '-n')]
+
         if self.execmd == 'tla':
             output = self.run0('tree-version', self.path)
         else:
             output = self.run0('tree-version', '-d', self.path)
         self.treeversion = output.strip()
 
-        self.ui.status(_('analyzing tree version %s...\n') % self.treeversion)
-
         # Get name of temporary directory
         version = self.treeversion.split('/')
         self.tmppath = os.path.join(tempfile.gettempdir(),
                                     'hg-%s' % version[1])
 
         # Generate parents dictionary
-        child = []
-        output, status = self.runlines('revisions', self.treeversion)
-        self.checkexit(status, 'archive registered?')
-        for l in output:
-            rev = l.strip()
-            self.changes[rev] = self.gnuarch_rev(rev)
+        self.parents[None] = []
+        treeversion = self.treeversion
+        child = None
+        while treeversion:
+            self.ui.status(_('analyzing tree version %s...\n') % treeversion)
+
+            archive = treeversion.split('/')[0]
+            if archive not in self.archives:
+                self.ui.status(_('tree analysis stopped because it points to an unregistered archive %s...\n') % archive)
+                break
+
+            # Get the complete list of revisions for that tree version
+            output, status = self.runlines('revisions', '-r', '-f', treeversion)
+            self.checkexit(status, 'failed retrieveing revisions for %s' % treeversion)
+
+            # No new iteration unless a revision has a continuation-of header
+            treeversion = None
+
+            for l in output:
+                rev = l.strip()
+                self.changes[rev] = self.gnuarch_rev(rev)
+                self.parents[rev] = []
 
-            # Read author, date and summary
-            catlog = self.runlines0('cat-log', '-d', self.path, rev)
-            self._parsecatlog(catlog, rev)
+                # Read author, date and summary
+                catlog, status = self.run('cat-log', '-d', self.path, rev)
+                if status:
+                    catlog  = self.run0('cat-archive-log', rev)
+                self._parsecatlog(catlog, rev)
+
+                # Populate the parents map
+                self.parents[child].append(rev)
 
-            self.parents[rev] = child
-            child = [rev]
-            if rev == self.rev:
-                break
-        self.parents[None] = child
+                # Keep track of the current revision as the child of the next
+                # revision scanned
+                child = rev
+
+                # Check if we have to follow the usual incremental history
+                # or if we have to 'jump' to a different treeversion given
+                # by the continuation-of header.
+                if self.changes[rev].continuationof:
+                    treeversion = '--'.join(self.changes[rev].continuationof.split('--')[:-1])
+                    break
+
+                # If we reached a base-0 revision w/o any continuation-of
+                # header, it means the tree history ends here.
+                if rev[-6:] == 'base-0':
+                    break
 
     def after(self):
         self.ui.debug(_('cleaning up %s\n') % self.tmppath)
@@ -135,7 +173,7 @@
     def getcommit(self, rev):
         changes = self.changes[rev]
         return commit(author = changes.author, date = changes.date,
-                      desc = changes.summary, parents = self.parents[rev])
+                      desc = changes.summary, parents = self.parents[rev], rev=rev)
 
     def gettags(self):
         return self.tags
@@ -150,26 +188,19 @@
         return os.system(cmdline)
 
     def _update(self, rev):
-        if rev == 'base-0':
-            # Initialise 'base-0' revision
+        self.ui.debug(_('applying revision %s...\n') % rev)
+        changeset, status = self.runlines('replay', '-d', self.tmppath,
+                                              rev)
+        if status:
+            # Something went wrong while merging (baz or tla
+            # issue?), get latest revision and try from there
+            shutil.rmtree(self.tmppath, ignore_errors=True)
             self._obtainrevision(rev)
         else:
-            self.ui.debug(_('applying revision %s...\n') % rev)
-            revision = '%s--%s' % (self.treeversion, rev)
-            changeset, status = self.runlines('replay', '-d', self.tmppath,
-                                              revision)
-            if status:
-                # Something went wrong while merging (baz or tla
-                # issue?), get latest revision and try from there
-                shutil.rmtree(self.tmppath, ignore_errors=True)
-                self._obtainrevision(rev)
-            else:
-                old_rev = self.parents[rev][0]
-                self.ui.debug(_('computing changeset between %s and %s...\n')
-                              % (old_rev, rev))
-                rev_a = '%s--%s' % (self.treeversion, old_rev)
-                rev_b = '%s--%s' % (self.treeversion, rev)
-                self._parsechangeset(changeset, rev)
+            old_rev = self.parents[rev][0]
+            self.ui.debug(_('computing changeset between %s and %s...\n')
+                          % (old_rev, rev))
+            self._parsechangeset(changeset, rev)
 
     def _getfile(self, name, rev):
         mode = os.lstat(os.path.join(self.tmppath, name)).st_mode
@@ -217,8 +248,7 @@
 
     def _obtainrevision(self, rev):
         self.ui.debug(_('obtaining revision %s...\n') % rev)
-        revision = '%s--%s' % (self.treeversion, rev)
-        output = self._execute('get', revision, self.tmppath)
+        output = self._execute('get', rev, self.tmppath)
         self.checkexit(output)
         self.ui.debug(_('analysing revision %s...\n') % rev)
         files = self._readcontents(self.tmppath)
@@ -230,20 +260,27 @@
         return path
 
     def _parsecatlog(self, data, rev):
-        summary = []
-        for l in data:
-            l = l.strip()
-            if summary:
-                summary.append(l)
-            elif l.startswith('Summary:'):
-                summary.append(l[len('Summary: '):])
-            elif l.startswith('Standard-date:'):
-                date = l[len('Standard-date: '):]
-                strdate = util.strdate(date, '%Y-%m-%d %H:%M:%S')
-                self.changes[rev].date = util.datestr(strdate)
-            elif l.startswith('Creator:'):
-                self.changes[rev].author = l[len('Creator: '):]
-        self.changes[rev].summary = '\n'.join(summary)
+        try:
+            catlog = self.catlogparser.parsestr(data)
+
+            # Commit date
+            self.changes[rev].date = util.datestr(
+                util.strdate(catlog['Standard-date'],
+                             '%Y-%m-%d %H:%M:%S'))
+
+            # Commit author
+            self.changes[rev].author = self.recode(catlog['Creator'])
+
+            # Commit description
+            self.changes[rev].summary = '\n\n'.join((catlog['Summary'],
+                                                    catlog.get_payload()))
+            self.changes[rev].summary = self.recode(self.changes[rev].summary)
+
+            # Commit revision origin when dealing with a branch or tag
+            if catlog.has_key('Continuation-of'):
+                self.changes[rev].continuationof = self.recode(catlog['Continuation-of'])
+        except Exception, err:
+            raise util.Abort(_('could not parse cat-log of %s') % rev)
 
     def _parsechangeset(self, data, rev):
         for l in data:
--- a/hgext/convert/subversion.py	Tue Jan 13 22:41:06 2009 +0100
+++ b/hgext/convert/subversion.py	Tue Jan 13 23:17:19 2009 +0100
@@ -601,7 +601,7 @@
                         part = "/".join(parts[:i])
                         info = part, copyfrom.get(part, None)
                         if info[1] is not None:
-                            self.ui.debug(_("Found parent directory %s\n") % info[1])
+                            self.ui.debug(_("found parent directory %s\n") % info[1])
                             rc = info
                     return rc
 
@@ -616,7 +616,7 @@
                     self.ui.debug(entrypath[len(frompath):] + '\n')
                     entrypath = froment.copyfrom_path + entrypath[len(frompath):]
                     fromrev = froment.copyfrom_rev
-                    self.ui.debug(_("Info: %s %s %s %s\n") % (frompath, froment, ent, entrypath))
+                    self.ui.debug(_("info: %s %s %s %s\n") % (frompath, froment, ent, entrypath))
 
                 # We can avoid the reparent calls if the module has not changed
                 # but it probably does not worth the pain.
@@ -757,7 +757,7 @@
                             self.ui.note(_('found parent of branch %s at %d: %s\n') %
                                          (self.module, prevnum, prevmodule))
                 else:
-                    self.ui.debug(_("No copyfrom path, don't know what to do.\n"))
+                    self.ui.debug(_("no copyfrom path, don't know what to do.\n"))
 
             paths = []
             # filter out unrelated paths
--- a/hgext/extdiff.py	Tue Jan 13 22:41:06 2009 +0100
+++ b/hgext/extdiff.py	Tue Jan 13 23:17:19 2009 +0100
@@ -80,9 +80,7 @@
     '''snapshot files from working directory.
     if not using snapshot, -I/-X does not work and recursive diff
     in tools like kdiff3 and meld displays too many files.'''
-    repo_root = repo.root
-
-    dirname = os.path.basename(repo_root)
+    dirname = os.path.basename(repo.root)
     if dirname == "":
         dirname = "root"
     base = os.path.join(tmproot, dirname)
@@ -105,8 +103,7 @@
             fp.write(chunk)
         fp.close()
 
-        fns_and_mtime.append((dest, os.path.join(repo_root, fn),
-            os.path.getmtime(dest)))
+        fns_and_mtime.append((dest, repo.wjoin(fn), os.path.getmtime(dest)))
 
 
     return dirname, fns_and_mtime
@@ -169,7 +166,7 @@
 
         for copy_fn, working_fn, mtime in fns_and_mtime:
             if os.path.getmtime(copy_fn) != mtime:
-                ui.debug(_('File changed while diffing. '
+                ui.debug(_('file changed while diffing. '
                          'Overwriting: %s (src: %s)\n') % (working_fn, copy_fn))
                 util.copyfile(copy_fn, working_fn)
 
--- a/hgext/fetch.py	Tue Jan 13 22:41:06 2009 +0100
+++ b/hgext/fetch.py	Tue Jan 13 23:17:19 2009 +0100
@@ -11,7 +11,7 @@
 from mercurial import commands, cmdutil, hg, util, url
 
 def fetch(ui, repo, source='default', **opts):
-    '''Pull changes from a remote repository, merge new changes if needed.
+    '''pull changes from a remote repository, merge new changes if needed.
 
     This finds all changes from the repository at the specified path
     or URL and adds them to the local repository.
--- a/hgext/hgk.py	Tue Jan 13 22:41:06 2009 +0100
+++ b/hgext/hgk.py	Tue Jan 13 23:17:19 2009 +0100
@@ -130,7 +130,7 @@
         ui.write('\0')
 
 def base(ui, repo, node1, node2):
-    """Output common ancestor information"""
+    """output common ancestor information"""
     node1 = repo.lookup(node1)
     node2 = repo.lookup(node2)
     n = repo.changelog.ancestor(node1, node2)
@@ -282,7 +282,7 @@
             count += 1
 
 def revparse(ui, repo, *revs, **opts):
-    """Parse given revisions"""
+    """parse given revisions"""
     def revstr(rev):
         if rev == 'HEAD':
             rev = 'tip'
--- a/hgext/keyword.py	Tue Jan 13 22:41:06 2009 +0100
+++ b/hgext/keyword.py	Tue Jan 13 23:17:19 2009 +0100
@@ -425,14 +425,10 @@
     keyword substitutions.
     Monkeypatches patch and webcommands.'''
 
-    try:
-        if (not repo.local() or not kwtools['inc']
-            or kwtools['hgcmd'] in nokwcommands.split()
-            or '.hg' in util.splitpath(repo.root)
-            or repo._url.startswith('bundle:')):
-            return
-    except AttributeError:
-        pass
+    if (not hasattr(repo, 'dirstate') or not kwtools['inc']
+        or kwtools['hgcmd'] in nokwcommands.split()
+        or '.hg' in util.splitpath(repo.root)):
+        return
 
     kwtools['templater'] = kwt = kwtemplater(ui, repo)
 
--- a/hgext/mq.py	Tue Jan 13 22:41:06 2009 +0100
+++ b/hgext/mq.py	Tue Jan 13 23:17:19 2009 +0100
@@ -963,10 +963,10 @@
                 raise
             top = self.applied[-1].name
             if ret[0]:
-                self.ui.write(
-                    "Errors during apply, please fix and refresh %s\n" % top)
+                self.ui.write(_("errors during apply, please fix and "
+                                "refresh %s\n") % top)
             else:
-                self.ui.write("Now at: %s\n" % top)
+                self.ui.write(_("now at: %s\n") % top)
             return ret[0]
         finally:
             del wlock
@@ -993,6 +993,17 @@
                 self.ui.warn(_("no patches applied\n"))
                 return not all
 
+            if all:
+                start = 0
+            elif patch:
+                start = info[0] + 1
+            else:
+                start = len(self.applied) - 1
+
+            if start >= len(self.applied):
+                self.ui.warn(_("qpop: %s is already at the top\n") % patch)
+                return
+
             if not update:
                 parents = repo.dirstate.parents()
                 rr = [ revlog.bin(x.rev) for x in self.applied ]
@@ -1000,31 +1011,31 @@
                     if p in rr:
                         self.ui.warn(_("qpop: forcing dirstate update\n"))
                         update = True
+            else:
+                parents = [p.hex() for p in repo[None].parents()]
+                needupdate = False
+                for entry in self.applied[start:]:
+                    if entry.rev in parents:
+                        needupdate = True
+                        break
+                update = needupdate
 
             if not force and update:
                 self.check_localchanges(repo)
 
-            self.applied_dirty = 1;
+            self.applied_dirty = 1
             end = len(self.applied)
-            if not patch:
-                if all:
-                    popi = 0
-                else:
-                    popi = len(self.applied) - 1
-            else:
-                popi = info[0] + 1
-                if popi >= end:
-                    self.ui.warn(_("qpop: %s is already at the top\n") % patch)
-                    return
-            info = [ popi ] + [self.applied[popi].rev, self.applied[popi].name]
-
-            start = info[0]
-            rev = revlog.bin(info[1])
-
+            rev = revlog.bin(self.applied[start].rev)
             if update:
                 top = self.check_toppatch(repo)
 
-            if repo.changelog.heads(rev) != [revlog.bin(self.applied[-1].rev)]:
+            try:
+                heads = repo.changelog.heads(rev)
+            except revlog.LookupError:
+                node = short(rev)
+                raise util.Abort(_('trying to pop unknown node %s') % node)
+
+            if heads != [revlog.bin(self.applied[-1].rev)]:
                 raise util.Abort(_("popping would remove a revision not "
                                    "managed by this patch queue"))
 
@@ -1056,16 +1067,16 @@
             del self.applied[start:end]
             self.strip(repo, rev, update=False, backup='strip')
             if len(self.applied):
-                self.ui.write(_("Now at: %s\n") % self.applied[-1].name)
+                self.ui.write(_("now at: %s\n") % self.applied[-1].name)
             else:
-                self.ui.write(_("Patch queue now empty\n"))
+                self.ui.write(_("patch queue now empty\n"))
         finally:
             del wlock
 
     def diff(self, repo, pats, opts):
         top = self.check_toppatch(repo)
         if not top:
-            self.ui.write(_("No patches applied\n"))
+            self.ui.write(_("no patches applied\n"))
             return
         qp = self.qparents(repo, top)
         self._diffopts = patch.diffopts(self.ui, opts)
@@ -1073,7 +1084,7 @@
 
     def refresh(self, repo, pats=None, **opts):
         if len(self.applied) == 0:
-            self.ui.write(_("No patches applied\n"))
+            self.ui.write(_("no patches applied\n"))
             return 1
         msg = opts.get('msg', '').rstrip()
         newuser = opts.get('user')
@@ -1602,7 +1613,7 @@
                 index = self.full_series_end() + i
                 self.full_series[index:index] = [patchname]
             self.parse_series()
-            self.ui.warn("adding %s to series file\n" % patchname)
+            self.ui.warn(_("adding %s to series file\n") % patchname)
             i += 1
             added.append(patchname)
             patchname = None
@@ -1786,7 +1797,7 @@
         return q.qseries(repo, start=t-1, length=1, status='A',
                          summary=opts.get('summary'))
     else:
-        ui.write("No patches applied\n")
+        ui.write(_("no patches applied\n"))
         return 1
 
 def next(ui, repo, **opts):
@@ -1794,7 +1805,7 @@
     q = repo.mq
     end = q.series_end()
     if end == len(q.series):
-        ui.write("All patches applied\n")
+        ui.write(_("all patches applied\n"))
         return 1
     return q.qseries(repo, start=end, length=1, summary=opts.get('summary'))
 
@@ -1803,10 +1814,10 @@
     q = repo.mq
     l = len(q.applied)
     if l == 1:
-        ui.write("Only one patch applied\n")
+        ui.write(_("only one patch applied\n"))
         return 1
     if not l:
-        ui.write("No patches applied\n")
+        ui.write(_("no patches applied\n"))
         return 1
     return q.qseries(repo, start=l-2, length=1, status='A',
                      summary=opts.get('summary'))
@@ -1870,7 +1881,7 @@
     message = cmdutil.logmessage(opts)
     if opts['edit']:
         if not q.applied:
-            ui.write(_("No patches applied\n"))
+            ui.write(_("no patches applied\n"))
             return 1
         if message:
             raise util.Abort(_('option "-e" incompatible with "-m" or "-l"'))
@@ -2018,14 +2029,14 @@
         status(q.series.index(q.lookup(patch)))
 
 def header(ui, repo, patch=None):
-    """Print the header of the topmost or specified patch"""
+    """print the header of the topmost or specified patch"""
     q = repo.mq
 
     if patch:
         patch = q.lookup(patch)
     else:
         if not q.applied:
-            ui.write('No patches applied\n')
+            ui.write('no patches applied\n')
             return 1
         patch = q.lookup('qtip')
     ph = repo.mq.readheaders(patch)
@@ -2112,7 +2123,7 @@
         patch = q.lookup(patch)
     else:
         if not q.applied:
-            ui.write(_('No patches applied\n'))
+            ui.write(_('no patches applied\n'))
             return
         patch = q.lookup('qtip')
     absdest = q.join(name)
@@ -2126,7 +2137,7 @@
         raise util.Abort(_('A patch named %s already exists in the series file') % name)
 
     if ui.verbose:
-        ui.write('Renaming %s to %s\n' % (patch, name))
+        ui.write('renaming %s to %s\n' % (patch, name))
     i = q.find_series(patch)
     guards = q.guard_re.findall(q.full_series[i])
     q.full_series[i] = name + ''.join([' #' + g for g in guards])
--- a/hgext/patchbomb.py	Tue Jan 13 22:41:06 2009 +0100
+++ b/hgext/patchbomb.py	Tue Jan 13 23:17:19 2009 +0100
@@ -9,8 +9,7 @@
 
   The remainder of the changeset description.
 
-  [Optional] If the diffstat program is installed, the result of
-  running diffstat on the patch.
+  [Optional] The result of running diffstat on the patch.
 
   The patch itself, as generated by "hg export".
 
@@ -64,23 +63,10 @@
 import os, errno, socket, tempfile, cStringIO
 import email.MIMEMultipart, email.MIMEBase
 import email.Utils, email.Encoders, email.Generator
-from mercurial import cmdutil, commands, hg, mail, patch, util
+from mercurial import cmdutil, commands, hg, mail, mdiff, patch, util
 from mercurial.i18n import _
 from mercurial.node import bin
 
-class exportee:
-    def __init__(self, container):
-        self.lines = []
-        self.container = container
-        self.name = 'email'
-
-    def write(self, data):
-        self.lines.append(data)
-
-    def close(self):
-        self.container.append(''.join(self.lines).split('\n'))
-        self.lines = []
-
 def prompt(ui, prompt, default=None, rest=': ', empty_ok=False):
     if not ui.interactive:
         return default
@@ -99,16 +85,12 @@
 
 def cdiffstat(ui, summary, patchlines):
     s = patch.diffstat(patchlines)
-    if s:
-        if summary:
-            ui.write(summary, '\n')
-            ui.write(s, '\n')
-        ans = prompt(ui, _('Does the diffstat above look okay? '), 'y')
-        if not ans.lower().startswith('y'):
-            raise util.Abort(_('diffstat rejected'))
-    elif s is None:
-        ui.warn(_('no diffstat information available\n'))
-        s = ''
+    if summary:
+        ui.write(summary, '\n')
+        ui.write(s, '\n')
+    ans = prompt(ui, _('does the diffstat above look okay? '), 'y')
+    if not ans.lower().startswith('y'):
+        raise util.Abort(_('diffstat rejected'))
     return s
 
 def makepatch(ui, repo, patch, opts, _charsets, idx, total, patchname=None):
@@ -239,6 +221,13 @@
         o = repo.changelog.nodesbetween(o, revs or None)[0]
         return [str(repo.changelog.rev(r)) for r in o]
 
+    def getpatches(revs):
+        for r in cmdutil.revrange(repo, revs):
+            output = cStringIO.StringIO()
+            p = patch.export(repo, [r], fp=output,
+                             opts=mdiff.diffopts(git=opts.get('git')))
+            yield output.getvalue().split('\n')
+
     def getbundle(dest):
         tmpdir = tempfile.mkdtemp(prefix='hg-email-bundle-')
         tmpfn = os.path.join(tmpdir, 'bundle')
@@ -308,7 +297,7 @@
                  % len(patches))
 
         name = None
-        for p, i in zip(patches, xrange(len(patches))):
+        for i, p in enumerate(patches):
             jumbo.extend(p)
             if patchnames:
                 name = patchnames[i]
@@ -360,18 +349,14 @@
               ui.config('patchbomb', 'from') or
               prompt(ui, 'From', ui.username()))
 
+    # internal option used by pbranches
     patches = opts.get('patches')
     if patches:
         msgs = getpatchmsgs(patches, opts.get('patchnames'))
     elif opts.get('bundle'):
         msgs = getbundlemsgs(getbundle(dest))
     else:
-        patches = []
-        commands.export(ui, repo, *revs, **{'output': exportee(patches),
-                                            'switch_parent': False,
-                                            'text': None,
-                                            'git': opts.get('git')})
-        msgs = getpatchmsgs(patches)
+        msgs = getpatchmsgs(list(getpatches(revs)))
 
     def getaddrs(opt, prpt, default = None):
         addrs = opts.get(opt) or (ui.config('email', opt) or
--- a/hgext/purge.py	Tue Jan 13 22:41:06 2009 +0100
+++ b/hgext/purge.py	Tue Jan 13 23:17:19 2009 +0100
@@ -32,28 +32,26 @@
 import os
 
 def purge(ui, repo, *dirs, **opts):
-    '''removes files not tracked by mercurial
+    '''removes files not tracked by Mercurial
 
-    Delete files not known to mercurial, this is useful to test local and
-    uncommitted changes in the otherwise clean source tree.
+    Delete files not known to Mercurial. This is useful to test local and
+    uncommitted changes in an otherwise-clean source tree.
 
     This means that purge will delete:
      - Unknown files: files marked with "?" by "hg status"
-     - Ignored files: files usually ignored by Mercurial because they match
-       a pattern in a ".hgignore" file
      - Empty directories: in fact Mercurial ignores directories unless they
        contain files under source control managment
     But it will leave untouched:
-     - Unmodified tracked files
-     - Modified tracked files
+     - Modified and unmodified tracked files
+     - Ignored files (unless --all is specified)
      - New files added to the repository (with "hg add")
 
     If directories are given on the command line, only files in these
     directories are considered.
 
-    Be careful with purge, you could irreversibly delete some files you
+    Be careful with purge, as you could irreversibly delete some files you
     forgot to add to the repository. If you only want to print the list of
-    files that this program would delete use the --print option.
+    files that this program would delete, use the --print option.
     '''
     act = not opts['print']
     eol = '\n'
@@ -64,7 +62,7 @@
     def remove(remove_func, name):
         if act:
             try:
-                remove_func(os.path.join(repo.root, name))
+                remove_func(repo.wjoin(name))
             except OSError:
                 m = _('%s cannot be removed') % name
                 if opts['abort_on_err']:
--- a/hgext/rebase.py	Tue Jan 13 22:41:06 2009 +0100
+++ b/hgext/rebase.py	Tue Jan 13 23:17:19 2009 +0100
@@ -34,7 +34,7 @@
     if not first:
         ancestor.ancestor = newancestor
     else:
-        repo.ui.debug(_("First revision, do not change ancestor\n"))
+        repo.ui.debug(_("first revision, do not change ancestor\n"))
     stats = merge.update(repo, rev, True, True, False)
     return stats
 
@@ -281,7 +281,7 @@
     f.write(repo[target].hex() + '\n')
     f.write(repo[external].hex() + '\n')
     f.write('%d\n' % int(collapse))
-    for d, v in state.items():
+    for d, v in state.iteritems():
         oldrev = repo[d].hex()
         newrev = repo[v].hex()
         f.write("%s:%s\n" % (oldrev, newrev))
--- a/hgext/transplant.py	Tue Jan 13 22:41:06 2009 +0100
+++ b/hgext/transplant.py	Tue Jan 13 23:17:19 2009 +0100
@@ -5,11 +5,6 @@
 # This software may be used and distributed according to the terms
 # of the GNU General Public License, incorporated herein by reference.
 
-from mercurial.i18n import _
-import os, tempfile
-from mercurial import bundlerepo, changegroup, cmdutil, hg, merge
-from mercurial import patch, revlog, util
-
 '''patch transplanting tool
 
 This extension allows you to transplant patches from another branch.
@@ -18,6 +13,11 @@
 from a changeset hash to its hash in the source repository.
 '''
 
+from mercurial.i18n import _
+import os, tempfile
+from mercurial import bundlerepo, changegroup, cmdutil, hg, merge
+from mercurial import patch, revlog, util
+
 class transplantentry:
     def __init__(self, lnode, rnode):
         self.lnode = lnode
--- a/hgext/win32mbcs.py	Tue Jan 13 22:41:06 2009 +0100
+++ b/hgext/win32mbcs.py	Tue Jan 13 23:17:19 2009 +0100
@@ -8,7 +8,7 @@
 # This software may be used and distributed according to the terms
 # of the GNU General Public License, incorporated herein by reference.
 #
-"""Allow to use MBCS path with problematic encoding.
+"""allow to use MBCS path with problematic encoding.
 
 Some MBCS encodings are not good for some path operations
 (i.e. splitting path, case conversion, etc.) with its encoded bytes.
--- a/hgext/zeroconf/__init__.py	Tue Jan 13 22:41:06 2009 +0100
+++ b/hgext/zeroconf/__init__.py	Tue Jan 13 23:17:19 2009 +0100
@@ -6,6 +6,29 @@
 # the GNU General Public License (version 2), incorporated herein by
 # reference.
 
+'''zeroconf support for mercurial repositories
+
+Zeroconf enabled repositories will be announced in a network without 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.
+
+ $ cd test
+ $ hg serve
+
+You can discover zeroconf enabled repositories by running "hg paths".
+
+ $ hg paths
+ zc-test = http://example.com:8000/test
+'''
+
 import Zeroconf, socket, time, os
 from mercurial import ui
 from mercurial import extensions
--- a/mercurial/bdiff.c	Tue Jan 13 22:41:06 2009 +0100
+++ b/mercurial/bdiff.c	Tue Jan 13 23:17:19 2009 +0100
@@ -261,6 +261,7 @@
 
 	free(pos);
 
+	/* normalize the hunk list, try to push each hunk towards the end */
 	for (curr = l.base; curr != l.head; curr++) {
 		struct hunk *next = curr+1;
 		int shift = 0;
--- a/mercurial/changelog.py	Tue Jan 13 22:41:06 2009 +0100
+++ b/mercurial/changelog.py	Tue Jan 13 23:17:19 2009 +0100
@@ -179,7 +179,7 @@
 
         user = user.strip()
         if "\n" in user:
-            raise RevlogError(_("username %s contains a newline") % `user`)
+            raise RevlogError(_("username %s contains a newline") % repr(user))
         user, desc = util.fromlocal(user), util.fromlocal(desc)
 
         if date:
--- a/mercurial/commands.py	Tue Jan 13 22:41:06 2009 +0100
+++ b/mercurial/commands.py	Tue Jan 13 23:17:19 2009 +0100
@@ -328,29 +328,34 @@
     state = hbisect.load_state(repo)
 
     if command:
+        commandpath = util.find_exe(command)
         changesets = 1
-        while changesets:
-            # update state
-            status = os.spawnlp(os.P_WAIT, command)
-            node = repo.lookup(rev or '.')
-            if status == 125:
-                transition = "skip"
-            elif status == 0:
-                transition = "good"
-            # status < 0 means process was killed
-            elif status == 127 or status < 0:
-                break
-            else:
-                transition = "bad"
-            state[transition].append(node)
-            ui.note(_('Changeset %s: %s\n') % (short(node), transition))
-            check_state(state, interactive=False)
-            # bisect
-            nodes, changesets, good = hbisect.bisect(repo.changelog, state)
-            # update to next check
-            cmdutil.bail_if_changed(repo)
-            hg.clean(repo, nodes[0], show_stats=False)
-        hbisect.save_state(repo, state)
+        try:
+            while changesets:
+                # update state
+                status = os.spawnl(os.P_WAIT, commandpath)
+                if status == 125:
+                    transition = "skip"
+                elif status == 0:
+                    transition = "good"
+                # status < 0 means process was killed
+                elif status == 127:
+                    raise util.Abort(_("failed to execute %s") % command)
+                elif status < 0:
+                    raise util.Abort(_("%s killed") % command)
+                else:
+                    transition = "bad"
+                node = repo.lookup(rev or '.')
+                state[transition].append(node)
+                ui.note(_('Changeset %s: %s\n') % (short(node), transition))
+                check_state(state, interactive=False)
+                # bisect
+                nodes, changesets, good = hbisect.bisect(repo.changelog, state)
+                # update to next check
+                cmdutil.bail_if_changed(repo)
+                hg.clean(repo, nodes[0], show_stats=False)
+        finally:
+            hbisect.save_state(repo, state)
         return print_result(nodes, not status)
 
     # update state
@@ -688,7 +693,10 @@
         ui.write("%s\n" % "\n".join(options))
         return
 
-    ui.write("%s\n" % "\n".join(util.sort(cmdutil.findpossible(cmd, table))))
+    cmdlist = cmdutil.findpossible(cmd, table)
+    if ui.verbose:
+        cmdlist = [' '.join(c[0]) for c in cmdlist.values()]
+    ui.write("%s\n" % "\n".join(util.sort(cmdlist)))
 
 def debugfsinfo(ui, path = "."):
     file('.debugfsinfo', 'w').write('')
@@ -780,7 +788,7 @@
     """show the contents of the current dirstate"""
     timestr = ""
     showdate = not nodates
-    for file_, ent in util.sort(repo.dirstate._map.items()):
+    for file_, ent in util.sort(repo.dirstate._map.iteritems()):
         if showdate:
             if ent[3] == -1:
                 # Pad or slice to locale representation
@@ -1007,7 +1015,18 @@
     Use the --git option to generate diffs in the git extended diff
     format. Read the diffs help topic for more information.
     """
-    node1, node2 = cmdutil.revpair(repo, opts.get('rev'))
+
+    revs = opts.get('rev')
+    change = opts.get('change')
+
+    if revs and change:
+        msg = _('cannot specify --rev and --change at the same time')
+        raise util.Abort(msg)
+    elif change:
+        node2 = repo.lookup(change)
+        node1 = repo[node2].parents()[0].node()
+    else:
+        node1, node2 = cmdutil.revpair(repo, revs)
 
     m = cmdutil.match(repo, pats, opts)
     it = patch.diff(repo, node1, node2, match=m, opts=patch.diffopts(ui, opts))
@@ -1325,7 +1344,7 @@
         # description
         doc = gettext(i[0].__doc__)
         if not doc:
-            doc = _("(No help text available)")
+            doc = _("(no help text available)")
         if ui.quiet:
             doc = doc.splitlines(0)[0]
         ui.write("\n%s\n" % doc.rstrip())
@@ -1340,7 +1359,7 @@
     def helplist(header, select=None):
         h = {}
         cmds = {}
-        for c, e in table.items():
+        for c, e in table.iteritems():
             f = c.split("|", 1)[0]
             if select and not select(f):
                 continue
@@ -1354,7 +1373,7 @@
                 continue
             doc = gettext(e[0].__doc__)
             if not doc:
-                doc = _("(No help text available)")
+                doc = _("(no help text available)")
             h[f] = doc.splitlines(0)[0].rstrip()
             cmds[f] = c.lstrip("^")
 
@@ -1397,7 +1416,7 @@
 
         # description
         if not doc:
-            doc = _("(No help text available)")
+            doc = _("(no help text available)")
         if callable(doc):
             doc = doc()
 
@@ -1410,7 +1429,7 @@
         except KeyError:
             raise cmdutil.UnknownCommand(name)
 
-        doc = gettext(mod.__doc__) or _('No help text available')
+        doc = gettext(mod.__doc__) or _('no help text available')
         doc = doc.splitlines(0)
         ui.write(_('%s extension - %s\n') % (name.split('.')[-1], doc[0]))
         for d in doc[1:]:
@@ -1794,7 +1813,7 @@
         if not rev and abs not in repo.dirstate:
             continue
         if opts.get('fullpath'):
-            ui.write(os.path.join(repo.root, abs), end)
+            ui.write(repo.wjoin(abs), end)
         else:
             ui.write(((pats and m.rel(abs)) or abs), end)
         ret = 0
@@ -2738,7 +2757,7 @@
         if node2 is None:
             added = stat[0] + stat[1] # merged?
 
-        for k, v in copies.copies(repo, ctx1, ctx2, ctxn)[0].items():
+        for k, v in copies.copies(repo, ctx1, ctx2, ctxn)[0].iteritems():
             if k in added:
                 copy[k] = v
             elif v in added:
@@ -2822,8 +2841,6 @@
 def tags(ui, repo):
     """list repository tags
 
-    List the repository tags.
-
     This lists both regular and local tags. When the -v/--verbose switch
     is used, a third column "local" is printed for local tags.
     """
@@ -3071,7 +3088,7 @@
           ('g', 'good', False, _('mark changeset good')),
           ('b', 'bad', False, _('mark changeset bad')),
           ('s', 'skip', False, _('skip testing changeset')),
-          ('c', 'command', '', _('Use command to check changeset state')),
+          ('c', 'command', '', _('use command to check changeset state')),
           ('U', 'noupdate', False, _('do not update to target'))],
          _("[-gbsr] [-c CMD] [REV]")),
     "branch":
@@ -3166,7 +3183,8 @@
     "debugwalk": (debugwalk, walkopts, _('[OPTION]... [FILE]...')),
     "^diff":
         (diff,
-         [('r', 'rev', [], _('revision'))
+         [('r', 'rev', [], _('revision')),
+          ('c', 'change', '', _('change made by revision'))
          ] + diffopts + diffopts2 + walkopts,
          _('[OPTION]... [-r REV1 [-r REV2]] [FILE]...')),
     "^export":
--- a/mercurial/copies.py	Tue Jan 13 22:41:06 2009 +0100
+++ b/mercurial/copies.py	Tue Jan 13 23:17:19 2009 +0100
@@ -189,7 +189,7 @@
 
     # examine each file copy for a potential directory move, which is
     # when all the files in a directory are moved to a new directory
-    for dst, src in fullcopy.items():
+    for dst, src in fullcopy.iteritems():
         dsrc, ddst = _dirname(src), _dirname(dst)
         if dsrc in invalid:
             # already seen to be uninteresting
--- a/mercurial/dispatch.py	Tue Jan 13 22:41:06 2009 +0100
+++ b/mercurial/dispatch.py	Tue Jan 13 23:17:19 2009 +0100
@@ -90,7 +90,7 @@
             else:
                 raise
     except socket.error, inst:
-        ui.warn(_("abort: %s\n") % inst[-1])
+        ui.warn(_("abort: %s\n") % inst.args[-1])
     except IOError, inst:
         if hasattr(inst, "code"):
             ui.warn(_("abort: %s\n") % inst)
@@ -100,7 +100,7 @@
             except: # it might be anything, for example a string
                 reason = inst.reason
             ui.warn(_("abort: error: %s\n") % reason)
-        elif hasattr(inst, "args") and inst[0] == errno.EPIPE:
+        elif hasattr(inst, "args") and inst.args[0] == errno.EPIPE:
             if ui.debugflag:
                 ui.warn(_("broken pipe\n"))
         elif getattr(inst, "strerror", None):
@@ -116,13 +116,13 @@
         else:
             ui.warn(_("abort: %s\n") % inst.strerror)
     except util.UnexpectedOutput, inst:
-        ui.warn(_("abort: %s") % inst[0])
-        if not isinstance(inst[1], basestring):
-            ui.warn(" %r\n" % (inst[1],))
-        elif not inst[1]:
+        ui.warn(_("abort: %s") % inst.args[0])
+        if not isinstance(inst.args[1], basestring):
+            ui.warn(" %r\n" % (inst.args[1],))
+        elif not inst.args[1]:
             ui.warn(_(" empty string\n"))
         else:
-            ui.warn("\n%r\n" % util.ellipsis(inst[1]))
+            ui.warn("\n%r\n" % util.ellipsis(inst.args[1]))
     except ImportError, inst:
         m = str(inst).split()[-1]
         ui.warn(_("abort: could not import module %s!\n") % m)
--- a/mercurial/filelog.py	Tue Jan 13 22:41:06 2009 +0100
+++ b/mercurial/filelog.py	Tue Jan 13 23:17:19 2009 +0100
@@ -50,7 +50,7 @@
         if meta or text.startswith('\1\n'):
             mt = ""
             if meta:
-                mt = [ "%s: %s\n" % (k, v) for k,v in meta.items() ]
+                mt = ["%s: %s\n" % (k, v) for k, v in meta.iteritems()]
             text = "\1\n%s\1\n%s" % ("".join(mt), text)
         return self.addrevision(text, transaction, link, p1, p2)
 
--- a/mercurial/hbisect.py	Tue Jan 13 22:41:06 2009 +0100
+++ b/mercurial/hbisect.py	Tue Jan 13 23:17:19 2009 +0100
@@ -102,7 +102,7 @@
             if value == perfect: # found a perfect candidate? quit early
                 break
 
-        if y < perfect: # all downhill from here?
+        if y < perfect and rev not in skip: # all downhill from here?
             for c in children.get(rev, []):
                 poison[c] = True # poison children
             continue
--- a/mercurial/hg.py	Tue Jan 13 22:41:06 2009 +0100
+++ b/mercurial/hg.py	Tue Jan 13 23:17:19 2009 +0100
@@ -252,8 +252,6 @@
     note = ", ".join([_("%d files %s") % s for s in stats])
     repo.ui.status("%s\n" % note)
 
-def _update(repo, node): return update(repo, node)
-
 def update(repo, node):
     """update the working directory to node, merging linear changes"""
     stats = _merge.update(repo, node, False, False, None)
@@ -262,6 +260,9 @@
         repo.ui.status(_("use 'hg resolve' to retry unresolved file merges\n"))
     return stats[3] > 0
 
+# naming conflict in clone()
+_update = update
+
 def clean(repo, node, show_stats=True):
     """forcibly switch the working directory to node, clobbering changes"""
     stats = _merge.update(repo, node, False, True, None)
--- a/mercurial/hgweb/hgweb_mod.py	Tue Jan 13 22:41:06 2009 +0100
+++ b/mercurial/hgweb/hgweb_mod.py	Tue Jan 13 23:17:19 2009 +0100
@@ -284,14 +284,13 @@
             raise ErrorResponse(HTTP_UNAUTHORIZED, 'read not authorized')
 
         allow_read = self.configlist('web', 'allow_read')
-        result = (not allow_read) or (allow_read == ['*']) or (user in allow_read)
-        if not result:
+        result = (not allow_read) or (allow_read == ['*'])
+        if not result or user in allow_read:
             raise ErrorResponse(HTTP_UNAUTHORIZED, 'read not authorized')
 
         if op == 'pull' and not self.allowpull:
             raise ErrorResponse(HTTP_UNAUTHORIZED, 'pull not authorized')
-        # op is None when checking allow/deny_read permissions for a web-browser request
-        elif op == 'pull' or op is None:
+        elif op == 'pull' or op is None: # op is None for interface requests
             return
 
         # enforce that you can only push using POST requests
--- a/mercurial/hgweb/hgwebdir_mod.py	Tue Jan 13 22:41:06 2009 +0100
+++ b/mercurial/hgweb/hgwebdir_mod.py	Tue Jan 13 23:17:19 2009 +0100
@@ -102,11 +102,11 @@
 
         user = req.env.get('REMOTE_USER')
 
-        deny_read = ui.configlist('web', 'deny_read', default=None, untrusted=True)
+        deny_read = ui.configlist('web', 'deny_read', untrusted=True)
         if deny_read and (not user or deny_read == ['*'] or user in deny_read):
             return False
 
-        allow_read = ui.configlist('web', 'allow_read', default=None, untrusted=True)
+        allow_read = ui.configlist('web', 'allow_read', untrusted=True)
         # by default, allow reading if no allow_read option has been set
         if (not allow_read) or (allow_read == ['*']) or (user in allow_read):
             return True
--- a/mercurial/hgweb/webcommands.py	Tue Jan 13 22:41:06 2009 +0100
+++ b/mercurial/hgweb/webcommands.py	Tue Jan 13 23:17:19 2009 +0100
@@ -275,7 +275,7 @@
     l = len(path)
     abspath = "/" + path
 
-    for f, n in mf.items():
+    for f, n in mf.iteritems():
         if f[:l] != path:
             continue
         remain = f[l:]
@@ -386,7 +386,7 @@
         parity = paritygen(web.stripecount)
 
         b = web.repo.branchtags()
-        l = [(-web.repo.changelog.rev(n), n, t) for t, n in b.items()]
+        l = [(-web.repo.changelog.rev(n), n, t) for t, n in b.iteritems()]
         for r,n,t in util.sort(l):
             yield {'parity': parity.next(),
                    'branch': t,
--- a/mercurial/hgweb/wsgicgi.py	Tue Jan 13 22:41:06 2009 +0100
+++ b/mercurial/hgweb/wsgicgi.py	Tue Jan 13 23:17:19 2009 +0100
@@ -15,7 +15,7 @@
     util.set_binary(sys.stdin)
     util.set_binary(sys.stdout)
 
-    environ = dict(os.environ.items())
+    environ = dict(os.environ.iteritems())
     environ.setdefault('PATH_INFO', '')
     if '.cgi' in environ['PATH_INFO']:
         environ['PATH_INFO'] = environ['PATH_INFO'].split('.cgi', 1)[1]
--- a/mercurial/ignore.py	Tue Jan 13 22:41:06 2009 +0100
+++ b/mercurial/ignore.py	Tue Jan 13 23:17:19 2009 +0100
@@ -60,7 +60,7 @@
                         warn(_("%s: ignoring invalid syntax '%s'\n") % (f, s))
                     continue
                 pat = syntax + line
-                for s, rels in syntaxes.items():
+                for s, rels in syntaxes.iteritems():
                     if line.startswith(rels):
                         pat = line
                         break
@@ -83,7 +83,7 @@
             util.matcher(root, inc=allpats, src='.hgignore'))
     except util.Abort:
         # Re-raise an exception where the src is the right file
-        for f, patlist in pats.items():
+        for f, patlist in pats.iteritems():
             files, ignorefunc, anypats = (
                 util.matcher(root, inc=patlist, src=f))
 
--- a/mercurial/keepalive.py	Tue Jan 13 22:41:06 2009 +0100
+++ b/mercurial/keepalive.py	Tue Jan 13 23:17:19 2009 +0100
@@ -197,7 +197,7 @@
 
     def close_all(self):
         """close all open connections"""
-        for host, conns in self._cm.get_all().items():
+        for host, conns in self._cm.get_all().iteritems():
             for h in conns:
                 self._cm.remove(h)
                 h.close()
--- a/mercurial/localrepo.py	Tue Jan 13 22:41:06 2009 +0100
+++ b/mercurial/localrepo.py	Tue Jan 13 23:17:19 2009 +0100
@@ -265,7 +265,7 @@
                     h.append(n)
                 filetags[key] = (bin_n, h)
 
-            for k, nh in filetags.items():
+            for k, nh in filetags.iteritems():
                 if k not in globaltags:
                     globaltags[k] = nh
                     tagtypes[k] = tagtype
@@ -301,7 +301,7 @@
 
         self.tagscache = {}
         self._tagstypecache = {}
-        for k,nh in globaltags.items():
+        for k, nh in globaltags.iteritems():
             n = nh[0]
             if n != nullid:
                 self.tagscache[k] = n
@@ -343,7 +343,7 @@
     def tagslist(self):
         '''return a list of tags ordered by revision'''
         l = []
-        for t, n in self.tags().items():
+        for t, n in self.tags().iteritems():
             try:
                 r = self.changelog.rev(n)
             except:
@@ -355,7 +355,7 @@
         '''return the tags associated with a node'''
         if not self.nodetagscache:
             self.nodetagscache = {}
-            for t, n in self.tags().items():
+            for t, n in self.tags().iteritems():
                 self.nodetagscache.setdefault(n, []).append(t)
         return self.nodetagscache.get(node, [])
 
@@ -388,7 +388,7 @@
 
         # the branch cache is stored on disk as UTF-8, but in the local
         # charset internally
-        for k, v in partial.items():
+        for k, v in partial.iteritems():
             self.branchcache[util.tolocal(k)] = v
         self._ubranchcache = partial
         return self.branchcache
@@ -1579,7 +1579,7 @@
         if self.ui.verbose or source == 'bundle':
             self.ui.status(_("%d changesets found\n") % len(nodes))
         if self.ui.debugflag:
-            self.ui.debug(_("List of changesets:\n"))
+            self.ui.debug(_("list of changesets:\n"))
             for node in nodes:
                 self.ui.debug("%s\n" % hex(node))
 
@@ -1756,7 +1756,7 @@
                     # we only need to see a diff.
                     deltamf = mnfst.readdelta(mnfstnode)
                     # For each line in the delta
-                    for f, fnode in deltamf.items():
+                    for f, fnode in deltamf.iteritems():
                         f = changedfiles.get(f, None)
                         # And if the file is in the list of files we care
                         # about.
--- a/mercurial/lsprof.py	Tue Jan 13 22:41:06 2009 +0100
+++ b/mercurial/lsprof.py	Tue Jan 13 23:17:19 2009 +0100
@@ -88,7 +88,7 @@
     try:
         mname = _fn2mod[code.co_filename]
     except KeyError:
-        for k, v in sys.modules.items():
+        for k, v in sys.modules.iteritems():
             if v is None:
                 continue
             if not hasattr(v, '__file__'):
--- a/mercurial/merge.py	Tue Jan 13 22:41:06 2009 +0100
+++ b/mercurial/merge.py	Tue Jan 13 23:17:19 2009 +0100
@@ -36,7 +36,7 @@
     def _write(self):
         f = self._repo.opener("merge/state", "w")
         f.write(hex(self._local) + "\n")
-        for d, v in self._state.items():
+        for d, v in self._state.iteritems():
             f.write("\0".join([d] + v) + "\n")
     def add(self, fcl, fco, fca, fd, flags):
         hash = util.sha1(fcl.path()).hexdigest()
@@ -166,7 +166,7 @@
             dirs = repo.ui.configbool("merge", "followdirs", True)
             copy, diverge = copies.copies(repo, p1, p2, pa, dirs)
         copied = dict.fromkeys(copy.values())
-        for of, fl in diverge.items():
+        for of, fl in diverge.iteritems():
             act("divergent renames", "dr", of, fl)
 
     # Compare manifests
--- a/mercurial/patch.py	Tue Jan 13 22:41:06 2009 +0100
+++ b/mercurial/patch.py	Tue Jan 13 23:17:19 2009 +0100
@@ -9,7 +9,7 @@
 from i18n import _
 from node import hex, nullid, short
 import base85, cmdutil, mdiff, util, revlog, diffhelpers, copies
-import cStringIO, email.Parser, os, re, errno
+import cStringIO, email.Parser, os, re, errno, math
 import sys, tempfile, zlib
 
 gitre = re.compile('diff --git a/(.*) b/(.*)')
@@ -794,9 +794,7 @@
 
     def readline(self):
         if self.buf:
-            l = self.buf[0]
-            del self.buf[0]
-            return l
+            return self.buf.pop(0)
         return self.fp.readline()
 
     def __iter__(self):
@@ -1059,7 +1057,7 @@
         gp = patches[f]
         if gp and gp.mode:
             islink, isexec = gp.mode
-            dst = os.path.join(repo.root, gp.path)
+            dst = repo.wjoin(gp.path)
             # patch won't create empty files
             if gp.op == 'ADD' and not os.path.exists(dst):
                 flags = (isexec and 'x' or '') + (islink and 'l' or '')
@@ -1340,19 +1338,61 @@
 
         for chunk in diff(repo, prev, node, opts=opts):
             fp.write(chunk)
-        if fp not in (sys.stdout, repo.ui):
-            fp.close()
 
     for seqno, rev in enumerate(revs):
         single(rev, seqno+1, fp)
 
-def diffstat(patchlines):
-    if not util.find_exe('diffstat'):
-        return
-    output = util.filter('\n'.join(patchlines),
-                         'diffstat -p1 -w79 2>%s' % util.nulldev)
-    stat = [l.lstrip() for l in output.splitlines(True)]
-    last = stat.pop()
-    stat.insert(0, last)
-    stat = ''.join(stat)
-    return stat
+def diffstatdata(lines):
+    filename = None
+    for line in lines:
+        if line.startswith('diff'):
+            if filename:
+                yield (filename, adds, removes)
+            # set numbers to 0 anyway when starting new file
+            adds = 0
+            removes = 0
+            if line.startswith('diff --git'):
+                filename = gitre.search(line).group(1)
+            else:
+                # format: "diff -r ... -r ... file name"
+                filename = line.split(None, 5)[-1]
+        elif line.startswith('+') and not line.startswith('+++'):
+            adds += 1
+        elif line.startswith('-') and not line.startswith('---'):
+            removes += 1
+    yield (filename, adds, removes)
+
+def diffstat(lines):
+    output = []
+    stats = list(diffstatdata(lines))
+    width = util.termwidth() - 2
+
+    maxtotal, maxname = 0, 0
+    totaladds, totalremoves = 0, 0
+    for filename, adds, removes in stats:
+        totaladds += adds
+        totalremoves += removes
+        maxname = max(maxname, len(filename))
+        maxtotal = max(maxtotal, adds+removes)
+
+    countwidth = len(str(maxtotal))
+    graphwidth = width - countwidth - maxname
+    if graphwidth < 10:
+        graphwidth = 10
+
+    factor = int(math.ceil(float(maxtotal) / graphwidth))
+
+    for filename, adds, removes in stats:
+        # If diffstat runs out of room it doesn't print anything, which
+        # isn't very useful, so always print at least one + or - if there
+        # were at least some changes
+        pluses = '+' * max(adds/factor, int(bool(adds)))
+        minuses = '-' * max(removes/factor, int(bool(removes)))
+        output.append(' %-*s |  %*.d %s%s\n' % (maxname, filename, countwidth,
+                                                adds+removes, pluses, minuses))
+
+    if stats:
+        output.append(' %d files changed, %d insertions(+), %d deletions(-)\n' %
+                      (len(stats), totaladds, totalremoves))
+
+    return ''.join(output)
--- a/mercurial/sshrepo.py	Tue Jan 13 22:41:06 2009 +0100
+++ b/mercurial/sshrepo.py	Tue Jan 13 23:17:19 2009 +0100
@@ -115,7 +115,7 @@
     def do_cmd(self, cmd, **args):
         self.ui.debug(_("sending %s command\n") % cmd)
         self.pipeo.write("%s\n" % cmd)
-        for k, v in args.items():
+        for k, v in args.iteritems():
             self.pipeo.write("%s %d\n" % (k, len(v)))
             self.pipeo.write(v)
         self.pipeo.flush()
--- a/mercurial/ui.py	Tue Jan 13 22:41:06 2009 +0100
+++ b/mercurial/ui.py	Tue Jan 13 23:17:19 2009 +0100
@@ -340,7 +340,7 @@
         if user is None:
             user = os.environ.get("EMAIL")
         if user is None and self.configbool("ui", "askusername"):
-            user = self.prompt(_("Enter a commit username:"), default=None)
+            user = self.prompt(_("enter a commit username:"), default=None)
         if user is None:
             try:
                 user = '%s@%s' % (util.getuser(), socket.getfqdn())
@@ -350,7 +350,7 @@
         if not user:
             raise util.Abort(_("Please specify a username."))
         if "\n" in user:
-            raise util.Abort(_("username %s contains a newline\n") % `user`)
+            raise util.Abort(_("username %s contains a newline\n") % repr(user))
         return user
 
     def shortuser(self, user):
--- a/mercurial/util.py	Tue Jan 13 22:41:06 2009 +0100
+++ b/mercurial/util.py	Tue Jan 13 23:17:19 2009 +0100
@@ -705,7 +705,7 @@
         if cwd is not None and oldcwd != cwd:
             os.chdir(oldcwd)
 
-class SignatureError:
+class SignatureError(Exception):
     pass
 
 def checksignature(func):
@@ -1991,3 +1991,24 @@
 def uirepr(s):
     # Avoid double backslash in Windows path repr()
     return repr(s).replace('\\\\', '\\')
+
+def termwidth():
+    if 'COLUMNS' in os.environ:
+        try:
+            return int(os.environ['COLUMNS'])
+        except ValueError:
+            pass
+    try:
+        import termios, array, fcntl
+        for dev in (sys.stdout, sys.stdin):
+            try:
+                fd = dev.fileno()
+                if not os.isatty(fd):
+                    continue
+                arri = fcntl.ioctl(fd, termios.TIOCGWINSZ, '\0' * 8)
+                return array.array('h', arri)[1]
+            except ValueError:
+                pass
+    except ImportError:
+        pass
+    return 80
--- a/mercurial/util_win32.py	Tue Jan 13 22:41:06 2009 +0100
+++ b/mercurial/util_win32.py	Tue Jan 13 23:17:19 2009 +0100
@@ -19,7 +19,7 @@
 import util
 from win32com.shell import shell,shellcon
 
-class WinError:
+class WinError(Exception):
     winerror_map = {
         winerror.ERROR_ACCESS_DENIED: errno.EACCES,
         winerror.ERROR_ACCOUNT_DISABLED: errno.EACCES,
--- a/mercurial/verify.py	Tue Jan 13 22:41:06 2009 +0100
+++ b/mercurial/verify.py	Tue Jan 13 23:17:19 2009 +0100
@@ -220,7 +220,7 @@
 
         # cross-check
         if f in filenodes:
-            fns = [(mf.linkrev(l), n) for n,l in filenodes[f].items()]
+            fns = [(mf.linkrev(l), n) for n,l in filenodes[f].iteritems()]
             for lr, node in util.sort(fns):
                 err(lr, _("%s in manifests not found") % short(node), f)
 
--- a/setup.py	Tue Jan 13 22:41:06 2009 +0100
+++ b/setup.py	Tue Jan 13 23:17:19 2009 +0100
@@ -9,6 +9,23 @@
 if not hasattr(sys, 'version_info') or sys.version_info < (2, 3, 0, 'final'):
     raise SystemExit("Mercurial requires python 2.3 or later.")
 
+# Solaris Python packaging brain damage
+try:
+    import hashlib
+    sha = hashlib.sha1()
+except:
+    try:
+        import sha
+    except:
+        raise SystemExit(
+            "Couldn't import standard hashlib (incomplete Python install).")
+
+try:
+    import zlib
+except:
+    raise SystemExit(
+        "Couldn't import standard zlib (incomplete Python install).")
+
 import os
 import shutil
 import tempfile
Binary file tests/tampered.hg has changed
--- a/tests/test-acl.out	Tue Jan 13 22:41:06 2009 +0100
+++ b/tests/test-acl.out	Tue Jan 13 23:17:19 2009 +0100
@@ -15,7 +15,7 @@
 searching for changes
 common changesets up to 6675d58eff77
 3 changesets found
-List of changesets:
+list of changesets:
 ef1ea85a6374b77d6da9dcda9541f498f2d17df7
 f9cafe1212c8c6fa1120d14a556e18cc44ff8bdd
 911600dab2ae7a9baff75958b84fe606851ce955
@@ -43,7 +43,7 @@
 searching for changes
 common changesets up to 6675d58eff77
 3 changesets found
-List of changesets:
+list of changesets:
 ef1ea85a6374b77d6da9dcda9541f498f2d17df7
 f9cafe1212c8c6fa1120d14a556e18cc44ff8bdd
 911600dab2ae7a9baff75958b84fe606851ce955
@@ -75,7 +75,7 @@
 searching for changes
 common changesets up to 6675d58eff77
 3 changesets found
-List of changesets:
+list of changesets:
 ef1ea85a6374b77d6da9dcda9541f498f2d17df7
 f9cafe1212c8c6fa1120d14a556e18cc44ff8bdd
 911600dab2ae7a9baff75958b84fe606851ce955
@@ -112,7 +112,7 @@
 searching for changes
 common changesets up to 6675d58eff77
 3 changesets found
-List of changesets:
+list of changesets:
 ef1ea85a6374b77d6da9dcda9541f498f2d17df7
 f9cafe1212c8c6fa1120d14a556e18cc44ff8bdd
 911600dab2ae7a9baff75958b84fe606851ce955
@@ -151,7 +151,7 @@
 searching for changes
 common changesets up to 6675d58eff77
 3 changesets found
-List of changesets:
+list of changesets:
 ef1ea85a6374b77d6da9dcda9541f498f2d17df7
 f9cafe1212c8c6fa1120d14a556e18cc44ff8bdd
 911600dab2ae7a9baff75958b84fe606851ce955
@@ -193,7 +193,7 @@
 searching for changes
 common changesets up to 6675d58eff77
 3 changesets found
-List of changesets:
+list of changesets:
 ef1ea85a6374b77d6da9dcda9541f498f2d17df7
 f9cafe1212c8c6fa1120d14a556e18cc44ff8bdd
 911600dab2ae7a9baff75958b84fe606851ce955
@@ -234,7 +234,7 @@
 searching for changes
 common changesets up to 6675d58eff77
 3 changesets found
-List of changesets:
+list of changesets:
 ef1ea85a6374b77d6da9dcda9541f498f2d17df7
 f9cafe1212c8c6fa1120d14a556e18cc44ff8bdd
 911600dab2ae7a9baff75958b84fe606851ce955
@@ -278,7 +278,7 @@
 searching for changes
 common changesets up to 6675d58eff77
 3 changesets found
-List of changesets:
+list of changesets:
 ef1ea85a6374b77d6da9dcda9541f498f2d17df7
 f9cafe1212c8c6fa1120d14a556e18cc44ff8bdd
 911600dab2ae7a9baff75958b84fe606851ce955
@@ -321,7 +321,7 @@
 searching for changes
 common changesets up to 6675d58eff77
 3 changesets found
-List of changesets:
+list of changesets:
 ef1ea85a6374b77d6da9dcda9541f498f2d17df7
 f9cafe1212c8c6fa1120d14a556e18cc44ff8bdd
 911600dab2ae7a9baff75958b84fe606851ce955
@@ -365,7 +365,7 @@
 searching for changes
 common changesets up to 6675d58eff77
 3 changesets found
-List of changesets:
+list of changesets:
 ef1ea85a6374b77d6da9dcda9541f498f2d17df7
 f9cafe1212c8c6fa1120d14a556e18cc44ff8bdd
 911600dab2ae7a9baff75958b84fe606851ce955
@@ -409,7 +409,7 @@
 searching for changes
 common changesets up to 6675d58eff77
 3 changesets found
-List of changesets:
+list of changesets:
 ef1ea85a6374b77d6da9dcda9541f498f2d17df7
 f9cafe1212c8c6fa1120d14a556e18cc44ff8bdd
 911600dab2ae7a9baff75958b84fe606851ce955
@@ -458,7 +458,7 @@
 searching for changes
 common changesets up to 6675d58eff77
 3 changesets found
-List of changesets:
+list of changesets:
 ef1ea85a6374b77d6da9dcda9541f498f2d17df7
 f9cafe1212c8c6fa1120d14a556e18cc44ff8bdd
 911600dab2ae7a9baff75958b84fe606851ce955
@@ -506,7 +506,7 @@
 searching for changes
 common changesets up to 6675d58eff77
 3 changesets found
-List of changesets:
+list of changesets:
 ef1ea85a6374b77d6da9dcda9541f498f2d17df7
 f9cafe1212c8c6fa1120d14a556e18cc44ff8bdd
 911600dab2ae7a9baff75958b84fe606851ce955
@@ -561,7 +561,7 @@
 searching for changes
 common changesets up to 6675d58eff77
 3 changesets found
-List of changesets:
+list of changesets:
 ef1ea85a6374b77d6da9dcda9541f498f2d17df7
 f9cafe1212c8c6fa1120d14a556e18cc44ff8bdd
 911600dab2ae7a9baff75958b84fe606851ce955
--- a/tests/test-audit-path	Tue Jan 13 22:41:06 2009 +0100
+++ b/tests/test-audit-path	Tue Jan 13 23:17:19 2009 +0100
@@ -41,4 +41,8 @@
 hg manifest -r3
 hg update -Cr3
 
+echo % attack /tmp/test
+hg manifest -r4
+hg update -Cr4 2>&1 | sed -e "s|$HGTMP|[HGTMP]|"
+
 exit 0
--- a/tests/test-audit-path.out	Tue Jan 13 22:41:06 2009 +0100
+++ b/tests/test-audit-path.out	Tue Jan 13 23:17:19 2009 +0100
@@ -10,7 +10,7 @@
 adding changesets
 adding manifests
 adding file changes
-added 4 changesets with 5 changes to 5 files (+3 heads)
+added 5 changesets with 6 changes to 6 files (+4 heads)
 (run 'hg heads' to see heads, 'hg merge' to merge)
 % attack .hg/test
 .hg/test
@@ -25,3 +25,6 @@
 % attack ../test
 ../test
 abort: path contains illegal component: ../test
+% attack /tmp/test
+/tmp/test
+abort: No such file or directory: [HGTMP]/test-audit-path/target//tmp/test
--- a/tests/test-bdiff	Tue Jan 13 22:41:06 2009 +0100
+++ b/tests/test-bdiff	Tue Jan 13 23:17:19 2009 +0100
@@ -9,13 +9,13 @@
     if d:
         c = mpatch.patches(a, [d])
     if c != b:
-        print "***", `a`, `b`
+        print "***", repr(a), repr(b)
         print "bad:"
-        print `c`[:200]
-        print `d`
+        print repr(c)[:200]
+        print repr(d)
 
 def test(a, b):
-    print "***", `a`, `b`
+    print "***", repr(a), repr(b)
     test1(a, b)
     test1(b, a)
 
--- a/tests/test-bisect	Tue Jan 13 22:41:06 2009 +0100
+++ b/tests/test-bisect	Tue Jan 13 23:17:19 2009 +0100
@@ -72,3 +72,13 @@
 echo % test no action
 hg bisect -r
 hg bisect || echo failure
+
+echo % reproduce AssertionError, issue1445
+hg bisect -r
+hg bisect -b 6
+hg bisect -g 0
+hg bisect -s
+hg bisect -s
+hg bisect -s
+hg bisect -s 
+hg bisect -g
--- a/tests/test-bisect.out	Tue Jan 13 22:41:06 2009 +0100
+++ b/tests/test-bisect.out	Tue Jan 13 23:17:19 2009 +0100
@@ -286,3 +286,20 @@
 % test no action
 abort: cannot bisect (no known good revisions)
 failure
+% reproduce AssertionError, issue1445
+Testing changeset 3:b53bea5e2fcb (6 changesets remaining, ~2 tests)
+1 files updated, 0 files merged, 0 files removed, 0 files unresolved
+Testing changeset 2:db07c04beaca (6 changesets remaining, ~2 tests)
+1 files updated, 0 files merged, 0 files removed, 0 files unresolved
+Testing changeset 4:9b2ba8336a65 (6 changesets remaining, ~2 tests)
+1 files updated, 0 files merged, 0 files removed, 0 files unresolved
+Testing changeset 1:5cd978ea5149 (6 changesets remaining, ~2 tests)
+1 files updated, 0 files merged, 0 files removed, 0 files unresolved
+Testing changeset 5:7874a09ea728 (6 changesets remaining, ~2 tests)
+1 files updated, 0 files merged, 0 files removed, 0 files unresolved
+The first bad revision is:
+changeset:   6:a3d5c6fdf0d3
+user:        test
+date:        Thu Jan 01 00:00:06 1970 +0000
+summary:     msg 6
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tests/test-bookmarks-current	Tue Jan 13 23:17:19 2009 +0100
@@ -0,0 +1,41 @@
+#!/bin/sh
+
+echo "[extensions]" >> $HGRCPATH
+echo "bookmarks=" >> $HGRCPATH
+
+echo "[bookmarks]" >> $HGRCPATH
+echo "track.current = True" >> $HGRCPATH
+
+hg init
+
+echo % no bookmarks
+hg bookmarks
+
+echo % set bookmark X
+hg bookmark X
+
+echo % update to bookmark X
+hg update X
+
+echo % list bookmarks
+hg bookmarks
+
+echo % rename
+hg bookmark -m X Z
+
+echo % list bookmarks
+hg bookmarks
+
+echo % new bookmark Y
+hg bookmark Y
+
+echo % list bookmarks
+hg bookmark
+
+echo % commit
+echo 'b' > b
+hg add b
+hg commit -m'test'
+
+echo % list bookmarks
+hg bookmark
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tests/test-bookmarks-current.out	Tue Jan 13 23:17:19 2009 +0100
@@ -0,0 +1,18 @@
+% no bookmarks
+no bookmarks set
+% set bookmark X
+% update to bookmark X
+0 files updated, 0 files merged, 0 files removed, 0 files unresolved
+% list bookmarks
+ * X                         -1:000000000000
+% rename
+% list bookmarks
+ * Z                         -1:000000000000
+% new bookmark Y
+% list bookmarks
+   Y                         -1:000000000000
+ * Z                         -1:000000000000
+% commit
+% list bookmarks
+   Y                         -1:000000000000
+ * Z                         0:719295282060
--- a/tests/test-bookmarks-rebase.out	Tue Jan 13 22:41:06 2009 +0100
+++ b/tests/test-bookmarks-rebase.out	Tue Jan 13 23:17:19 2009 +0100
@@ -18,6 +18,8 @@
 rebase completed
 changeset:   3:9163974d1cb5
 tag:         tip
+tag:         two
+tag:         one
 parent:      1:925d80f479bb
 parent:      2:db815d6d32e6
 user:        test
--- a/tests/test-bookmarks.out	Tue Jan 13 22:41:06 2009 +0100
+++ b/tests/test-bookmarks.out	Tue Jan 13 23:17:19 2009 +0100
@@ -7,6 +7,7 @@
  * X                         0:f7b1eb17ad24
 % look up bookmark
 changeset:   0:f7b1eb17ad24
+tag:         X
 tag:         tip
 user:        test
 date:        Thu Jan 01 00:00:00 1970 +0000
@@ -51,7 +52,10 @@
  * x  y                      2:0316ce92851d
 % look up stripped bookmark name
 changeset:   2:0316ce92851d
+tag:         X2
+tag:         Y
 tag:         tip
+tag:         x  y
 user:        test
 date:        Thu Jan 01 00:00:00 1970 +0000
 summary:     2
--- a/tests/test-convert-bzr-merges	Tue Jan 13 22:41:06 2009 +0100
+++ b/tests/test-convert-bzr-merges	Tue Jan 13 23:17:19 2009 +0100
@@ -18,6 +18,7 @@
 bzr add -q file-branch1
 bzr commit -q -m 'Added branch1 file'
 cd ../source
+sleep 1
 echo content > file-parent
 bzr add -q file-parent
 bzr commit -q -m 'Added parent file'
@@ -27,6 +28,7 @@
 echo somecontent > file-branch2
 bzr add -q file-branch2
 bzr commit -q -m 'Added brach2 file'
+sleep 1
 cd ../source
 bzr merge -q ../source-branch1
 bzr merge -q --force ../source-branch2
--- a/tests/test-convert-cvs-builtincvsps	Tue Jan 13 22:41:06 2009 +0100
+++ b/tests/test-convert-cvs-builtincvsps	Tue Jan 13 23:17:19 2009 +0100
@@ -100,5 +100,22 @@
 hgcat b/c
 hg -R src-filemap log --template '#rev# #desc# files: #files#\n'
 
+echo % commit a new revision with funny log message
+cd src
+sleep 1
+echo e >> a
+cvscall -q commit -m'funny
+----------------------------
+log message' . | grep '<--' |\
+    sed -e 's:.*src/\(.*\),v.*:checking in src/\1,v:g'
+cd ..
+
+echo % convert again
+hg convert src src-hg | sed -e 's/connecting to.*cvsrepo/connecting to cvsrepo/g'
+
 echo "graphlog = " >> $HGRCPATH
 hg -R src-hg glog --template '#rev# (#branches#) #desc# files: #files#\n'
+
+echo % testing debugcvsps
+cd src
+hg debugcvsps | sed -e 's/Author:.*/Author:/' -e 's/Date:.*/Date:/' 
--- a/tests/test-convert-cvs-builtincvsps.out	Tue Jan 13 22:41:06 2009 +0100
+++ b/tests/test-convert-cvs-builtincvsps.out	Tue Jan 13 23:17:19 2009 +0100
@@ -124,6 +124,22 @@
 2 update tags files: .hgtags
 1 ci0 files: b/c
 0 Initial revision files: b/c
+% commit a new revision with funny log message
+checking in src/a,v
+% convert again
+using builtin cvsps
+collecting CVS rlog
+9 log entries
+creating changesets
+6 changeset entries
+connecting to cvsrepo
+scanning source...
+sorting...
+converting...
+0 funny
+o  6 (branch) funny
+|  ----------------------------
+|  log message files: a
 o  5 (branch) ci2 files: b/c
 |
 | o  4 () ci1 files: a b/c
@@ -136,3 +152,85 @@
 |/
 o  0 () Initial revision files: a b/c
 
+% testing debugcvsps
+collecting CVS rlog
+9 log entries
+creating changesets
+6 changeset entries
+---------------------
+PatchSet 1 
+Date:
+Author:
+Branch: HEAD
+Tag: (none) 
+Log:
+Initial revision
+
+Members: 
+	a:INITIAL->1.1 
+	b/c:INITIAL->1.1 
+
+---------------------
+PatchSet 2 
+Date:
+Author:
+Branch: INITIAL
+Tag: start 
+Log:
+import
+
+Members: 
+	a:1.1->1.1.1.1 
+	b/c:1.1->1.1.1.1 
+
+---------------------
+PatchSet 3 
+Date:
+Author:
+Branch: HEAD
+Tag: (none) 
+Log:
+ci0
+
+Members: 
+	b/c:1.1->1.2 
+
+---------------------
+PatchSet 4 
+Date:
+Author:
+Branch: HEAD
+Tag: (none) 
+Log:
+ci1
+
+Members: 
+	a:1.1->1.2 
+	b/c:1.2->1.3 
+
+---------------------
+PatchSet 5 
+Date:
+Author:
+Branch: branch
+Tag: (none) 
+Log:
+ci2
+
+Members: 
+	b/c:1.1->1.1.2.1 
+
+---------------------
+PatchSet 6 
+Date:
+Author:
+Branch: branch
+Tag: (none) 
+Log:
+funny
+----------------------------
+log message
+
+Members: 
+	a:1.2->1.2.2.1 
+
--- a/tests/test-convert.out	Tue Jan 13 22:41:06 2009 +0100
+++ b/tests/test-convert.out	Tue Jan 13 23:17:19 2009 +0100
@@ -1,6 +1,6 @@
 hg convert [OPTION]... SOURCE [DEST [REVMAP]]
 
-Convert a foreign SCM repository to a Mercurial one.
+convert a foreign SCM repository to a Mercurial one.
 
     Accepted source formats [identifiers]:
     - Mercurial [hg]
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tests/test-diff-change	Tue Jan 13 23:17:19 2009 +0100
@@ -0,0 +1,61 @@
+#!/bin/sh -e
+
+# test of hg diff --change
+
+set -e
+
+ec() {
+	echo "invoking $@:"
+	"$@"
+}
+
+hg init a
+cd a
+
+echo "first" > file.txt
+hg add file.txt
+hg commit -m 'first commit' # 0
+
+echo "second" > file.txt
+hg commit -m 'second commit' # 1
+
+echo "third" > file.txt
+hg commit -m 'third commit' # 2
+
+ec hg diff --nodates --change 1
+
+echo
+
+#rev=$(hg log -r 1 --template '{node|short}')
+rev=e9b286083166
+ec hg diff --nodates --change "$rev"
+
+##
+# Testing diff -c when merge
+
+for i in 1 2 3 4 5 6 7 8 9 10; do
+    echo $i >> file.txt
+done
+hg commit -m "lots of text" # 3
+
+sed -i -e 's,^2$,x,' file.txt
+hg commit -m "changed 2 to x" # 4
+
+hg up -r 3 > /dev/null 2>&1 # updated, merged, removed, unresolved
+sed -i -e 's,^8$,y,' file.txt
+hg commit -m "change 8 to y" > /dev/null 2>&1 # 5 # created new head
+
+hg up -C -r 4 > /dev/null 2>&1 # updated, merged, removed, unresolved
+hg merge -r 5 > /dev/null 2>&1 # updated, merged, removed, unresolved
+hg commit -m "merging 8 to y" # 6
+
+echo
+ec hg diff --nodates --change 6 # must be similar to hg diff --nodates --change 5
+
+#echo
+#hg log
+
+echo
+echo "EOF"
+
+# vim: set ts=4 sw=4 et:
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tests/test-diff-change.out	Tue Jan 13 23:17:19 2009 +0100
@@ -0,0 +1,30 @@
+invoking hg diff --nodates --change 1:
+diff -r 4bb65dda5db4 -r e9b286083166 file.txt
+--- a/file.txt
++++ b/file.txt
+@@ -1,1 +1,1 @@
+-first
++second
+
+invoking hg diff --nodates --change e9b286083166:
+diff -r 4bb65dda5db4 -r e9b286083166 file.txt
+--- a/file.txt
++++ b/file.txt
+@@ -1,1 +1,1 @@
+-first
++second
+
+invoking hg diff --nodates --change 6:
+diff -r e8a0797e73a6 -r aa9873050139 file.txt
+--- a/file.txt
++++ b/file.txt
+@@ -6,6 +6,6 @@
+ 5
+ 6
+ 7
+-8
++y
+ 9
+ 10
+
+EOF
--- a/tests/test-hardlinks-safety.out	Tue Jan 13 22:41:06 2009 +0100
+++ b/tests/test-hardlinks-safety.out	Tue Jan 13 23:17:19 2009 +0100
@@ -2,10 +2,10 @@
 % init
 adding foo to series file
 applying foo
-Now at: foo
+now at: foo
 adding bar to series file
 applying bar
-Now at: bar
+now at: bar
 430ed4828a74fa4047bc816a25500f7472ab4bfe:foo
 4e7abb4840c46a910f6d7b4d3c3fc7e5209e684c:bar
 %
--- a/tests/test-help.out	Tue Jan 13 22:41:06 2009 +0100
+++ b/tests/test-help.out	Tue Jan 13 23:17:19 2009 +0100
@@ -221,6 +221,7 @@
 options:
 
  -r --rev                  revision
+ -c --change               change made by revision
  -a --text                 treat all files as text
  -g --git                  use git extended diff format
     --nodates              don't include dates in diff headers
--- a/tests/test-hgweb-filelog	Tue Jan 13 22:41:06 2009 +0100
+++ b/tests/test-hgweb-filelog	Tue Jan 13 23:17:19 2009 +0100
@@ -19,6 +19,9 @@
 hg mv b c
 hg ci -m "mv b"
 
+echo c >> c
+hg ci -m "change c"
+
 hg log -p
 
 hg serve -n test -p $HGPORT -d --pid-file=hg.pid -E errors.log
--- a/tests/test-hgweb-filelog.out	Tue Jan 13 22:41:06 2009 +0100
+++ b/tests/test-hgweb-filelog.out	Tue Jan 13 23:17:19 2009 +0100
@@ -1,8 +1,20 @@
 adding b
 adding a
 adding a
+changeset:   6:38d962e6234d
+tag:         tip
+user:        test
+date:        Thu Jan 01 00:00:00 1970 +0000
+summary:     change c
+
+diff -r a3b6a9e4507e -r 38d962e6234d c
+--- a/c	Thu Jan 01 00:00:00 1970 +0000
++++ b/c	Thu Jan 01 00:00:00 1970 +0000
+@@ -1,1 +1,2 @@
+ b
++c
+
 changeset:   5:a3b6a9e4507e
-tag:         tip
 user:        test
 date:        Thu Jan 01 00:00:00 1970 +0000
 summary:     mv b
@@ -474,8 +486,8 @@
 <a href="/shortlog?style=spartan">shortlog</a>
 <a href="/graph?style=spartan">graph</a>
 <a href="/tags?style=spartan">tags</a>
-<a href="/file/a3b6a9e4507e/c?style=spartan">file</a>
-<a href="/annotate/a3b6a9e4507e/c?style=spartan">annotate</a>
+<a href="/file/38d962e6234d/c?style=spartan">file</a>
+<a href="/annotate/38d962e6234d/c?style=spartan">annotate</a>
 <a type="application/rss+xml" href="/rss-log/tip/c">rss</a>
 <a type="application/atom+xml" href="/atom-log/tip/c" title="Atom feed for test:c">atom</a>
 </div>
@@ -487,6 +499,31 @@
 <table class="logEntry parity0">
  <tr>
   <th class="age">many years ago:</th>
+  <th class="firstline"><a href="/rev/38d962e6234d?style=spartan">change c</a></th>
+ </tr>
+ <tr>
+  <th class="revision">revision 1:</td>
+  <td class="node">
+   <a href="/file/38d962e6234d/c?style=spartan">38d962e6234d</a>
+   <a href="/diff/38d962e6234d/c?style=spartan">(diff)</a>
+   <a href="/annotate/38d962e6234d/c?style=spartan">(annotate)</a>
+  </td>
+ </tr>
+ 
+ <tr>
+  <th class="author">author:</th>
+  <td class="author">&#116;&#101;&#115;&#116;</td>
+ </tr>
+ <tr>
+  <th class="date">date:</th>
+  <td class="date">Thu Jan 01 00:00:00 1970 +0000</td>
+ </tr>
+</table>
+
+
+<table class="logEntry parity1">
+ <tr>
+  <th class="age">many years ago:</th>
   <th class="firstline"><a href="/rev/a3b6a9e4507e?style=spartan">mv b</a></th>
  </tr>
  <tr>
--- a/tests/test-highlight	Tue Jan 13 22:41:06 2009 +0100
+++ b/tests/test-highlight	Tue Jan 13 23:17:19 2009 +0100
@@ -68,7 +68,7 @@
 
 echo % errors encountered
 cat errors.log
-kill `cat hg.pid`
+"$TESTDIR/killdaemons.py"
 
 # Change the pygments style
 cat > .hg/hgrc <<EOF
@@ -87,4 +87,3 @@
 
 echo % errors encountered
 cat errors.log
-
--- a/tests/test-keyword.out	Tue Jan 13 22:41:06 2009 +0100
+++ b/tests/test-keyword.out	Tue Jan 13 23:17:19 2009 +0100
@@ -213,15 +213,15 @@
 +$Id$
 +tests for different changenodes
 % qpop
-Patch queue now empty
+patch queue now empty
 % qgoto - should imply qpush
 applying mqtest.diff
-Now at: mqtest.diff
+now at: mqtest.diff
 % cat
 $Id: c,v 40a904bbbe4c 1970/01/01 00:00:01 user $
 tests for different changenodes
 % qpop and move on
-Patch queue now empty
+patch queue now empty
 % copy
 % kwfiles added
 a
--- a/tests/test-mq	Tue Jan 13 22:41:06 2009 +0100
+++ b/tests/test-mq	Tue Jan 13 23:17:19 2009 +0100
@@ -289,7 +289,7 @@
 hg strip -qn tip
 hg tip 2>&1 | sed -e 's/unknown node .*/unknown node/'
 hg branches 2>&1 | sed -e 's/unknown node .*/unknown node/'
-hg qpop
+hg qpop 2>&1 | sed -e 's/unknown node .*/unknown node/'
 
 cat >>$HGRCPATH <<EOF
 [diff]
@@ -510,3 +510,8 @@
 hg st
 hg diff --config diff.nodates=True
 hg qdiff --config diff.nodates=True
+
+echo % test popping revisions not in working dir ancestry
+hg qseries -v
+hg up qparent
+hg qpop
--- a/tests/test-mq-caches.out	Tue Jan 13 22:41:06 2009 +0100
+++ b/tests/test-mq-caches.out	Tue Jan 13 23:17:19 2009 +0100
@@ -5,7 +5,7 @@
 No branch cache
 
 # some regular revisions
-Patch queue now empty
+patch queue now empty
 tip: 1
 3f910abad313ff802d3a23a7529433872df9b3ae 1
 3f910abad313ff802d3a23a7529433872df9b3ae bar
@@ -13,7 +13,7 @@
 
 # add some mq patches
 applying p1
-Now at: p1
+now at: p1
 tip: 2
 3f910abad313ff802d3a23a7529433872df9b3ae 1
 3f910abad313ff802d3a23a7529433872df9b3ae bar
@@ -43,11 +43,11 @@
 qbase: 1
 
 # detect an invalid cache
-Patch queue now empty
+patch queue now empty
 applying p0
 applying p1
 applying p2
-Now at: p2
+now at: p2
 tip: 3
 9539f35bdc80732cc9a3f84e46508f1ed1ec8cff 0
 9539f35bdc80732cc9a3f84e46508f1ed1ec8cff foo
--- a/tests/test-mq-guards.out	Tue Jan 13 22:41:06 2009 +0100
+++ b/tests/test-mq-guards.out	Tue Jan 13 23:17:19 2009 +0100
@@ -1,68 +1,68 @@
 adding x
-Patch queue now empty
+patch queue now empty
 % should fail
 abort: no patch named does-not-exist.patch
 % should fail
 abort: no patches applied
 applying a.patch
-Now at: a.patch
+now at: a.patch
 % should guard a.patch
 % should print +a
 a.patch: +a
-Patch queue now empty
+patch queue now empty
 % should fail
 cannot push 'a.patch' - guarded by ['+a']
 a.patch: +a
 % should push b.patch
 applying b.patch
-Now at: b.patch
-Patch queue now empty
+now at: b.patch
+patch queue now empty
 % test selection of an empty guard
 abort: guard cannot be an empty string
 number of unguarded, unapplied patches has changed from 2 to 3
 % should push a.patch
 applying a.patch
-Now at: a.patch
+now at: a.patch
 % should print -a
 c.patch: -a
 % should skip c.patch
 applying b.patch
 skipping c.patch - guarded by '-a'
-Now at: b.patch
+now at: b.patch
 % should display b.patch
 b.patch
 % should push c.patch
 applying c.patch
-Now at: c.patch
-Patch queue now empty
+now at: c.patch
+patch queue now empty
 guards deactivated
 number of unguarded, unapplied patches has changed from 3 to 2
 % should push all
 applying b.patch
 applying c.patch
-Now at: c.patch
-Patch queue now empty
+now at: c.patch
+patch queue now empty
 number of unguarded, unapplied patches has changed from 1 to 2
 % should push a.patch, not b.patch
 applying a.patch
-Now at: a.patch
+now at: a.patch
 applying c.patch
-Now at: c.patch
-Patch queue now empty
+now at: c.patch
+patch queue now empty
 % should push b.patch
 applying b.patch
-Now at: b.patch
+now at: b.patch
 applying c.patch
-Now at: c.patch
+now at: c.patch
 c.patch
-Patch queue now empty
+patch queue now empty
 number of unguarded, unapplied patches has changed from 2 to 3
 % should push a.patch, b.patch
 applying a.patch
-Now at: a.patch
+now at: a.patch
 applying b.patch
-Now at: b.patch
-Patch queue now empty
+now at: b.patch
+patch queue now empty
 number of unguarded, unapplied patches has changed from 3 to 2
 % list patches and guards
 a.patch: +1 +2 -3
@@ -78,15 +78,15 @@
 3
 % should push b.patch
 applying b.patch
-Now at: b.patch
+now at: b.patch
 applying c.patch
-Now at: c.patch
+now at: c.patch
 guards deactivated
 popping guarded patches
-Patch queue now empty
+patch queue now empty
 reapplying unguarded patches
 applying c.patch
-Now at: c.patch
+now at: c.patch
 % guards in series file: +1 +2 -3
 +1
 +2
@@ -100,7 +100,7 @@
 new.patch: +1 +2 -3
 b.patch: +2
 c.patch: unguarded
-Now at: c.patch
+now at: c.patch
 % should show new.patch and b.patch as Guarded, c.patch as Applied
 % and d.patch as Unapplied
 0 G new.patch
@@ -112,7 +112,7 @@
 1 G b.patch
 2 A c.patch
 3 G d.patch
-Patch queue now empty
+patch queue now empty
 new.patch: +1 +2 -3
 b.patch: +2
 c.patch: unguarded
@@ -141,7 +141,7 @@
 skipping b.patch - guarded by ['+2']
 applying c.patch
 skipping d.patch - guarded by ['+2']
-Now at: c.patch
+now at: c.patch
 % hg qapplied
 new.patch
 c.patch
--- a/tests/test-mq-header-date.out	Tue Jan 13 22:41:06 2009 +0100
+++ b/tests/test-mq-header-date.out	Tue Jan 13 23:17:19 2009 +0100
@@ -42,7 +42,7 @@
 +2
 1: [mq]: 2.patch - test
 0: [mq]: 1.patch - test
-Now at: 1.patch
+now at: 1.patch
 ==== qnew -d -m
 # HG changeset patch
 # Date 6 0
@@ -127,10 +127,9 @@
 2: Four - test
 1: Three (again) - test
 0: [mq]: 1.patch - test
-Now at: 3.patch
+now at: 3.patch
 ==== qnew with HG header
-Now at: 3.patch
-Now at: 5.patch
+now at: 3.patch
 # HG changeset patch
 # Date 10 0
 2: imported patch 5.patch - test - 10.00
@@ -186,7 +185,7 @@
 2: [mq]: 5.patch - test
 1: Three (again) - test
 0: [mq]: 1.patch - test
-Now at: 5.patch
+now at: 5.patch
 ==== qnew -d
 adding 7
 # HG changeset patch
@@ -241,7 +240,7 @@
 2: [mq]: 5.patch - test
 1: Three (again) - test
 0: [mq]: 1.patch - test
-Now at: 7.patch
+now at: 7.patch
 ==== qnew -m
 adding 9
 Nine
@@ -271,14 +270,14 @@
 2: [mq]: 5.patch - test
 1: Three (again) - test
 0: [mq]: 1.patch - test
-Now at: 7.patch
+now at: 7.patch
 ==== qpop -a / qpush -a
-Patch queue now empty
+patch queue now empty
 applying 1.patch
 applying 3.patch
 applying 5.patch
 applying 7.patch
-Now at: 7.patch
+now at: 7.patch
 3: imported patch 7.patch - john - 13.00
 2: imported patch 5.patch - test - 11.00
 1: Three (again) - test - 8.00
--- a/tests/test-mq-header-from	Tue Jan 13 22:41:06 2009 +0100
+++ b/tests/test-mq-header-from	Tue Jan 13 23:17:19 2009 +0100
@@ -83,7 +83,7 @@
 hg qpop
 echo "# HG changeset patch" >>.hg/patches/5.patch
 echo "# User johndoe" >>.hg/patches/5.patch
-hg qpush 2>&1 | grep 'Now at'
+hg qpush 2>&1 | grep 'now at'
 catlog 5
 
 echo ==== hg qref
--- a/tests/test-mq-header-from.out	Tue Jan 13 22:41:06 2009 +0100
+++ b/tests/test-mq-header-from.out	Tue Jan 13 23:17:19 2009 +0100
@@ -131,8 +131,8 @@
 1: [mq]: 2.patch - jane
 0: [mq]: 1.patch - mary
 ==== qnew with HG header
-Now at: 4.patch
-Now at: 5.patch
+now at: 4.patch
+now at: 5.patch
 # HG changeset patch
 # User johndoe
 4: imported patch 5.patch - johndoe
@@ -184,13 +184,13 @@
 1: [mq]: 2.patch - jane
 0: [mq]: 1.patch - mary
 ==== qpop -a / qpush -a
-Patch queue now empty
+patch queue now empty
 applying 1.patch
 applying 2.patch
 applying 3.patch
 applying 4.patch
 applying 5.patch
-Now at: 5.patch
+now at: 5.patch
 4: imported patch 5.patch - johndeere
 3: Four - jane
 2: Three (again) - maria
--- a/tests/test-mq-merge.out	Tue Jan 13 22:41:06 2009 +0100
+++ b/tests/test-mq-merge.out	Tue Jan 13 23:17:19 2009 +0100
@@ -8,9 +8,9 @@
 b
 merging with queue at: .hg/patches.1
 applying rm_a
-Now at: rm_a
+now at: rm_a
 b
-Patch queue now empty
+patch queue now empty
 
 % init t2
 adding a
@@ -21,4 +21,4 @@
 merging with queue at refqueue
 (working directory not at tip)
 applying patcha
-Now at: patcha
+now at: patcha
--- a/tests/test-mq-missingfiles.out	Tue Jan 13 22:41:06 2009 +0100
+++ b/tests/test-mq-missingfiles.out	Tue Jan 13 23:17:19 2009 +0100
@@ -1,12 +1,12 @@
 adding b
-Patch queue now empty
+patch queue now empty
 % push patch with missing target
 applying changeb
 unable to find 'b' for patching
 2 out of 2 hunks FAILED -- saving rejects to file b.rej
 patch failed, unable to continue (try -v)
 patch failed, rejects left in working dir
-Errors during apply, please fix and refresh changeb
+errors during apply, please fix and refresh changeb
 % display added files
 a
 c
@@ -26,7 +26,7 @@
 +c
 +c
 adding b
-Patch queue now empty
+patch queue now empty
 % push git patch with missing target
 applying changeb
 unable to find 'b' for patching
@@ -35,7 +35,7 @@
 b: No such file or directory
 b not tracked!
 patch failed, rejects left in working dir
-Errors during apply, please fix and refresh changeb
+errors during apply, please fix and refresh changeb
 ? b.rej
 % display added files
 a
@@ -49,6 +49,6 @@
 
 % test push creating directory during git copy or rename
 adding a
-Patch queue now empty
+patch queue now empty
 applying patch
-Now at: patch
+now at: patch
--- a/tests/test-mq-pull-from-bundle.out	Tue Jan 13 22:41:06 2009 +0100
+++ b/tests/test-mq-pull-from-bundle.out	Tue Jan 13 23:17:19 2009 +0100
@@ -13,7 +13,7 @@
 1: main: one updated.
 ====== Setup queue
 adding two
-Patch queue now empty
+patch queue now empty
 ====== Bundle queue
 1 changesets found
 ====== Clone base
--- a/tests/test-mq-qclone-http.out	Tue Jan 13 22:41:06 2009 +0100
+++ b/tests/test-mq-qclone-http.out	Tue Jan 13 23:17:19 2009 +0100
@@ -25,7 +25,7 @@
 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
 a
 applying b.patch
-Now at: b.patch
+now at: b.patch
 imported patch b.patch
 a
 % test with normal collection
@@ -50,7 +50,7 @@
 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
 a
 applying b.patch
-Now at: b.patch
+now at: b.patch
 imported patch b.patch
 a
 % test with old-style collection
@@ -75,6 +75,6 @@
 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
 a
 applying b.patch
-Now at: b.patch
+now at: b.patch
 imported patch b.patch
 a
--- a/tests/test-mq-qdelete.out	Tue Jan 13 22:41:06 2009 +0100
+++ b/tests/test-mq-qdelete.out	Tue Jan 13 23:17:19 2009 +0100
@@ -1,14 +1,14 @@
 adding base
 abort: qdelete requires at least one revision or patch name
 abort: cannot delete applied patch c
-Now at: b
+now at: b
 a
 b
 a
 b
 series
 status
-Now at: a
+now at: a
 a
 b
 series
@@ -27,10 +27,10 @@
 no patches applied
 abort: revision 0 is not managed
 abort: cannot delete revision 2 above applied patches
-Now at: b
+now at: b
 abort: unknown revision 'c'!
 applying c
-Now at: c
+now at: c
 c
 3 imported patch c
 2 [mq]: b
--- a/tests/test-mq-qdiff.out	Tue Jan 13 22:41:06 2009 +0100
+++ b/tests/test-mq-qdiff.out	Tue Jan 13 23:17:19 2009 +0100
@@ -26,7 +26,7 @@
 +patched
 % revert
 % qpop
-Patch queue now empty
+patch queue now empty
 % qdelete mqbase
 % commit 2
 adding lines
--- a/tests/test-mq-qgoto.out	Tue Jan 13 22:41:06 2009 +0100
+++ b/tests/test-mq-qgoto.out	Tue Jan 13 23:17:19 2009 +0100
@@ -1,21 +1,21 @@
 adding a
-Now at: a.patch
+now at: a.patch
 applying b.patch
 applying c.patch
-Now at: c.patch
-Now at: b.patch
+now at: c.patch
+now at: b.patch
 
 % Using index
-Now at: a.patch
+now at: a.patch
 applying b.patch
 applying c.patch
-Now at: c.patch
+now at: c.patch
 
 % No warnings when using index
-Now at: b.patch
+now at: b.patch
 applying c.patch
 applying bug314159
-Now at: bug314159
+now at: bug314159
 
 % Detect ambiguous non-index
 patch name "14" is ambiguous:
--- a/tests/test-mq-qimport.out	Tue Jan 13 22:41:06 2009 +0100
+++ b/tests/test-mq-qimport.out	Tue Jan 13 23:17:19 2009 +0100
@@ -6,19 +6,19 @@
 % import patch that already exists
 abort: patch "url.diff" already exists
 applying url.diff
-Now at: url.diff
+now at: url.diff
 foo
-Patch queue now empty
+patch queue now empty
 % qimport -f
 adding url.diff to series file
 applying url.diff
-Now at: url.diff
+now at: url.diff
 foo2
-Patch queue now empty
+patch queue now empty
 % build diff with CRLF
 adding b
 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
 % qimport CRLF diff
 adding b.diff to series file
 applying b.diff
-Now at: b.diff
+now at: b.diff
--- a/tests/test-mq-qnew.out	Tue Jan 13 22:41:06 2009 +0100
+++ b/tests/test-mq-qnew.out	Tue Jan 13 23:17:19 2009 +0100
@@ -20,7 +20,7 @@
 abort: patch "first.patch" already exists
 abort: patch "first.patch" already exists
 % qnew -f from a subdirectory
-Patch queue now empty
+patch queue now empty
 adding d/b
 M d/b
 diff --git a/d/b b/d/b
--- a/tests/test-mq-qpush-fail.out	Tue Jan 13 22:41:06 2009 +0100
+++ b/tests/test-mq-qpush-fail.out	Tue Jan 13 23:17:19 2009 +0100
@@ -1,5 +1,5 @@
 adding foo
-Patch queue now empty
+patch queue now empty
 applying patch1
 applying patch2
 applying bad-patch
--- a/tests/test-mq-qrefresh-replace-log-message.out	Tue Jan 13 22:41:06 2009 +0100
+++ b/tests/test-mq-qrefresh-replace-log-message.out	Tue Jan 13 23:17:19 2009 +0100
@@ -1,7 +1,7 @@
 =======================
 Should fail if no patches applied
-No patches applied
-No patches applied
+no patches applied
+no patches applied
 =======================
 Should display 'First commit message'
 description:
--- a/tests/test-mq-safety.out	Tue Jan 13 22:41:06 2009 +0100
+++ b/tests/test-mq-safety.out	Tue Jan 13 23:17:19 2009 +0100
@@ -1,7 +1,7 @@
 % try to commit on top of a patch
 abort: cannot commit over an applied mq patch
 % qpop/qrefresh on the wrong revision
-abort: working directory revision is not qtip
+abort: popping would remove a revision not managed by this patch queue
 using patch queue:
 abort: popping would remove a revision not managed by this patch queue
 abort: working directory revision is not qtip
--- a/tests/test-mq-symlinks.out	Tue Jan 13 22:41:06 2009 +0100
+++ b/tests/test-mq-symlinks.out	Tue Jan 13 23:17:19 2009 +0100
@@ -1,10 +1,10 @@
 a -> a not a symlink
 a -> b
-Now at: base.patch
+now at: base.patch
 applying symlink.patch
-Now at: symlink.patch
+now at: symlink.patch
 a -> b
-Now at: symlink.patch
+now at: symlink.patch
 applying removesl.patch
-Now at: removesl.patch
+now at: removesl.patch
 C b
--- a/tests/test-mq.out	Tue Jan 13 22:41:06 2009 +0100
+++ b/tests/test-mq.out	Tue Jan 13 23:17:19 2009 +0100
@@ -33,7 +33,7 @@
  qfold        fold the named patches into the current patch
  qgoto        push or pop patches until named patch is at top of stack
  qguard       set or print guards for a patch
- qheader      Print the header of the topmost or specified patch
+ qheader      print the header of the topmost or specified patch
  qimport      import a patch
  qinit        init a new queue repository
  qnew         create a new patch
@@ -108,28 +108,28 @@
  a
 +a
 % qpop
-Patch queue now empty
+patch queue now empty
 % qpush
 applying test.patch
-Now at: test.patch
+now at: test.patch
 % pop/push outside repo
-Patch queue now empty
+patch queue now empty
 applying test.patch
-Now at: test.patch
+now at: test.patch
 % qrefresh in subdir
 % pop/push -a in subdir
-Patch queue now empty
+patch queue now empty
 applying test.patch
 applying test2.patch
-Now at: test2.patch
+now at: test2.patch
 % qseries
 test.patch
 test2.patch
-Now at: test.patch
+now at: test.patch
 0 A test.patch: foo bar
 1 U test2.patch: 
 applying test2.patch
-Now at: test2.patch
+now at: test2.patch
 % qapplied
 test.patch
 test2.patch
@@ -138,11 +138,11 @@
 % qprev
 test.patch
 % qnext
-All patches applied
+all patches applied
 % pop, qnext, qprev, qapplied
-Now at: test.patch
+now at: test.patch
 test2.patch
-Only one patch applied
+only one patch applied
 test.patch
 % commit should fail
 abort: cannot commit over an applied mq patch
@@ -155,19 +155,19 @@
 test2.patch
 % qpush/qpop with index
 applying test2.patch
-Now at: test2.patch
-Now at: test.patch
+now at: test2.patch
+now at: test.patch
 applying test1b.patch
-Now at: test1b.patch
+now at: test1b.patch
 applying test2.patch
-Now at: test2.patch
-Now at: test1b.patch
-Now at: test.patch
+now at: test2.patch
+now at: test1b.patch
+now at: test.patch
 applying test1b.patch
 applying test2.patch
-Now at: test2.patch
+now at: test2.patch
 % push should succeed
-Patch queue now empty
+patch queue now empty
 pushing to ../../k
 searching for changes
 adding changesets
@@ -178,9 +178,9 @@
 applying test.patch
 applying test1b.patch
 applying test2.patch
-Now at: test2.patch
+now at: test2.patch
   % pops all patches and succeeds
-Patch queue now empty
+patch queue now empty
   qpop -a succeeds
   % does nothing and succeeds
 no patches applied
@@ -190,15 +190,15 @@
   qpop fails
   % pushes a patch and succeeds
 applying test.patch
-Now at: test.patch
+now at: test.patch
   qpush succeeds
   % pops a patch and succeeds
-Patch queue now empty
+patch queue now empty
   qpop succeeds
   % pushes up to test1b.patch and succeeds
 applying test.patch
 applying test1b.patch
-Now at: test1b.patch
+now at: test1b.patch
   qpush test1b.patch succeeds
   % does nothing and succeeds
 qpush: test1b.patch is already at the top
@@ -213,12 +213,12 @@
 abort: patch test2.patch is not applied
   qpop test2.patch fails
   % pops up to test.patch and succeeds
-Now at: test.patch
+now at: test.patch
   qpop test.patch succeeds
   % pushes all patches and succeeds
 applying test1b.patch
 applying test2.patch
-Now at: test2.patch
+now at: test2.patch
   qpush -a succeeds
   % does nothing and succeeds
 all patches are currently applied
@@ -269,14 +269,14 @@
 +f
 M a
 % qpush failure
-Patch queue now empty
+patch queue now empty
 applying foo
 applying bar
 file foo already exists
 1 out of 1 hunks FAILED -- saving rejects to file foo.rej
 patch failed, unable to continue (try -v)
 patch failed, rejects left in working dir
-Errors during apply, please fix and refresh bar
+errors during apply, please fix and refresh bar
 ? foo
 ? foo.rej
 % mq tags
@@ -284,7 +284,7 @@
 1 qbase foo
 2 qtip bar tip
 % bad node in status
-Now at: foo
+now at: foo
 changeset:   0:cb9a9f314b8b
 mq status file refers to unknown node
 tag:         tip
@@ -294,7 +294,7 @@
 
 mq status file refers to unknown node
 default                        0:cb9a9f314b8b
-abort: working directory revision is not qtip
+abort: trying to pop unknown node
 new file
 
 diff --git a/new b/new
@@ -308,9 +308,9 @@
 diff --git a/new b/copy
 copy from new
 copy to copy
-Now at: new
+now at: new
 applying copy
-Now at: copy
+now at: copy
 diff --git a/new b/copy
 copy from new
 copy to copy
@@ -325,10 +325,10 @@
 adding manifests
 adding file changes
 added 1 changesets with 1 changes to 1 files
-Patch queue now empty
+patch queue now empty
 (working directory not at tip)
 applying bar
-Now at: bar
+now at: bar
 diff --git a/bar b/bar
 new file mode 100644
 --- /dev/null
@@ -359,10 +359,10 @@
 adding manifests
 adding file changes
 added 1 changesets with 1 changes to 1 files
-Patch queue now empty
+patch queue now empty
 (working directory not at tip)
 applying bar
-Now at: bar
+now at: bar
 diff --git a/foo b/bleh
 rename from foo
 rename to bleh
@@ -386,16 +386,16 @@
 % refresh omitting an added file
 C newfile
 A newfile
-Now at: bar
+now at: bar
 % create a git patch
 diff --git a/alexander b/alexander
 % create a git binary patch
 8ba2a2f3e77b55d03051ff9c24ad65e7  bucephalus
 diff --git a/bucephalus b/bucephalus
 % check binary patches can be popped and pushed
-Now at: addalexander
+now at: addalexander
 applying addbucephalus
-Now at: addbucephalus
+now at: addbucephalus
 8ba2a2f3e77b55d03051ff9c24ad65e7  bucephalus
 % strip again
 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
@@ -461,7 +461,7 @@
     rev 0: add foo
 patch repo:
     rev 0: checkpoint
-Patch queue now empty
+patch queue now empty
 main repo:
     rev 0: add foo
 patch repo:
@@ -475,13 +475,13 @@
     rev 0: checkpoint
 % test applying on an empty file (issue 1033)
 adding a
-Patch queue now empty
+patch queue now empty
 applying changea
-Now at: changea
+now at: changea
 % test qpush with --force, issue1087
 adding bye.txt
 adding hello.txt
-Patch queue now empty
+patch queue now empty
 % qpush should fail, local changes
 abort: local changes found, refresh first
 % apply force, should not discard changes with empty patch
@@ -489,7 +489,7 @@
 patch: **** Only garbage was found in the patch input.
 patch failed, unable to continue (try -v)
 patch empty is empty
-Now at: empty
+now at: empty
 diff -r bf5fc3f07a0a hello.txt
 --- a/hello.txt
 +++ b/hello.txt
@@ -512,12 +512,12 @@
 summary:     imported patch empty
 
 
-Patch queue now empty
+patch queue now empty
 % qpush should fail, local changes
 abort: local changes found, refresh first
 % apply force, should discard changes in hello, but not bye
 applying empty
-Now at: empty
+now at: empty
 M bye.txt
 diff -r ba252371dbc1 bye.txt
 --- a/bye.txt
@@ -538,3 +538,7 @@
  hello
 +world
 +universe
+% test popping revisions not in working dir ancestry
+0 A empty
+1 files updated, 0 files merged, 0 files removed, 0 files unresolved
+patch queue now empty
--- a/tests/test-notify.out	Tue Jan 13 22:41:06 2009 +0100
+++ b/tests/test-notify.out	Tue Jan 13 23:17:19 2009 +0100
@@ -150,7 +150,8 @@
 	b
 diffstat:
 
-files patched: 1
+ a |  1 +
+ 1 files changed, 1 insertions(+), 0 deletions(-)
 
 diffs (6 lines):
 
--- a/tests/test-patchbomb	Tue Jan 13 22:41:06 2009 +0100
+++ b/tests/test-patchbomb	Tue Jan 13 23:17:19 2009 +0100
@@ -11,6 +11,8 @@
 echo "[extensions]" >> $HGRCPATH
 echo "patchbomb=" >> $HGRCPATH
 
+COLUMNS=80; export COLUMNS
+
 hg init t
 cd t
 echo a > a
--- a/tests/test-patchbomb.out	Tue Jan 13 22:41:06 2009 +0100
+++ b/tests/test-patchbomb.out	Tue Jan 13 23:17:19 2009 +0100
@@ -196,7 +196,8 @@
 
 c
 
-files patched: 1
+ c |  1 +
+ 1 files changed, 1 insertions(+), 0 deletions(-)
 
 
 Displaying [PATCH] test ...
@@ -211,7 +212,8 @@
 To: foo
 Cc: bar
 
-files patched: 1
+ c |  1 +
+ 1 files changed, 1 insertions(+), 0 deletions(-)
 
 
 # HG changeset patch
@@ -232,15 +234,19 @@
 
 a
 
-files patched: 1
+ a |  1 +
+ 1 files changed, 1 insertions(+), 0 deletions(-)
 
 b
 
-files patched: 1
+ b |  1 +
+ 1 files changed, 1 insertions(+), 0 deletions(-)
 
 Final summary:
 
-files patched: 2
+ a |  1 +
+ b |  1 +
+ 2 files changed, 2 insertions(+), 0 deletions(-)
 
 
 Write the introductory message for the patch series.
@@ -258,7 +264,9 @@
 Cc: bar
 
 
-files patched: 2
+ a |  1 +
+ b |  1 +
+ 2 files changed, 2 insertions(+), 0 deletions(-)
 
 Displaying [PATCH 1 of 2] a ...
 Content-Type: text/plain; charset="us-ascii"
@@ -274,7 +282,8 @@
 To: foo
 Cc: bar
 
-files patched: 1
+ a |  1 +
+ 1 files changed, 1 insertions(+), 0 deletions(-)
 
 
 # HG changeset patch
@@ -304,7 +313,8 @@
 To: foo
 Cc: bar
 
-files patched: 1
+ b |  1 +
+ 1 files changed, 1 insertions(+), 0 deletions(-)
 
 
 # HG changeset patch
--- a/tests/test-pull-http	Tue Jan 13 22:41:06 2009 +0100
+++ b/tests/test-pull-http	Tue Jan 13 23:17:19 2009 +0100
@@ -19,7 +19,7 @@
 hg serve -p $HGPORT -d --pid-file=hg.pid -E errors.log
 cat hg.pid >> $DAEMON_PIDS
 hg clone http://localhost:$HGPORT/ test3 | sed -e 's,:[0-9][0-9]*/,/,'
-kill `cat hg.pid`
+"$TESTDIR/killdaemons.py"
 echo % serve errors
 cat errors.log
 
--- a/tests/test-walkrepo.py	Tue Jan 13 22:41:06 2009 +0100
+++ b/tests/test-walkrepo.py	Tue Jan 13 23:17:19 2009 +0100
@@ -24,26 +24,26 @@
     reposet = frozenset(walkrepos('.', followsym=True))
     if sym and (len(reposet) != 3):
         print "reposet = %r" % (reposet,)
-        raise SystemExit(1, "Found %d repositories when I should have found 3" % (len(reposet),))
+        print "Found %d repositories when I should have found 3" % (len(reposet),)
     if (not sym) and (len(reposet) != 2):
         print "reposet = %r" % (reposet,)
-        raise SystemExit(1, "Found %d repositories when I should have found 2" % (len(reposet),))
+        print "Found %d repositories when I should have found 2" % (len(reposet),)
     sub1set = frozenset((pjoin('.', 'sub1'),
                          pjoin('.', 'circle', 'subdir', 'sub1')))
     if len(sub1set & reposet) != 1:
         print "sub1set = %r" % (sub1set,)
         print "reposet = %r" % (reposet,)
-        raise SystemExit(1, "sub1set and reposet should have exactly one path in common.")
+        print "sub1set and reposet should have exactly one path in common."
     sub2set = frozenset((pjoin('.', 'subsub1'),
                          pjoin('.', 'subsubdir', 'subsub1')))
     if len(sub2set & reposet) != 1:
         print "sub2set = %r" % (sub2set,)
         print "reposet = %r" % (reposet,)
-        raise SystemExit(1, "sub1set and reposet should have exactly one path in common.")
+        print "sub1set and reposet should have exactly one path in common."
     sub3 = pjoin('.', 'circle', 'top1')
     if sym and not (sub3 in reposet):
         print "reposet = %r" % (reposet,)
-        raise SystemExit(1, "Symbolic links are supported and %s is not in reposet" % (sub3,))
+        print "Symbolic links are supported and %s is not in reposet" % (sub3,)
 
 runtest()
 if sym: