merge with stable
authorMatt Mackall <mpm@selenic.com>
Fri, 22 Jul 2011 17:17:23 -0500
changeset 14918 ebdfdba0faaf
parent 14913 44382887d012 (diff)
parent 14917 2957b8b1e809 (current diff)
child 14921 e259375459d6
merge with stable
mercurial/commands.py
mercurial/dispatch.py
mercurial/util.py
--- a/hgext/eol.py	Thu Jul 21 15:56:15 2011 -0500
+++ b/hgext/eol.py	Fri Jul 22 17:17:23 2011 -0500
@@ -52,9 +52,10 @@
    The rules will first apply when files are touched in the working
    copy, e.g. by updating to null and back to tip to touch all files.
 
-The extension uses an optional ``[eol]`` section in your hgrc file
-(not the ``.hgeol`` file) for settings that control the overall
-behavior. There are two settings:
+The extension uses an optional ``[eol]`` section read from both the
+normal Mercurial configuration files and the ``.hgeol`` file, with the
+latter overriding the former. You can use that section to control the
+overall behavior. There are three settings:
 
 - ``eol.native`` (default ``os.linesep``) can be set to ``LF`` or
   ``CRLF`` to override the default interpretation of ``native`` for
@@ -67,6 +68,10 @@
   Such files are normally not touched under the assumption that they
   have mixed EOLs on purpose.
 
+- ``eol.fix-trailing-newline`` (default False) can be set to True to
+  ensure that converted files end with a EOL character (either ``\\n``
+  or ``\\r\\n`` as per the configured patterns).
+
 The extension provides ``cleverencode:`` and ``cleverdecode:`` filters
 like the deprecated win32text extension does. This means that you can
 disable win32text and enable eol and your filters will still work. You
@@ -106,6 +111,8 @@
         return s
     if ui.configbool('eol', 'only-consistent', True) and inconsistenteol(s):
         return s
+    if ui.configbool('eol', 'fix-trailing-newline', False) and s and s[-1] != '\n':
+        s = s + '\n'
     return eolre.sub('\n', s)
 
 def tocrlf(s, params, ui, **kwargs):
@@ -114,6 +121,8 @@
         return s
     if ui.configbool('eol', 'only-consistent', True) and inconsistenteol(s):
         return s
+    if ui.configbool('eol', 'fix-trailing-newline', False) and s and s[-1] != '\n':
+        s = s + '\n'
     return eolre.sub('\r\n', s)
 
 def isbinary(s, params):
@@ -158,7 +167,7 @@
         # about inconsistent newlines.
         self.match = match.match(root, '', [], include, exclude)
 
-    def setfilters(self, ui):
+    def copytoui(self, ui):
         for pattern, style in self.cfg.items('patterns'):
             key = style.upper()
             try:
@@ -167,6 +176,9 @@
             except KeyError:
                 ui.warn(_("ignoring unknown EOL style '%s' from %s\n")
                         % (style, self.cfg.source('patterns', pattern)))
+        # eol.only-consistent can be specified in ~/.hgrc or .hgeol
+        for k, v in self.cfg.items('eol'):
+            ui.setconfig('eol', k, v)
 
     def checkrev(self, repo, ctx, files):
         failed = []
@@ -273,7 +285,7 @@
             eol = parseeol(self.ui, self, nodes)
             if eol is None:
                 return None
-            eol.setfilters(self.ui)
+            eol.copytoui(self.ui)
             return eol.match
 
         def _hgcleardirstate(self):
--- a/hgext/keyword.py	Thu Jul 21 15:56:15 2011 -0500
+++ b/hgext/keyword.py	Fri Jul 22 17:17:23 2011 -0500
@@ -322,11 +322,11 @@
         text = self.kwt.shrink(self.path, text)
         return super(kwfilelog, self).cmp(node, text)
 
-def _status(ui, repo, kwt, *pats, **opts):
+def _status(ui, repo, wctx, kwt, *pats, **opts):
     '''Bails out if [keyword] configuration is not active.
     Returns status of working directory.'''
     if kwt:
-        return repo.status(match=scmutil.match(repo[None], pats, opts), clean=True,
+        return repo.status(match=scmutil.match(wctx, pats, opts), clean=True,
                            unknown=opts.get('unknown') or opts.get('all'))
     if ui.configitems('keyword'):
         raise util.Abort(_('[keyword] patterns cannot match'))
@@ -340,7 +340,7 @@
     kwt = kwtools['templater']
     wlock = repo.wlock()
     try:
-        status = _status(ui, repo, kwt, *pats, **opts)
+        status = _status(ui, repo, wctx, kwt, *pats, **opts)
         modified, added, removed, deleted, unknown, ignored, clean = status
         if modified or added or removed or deleted:
             raise util.Abort(_('outstanding uncommitted changes'))
@@ -475,13 +475,13 @@
       i = ignored (not tracked)
     '''
     kwt = kwtools['templater']
-    status = _status(ui, repo, kwt, *pats, **opts)
+    wctx = repo[None]
+    status = _status(ui, repo, wctx, kwt, *pats, **opts)
     cwd = pats and repo.getcwd() or ''
     modified, added, removed, deleted, unknown, ignored, clean = status
     files = []
     if not opts.get('unknown') or opts.get('all'):
         files = sorted(modified + added + clean)
-    wctx = repo[None]
     kwfiles = kwt.iskwfile(files, wctx)
     kwdeleted = kwt.iskwfile(deleted, wctx)
     kwunknown = kwt.iskwfile(unknown, wctx)
--- a/hgext/notify.py	Thu Jul 21 15:56:15 2011 -0500
+++ b/hgext/notify.py	Fri Jul 22 17:17:23 2011 -0500
@@ -167,9 +167,6 @@
         return [mail.addressencode(self.ui, s, self.charsets, self.test)
                 for s in sorted(subs)]
 
-    def url(self, path=None):
-        return self.ui.config('web', 'baseurl') + (path or self.root)
-
     def node(self, ctx, **props):
         '''format one changeset, unless it is a suppressed merge.'''
         if not self.merge and len(ctx.parents()) > 1:
--- a/hgext/progress.py	Thu Jul 21 15:56:15 2011 -0500
+++ b/hgext/progress.py	Fri Jul 22 17:17:23 2011 -0500
@@ -27,6 +27,9 @@
 
   [progress]
   delay = 3 # number of seconds (float) before showing the progress bar
+  changedelay = 1 # changedelay: minimum delay before showing a new topic.
+                  # If set to less than 3 * refresh, that value will
+                  # be used instead.
   refresh = 0.1 # time in seconds between refreshes of the progress bar
   format = topic bar number estimate # format of the progress bar
   width = <none> # if set, the maximum width of the progress information
@@ -53,7 +56,7 @@
     return ' '.join(s for s in args if s)
 
 def shouldprint(ui):
-    return (util.isatty(sys.stderr) or ui.configbool('progress', 'assume-tty'))
+    return util.isatty(sys.stderr) or ui.configbool('progress', 'assume-tty')
 
 def fmtremaining(seconds):
     if seconds < 60:
@@ -105,9 +108,13 @@
         self.printed = False
         self.lastprint = time.time() + float(self.ui.config(
             'progress', 'delay', default=3))
+        self.lasttopic = None
         self.indetcount = 0
         self.refresh = float(self.ui.config(
             'progress', 'refresh', default=0.1))
+        self.changedelay = max(3 * self.refresh,
+                               float(self.ui.config(
+                                   'progress', 'changedelay', default=1)))
         self.order = self.ui.configlist(
             'progress', 'format',
             default=['topic', 'bar', 'number', 'estimate'])
@@ -184,6 +191,7 @@
         else:
             out = spacejoin(head, tail)
         sys.stderr.write('\r' + out[:termwidth])
+        self.lasttopic = topic
         sys.stderr.flush()
 
     def clear(self):
@@ -248,10 +256,18 @@
                 self.topics.append(topic)
             self.topicstates[topic] = pos, item, unit, total
             if now - self.lastprint >= self.refresh and self.topics:
-                self.lastprint = now
-                self.show(now, topic, *self.topicstates[topic])
+                if (self.lasttopic is None # first time we printed
+                    # not a topic change
+                    or topic == self.lasttopic
+                    # it's been long enough we should print anyway
+                    or now - self.lastprint >= self.changedelay):
+                    self.lastprint = now
+                    self.show(now, topic, *self.topicstates[topic])
+
+_singleton = None
 
 def uisetup(ui):
+    global _singleton
     class progressui(ui.__class__):
         _progbar = None
 
@@ -278,7 +294,9 @@
         # we instantiate one globally shared progress bar to avoid
         # competing progress bars when multiple UI objects get created
         if not progressui._progbar:
-            progressui._progbar = progbar(ui)
+            if _singleton is None:
+                _singleton = progbar(ui)
+            progressui._progbar = _singleton
 
 def reposetup(ui, repo):
     uisetup(repo.ui)
--- a/mercurial/bookmarks.py	Thu Jul 21 15:56:15 2011 -0500
+++ b/mercurial/bookmarks.py	Fri Jul 22 17:17:23 2011 -0500
@@ -26,7 +26,13 @@
     bookmarks = {}
     try:
         for line in repo.opener('bookmarks'):
-            sha, refspec = line.strip().split(' ', 1)
+            line = line.strip()
+            if not line:
+                continue
+            if ' ' not in line:
+                repo.ui.warn(_('malformed line in .hg/bookmarks: %r\n') % line)
+                continue
+            sha, refspec = line.split(' ', 1)
             refspec = encoding.tolocal(refspec)
             try:
                 bookmarks[refspec] = repo.changelog.lookup(sha)
--- a/mercurial/commands.py	Thu Jul 21 15:56:15 2011 -0500
+++ b/mercurial/commands.py	Fri Jul 22 17:17:23 2011 -0500
@@ -119,6 +119,10 @@
     ('', 'stat', None, _('output diffstat-style summary of changes')),
 ]
 
+mergetoolopts = [
+    ('t', 'tool', '', _('specify merge tool')),
+]
+
 similarityopts = [
     ('s', 'similarity', '',
      _('guess renamed files by similarity (0<=s<=100)'), _('SIMILARITY'))
@@ -349,9 +353,8 @@
 @command('backout',
     [('', 'merge', None, _('merge with old dirstate parent after backout')),
     ('', 'parent', '', _('parent to choose when backing out merge'), _('REV')),
-    ('t', 'tool', '', _('specify merge tool')),
     ('r', 'rev', '', _('revision to backout'), _('REV')),
-    ] + walkopts + commitopts + commitopts2,
+    ] + mergetoolopts + walkopts + commitopts + commitopts2,
     _('[OPTION]... [-r] REV'))
 def backout(ui, repo, node=None, rev=None, **opts):
     '''reverse effect of earlier changeset
@@ -1102,8 +1105,8 @@
     ctx = repo[node]
     parents = ctx.parents()
 
-    if bheads and not [x for x in parents
-                       if x.node() in bheads and x.branch() == branch]:
+    if (bheads and node not in bheads and not
+        [x for x in parents if x.node() in bheads and x.branch() == branch]):
         ui.status(_('created new head\n'))
         # The message is not printed for initial roots. For the other
         # changesets, it is printed in the following situations:
@@ -3505,10 +3508,10 @@
 
 @command('^merge',
     [('f', 'force', None, _('force a merge with outstanding changes')),
-    ('t', 'tool', '', _('specify merge tool')),
     ('r', 'rev', '', _('revision to merge'), _('REV')),
     ('P', 'preview', None,
-     _('review revisions to merge (no merge is performed)'))],
+     _('review revisions to merge (no merge is performed)'))
+     ] + mergetoolopts,
     _('[-P] [-f] [[-r] REV]'))
 def merge(ui, repo, node=None, **opts):
     """merge working directory with another revision
@@ -3587,7 +3590,7 @@
 
     try:
         # ui.forcemerge is an internal variable, do not document
-        ui.setconfig('ui', 'forcemerge', opts.get('tool', ''))
+        repo.ui.setconfig('ui', 'forcemerge', opts.get('tool', ''))
         return hg.merge(repo, node, force=opts.get('force'))
     finally:
         ui.setconfig('ui', 'forcemerge', '')
@@ -4049,9 +4052,8 @@
     ('l', 'list', None, _('list state of files needing merge')),
     ('m', 'mark', None, _('mark files as resolved')),
     ('u', 'unmark', None, _('mark files as unresolved')),
-    ('t', 'tool', '', _('specify merge tool')),
     ('n', 'no-status', None, _('hide status prefix'))]
-    + walkopts,
+    + mergetoolopts + walkopts,
     _('[OPTION]... [FILE]...'))
 def resolve(ui, repo, *pats, **opts):
     """redo merges or set/view the merge status of files
@@ -4725,6 +4727,7 @@
     ctx = repo[None]
     parents = ctx.parents()
     pnode = parents[0].node()
+    marks = []
 
     for p in parents:
         # label with log.changeset (instead of log.parent) since this
@@ -4733,7 +4736,7 @@
                  label='log.changeset')
         ui.write(' '.join(p.tags()), label='log.tag')
         if p.bookmarks():
-            ui.write(' ' + ' '.join(p.bookmarks()), label='log.bookmark')
+            marks.extend(p.bookmarks())
         if p.rev() == -1:
             if not len(repo):
                 ui.write(_(' (empty repository)'))
@@ -4752,6 +4755,20 @@
     else:
         ui.status(m, label='log.branch')
 
+    if marks:
+        current = repo._bookmarkcurrent
+        ui.write(_('bookmarks:'), label='log.bookmark')
+        if current is not None:
+            try:
+                marks.remove(current)
+                ui.write(' *' + current, label='bookmarks.current')
+            except ValueError:
+                # current bookmark not in parent ctx marks
+                pass
+        for m in marks:
+          ui.write(' ' + m, label='log.bookmark')
+        ui.write('\n', label='log.bookmark')
+
     st = list(repo.status(unknown=True))[:6]
 
     c = repo.dirstate.copies()
--- a/mercurial/dispatch.py	Thu Jul 21 15:56:15 2011 -0500
+++ b/mercurial/dispatch.py	Fri Jul 22 17:17:23 2011 -0500
@@ -474,15 +474,14 @@
         lui = ui.copy()
         lui.readconfig(os.path.join(path, ".hg", "hgrc"), path)
 
-    if rpath:
+    if rpath and rpath[-1]:
         path = lui.expandpath(rpath[-1])
         lui = ui.copy()
         lui.readconfig(os.path.join(path, ".hg", "hgrc"), path)
 
     return path, lui
 
-def _checkshellalias(ui, args):
-    cwd = os.getcwd()
+def _checkshellalias(lui, ui, args):
     norepo = commands.norepo
     options = {}
 
@@ -494,12 +493,6 @@
     if not args:
         return
 
-    _parseconfig(ui, options['config'])
-    if options['cwd']:
-        os.chdir(options['cwd'])
-
-    path, lui = _getlocal(ui, [options['repository']])
-
     cmdtable = commands.table.copy()
     addaliases(lui, cmdtable)
 
@@ -508,7 +501,6 @@
         aliases, entry = cmdutil.findcmd(cmd, cmdtable, lui.config("ui", "strict"))
     except (error.AmbiguousCommand, error.UnknownCommand):
         commands.norepo = norepo
-        os.chdir(cwd)
         return
 
     cmd = aliases[0]
@@ -519,17 +511,12 @@
         return lambda: runcommand(lui, None, cmd, args[:1], ui, options, d, [], {})
 
     commands.norepo = norepo
-    os.chdir(cwd)
 
 _loaded = set()
 def _dispatch(req):
     args = req.args
     ui = req.ui
 
-    shellaliasfn = _checkshellalias(ui, args)
-    if shellaliasfn:
-        return shellaliasfn()
-
     # read --config before doing anything else
     # (e.g. to change trust settings for reading .hg/hgrc)
     cfgs = _parseconfig(ui, _earlygetopt(['--config'], args))
@@ -542,6 +529,12 @@
     rpath = _earlygetopt(["-R", "--repository", "--repo"], args)
     path, lui = _getlocal(ui, rpath)
 
+    # Now that we're operating in the right directory/repository with
+    # the right config settings, check for shell aliases
+    shellaliasfn = _checkshellalias(lui, ui, args)
+    if shellaliasfn:
+        return shellaliasfn()
+
     # Configure extensions in phases: uisetup, extsetup, cmdtable, and
     # reposetup. Programs like TortoiseHg will call _dispatch several
     # times so we keep track of configured extensions in _loaded.
--- a/mercurial/hbisect.py	Thu Jul 21 15:56:15 2011 -0500
+++ b/mercurial/hbisect.py	Fri Jul 22 17:17:23 2011 -0500
@@ -35,17 +35,18 @@
         # build visit array
         ancestors = [None] * (len(changelog) + 1) # an extra for [-1]
 
-        # set nodes descended from goodrev
-        ancestors[goodrev] = []
+        # set nodes descended from goodrevs
+        for rev in goodrevs:
+            ancestors[rev] = []
         for rev in xrange(goodrev + 1, len(changelog)):
             for prev in clparents(rev):
                 if ancestors[prev] == []:
                     ancestors[rev] = []
 
         # clear good revs from array
-        for node in goodrevs:
-            ancestors[node] = None
-        for rev in xrange(len(changelog), -1, -1):
+        for rev in goodrevs:
+            ancestors[rev] = None
+        for rev in xrange(len(changelog), goodrev, -1):
             if ancestors[rev] is None:
                 for prev in clparents(rev):
                     ancestors[prev] = None
--- a/mercurial/help/config.txt	Thu Jul 21 15:56:15 2011 -0500
+++ b/mercurial/help/config.txt	Fri Jul 22 17:17:23 2011 -0500
@@ -223,6 +223,10 @@
 ``$HG_ARGS`` expand to the arguments given to Mercurial. In the ``hg
 echo foo`` call above, ``$HG_ARGS`` would expand to ``echo foo``.
 
+.. note:: Some global configuration options such as ``-R`` are
+   processed before shell aliases and will thus not be passed to
+   aliases.
+
 ``auth``
 """"""""
 
--- a/mercurial/hg.py	Thu Jul 21 15:56:15 2011 -0500
+++ b/mercurial/hg.py	Fri Jul 22 17:17:23 2011 -0500
@@ -98,9 +98,9 @@
             hook(ui, repo)
     return repo
 
-def peer(ui, opts, path, create=False):
+def peer(uiorrepo, opts, path, create=False):
     '''return a repository peer for the specified path'''
-    rui = remoteui(ui, opts)
+    rui = remoteui(uiorrepo, opts)
     return repository(rui, path, create)
 
 def defaultdest(source):
--- a/mercurial/hgweb/hgweb_mod.py	Thu Jul 21 15:56:15 2011 -0500
+++ b/mercurial/hgweb/hgweb_mod.py	Fri Jul 22 17:17:23 2011 -0500
@@ -235,6 +235,7 @@
         port = port != default_port and (":" + port) or ""
         urlbase = '%s://%s%s' % (proto, req.env['SERVER_NAME'], port)
         logourl = self.config("web", "logourl", "http://mercurial.selenic.com/")
+        logoimg = self.config("web", "logoimg", "hglogo.png")
         staticurl = self.config("web", "staticurl") or req.url + 'static/'
         if not staticurl.endswith('/'):
             staticurl += '/'
@@ -275,6 +276,7 @@
         tmpl = templater.templater(mapfile,
                                    defaults={"url": req.url,
                                              "logourl": logourl,
+                                             "logoimg": logoimg,
                                              "staticurl": staticurl,
                                              "urlbase": urlbase,
                                              "repo": self.reponame,
--- a/mercurial/hgweb/hgwebdir_mod.py	Thu Jul 21 15:56:15 2011 -0500
+++ b/mercurial/hgweb/hgwebdir_mod.py	Fri Jul 22 17:17:23 2011 -0500
@@ -348,6 +348,7 @@
         start = url[-1] == '?' and '&' or '?'
         sessionvars = webutil.sessionvars(vars, start)
         logourl = config('web', 'logourl', 'http://mercurial.selenic.com/')
+        logoimg = config('web', 'logoimg', 'hglogo.png')
         staticurl = config('web', 'staticurl') or url + 'static/'
         if not staticurl.endswith('/'):
             staticurl += '/'
@@ -358,6 +359,7 @@
                                              "motd": motd,
                                              "url": url,
                                              "logourl": logourl,
+                                             "logoimg": logoimg,
                                              "staticurl": staticurl,
                                              "sessionvars": sessionvars})
         return tmpl
--- a/mercurial/localrepo.py	Thu Jul 21 15:56:15 2011 -0500
+++ b/mercurial/localrepo.py	Fri Jul 22 17:17:23 2011 -0500
@@ -10,7 +10,7 @@
 import repo, changegroup, subrepo, discovery, pushkey
 import changelog, dirstate, filelog, manifest, context, bookmarks
 import lock, transaction, store, encoding
-import scmutil, util, extensions, hook, error
+import scmutil, util, extensions, hook, error, revset
 import match as matchmod
 import merge as mergemod
 import tags as tagsmod
@@ -63,6 +63,7 @@
                     )
                 if self.ui.configbool('format', 'generaldelta', False):
                     requirements.append("generaldelta")
+                requirements = set(requirements)
             else:
                 raise error.RepoError(_("repository %s not found") % path)
         elif create:
@@ -217,6 +218,17 @@
         for i in xrange(len(self)):
             yield i
 
+    def set(self, expr, *args):
+        '''
+        Yield a context for each matching revision, after doing arg
+        replacement via revset.formatspec
+        '''
+
+        expr = revset.formatspec(expr, *args)
+        m = revset.match(None, expr)
+        for r in m(self, range(len(self))):
+            yield self[r]
+
     def url(self):
         return 'file:' + self.root
 
--- a/mercurial/osutil.c	Thu Jul 21 15:56:15 2011 -0500
+++ b/mercurial/osutil.c	Fri Jul 22 17:17:23 2011 -0500
@@ -12,6 +12,7 @@
 #include <fcntl.h>
 #include <stdio.h>
 #include <string.h>
+#include <errno.h>
 
 #ifdef _WIN32
 #include <windows.h>
@@ -288,7 +289,8 @@
 #endif
 
 	if (pathlen >= PATH_MAX) {
-		PyErr_SetString(PyExc_ValueError, "path too long");
+		errno = ENAMETOOLONG;
+		PyErr_SetFromErrnoWithFilename(PyExc_OSError, path);
 		goto error_value;
 	}
 	strncpy(fullpath, path, PATH_MAX);
--- a/mercurial/posix.py	Thu Jul 21 15:56:15 2011 -0500
+++ b/mercurial/posix.py	Fri Jul 22 17:17:23 2011 -0500
@@ -325,3 +325,26 @@
     except ImportError:
         pass
     return 80
+
+def makedir(path, notindexed):
+    os.mkdir(path)
+
+def unlinkpath(f):
+    """unlink and remove the directory if it is empty"""
+    os.unlink(f)
+    # try removing directories that might now be empty
+    try:
+        os.removedirs(os.path.dirname(f))
+    except OSError:
+        pass
+
+def lookupreg(key, name=None, scope=None):
+    return None
+
+def hidewindow():
+    """Hide current shell window.
+
+    Used to hide the window opened when starting asynchronous
+    child process under Windows, unneeded on other systems.
+    """
+    pass
--- a/mercurial/revset.py	Thu Jul 21 15:56:15 2011 -0500
+++ b/mercurial/revset.py	Fri Jul 22 17:17:23 2011 -0500
@@ -6,7 +6,7 @@
 # GNU General Public License version 2 or any later version.
 
 import re
-import parser, util, error, discovery, hbisect
+import parser, util, error, discovery, hbisect, node
 import bookmarks as bookmarksmod
 import match as matchmod
 from i18n import _
@@ -1019,11 +1019,69 @@
     tree, pos = parse(spec)
     if (pos != len(spec)):
         raise error.ParseError(_("invalid token"), pos)
-    tree = findaliases(ui, tree)
+    if ui:
+        tree = findaliases(ui, tree)
     weight, tree = optimize(tree, True)
     def mfunc(repo, subset):
         return getset(repo, subset, tree)
     return mfunc
 
+def formatspec(expr, *args):
+    '''
+    This is a convenience function for using revsets internally, and
+    escapes arguments appropriately. Aliases are intentionally ignored
+    so that intended expression behavior isn't accidentally subverted.
+
+    Supported arguments:
+
+    %d = int(arg), no quoting
+    %s = string(arg), escaped and single-quoted
+    %b = arg.branch(), escaped and single-quoted
+    %n = hex(arg), single-quoted
+    %% = a literal '%'
+
+    >>> formatspec('%d:: and not %d::', 10, 20)
+    '10:: and not 20::'
+    >>> formatspec('keyword(%s)', 'foo\\xe9')
+    "keyword('foo\\\\xe9')"
+    >>> b = lambda: 'default'
+    >>> b.branch = b
+    >>> formatspec('branch(%b)', b)
+    "branch('default')"
+    '''
+
+    def quote(s):
+        return repr(str(s))
+
+    ret = ''
+    pos = 0
+    arg = 0
+    while pos < len(expr):
+        c = expr[pos]
+        if c == '%':
+            pos += 1
+            d = expr[pos]
+            if d == '%':
+                ret += d
+            elif d == 'd':
+                ret += str(int(args[arg]))
+                arg += 1
+            elif d == 's':
+                ret += quote(args[arg])
+                arg += 1
+            elif d == 'n':
+                ret += quote(node.hex(args[arg]))
+                arg += 1
+            elif d == 'b':
+                ret += quote(args[arg].branch())
+                arg += 1
+            else:
+                raise util.Abort('unexpected revspec format character %s' % d)
+        else:
+            ret += c
+        pos += 1
+
+    return ret
+
 # tell hggettext to extract docstrings from these functions:
 i18nfunctions = symbols.values()
--- a/mercurial/templates/monoblue/footer.tmpl	Thu Jul 21 15:56:15 2011 -0500
+++ b/mercurial/templates/monoblue/footer.tmpl	Fri Jul 22 17:17:23 2011 -0500
@@ -9,7 +9,7 @@
     </div>
 
     <div id="powered-by">
-        <p><a href="{logourl}" title="Mercurial"><img src="{staticurl}hglogo.png" width=75 height=90 border=0 alt="mercurial"></a></p>
+        <p><a href="{logourl}" title="Mercurial"><img src="{staticurl}{logoimg}" width=75 height=90 border=0 alt="mercurial"></a></p>
     </div>
 
     <div id="corner-top-left"></div>
--- a/mercurial/templates/monoblue/index.tmpl	Thu Jul 21 15:56:15 2011 -0500
+++ b/mercurial/templates/monoblue/index.tmpl	Fri Jul 22 17:17:23 2011 -0500
@@ -26,7 +26,7 @@
     </div>
 
     <div id="powered-by">
-        <p><a href="{logourl}" title="Mercurial"><img src="{staticurl}hglogo.png" width=75 height=90 border=0 alt="mercurial"></a></p>
+        <p><a href="{logourl}" title="Mercurial"><img src="{staticurl}{logoimg}" width=75 height=90 border=0 alt="mercurial"></a></p>
     </div>
 
     <div id="corner-top-left"></div>
--- a/mercurial/templates/paper/bookmarks.tmpl	Thu Jul 21 15:56:15 2011 -0500
+++ b/mercurial/templates/paper/bookmarks.tmpl	Fri Jul 22 17:17:23 2011 -0500
@@ -11,7 +11,7 @@
 <div class="menu">
 <div class="logo">
 <a href="{logourl}">
-<img src="{staticurl}hglogo.png" alt="mercurial" /></a>
+<img src="{staticurl}{logoimg}" alt="mercurial" /></a>
 </div>
 <ul>
 <li><a href="{url}shortlog{sessionvars%urlparameter}">log</a></li>
--- a/mercurial/templates/paper/branches.tmpl	Thu Jul 21 15:56:15 2011 -0500
+++ b/mercurial/templates/paper/branches.tmpl	Fri Jul 22 17:17:23 2011 -0500
@@ -11,7 +11,7 @@
 <div class="menu">
 <div class="logo">
 <a href="{logourl}">
-<img src="{staticurl}hglogo.png" alt="mercurial" /></a>
+<img src="{staticurl}{logoimg}" alt="mercurial" /></a>
 </div>
 <ul>
 <li><a href="{url}shortlog{sessionvars%urlparameter}">log</a></li>
--- a/mercurial/templates/paper/changeset.tmpl	Thu Jul 21 15:56:15 2011 -0500
+++ b/mercurial/templates/paper/changeset.tmpl	Fri Jul 22 17:17:23 2011 -0500
@@ -6,7 +6,7 @@
 <div class="menu">
 <div class="logo">
 <a href="{logourl}">
-<img src="{staticurl}hglogo.png" alt="mercurial" /></a>
+<img src="{staticurl}{logoimg}" alt="mercurial" /></a>
 </div>
 <ul>
  <li><a href="{url}shortlog/{node|short}{sessionvars%urlparameter}">log</a></li>
--- a/mercurial/templates/paper/error.tmpl	Thu Jul 21 15:56:15 2011 -0500
+++ b/mercurial/templates/paper/error.tmpl	Fri Jul 22 17:17:23 2011 -0500
@@ -7,7 +7,7 @@
 <div class="menu">
 <div class="logo">
 <a href="{logourl}">
-<img src="{staticurl}hglogo.png" width=75 height=90 border=0 alt="mercurial" /></a>
+<img src="{staticurl}{logoimg}" width=75 height=90 border=0 alt="mercurial" /></a>
 </div>
 <ul>
 <li><a href="{url}shortlog{sessionvars%urlparameter}">log</a></li>
--- a/mercurial/templates/paper/fileannotate.tmpl	Thu Jul 21 15:56:15 2011 -0500
+++ b/mercurial/templates/paper/fileannotate.tmpl	Fri Jul 22 17:17:23 2011 -0500
@@ -7,7 +7,7 @@
 <div class="menu">
 <div class="logo">
 <a href="{logourl}">
-<img src="{staticurl}hglogo.png" alt="mercurial" /></a>
+<img src="{staticurl}{logoimg}" alt="mercurial" /></a>
 </div>
 <ul>
 <li><a href="{url}shortlog/{node|short}{sessionvars%urlparameter}">log</a></li>
--- a/mercurial/templates/paper/filediff.tmpl	Thu Jul 21 15:56:15 2011 -0500
+++ b/mercurial/templates/paper/filediff.tmpl	Fri Jul 22 17:17:23 2011 -0500
@@ -7,7 +7,7 @@
 <div class="menu">
 <div class="logo">
 <a href="{logourl}">
-<img src="{staticurl}hglogo.png" alt="mercurial" /></a>
+<img src="{staticurl}{logoimg}" alt="mercurial" /></a>
 </div>
 <ul>
 <li><a href="{url}shortlog/{node|short}{sessionvars%urlparameter}">log</a></li>
--- a/mercurial/templates/paper/filelog.tmpl	Thu Jul 21 15:56:15 2011 -0500
+++ b/mercurial/templates/paper/filelog.tmpl	Fri Jul 22 17:17:23 2011 -0500
@@ -11,7 +11,7 @@
 <div class="menu">
 <div class="logo">
 <a href="{logourl}">
-<img src="{staticurl}hglogo.png" alt="mercurial" /></a>
+<img src="{staticurl}{logoimg}" alt="mercurial" /></a>
 </div>
 <ul>
 <li><a href="{url}shortlog/{node|short}{sessionvars%urlparameter}">log</a></li>
--- a/mercurial/templates/paper/filerevision.tmpl	Thu Jul 21 15:56:15 2011 -0500
+++ b/mercurial/templates/paper/filerevision.tmpl	Fri Jul 22 17:17:23 2011 -0500
@@ -7,7 +7,7 @@
 <div class="menu">
 <div class="logo">
 <a href="{logourl}">
-<img src="{staticurl}hglogo.png" alt="mercurial" /></a>
+<img src="{staticurl}{logoimg}" alt="mercurial" /></a>
 </div>
 <ul>
 <li><a href="{url}shortlog/{node|short}{sessionvars%urlparameter}">log</a></li>
--- a/mercurial/templates/paper/graph.tmpl	Thu Jul 21 15:56:15 2011 -0500
+++ b/mercurial/templates/paper/graph.tmpl	Fri Jul 22 17:17:23 2011 -0500
@@ -12,7 +12,7 @@
 <div class="menu">
 <div class="logo">
 <a href="{logourl}">
-<img src="{staticurl}hglogo.png" alt="mercurial" /></a>
+<img src="{staticurl}{logoimg}" alt="mercurial" /></a>
 </div>
 <ul>
 <li><a href="{url}shortlog/{node|short}{sessionvars%urlparameter}">log</a></li>
--- a/mercurial/templates/paper/help.tmpl	Thu Jul 21 15:56:15 2011 -0500
+++ b/mercurial/templates/paper/help.tmpl	Fri Jul 22 17:17:23 2011 -0500
@@ -11,7 +11,7 @@
 <div class="menu">
 <div class="logo">
 <a href="{logourl}">
-<img src="{staticurl}hglogo.png" alt="mercurial" /></a>
+<img src="{staticurl}{logoimg}" alt="mercurial" /></a>
 </div>
 <ul>
 <li><a href="{url}shortlog{sessionvars%urlparameter}">log</a></li>
--- a/mercurial/templates/paper/helptopics.tmpl	Thu Jul 21 15:56:15 2011 -0500
+++ b/mercurial/templates/paper/helptopics.tmpl	Fri Jul 22 17:17:23 2011 -0500
@@ -11,7 +11,7 @@
 <div class="menu">
 <div class="logo">
 <a href="{logourl}">
-<img src="{staticurl}hglogo.png" alt="mercurial" /></a>
+<img src="{staticurl}{logoimg}" alt="mercurial" /></a>
 </div>
 <ul>
 <li><a href="{url}shortlog{sessionvars%urlparameter}">log</a></li>
--- a/mercurial/templates/paper/index.tmpl	Thu Jul 21 15:56:15 2011 -0500
+++ b/mercurial/templates/paper/index.tmpl	Fri Jul 22 17:17:23 2011 -0500
@@ -6,7 +6,7 @@
 <div class="container">
 <div class="menu">
 <a href="{logourl}">
-<img src="{staticurl}hglogo.png" width=75 height=90 border=0 alt="mercurial" /></a>
+<img src="{staticurl}{logoimg}" width=75 height=90 border=0 alt="mercurial" /></a>
 </div>
 <div class="main">
 <h2>Mercurial Repositories</h2>
--- a/mercurial/templates/paper/manifest.tmpl	Thu Jul 21 15:56:15 2011 -0500
+++ b/mercurial/templates/paper/manifest.tmpl	Fri Jul 22 17:17:23 2011 -0500
@@ -7,7 +7,7 @@
 <div class="menu">
 <div class="logo">
 <a href="{logourl}">
-<img src="{staticurl}hglogo.png" alt="mercurial" /></a>
+<img src="{staticurl}{logoimg}" alt="mercurial" /></a>
 </div>
 <ul>
 <li><a href="{url}shortlog/{node|short}{sessionvars%urlparameter}">log</a></li>
--- a/mercurial/templates/paper/search.tmpl	Thu Jul 21 15:56:15 2011 -0500
+++ b/mercurial/templates/paper/search.tmpl	Fri Jul 22 17:17:23 2011 -0500
@@ -7,7 +7,7 @@
 <div class="menu">
 <div class="logo">
 <a href="{logourl}">
-<img src="{staticurl}hglogo.png" width=75 height=90 border=0 alt="mercurial"></a>
+<img src="{staticurl}{logoimg}" width=75 height=90 border=0 alt="mercurial"></a>
 </div>
 <ul>
 <li><a href="{url}shortlog{sessionvars%urlparameter}">log</a></li>
--- a/mercurial/templates/paper/shortlog.tmpl	Thu Jul 21 15:56:15 2011 -0500
+++ b/mercurial/templates/paper/shortlog.tmpl	Fri Jul 22 17:17:23 2011 -0500
@@ -11,7 +11,7 @@
 <div class="menu">
 <div class="logo">
 <a href="{logourl}">
-<img src="{staticurl}hglogo.png" alt="mercurial" /></a>
+<img src="{staticurl}{logoimg}" alt="mercurial" /></a>
 </div>
 <ul>
 <li class="active">log</li>
--- a/mercurial/templates/paper/tags.tmpl	Thu Jul 21 15:56:15 2011 -0500
+++ b/mercurial/templates/paper/tags.tmpl	Fri Jul 22 17:17:23 2011 -0500
@@ -11,7 +11,7 @@
 <div class="menu">
 <div class="logo">
 <a href="{logourl}">
-<img src="{staticurl}hglogo.png" alt="mercurial" /></a>
+<img src="{staticurl}{logoimg}" alt="mercurial" /></a>
 </div>
 <ul>
 <li><a href="{url}shortlog{sessionvars%urlparameter}">log</a></li>
--- a/mercurial/templates/spartan/footer.tmpl	Thu Jul 21 15:56:15 2011 -0500
+++ b/mercurial/templates/spartan/footer.tmpl	Fri Jul 22 17:17:23 2011 -0500
@@ -2,7 +2,7 @@
 {motd}
 <div class="logo">
 <a href="{logourl}">
-<img src="{staticurl}hglogo.png" width=75 height=90 border=0 alt="mercurial"></a>
+<img src="{staticurl}{logoimg}" width=75 height=90 border=0 alt="mercurial"></a>
 </div>
 
 </body>
--- a/mercurial/ui.py	Thu Jul 21 15:56:15 2011 -0500
+++ b/mercurial/ui.py	Fri Jul 22 17:17:23 2011 -0500
@@ -46,7 +46,7 @@
     def copy(self):
         return self.__class__(self)
 
-    def _is_trusted(self, fp, f):
+    def _trusted(self, fp, f):
         st = util.fstat(fp)
         if util.isowner(st):
             return True
@@ -75,7 +75,7 @@
             raise
 
         cfg = config.config()
-        trusted = sections or trust or self._is_trusted(fp, filename)
+        trusted = sections or trust or self._trusted(fp, filename)
 
         try:
             cfg.read(filename, fp, sections=sections, remap=remap)
--- a/mercurial/util.py	Thu Jul 21 15:56:15 2011 -0500
+++ b/mercurial/util.py	Fri Jul 22 17:17:23 2011 -0500
@@ -19,6 +19,11 @@
 import os, time, calendar, textwrap, unicodedata, signal
 import imp, socket, urllib
 
+if os.name == 'nt':
+    from windows import *
+else:
+    from posix import *
+
 # Python compatibility
 
 def sha1(s):
@@ -390,18 +395,6 @@
 
     return check
 
-def makedir(path, notindexed):
-    os.mkdir(path)
-
-def unlinkpath(f):
-    """unlink and remove the directory if it is empty"""
-    os.unlink(f)
-    # try removing directories that might now be empty
-    try:
-        os.removedirs(os.path.dirname(f))
-    except OSError:
-        pass
-
 def copyfile(src, dest):
     "copy a file, preserving mode and atime/mtime"
     if os.path.islink(src):
@@ -487,22 +480,8 @@
             return _("filename ends with '%s', which is not allowed "
                      "on Windows") % t
 
-def lookupreg(key, name=None, scope=None):
-    return None
-
-def hidewindow():
-    """Hide current shell window.
-
-    Used to hide the window opened when starting asynchronous
-    child process under Windows, unneeded on other systems.
-    """
-    pass
-
 if os.name == 'nt':
     checkosfilename = checkwinfilename
-    from windows import *
-else:
-    from posix import *
 
 def makelock(info, pathname):
     try:
--- a/tests/run-tests.py	Thu Jul 21 15:56:15 2011 -0500
+++ b/tests/run-tests.py	Fri Jul 22 17:17:23 2011 -0500
@@ -726,6 +726,7 @@
                     rename(testpath + ".err", testpath)
                 else:
                     rename(testpath + ".err", testpath + ".out")
+                result('p', test)
                 return
         result('f', (test, msg))
 
--- a/tests/test-alias.t	Thu Jul 21 15:56:15 2011 -0500
+++ b/tests/test-alias.t	Fri Jul 22 17:17:23 2011 -0500
@@ -251,7 +251,7 @@
   $ hg --cwd .. count 'branch(default)'
   2
   $ hg echo --cwd ..
-  --cwd ..
+  
 
 
 repo specific shell aliases
--- a/tests/test-bisect2.t	Thu Jul 21 15:56:15 2011 -0500
+++ b/tests/test-bisect2.t	Fri Jul 22 17:17:23 2011 -0500
@@ -431,3 +431,22 @@
   date:        Thu Jan 01 00:00:09 1970 +0000
   summary:     9
   
+
+user adds irrelevant but consistent information (here: -g 2) to bisect state
+
+  $ hg bisect -r
+  $ hg bisect -b 13
+  $ hg bisect -g 8
+  Testing changeset 11:82ca6f06eccd (3 changesets remaining, ~1 tests)
+  2 files updated, 0 files merged, 0 files removed, 0 files unresolved
+  $ hg bisect -g 2
+  Testing changeset 11:82ca6f06eccd (3 changesets remaining, ~1 tests)
+  0 files updated, 0 files merged, 0 files removed, 0 files unresolved
+  $ hg bisect -b
+  The first bad revision is:
+  changeset:   11:82ca6f06eccd
+  parent:      8:dab8161ac8fc
+  user:        test
+  date:        Thu Jan 01 00:00:11 1970 +0000
+  summary:     11
+  
--- a/tests/test-bookmarks.t	Thu Jul 21 15:56:15 2011 -0500
+++ b/tests/test-bookmarks.t	Fri Jul 22 17:17:23 2011 -0500
@@ -239,9 +239,10 @@
 test summary
 
   $ hg summary
-  parent: 2:db815d6d32e6 tip Y Z x  y
+  parent: 2:db815d6d32e6 tip
    2
   branch: default
+  bookmarks: *Z Y x  y
   commit: (clean)
   update: 1 new changesets, 2 branch heads (merge)
 
@@ -342,3 +343,19 @@
    * Z                         3:125c9a1d6df6
      x  y                      2:db815d6d32e6
 
+test wrongly formated bookmark
+
+  $ echo '' >> .hg/bookmarks
+  $ hg bookmarks
+     X2                        1:925d80f479bb
+     Y                         2:db815d6d32e6
+   * Z                         3:125c9a1d6df6
+     x  y                      2:db815d6d32e6
+  $ echo "Ican'thasformatedlines" >> .hg/bookmarks
+  $ hg bookmarks
+  malformed line in .hg/bookmarks: "Ican'thasformatedlines"
+     X2                        1:925d80f479bb
+     Y                         2:db815d6d32e6
+   * Z                         3:125c9a1d6df6
+     x  y                      2:db815d6d32e6
+
--- a/tests/test-convert-svn-move.t	Thu Jul 21 15:56:15 2011 -0500
+++ b/tests/test-convert-svn-move.t	Fri Jul 22 17:17:23 2011 -0500
@@ -167,6 +167,7 @@
   > [progress]
   > assume-tty = 1
   > delay = 0
+  > changedelay = 0
   > format = topic bar number
   > refresh = 0
   > width = 60
--- a/tests/test-debugcomplete.t	Thu Jul 21 15:56:15 2011 -0500
+++ b/tests/test-debugcomplete.t	Fri Jul 22 17:17:23 2011 -0500
@@ -196,7 +196,7 @@
   forget: include, exclude
   init: ssh, remotecmd, insecure
   log: follow, follow-first, date, copies, keyword, rev, removed, only-merges, user, only-branch, branch, prune, hidden, patch, git, limit, no-merges, stat, style, template, include, exclude
-  merge: force, tool, rev, preview
+  merge: force, rev, preview, tool
   pull: update, force, rev, bookmark, branch, ssh, remotecmd, insecure
   push: force, rev, bookmark, branch, new-branch, ssh, remotecmd, insecure
   remove: after, force, include, exclude
@@ -206,7 +206,7 @@
   update: clean, check, date, rev
   addremove: similarity, include, exclude, dry-run
   archive: no-decode, prefix, rev, type, subrepos, include, exclude
-  backout: merge, parent, tool, rev, include, exclude, message, logfile, date, user
+  backout: merge, parent, rev, tool, include, exclude, message, logfile, date, user
   bisect: reset, good, bad, skip, extend, command, noupdate
   bookmarks: force, rev, delete, rename, inactive
   branch: force, clean
@@ -255,7 +255,7 @@
   paths: 
   recover: 
   rename: after, force, include, exclude, dry-run
-  resolve: all, list, mark, unmark, tool, no-status, include, exclude
+  resolve: all, list, mark, unmark, no-status, tool, include, exclude
   revert: all, date, rev, no-backup, include, exclude, dry-run
   rollback: dry-run
   root: 
--- a/tests/test-doctest.py	Thu Jul 21 15:56:15 2011 -0500
+++ b/tests/test-doctest.py	Fri Jul 22 17:17:23 2011 -0500
@@ -33,3 +33,6 @@
 
 import hgext.convert.cvsps
 doctest.testmod(hgext.convert.cvsps)
+
+import mercurial.revset
+doctest.testmod(mercurial.revset)
--- a/tests/test-eol.t	Thu Jul 21 15:56:15 2011 -0500
+++ b/tests/test-eol.t	Fri Jul 22 17:17:23 2011 -0500
@@ -441,3 +441,82 @@
   warning: ignoring .hgeol file due to parse error at .hgeol:1: bad
   $ hg status
   ? .hgeol.orig
+
+Test eol.only-consistent can be specified in .hgeol
+
+  $ cd $TESTTMP
+  $ hg init only-consistent
+  $ cd only-consistent
+  $ printf "first\nsecond\r\n" > a.txt
+  $ hg add a.txt
+  $ cat > .hgeol << EOF
+  > [eol]
+  > only-consistent = True
+  > EOF
+  $ hg commit -m 'inconsistent'
+  abort: inconsistent newline style in a.txt
+  
+  [255]
+  $ cat > .hgeol << EOF
+  > [eol]
+  > only-consistent = False
+  > EOF
+  $ hg commit -m 'consistent'
+
+
+Test trailing newline
+
+  $ cat >> $HGRCPATH <<EOF
+  > [extensions]
+  > eol=
+  > EOF
+
+setup repository
+
+  $ cd $TESTTMP
+  $ hg init trailing
+  $ cd trailing
+  $ cat > .hgeol <<EOF
+  > [patterns]
+  > **.txt = native
+  > [eol]
+  > fix-trailing-newline = False
+  > EOF
+
+add text without trailing newline
+
+  $ printf "first\nsecond" > a.txt
+  $ hg commit --addremove -m 'checking in'
+  adding .hgeol
+  adding a.txt
+  $ rm a.txt
+  $ hg update -C -q
+  $ cat a.txt
+  first
+  second (no-eol)
+
+  $ cat > .hgeol <<EOF
+  > [patterns]
+  > **.txt = native
+  > [eol]
+  > fix-trailing-newline = True
+  > EOF
+  $ printf "third\nfourth" > a.txt
+  $ hg commit -m 'checking in with newline fix'
+  $ rm a.txt
+  $ hg update -C -q
+  $ cat a.txt
+  third
+  fourth
+
+append a line without trailing newline
+
+  $ printf "fifth" >> a.txt
+  $ hg commit -m 'adding another line line'
+  $ rm a.txt
+  $ hg update -C -q
+  $ cat a.txt
+  third
+  fourth
+  fifth
+
--- a/tests/test-init.t	Thu Jul 21 15:56:15 2011 -0500
+++ b/tests/test-init.t	Fri Jul 22 17:17:23 2011 -0500
@@ -19,8 +19,8 @@
   store created
   00changelog.i created
   revlogv1
+  fncache
   store
-  fncache
   dotencode
   $ echo this > local/foo
   $ hg ci --cwd local -A -m "init"
@@ -48,8 +48,8 @@
   store created
   00changelog.i created
   revlogv1
+  fncache
   store
-  fncache
 
 test failure
 
@@ -145,8 +145,8 @@
   store created
   00changelog.i created
   revlogv1
+  fncache
   store
-  fncache
   dotencode
 
 prepare test of init of url configured from paths
@@ -162,8 +162,8 @@
   store created
   00changelog.i created
   revlogv1
+  fncache
   store
-  fncache
   dotencode
 
 verify that clone also expand urls
@@ -175,8 +175,8 @@
   store created
   00changelog.i created
   revlogv1
+  fncache
   store
-  fncache
   dotencode
 
 clone bookmarks
--- a/tests/test-notify-changegroup.t	Thu Jul 21 15:56:15 2011 -0500
+++ b/tests/test-notify-changegroup.t	Fri Jul 22 17:17:23 2011 -0500
@@ -72,4 +72,57 @@
   @@ -0,0 +1,2 @@
   +a
   +a
+  $ hg --cwd a rollback
+  repository tip rolled back to revision -1 (undo push)
+  working directory now based on revision -1
 
+unbundle with unrelated source
+
+  $ hg --cwd b bundle ../test.hg ../a
+  searching for changes
+  2 changesets found
+  $ hg --cwd a unbundle ../test.hg
+  adding changesets
+  adding manifests
+  adding file changes
+  added 2 changesets with 2 changes to 1 files
+  (run 'hg update' to get a working copy)
+  $ hg --cwd a rollback
+  repository tip rolled back to revision -1 (undo unbundle)
+  working directory now based on revision -1
+
+unbundle with correct source
+
+  $ hg --config notify.sources=unbundle --cwd a unbundle ../test.hg 2>&1 |
+  >     python -c 'import sys,re; print re.sub("\n\t", " ", sys.stdin.read()),'
+  adding changesets
+  adding manifests
+  adding file changes
+  added 2 changesets with 2 changes to 1 files
+  Content-Type: text/plain; charset="us-ascii"
+  MIME-Version: 1.0
+  Content-Transfer-Encoding: 7bit
+  Date: * (glob)
+  Subject: * (glob)
+  From: test
+  X-Hg-Notification: changeset cb9a9f314b8b
+  Message-Id: <*> (glob)
+  To: baz, foo@bar
+  
+  changeset cb9a9f314b8b in $TESTTMP/a
+  details: $TESTTMP/a?cmd=changeset;node=cb9a9f314b8b
+  summary: a
+  
+  changeset ba677d0156c1 in $TESTTMP/a
+  details: $TESTTMP/a?cmd=changeset;node=ba677d0156c1
+  summary: b
+  
+  diffs (6 lines):
+  
+  diff -r 000000000000 -r ba677d0156c1 a
+  --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+  +++ b/a	Thu Jan 01 00:00:00 1970 +0000
+  @@ -0,0 +1,2 @@
+  +a
+  +a
+  (run 'hg update' to get a working copy)
--- a/tests/test-progress.t	Thu Jul 21 15:56:15 2011 -0500
+++ b/tests/test-progress.t	Fri Jul 22 17:17:23 2011 -0500
@@ -9,16 +9,28 @@
   >         total = loops
   >     if opts.get('total', None):
   >         total = int(opts.get('total'))
+  >     nested = False
+  >     if opts.get('nested', None):
+  >         nested = True
   >     loops = abs(loops)
   > 
   >     for i in range(loops):
   >         ui.progress('loop', i, 'loop.%d' % i, 'loopnum', total)
+  >         if opts.get('parallel'):
+  >             ui.progress('other', i, 'other.%d' % i, 'othernum', total)
+  >         if nested:
+  >             for j in range(2):
+  >                 ui.progress('nested', j, 'nested.%d' % j, 'nestnum', 2)
+  >             ui.progress('nested', None, 'nested.done', 'nestnum', 2)
   >     ui.progress('loop', None, 'loop.done', 'loopnum', total)
   > 
   > commands.norepo += " loop"
   > 
   > cmdtable = {
-  >     "loop": (loop, [('', 'total', '', 'override for total')],
+  >     "loop": (loop, [('', 'total', '', 'override for total'),
+  >                     ('', 'nested', False, 'show nested results'),
+  >                     ('', 'parallel', False, 'show parallel sets of results'),
+  >                    ],
   >              'hg loop LOOPS'),
   > }
   > EOF
@@ -47,6 +59,42 @@
   loop [===============================>                ] 2/3
                                                               \r (esc)
 
+
+test nested short-lived topics (which shouldn't display with nestdelay):
+
+  $ hg -y loop 3 --nested 2>&1 | \
+  > python $TESTDIR/filtercr.py
+  
+  loop [                                                ] 0/3
+  loop [===============>                                ] 1/3
+  loop [===============================>                ] 2/3
+                                                              \r (esc)
+
+
+  $ hg --config progress.changedelay=0 -y loop 3 --nested 2>&1 | \
+  > python $TESTDIR/filtercr.py
+  
+  loop [                                                ] 0/3
+  nested [                                              ] 0/2
+  nested [======================>                       ] 1/2
+  loop [===============>                                ] 1/3
+  nested [                                              ] 0/2
+  nested [======================>                       ] 1/2
+  loop [===============================>                ] 2/3
+  nested [                                              ] 0/2
+  nested [======================>                       ] 1/2
+                                                              \r (esc)
+
+
+test two topics being printed in parallel (as when we're doing a local
+--pull clone, where you get the unbundle and bundle progress at the
+same time):
+  $ hg loop 3 --parallel 2>&1 | python $TESTDIR/filtercr.py
+  
+  loop [                                                ] 0/3
+  loop [===============>                                ] 1/3
+  loop [===============================>                ] 2/3
+                                                              \r (esc)
 test refresh is taken in account
 
   $ hg -y --config progress.refresh=100 loop 3 2>&1 | $TESTDIR/filtercr.py