changeset 13171:b546db00cfd7

merge with i18n
author Matt Mackall <mpm@selenic.com>
date Mon, 20 Dec 2010 12:08:56 -0600
parents 537df3bfbaad (diff) 122f8a5e02db (current diff)
children 84cec5895d01
files
diffstat 37 files changed, 1392 insertions(+), 889 deletions(-) [+]
line wrap: on
line diff
--- a/contrib/check-code.py	Fri Dec 10 12:51:37 2010 +0100
+++ b/contrib/check-code.py	Mon Dec 20 12:08:56 2010 -0600
@@ -129,6 +129,8 @@
     (r'[\x80-\xff]', "non-ASCII character literal"),
     (r'("\')\.format\(', "str.format() not available in Python 2.4"),
     (r'^\s*with\s+', "with not available in Python 2.4"),
+    (r'^\s*except.* as .*:', "except as not available in Python 2.4"),
+    (r'^\s*os\.path\.relpath', "relpath not available in Python 2.4"),
     (r'(?<!def)\s+(any|all|format)\(',
      "any/all/format not available in Python 2.4"),
     (r'(?<!def)\s+(callable)\(',
--- a/hgext/churn.py	Fri Dec 10 12:51:37 2010 +0100
+++ b/hgext/churn.py	Mon Dec 20 12:08:56 2010 -0600
@@ -62,6 +62,7 @@
 
         key = getkey(ctx)
         key = amap.get(key, key) # alias remap
+        key = key.strip() # ignore leading and trailing spaces
         if opts.get('changesets'):
             rate[key] = (rate.get(key, (0,))[0] + 1, 0)
         else:
--- a/hgext/eol.py	Fri Dec 10 12:51:37 2010 +0100
+++ b/hgext/eol.py	Mon Dec 20 12:08:56 2010 -0600
@@ -11,18 +11,20 @@
 configuration files. It uses two sections, ``[patterns]`` and
 ``[repository]``.
 
-The ``[patterns]`` section specifies the line endings used in the
-working directory. The format is specified by a file pattern. The
-first match is used, so put more specific patterns first. The
-available line endings are ``LF``, ``CRLF``, and ``BIN``.
+The ``[patterns]`` section specifies how line endings should be
+converted between the working copy and the repository. The format is
+specified by a file pattern. The first match is used, so put more
+specific patterns first. The available line endings are ``LF``,
+``CRLF``, and ``BIN``.
 
 Files with the declared format of ``CRLF`` or ``LF`` are always
-checked out in that format and files declared to be binary (``BIN``)
-are left unchanged. Additionally, ``native`` is an alias for the
-platform's default line ending: ``LF`` on Unix (including Mac OS X)
-and ``CRLF`` on Windows. Note that ``BIN`` (do nothing to line
-endings) is Mercurial's default behaviour; it is only needed if you
-need to override a later, more general pattern.
+checked out and stored in the repository in that format and files
+declared to be binary (``BIN``) are left unchanged. Additionally,
+``native`` is an alias for checking out in the platform's default line
+ending: ``LF`` on Unix (including Mac OS X) and ``CRLF`` on
+Windows. Note that ``BIN`` (do nothing to line endings) is Mercurial's
+default behaviour; it is only needed if you need to override a later,
+more general pattern.
 
 The optional ``[repository]`` section specifies the line endings to
 use for files stored in the repository. It has a single setting,
@@ -46,6 +48,10 @@
   [repository]
   native = LF
 
+.. note::
+   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:
--- a/hgext/progress.py	Fri Dec 10 12:51:37 2010 +0100
+++ b/hgext/progress.py	Mon Dec 20 12:08:56 2010 -0600
@@ -28,7 +28,7 @@
   [progress]
   delay = 3 # number of seconds (float) before showing the progress bar
   refresh = 0.1 # time in seconds between refreshes of the progress bar
-  format = topic bar number # format 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
                  # (that is, min(width, term width) will be used)
   clear-complete = True # clear the progress bar after it's done
@@ -36,15 +36,17 @@
   assume-tty = False # if true, ALWAYS show a progress bar, unless
                      # disable is given
 
-Valid entries for the format field are topic, bar, number, unit, and
-item. item defaults to the last 20 characters of the item, but this
-can be changed by adding either ``-<num>`` which would take the last
-num characters, or ``+<num>`` for the first num characters.
+Valid entries for the format field are topic, bar, number, unit,
+estimate, and item. item defaults to the last 20 characters of the
+item, but this can be changed by adding either ``-<num>`` which would
+take the last num characters, or ``+<num>`` for the first num
+characters.
 """
 
 import sys
 import time
 
+from mercurial.i18n import _
 from mercurial import util
 
 def spacejoin(*args):
@@ -54,6 +56,22 @@
     return (getattr(sys.stderr, 'isatty', None) and
             (sys.stderr.isatty() or ui.configbool('progress', 'assume-tty')))
 
+def fmtremaining(seconds):
+    if seconds < 60:
+        # i18n: format XX seconds as "XXs"
+        return _("%02ds") % (seconds)
+    minutes = seconds // 60
+    if minutes < 60:
+        seconds -= minutes * 60
+        # i18n: format X minutes and YY seconds as "XmYYs"
+        return _("%dm%02ds") % (minutes, seconds)
+    # we're going to ignore seconds in this case
+    minutes += 1
+    hours = minutes // 60
+    minutes -= hours * 60
+    # i18n: format X hours and YY minutes as "XhYYm"
+    return _("%dh%02dm") % (hours, minutes)
+
 class progbar(object):
     def __init__(self, ui):
         self.ui = ui
@@ -61,6 +79,9 @@
 
     def resetstate(self):
         self.topics = []
+        self.topicstates = {}
+        self.starttimes = {}
+        self.startvals = {}
         self.printed = False
         self.lastprint = time.time() + float(self.ui.config(
             'progress', 'delay', default=3))
@@ -69,9 +90,9 @@
             'progress', 'refresh', default=0.1))
         self.order = self.ui.configlist(
             'progress', 'format',
-            default=['topic', 'bar', 'number'])
+            default=['topic', 'bar', 'number', 'estimate'])
 
-    def show(self, topic, pos, item, unit, total):
+    def show(self, now, topic, pos, item, unit, total):
         if not shouldprint(self.ui):
             return
         termwidth = self.width()
@@ -108,10 +129,12 @@
                 needprogress = True
             elif indicator == 'unit' and unit:
                 add = unit
+            elif indicator == 'estimate':
+                add = self.estimate(topic, pos, total, now)
             if not needprogress:
                 head = spacejoin(head, add)
             else:
-                tail = spacejoin(add, tail)
+                tail = spacejoin(tail, add)
         if needprogress:
             used = 0
             if head:
@@ -159,19 +182,44 @@
         tw = self.ui.termwidth()
         return min(int(self.ui.config('progress', 'width', default=tw)), tw)
 
+    def estimate(self, topic, pos, total, now):
+        if total is None:
+            return ''
+        initialpos = self.startvals[topic]
+        target = total - initialpos
+        delta = pos - initialpos
+        if delta > 0:
+            elapsed = now - self.starttimes[topic]
+            if elapsed > float(
+                self.ui.config('progress', 'estimate', default=2)):
+                seconds = (elapsed * (target - delta)) // delta + 1
+                return fmtremaining(seconds)
+        return ''
+
     def progress(self, topic, pos, item='', unit='', total=None):
+        now = time.time()
         if pos is None:
-            if self.topics and self.topics[-1] == topic and self.printed:
+            self.starttimes.pop(topic, None)
+            self.startvals.pop(topic, None)
+            self.topicstates.pop(topic, None)
+            # reset the progress bar if this is the outermost topic
+            if self.topics and self.topics[0] == topic and self.printed:
                 self.complete()
                 self.resetstate()
+            # truncate the list of topics assuming all topics within
+            # this one are also closed
+            if topic in self.topics:
+              self.topics = self.topics[:self.topics.index(topic)]
         else:
             if topic not in self.topics:
+                self.starttimes[topic] = now
+                self.startvals[topic] = pos
                 self.topics.append(topic)
-            now = time.time()
-            if (now - self.lastprint >= self.refresh
-                and topic == self.topics[-1]):
+            self.topicstates[topic] = pos, item, unit, total
+            if now - self.lastprint >= self.refresh and self.topics:
                 self.lastprint = now
-                self.show(topic, pos, item, unit, total)
+                current = self.topics[-1]
+                self.show(now, topic, *self.topicstates[topic])
 
 def uisetup(ui):
     class progressui(ui.__class__):
--- a/hgext/record.py	Fri Dec 10 12:51:37 2010 +0100
+++ b/hgext/record.py	Mon Dec 20 12:08:56 2010 -0600
@@ -42,7 +42,7 @@
         line = lr.readline()
         if not line:
             break
-        if line.startswith('diff --git a/'):
+        if line.startswith('diff --git a/') or line.startswith('diff -r '):
             def notheader(line):
                 s = line.split(None, 1)
                 return not s or s[0] not in ('---', 'diff')
@@ -70,7 +70,8 @@
 
     XXX shoudn't we move this to mercurial/patch.py ?
     """
-    diff_re = re.compile('diff --git a/(.*) b/(.*)$')
+    diffgit_re = re.compile('diff --git a/(.*) b/(.*)$')
+    diff_re = re.compile('diff -r .* (.*)$')
     allhunks_re = re.compile('(?:index|new file|deleted file) ')
     pretty_re = re.compile('(?:new file|deleted file) ')
     special_re = re.compile('(?:index|new|deleted|copy|rename) ')
@@ -110,10 +111,14 @@
                 return True
 
     def files(self):
-        fromfile, tofile = self.diff_re.match(self.header[0]).groups()
-        if fromfile == tofile:
-            return [fromfile]
-        return [fromfile, tofile]
+        match = self.diffgit_re.match(self.header[0])
+        if match:
+            fromfile, tofile = match.groups()
+            if fromfile == tofile:
+                return [fromfile]
+            return [fromfile, tofile]
+        else:
+            return self.diff_re.match(self.header[0]).groups()
 
     def filename(self):
         return self.files()[-1]
--- a/mercurial/archival.py	Fri Dec 10 12:51:37 2010 +0100
+++ b/mercurial/archival.py	Mon Dec 20 12:08:56 2010 -0600
@@ -262,13 +262,18 @@
 
         write('.hg_archival.txt', 0644, False, metadata)
 
-    for f in ctx:
+    total = len(ctx.manifest())
+    repo.ui.progress(_('archiving'), 0, unit=_('files'), total=total)
+    for i, f in enumerate(ctx):
         ff = ctx.flags(f)
         write(f, 'x' in ff and 0755 or 0644, 'l' in ff, ctx[f].data)
+        repo.ui.progress(_('archiving'), i + 1, item=f,
+                         unit=_('files'), total=total)
+    repo.ui.progress(_('archiving'), None)
 
     if subrepos:
         for subpath in ctx.substate:
             sub = ctx.sub(subpath)
-            sub.archive(archiver, prefix)
+            sub.archive(repo.ui, archiver, prefix)
 
     archiver.done()
--- a/mercurial/cmdutil.py	Fri Dec 10 12:51:37 2010 +0100
+++ b/mercurial/cmdutil.py	Mon Dec 20 12:08:56 2010 -0600
@@ -233,7 +233,8 @@
     writable = 'w' in mode or 'a' in mode
 
     if not pat or pat == '-':
-        return writable and sys.stdout or sys.stdin
+        fp = writable and sys.stdout or sys.stdin
+        return os.fdopen(os.dup(fp.fileno()), mode)
     if hasattr(pat, 'write') and writable:
         return pat
     if hasattr(pat, 'read') and 'r' in mode:
--- a/mercurial/commands.py	Fri Dec 10 12:51:37 2010 +0100
+++ b/mercurial/commands.py	Mon Dec 20 12:08:56 2010 -0600
@@ -3665,16 +3665,23 @@
 
     Tags are used to name particular revisions of the repository and are
     very useful to compare different revisions, to go back to significant
-    earlier versions or to mark branch points as releases, etc.
+    earlier versions or to mark branch points as releases, etc. Changing
+    an existing tag is normally disallowed; use -f/--force to override.
 
     If no revision is given, the parent of the working directory is
     used, or tip if no revision is checked out.
 
     To facilitate version control, distribution, and merging of tags,
-    they are stored as a file named ".hgtags" which is managed
-    similarly to other project files and can be hand-edited if
-    necessary. The file '.hg/localtags' is used for local tags (not
-    shared among repositories).
+    they are stored as a file named ".hgtags" which is managed similarly
+    to other project files and can be hand-edited if necessary. This
+    also means that tagging creates a new commit. The file
+    ".hg/localtags" is used for local tags (not shared among
+    repositories).
+
+    Tag commits are usually made at the head of a branch. If the parent
+    of the working directory is not a branch head, :hg:`tag` aborts; use
+    -f/--force to force the tag commit to be based on a non-head
+    changeset.
 
     See :hg:`help dates` for a list of formats valid for -d/--date.
 
@@ -3717,9 +3724,13 @@
             if n in repo.tags():
                 raise util.Abort(_('tag \'%s\' already exists '
                                    '(use -f to force)') % n)
-    if not rev_ and repo.dirstate.parents()[1] != nullid:
-        raise util.Abort(_('uncommitted merge - please provide a '
-                           'specific revision'))
+    if not opts.get('local'):
+        p1, p2 = repo.dirstate.parents()
+        if p2 != nullid:
+            raise util.Abort(_('uncommitted merge'))
+        bheads = repo.branchheads()
+        if not opts.get('force') and bheads and p1 not in bheads:
+            raise util.Abort(_('not at a branch head (use -f to force)'))
     r = cmdutil.revsingle(repo, rev_).node()
 
     if not message:
@@ -4481,7 +4492,7 @@
          _('[OPTION]... [FILE]...')),
     "tag":
         (tag,
-         [('f', 'force', None, _('replace existing tag')),
+         [('f', 'force', None, _('force tag')),
           ('l', 'local', None, _('make the tag local')),
           ('r', 'rev', '',
            _('revision to tag'), _('REV')),
--- a/mercurial/hg.py	Fri Dec 10 12:51:37 2010 +0100
+++ b/mercurial/hg.py	Mon Dec 20 12:08:56 2010 -0600
@@ -395,7 +395,8 @@
     return stats[3] > 0
 
 def merge(repo, node, force=None, remind=True):
-    """branch merge with node, resolving changes"""
+    """Branch merge with node, resolving changes. Return true if any
+    unresolved conflicts."""
     stats = mergemod.update(repo, node, True, force, False)
     _showstats(repo, stats)
     if stats[3]:
--- a/mercurial/hook.py	Fri Dec 10 12:51:37 2010 +0100
+++ b/mercurial/hook.py	Mon Dec 20 12:08:56 2010 -0600
@@ -135,6 +135,9 @@
             elif cmd.startswith('python:'):
                 if cmd.count(':') >= 2:
                     path, cmd = cmd[7:].rsplit(':', 1)
+                    path = util.expandpath(path)
+                    if repo:
+                        path = os.path.join(repo.root, path)
                     mod = extensions.loadpath(path, 'hghook.%s' % hname)
                     hookfn = getattr(mod, cmd)
                 else:
--- a/mercurial/httprepo.py	Fri Dec 10 12:51:37 2010 +0100
+++ b/mercurial/httprepo.py	Mon Dec 20 12:08:56 2010 -0600
@@ -160,7 +160,7 @@
                     break
 
         tempname = changegroup.writebundle(cg, None, type)
-        fp = url.httpsendfile(tempname, "rb")
+        fp = url.httpsendfile(self.ui, tempname, "rb")
         headers = {'Content-Type': 'application/mercurial-0.1'}
 
         try:
--- a/mercurial/localrepo.py	Fri Dec 10 12:51:37 2010 +0100
+++ b/mercurial/localrepo.py	Mon Dec 20 12:08:56 2010 -0600
@@ -308,10 +308,11 @@
 
         date: date tuple to use if committing'''
 
-        for x in self.status()[:5]:
-            if '.hgtags' in x:
-                raise util.Abort(_('working copy of .hgtags is changed '
-                                   '(please commit .hgtags manually)'))
+        if not local:
+            for x in self.status()[:5]:
+                if '.hgtags' in x:
+                    raise util.Abort(_('working copy of .hgtags is changed '
+                                       '(please commit .hgtags manually)'))
 
         self.tags() # instantiate the cache
         self._tag(names, node, message, local, user, date)
@@ -629,10 +630,6 @@
 
     def wwrite(self, filename, data, flags):
         data = self._filter(self._decodefilterpats, filename, data)
-        try:
-            os.unlink(self.wjoin(filename))
-        except OSError:
-            pass
         if 'l' in flags:
             self.wopener.symlink(data, filename)
         else:
@@ -952,6 +949,7 @@
 
             # commit subs
             if subs or removedsubs:
+                pstate = subrepo.substate(self['.'])
                 state = wctx.substate.copy()
                 for s in sorted(subs):
                     sub = wctx.sub(s)
@@ -959,7 +957,19 @@
                         subrepo.subrelpath(sub))
                     sr = sub.commit(cctx._text, user, date)
                     state[s] = (state[s][0], sr)
-                subrepo.writestate(self, state)
+
+                changed = False
+                if len(pstate) != len(state):
+                    changed = True
+                if not changed:
+                    for newstate in state:
+                        if state[newstate][1] != pstate[newstate]:
+                            changed = True
+                if changed:
+                    subrepo.writestate(self, state)
+                elif (changes[0] == ['.hgsubstate'] and changes[1] == [] and
+                     changes[2] == []):
+                    return None
 
             # Save commit message in case this transaction gets rolled back
             # (e.g. by a pretxncommit hook).  Leave the content alone on
@@ -1505,8 +1515,13 @@
             group = cl.group(msng_cl_lst, identity, collect)
             for cnt, chnk in enumerate(group):
                 yield chnk
-                self.ui.progress(_('bundling changes'), cnt, unit=_('chunks'))
-            self.ui.progress(_('bundling changes'), None)
+                # revlog.group yields three entries per node, so
+                # dividing by 3 gives an approximation of how many
+                # nodes have been processed.
+                self.ui.progress(_('bundling'), cnt / 3,
+                                 unit=_('changesets'))
+            changecount = cnt / 3
+            self.ui.progress(_('bundling'), None)
 
             prune(mnfst, msng_mnfst_set)
             add_extra_nodes(1, msng_mnfst_set)
@@ -1518,10 +1533,17 @@
             group = mnfst.group(msng_mnfst_lst,
                                 lambda mnode: msng_mnfst_set[mnode],
                                 filenode_collector(changedfiles))
+            efiles = {}
             for cnt, chnk in enumerate(group):
+                if cnt % 3 == 1:
+                    mnode = chnk[:20]
+                    efiles.update(mnfst.readdelta(mnode))
                 yield chnk
-                self.ui.progress(_('bundling manifests'), cnt, unit=_('chunks'))
-            self.ui.progress(_('bundling manifests'), None)
+                # see above comment for why we divide by 3
+                self.ui.progress(_('bundling'), cnt / 3,
+                                 unit=_('manifests'), total=changecount)
+            self.ui.progress(_('bundling'), None)
+            efiles = len(efiles)
 
             # These are no longer needed, dereference and toss the memory for
             # them.
@@ -1535,8 +1557,7 @@
                     msng_filenode_set.setdefault(fname, {})
                     changedfiles.add(fname)
             # Go through all our files in order sorted by name.
-            cnt = 0
-            for fname in sorted(changedfiles):
+            for idx, fname in enumerate(sorted(changedfiles)):
                 filerevlog = self.file(fname)
                 if not len(filerevlog):
                     raise util.Abort(_("empty or missing revlog for %s") % fname)
@@ -1559,13 +1580,16 @@
                     group = filerevlog.group(nodeiter,
                                              lambda fnode: missingfnodes[fnode])
                     for chnk in group:
+                        # even though we print the same progress on
+                        # most loop iterations, put the progress call
+                        # here so that time estimates (if any) can be updated
                         self.ui.progress(
-                            _('bundling files'), cnt, item=fname, unit=_('chunks'))
-                        cnt += 1
+                            _('bundling'), idx, item=fname,
+                            unit=_('files'), total=efiles)
                         yield chnk
             # Signal that no more groups are left.
             yield changegroup.closechunk()
-            self.ui.progress(_('bundling files'), None)
+            self.ui.progress(_('bundling'), None)
 
             if msng_cl_lst:
                 self.hook('outgoing', node=hex(msng_cl_lst[0]), source=source)
@@ -1613,20 +1637,30 @@
             collect = changegroup.collector(cl, mmfs, changedfiles)
 
             for cnt, chnk in enumerate(cl.group(nodes, identity, collect)):
-                self.ui.progress(_('bundling changes'), cnt, unit=_('chunks'))
+                # revlog.group yields three entries per node, so
+                # dividing by 3 gives an approximation of how many
+                # nodes have been processed.
+                self.ui.progress(_('bundling'), cnt / 3, unit=_('changesets'))
                 yield chnk
-            self.ui.progress(_('bundling changes'), None)
+            changecount = cnt / 3
+            self.ui.progress(_('bundling'), None)
 
             mnfst = self.manifest
             nodeiter = gennodelst(mnfst)
+            efiles = {}
             for cnt, chnk in enumerate(mnfst.group(nodeiter,
                                                    lookuplinkrev_func(mnfst))):
-                self.ui.progress(_('bundling manifests'), cnt, unit=_('chunks'))
+                if cnt % 3 == 1:
+                    mnode = chnk[:20]
+                    efiles.update(mnfst.readdelta(mnode))
+                # see above comment for why we divide by 3
+                self.ui.progress(_('bundling'), cnt / 3,
+                                 unit=_('manifests'), total=changecount)
                 yield chnk
-            self.ui.progress(_('bundling manifests'), None)
+            efiles = len(efiles)
+            self.ui.progress(_('bundling'), None)
 
-            cnt = 0
-            for fname in sorted(changedfiles):
+            for idx, fname in enumerate(sorted(changedfiles)):
                 filerevlog = self.file(fname)
                 if not len(filerevlog):
                     raise util.Abort(_("empty or missing revlog for %s") % fname)
@@ -1638,10 +1672,10 @@
                     lookup = lookuplinkrev_func(filerevlog)
                     for chnk in filerevlog.group(nodeiter, lookup):
                         self.ui.progress(
-                            _('bundling files'), cnt, item=fname, unit=_('chunks'))
-                        cnt += 1
+                            _('bundling'), idx, item=fname,
+                            total=efiles, unit=_('files'))
                         yield chnk
-            self.ui.progress(_('bundling files'), None)
+            self.ui.progress(_('bundling'), None)
 
             yield changegroup.closechunk()
 
--- a/mercurial/merge.py	Fri Dec 10 12:51:37 2010 +0100
+++ b/mercurial/merge.py	Mon Dec 20 12:08:56 2010 -0600
@@ -255,6 +255,9 @@
     wctx is the working copy context
     mctx is the context to be merged into the working copy
     actx is the context of the common ancestor
+
+    Return a tuple of counts (updated, merged, removed, unresolved) that
+    describes how many files were affected by the update.
     """
 
     updated, merged, removed, unresolved = 0, 0, 0, 0
@@ -462,6 +465,8 @@
                  use 'hg update -C' to discard changes)
     3 = abort: uncommitted local changes
     4 = incompatible options (checked in commands.py)
+
+    Return the same tuple as applyupdates().
     """
 
     onode = node
@@ -524,7 +529,7 @@
         action += manifestmerge(repo, wc, p2, pa, overwrite, partial)
 
         ### apply phase
-        if not branchmerge: # just jump to the new rev
+        if not branchmerge or fastforward: # just jump to the new rev
             fp1, fp2, xp1, xp2 = fp2, nullid, xp2, ''
         if not partial:
             repo.hook('preupdate', throw=True, parent1=xp1, parent2=xp2)
@@ -533,7 +538,7 @@
 
         if not partial:
             repo.dirstate.setparents(fp1, fp2)
-            recordupdates(repo, action, branchmerge)
+            recordupdates(repo, action, branchmerge and not fastforward)
             if not branchmerge and not fastforward:
                 repo.dirstate.setbranch(p2.branch())
     finally:
--- a/mercurial/patch.py	Fri Dec 10 12:51:37 2010 +0100
+++ b/mercurial/patch.py	Mon Dec 20 12:08:56 2010 -0600
@@ -6,7 +6,7 @@
 # This software may be used and distributed according to the terms of the
 # GNU General Public License version 2 or any later version.
 
-import cStringIO, email.Parser, os, re
+import cStringIO, email.Parser, os, errno, re
 import tempfile, zlib
 
 from i18n import _
@@ -429,10 +429,16 @@
         # Ensure supplied data ends in fname, being a regular file or
         # a symlink. cmdutil.updatedir will -too magically- take care
         # of setting it to the proper type afterwards.
+        st_mode = None
         islink = os.path.islink(fname)
         if islink:
             fp = cStringIO.StringIO()
         else:
+            try:
+                st_mode = os.lstat(fname).st_mode & 0777
+            except OSError, e:
+                if e.errno != errno.ENOENT:
+                    raise
             fp = self.opener(fname, 'w')
         try:
             if self.eolmode == 'auto':
@@ -451,6 +457,8 @@
                 fp.writelines(lines)
             if islink:
                 self.opener.symlink(fp.getvalue(), fname)
+            if st_mode is not None:
+                os.chmod(fname, st_mode)
         finally:
             fp.close()
 
--- a/mercurial/store.py	Fri Dec 10 12:51:37 2010 +0100
+++ b/mercurial/store.py	Mon Dec 20 12:08:56 2010 -0600
@@ -323,7 +323,8 @@
             self.fncache.rewrite(existing)
 
     def copylist(self):
-        d = _data + ' dh fncache'
+        d = ('data dh fncache'
+             ' 00manifest.d 00manifest.i 00changelog.d 00changelog.i')
         return (['requires', '00changelog.i'] +
                 [self.pathjoiner('store', f) for f in d.split()])
 
--- a/mercurial/subrepo.py	Fri Dec 10 12:51:37 2010 +0100
+++ b/mercurial/subrepo.py	Mon Dec 20 12:08:56 2010 -0600
@@ -13,6 +13,19 @@
 
 nullstate = ('', '', 'empty')
 
+
+def substate(ctx):
+    rev = {}
+    if '.hgsubstate' in ctx:
+        try:
+            for l in ctx['.hgsubstate'].data().splitlines():
+                revision, path = l.split(" ", 1)
+                rev[path] = revision
+        except IOError, err:
+            if err.errno != errno.ENOENT:
+                raise
+    return rev
+
 def state(ctx, ui):
     """return a state dict, mapping subrepo paths configured in .hgsub
     to tuple: (source from .hgsub, revision from .hgsubstate, kind
@@ -39,15 +52,7 @@
     for path, src in ui.configitems('subpaths'):
         p.set('subpaths', path, src, ui.configsource('subpaths', path))
 
-    rev = {}
-    if '.hgsubstate' in ctx:
-        try:
-            for l in ctx['.hgsubstate'].data().splitlines():
-                revision, path = l.split(" ", 1)
-                rev[path] = revision
-        except IOError, err:
-            if err.errno != errno.ENOENT:
-                raise
+    rev = substate(ctx)
 
     state = {}
     for path, src in p[''].items():
@@ -304,13 +309,21 @@
         """return file flags"""
         return ''
 
-    def archive(self, archiver, prefix):
-        for name in self.files():
+    def archive(self, ui, archiver, prefix):
+        files = self.files()
+        total = len(files)
+        relpath = subrelpath(self)
+        ui.progress(_('archiving (%s)') % relpath, 0,
+                    unit=_('files'), total=total)
+        for i, name in enumerate(files):
             flags = self.fileflags(name)
             mode = 'x' in flags and 0755 or 0644
             symlink = 'l' in flags
             archiver.addfile(os.path.join(prefix, self._path, name),
                              mode, symlink, self.filedata(name))
+            ui.progress(_('archiving (%s)') % relpath, i + 1,
+                        unit=_('files'), total=total)
+        ui.progress(_('archiving (%s)') % relpath, None)
 
 
 class hgsubrepo(abstractsubrepo):
@@ -373,14 +386,14 @@
             self._repo.ui.warn(_('warning: error "%s" in subrepository "%s"\n')
                                % (inst, subrelpath(self)))
 
-    def archive(self, archiver, prefix):
-        abstractsubrepo.archive(self, archiver, prefix)
+    def archive(self, ui, archiver, prefix):
+        abstractsubrepo.archive(self, ui, archiver, prefix)
 
         rev = self._state[1]
         ctx = self._repo[rev]
         for subpath in ctx.substate:
             s = subrepo(ctx, subpath)
-            s.archive(archiver, os.path.join(prefix, self._path))
+            s.archive(ui, archiver, os.path.join(prefix, self._path))
 
     def dirty(self):
         r = self._state[1]
@@ -484,13 +497,10 @@
     def _svncommand(self, commands, filename=''):
         path = os.path.join(self._ctx._repo.origroot, self._path, filename)
         cmd = ['svn'] + commands + [path]
-        cmd = [util.shellquote(arg) for arg in cmd]
-        cmd = util.quotecommand(' '.join(cmd))
         env = dict(os.environ)
         # Avoid localized output, preserve current locale for everything else.
         env['LC_MESSAGES'] = 'C'
-        p = subprocess.Popen(cmd, shell=True, bufsize=-1,
-                             close_fds=util.closefds,
+        p = subprocess.Popen(cmd, bufsize=-1, close_fds=util.closefds,
                              stdout=subprocess.PIPE, stderr=subprocess.PIPE,
                              universal_newlines=True, env=env)
         stdout, stderr = p.communicate()
@@ -604,7 +614,7 @@
         return self._svncommand(['cat'], name)
 
 
-class gitsubrepo(object):
+class gitsubrepo(abstractsubrepo):
     def __init__(self, ctx, path, state):
         # TODO add git version check.
         self._state = state
@@ -617,7 +627,6 @@
         return self._gitdir(commands, env=env, stream=stream)[0]
 
     def _gitdir(self, commands, env=None, stream=False):
-        commands = ['--no-pager'] + commands
         return self._gitnodir(commands, env=env, stream=stream, cwd=self._path)
 
     def _gitnodir(self, commands, env=None, stream=False, cwd=None):
@@ -626,14 +635,15 @@
         The methods tries to call the git command. versions previor to 1.6.0
         are not supported and very probably fail.
         """
-        cmd = ['git'] + commands
-        cmd = [util.shellquote(arg) for arg in cmd]
-        cmd = util.quotecommand(' '.join(cmd))
-
-        # print git's stderr, which is mostly progress and useful info
-        p = subprocess.Popen(cmd, shell=True, bufsize=-1, cwd=cwd, env=env,
+        self._ui.debug('%s: git %s\n' % (self._relpath, ' '.join(commands)))
+        # unless ui.quiet is set, print git's stderr,
+        # which is mostly progress and useful info
+        errpipe = None
+        if self._ui.quiet:
+            errpipe = open(os.devnull, 'w')
+        p = subprocess.Popen(['git'] + commands, bufsize=-1, cwd=cwd, env=env,
                              close_fds=util.closefds,
-                             stdout=subprocess.PIPE)
+                             stdout=subprocess.PIPE, stderr=errpipe)
         if stream:
             return p.stdout, None
 
@@ -641,25 +651,26 @@
         # wait for the child to exit to avoid race condition.
         p.wait()
 
-        if p.returncode != 0:
+        if p.returncode != 0 and p.returncode != 1:
             # there are certain error codes that are ok
-            command = None
-            for arg in commands:
-                if not arg.startswith('-'):
-                    command = arg
-                    break
-            if command == 'cat-file':
-                return retdata, p.returncode
-            if command in ('commit', 'status') and p.returncode == 1:
+            command = commands[0]
+            if command in ('cat-file', 'symbolic-ref'):
                 return retdata, p.returncode
             # for all others, abort
-            raise util.Abort('git %s error %d' % (command, p.returncode))
+            raise util.Abort('git %s error %d in %s' %
+                             (command, p.returncode, self._relpath))
 
         return retdata, p.returncode
 
     def _gitstate(self):
         return self._gitcommand(['rev-parse', 'HEAD'])
 
+    def _gitcurrentbranch(self):
+        current, err = self._gitdir(['symbolic-ref', 'HEAD', '--quiet'])
+        if err:
+            current = None
+        return current
+
     def _githavelocally(self, revision):
         out, code = self._gitdir(['cat-file', '-e', revision])
         return code == 0
@@ -670,41 +681,26 @@
 
     def _gitbranchmap(self):
         '''returns 3 things:
-        the current branch,
         a map from git branch to revision
-        a map from revision to branches'''
+        a map from revision to branches
+        a map from remote branch to local tracking branch'''
         branch2rev = {}
         rev2branch = {}
-        current = None
-        out = self._gitcommand(['branch', '-a', '--no-color',
-                                '--verbose', '--no-abbrev'])
+        tracking = {}
+        out = self._gitcommand(['for-each-ref', '--format',
+                                '%(objectname) %(refname) %(upstream) end'])
         for line in out.split('\n'):
-            if line[2:].startswith('(no branch)'):
+            revision, ref, upstream = line.split(' ')[:3]
+            if ref.startswith('refs/tags/'):
                 continue
-            branch, revision = line[2:].split()[:2]
-            if revision == '->' or branch.endswith('/HEAD'):
+            if ref.startswith('refs/remotes/') and ref.endswith('/HEAD'):
                 continue # ignore remote/HEAD redirects
-            if '/' in branch and not branch.startswith('remotes/'):
-                # old git compatability
-                branch = 'remotes/' + branch
-            if line[0] == '*':
-                current = branch
-            branch2rev[branch] = revision
-            rev2branch.setdefault(revision, []).append(branch)
-        return current, branch2rev, rev2branch
-
-    def _gittracking(self, branches):
-        'return map of remote branch to local tracking branch'
-        # assumes no more than one local tracking branch for each remote
-        tracking = {}
-        for b in branches:
-            if b.startswith('remotes/'):
-                continue
-            remote = self._gitcommand(['config', 'branch.%s.remote' % b])
-            if remote:
-                ref = self._gitcommand(['config', 'branch.%s.merge' % b])
-                tracking['remotes/%s/%s' % (remote, ref.split('/')[-1])] = b
-        return tracking
+            branch2rev[ref] = revision
+            rev2branch.setdefault(revision, []).append(ref)
+            if upstream:
+                # assumes no more than one local tracking branch for a remote
+                tracking[upstream] = ref
+        return branch2rev, rev2branch, tracking
 
     def _fetch(self, source, revision):
         if not os.path.exists('%s/.git' % self._path):
@@ -727,9 +723,8 @@
         if self._state[1] != self._gitstate(): # version checked out changed?
             return True
         # check for staged changes or modified files; ignore untracked files
-        status = self._gitcommand(['status'])
-        return ('\n# Changed but not updated:' in status or
-                '\n# Changes to be committed:' in status)
+        out, code = self._gitdir(['diff-index', '--quiet', 'HEAD'])
+        return code == 1
 
     def get(self, state):
         source, revision, kind = state
@@ -742,7 +737,7 @@
                 return
         elif self._gitstate() == revision:
             return
-        current, branch2rev, rev2branch = self._gitbranchmap()
+        branch2rev, rev2branch, tracking = self._gitbranchmap()
 
         def rawcheckout():
             # no branch to checkout, check it out with no branch
@@ -758,17 +753,16 @@
         branches = rev2branch[revision]
         firstlocalbranch = None
         for b in branches:
-            if b == 'master':
+            if b == 'refs/heads/master':
                 # master trumps all other branches
-                self._gitcommand(['checkout', 'master'])
+                self._gitcommand(['checkout', 'refs/heads/master'])
                 return
-            if not firstlocalbranch and not b.startswith('remotes/'):
+            if not firstlocalbranch and not b.startswith('refs/remotes/'):
                 firstlocalbranch = b
         if firstlocalbranch:
             self._gitcommand(['checkout', firstlocalbranch])
             return
 
-        tracking = self._gittracking(branch2rev.keys())
         # choose a remote branch already tracked if possible
         remote = branches[0]
         if remote not in tracking:
@@ -779,7 +773,7 @@
 
         if remote not in tracking:
             # create a new local tracking branch
-            local = remote.split('/')[-1]
+            local = remote.split('/', 2)[2]
             self._gitcommand(['checkout', '-b', local, remote])
         elif self._gitisancestor(branch2rev[tracking[remote]], remote):
             # When updating to a tracked remote branch,
@@ -788,7 +782,7 @@
             # which is equivalent to updating the local branch to the remote.
             # Since we are only looking at branching at update, we need to
             # detect this situation and perform this action lazily.
-            if tracking[remote] != current:
+            if tracking[remote] != self._gitcurrentbranch():
                 self._gitcommand(['checkout', tracking[remote]])
             self._gitcommand(['merge', '--ff', remote])
         else:
@@ -821,15 +815,21 @@
 
     def push(self, force):
         # if a branch in origin contains the revision, nothing to do
-        current, branch2rev, rev2branch = self._gitbranchmap()
+        branch2rev, rev2branch, tracking = self._gitbranchmap()
+        if self._state[1] in rev2branch:
+            for b in rev2branch[self._state[1]]:
+                if b.startswith('refs/remotes/origin/'):
+                    return True
         for b, revision in branch2rev.iteritems():
-            if b.startswith('remotes/origin'):
+            if b.startswith('refs/remotes/origin/'):
                 if self._gitisancestor(self._state[1], revision):
                     return True
         # otherwise, try to push the currently checked out branch
         cmd = ['push']
         if force:
             cmd.append('--force')
+
+        current = self._gitcurrentbranch()
         if current:
             # determine if the current branch is even useful
             if not self._gitisancestor(self._state[1], current):
@@ -837,7 +837,7 @@
                                 'in subrepo %s\n') % self._relpath)
                 return False
             self._ui.status(_('pushing branch %s of subrepo %s\n') %
-                            (current, self._relpath))
+                            (current.split('/', 2)[2], self._relpath))
             self._gitcommand(cmd + ['origin', current])
             return True
         else:
@@ -864,7 +864,7 @@
             else:
                 os.remove(path)
 
-    def archive(self, archiver, prefix):
+    def archive(self, ui, archiver, prefix):
         source, revision = self._state
         self._fetch(source, revision)
 
@@ -873,10 +873,16 @@
         # and objects with many subprocess calls.
         tarstream = self._gitcommand(['archive', revision], stream=True)
         tar = tarfile.open(fileobj=tarstream, mode='r|')
-        for info in tar:
+        relpath = subrelpath(self)
+        ui.progress(_('archiving (%s)') % relpath, 0, unit=_('files'))
+        for i, info in enumerate(tar):
             archiver.addfile(os.path.join(prefix, self._relpath, info.name),
                              info.mode, info.issym(),
                              tar.extractfile(info).read())
+            ui.progress(_('archiving (%s)') % relpath, i + 1,
+                        unit=_('files'))
+        ui.progress(_('archiving (%s)') % relpath, None)
+
 
 types = {
     'hg': hgsubrepo,
--- a/mercurial/templatekw.py	Fri Dec 10 12:51:37 2010 +0100
+++ b/mercurial/templatekw.py	Mon Dec 20 12:08:56 2010 -0600
@@ -145,6 +145,9 @@
 def showauthor(repo, ctx, templ, **args):
     return ctx.user()
 
+def showbranch(**args):
+    return args['ctx'].branch()
+
 def showbranches(**args):
     branch = args['ctx'].branch()
     if branch != 'default':
@@ -162,9 +165,8 @@
     return ctx.description().strip()
 
 def showdiffstat(repo, ctx, templ, **args):
-    diff = patch.diff(repo, ctx.parents()[0].node(), ctx.node())
     files, adds, removes = 0, 0, 0
-    for i in patch.diffstatdata(util.iterlines(diff)):
+    for i in patch.diffstatdata(util.iterlines(ctx.diff())):
         files += 1
         adds += i[1]
         removes += i[2]
@@ -248,6 +250,7 @@
 # revcache - a cache dictionary for the current revision
 keywords = {
     'author': showauthor,
+    'branch': showbranch,
     'branches': showbranches,
     'children': showchildren,
     'date': showdate,
--- a/mercurial/url.py	Fri Dec 10 12:51:37 2010 +0100
+++ b/mercurial/url.py	Mon Dec 20 12:08:56 2010 -0600
@@ -258,18 +258,36 @@
     defines a __len__ attribute to feed the Content-Length header.
     """
 
-    def __init__(self, *args, **kwargs):
+    def __init__(self, ui, *args, **kwargs):
         # We can't just "self._data = open(*args, **kwargs)" here because there
         # is an "open" function defined in this module that shadows the global
         # one
+        self.ui = ui
         self._data = __builtin__.open(*args, **kwargs)
-        self.read = self._data.read
         self.seek = self._data.seek
         self.close = self._data.close
         self.write = self._data.write
+        self._len = os.fstat(self._data.fileno()).st_size
+        self._pos = 0
+        self._total = len(self) / 1024 * 2
+
+    def read(self, *args, **kwargs):
+        try:
+            ret = self._data.read(*args, **kwargs)
+        except EOFError:
+            self.ui.progress(_('sending'), None)
+        self._pos += len(ret)
+        # We pass double the max for total because we currently have
+        # to send the bundle twice in the case of a server that
+        # requires authentication. Since we can't know until we try
+        # once whether authentication will be required, just lie to
+        # the user and maybe the push succeeds suddenly at 50%.
+        self.ui.progress(_('sending'), self._pos / 1024,
+                         unit=_('kb'), total=self._total)
+        return ret
 
     def __len__(self):
-        return os.fstat(self._data.fileno()).st_size
+        return self._len
 
 def _gen_sendfile(connection):
     def _sendfile(self, data):
@@ -527,6 +545,9 @@
                 self.ui.debug('%s certificate successfully verified\n' %
                               self.host)
             else:
+                self.ui.warn(_("warning: %s certificate not verified "
+                               "(check web.cacerts config setting)\n") % 
+                             self.host)
                 httplib.HTTPSConnection.connect(self)
 
     class httpsconnection(BetterHTTPS):
--- a/mercurial/util.py	Fri Dec 10 12:51:37 2010 +0100
+++ b/mercurial/util.py	Mon Dec 20 12:08:56 2010 -0600
@@ -391,7 +391,8 @@
             return '1'
         return str(val)
     origcmd = cmd
-    if os.name == 'nt':
+    if os.name == 'nt' and sys.version_info < (2, 7, 1):
+        # Python versions since 2.7.1 do this extra quoting themselves
         cmd = '"%s"' % cmd
     env = dict(os.environ)
     env.update((k, py2shell(v)) for k, v in environ.iteritems())
@@ -882,7 +883,6 @@
             mode += "b" # for that other OS
 
         nlink = -1
-        st_mode = None
         dirname, basename = os.path.split(f)
         # If basename is empty, then the path is malformed because it points
         # to a directory. Let the posixfile() call below raise IOError.
@@ -893,7 +893,6 @@
                 return atomictempfile(f, mode, self.createmode)
             try:
                 if 'w' in mode:
-                    st_mode = os.lstat(f).st_mode & 0777
                     os.unlink(f)
                     nlink = 0
                 else:
@@ -913,10 +912,7 @@
                     rename(mktempcopy(f), f)
         fp = posixfile(f, mode)
         if nlink == 0:
-            if st_mode is None:
-                self._fixfilemode(f)
-            else:
-                os.chmod(f, st_mode)
+            self._fixfilemode(f)
         return fp
 
     def symlink(self, src, dst):
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tests/filtercr.py	Mon Dec 20 12:08:56 2010 -0600
@@ -0,0 +1,10 @@
+#!/usr/bin/env python
+
+# Filter output by the progress extension to make it readable in tests
+
+import sys, re
+
+for line in sys.stdin:
+    line = re.sub(r'\r+[^\n]', lambda m: '\n' + m.group()[-1:], line)
+    sys.stdout.write(line)
+print
--- a/tests/test-1102.t	Fri Dec 10 12:51:37 2010 +0100
+++ b/tests/test-1102.t	Mon Dec 20 12:08:56 2010 -0600
@@ -9,7 +9,7 @@
 
   $ hg co 1
   1 files updated, 0 files merged, 0 files removed, 0 files unresolved
-  $ hg tag -r0 t1
+  $ hg tag -f -r0 t1
   $ hg tags
   tip                                3:a49829c4fc11
   t1                                 0:f7b1eb17ad24
--- a/tests/test-acl.t	Fri Dec 10 12:51:37 2010 +0100
+++ b/tests/test-acl.t	Mon Dec 20 12:08:56 2010 -0600
@@ -90,38 +90,38 @@
   f9cafe1212c8c6fa1120d14a556e18cc44ff8bdd
   911600dab2ae7a9baff75958b84fe606851ce955
   adding changesets
-  bundling changes: 0 chunks
-  bundling changes: 1 chunks
-  bundling changes: 2 chunks
-  bundling changes: 3 chunks
-  bundling changes: 4 chunks
-  bundling changes: 5 chunks
-  bundling changes: 6 chunks
-  bundling changes: 7 chunks
-  bundling changes: 8 chunks
-  bundling changes: 9 chunks
-  bundling manifests: 0 chunks
-  bundling manifests: 1 chunks
-  bundling manifests: 2 chunks
-  bundling manifests: 3 chunks
-  bundling manifests: 4 chunks
-  bundling manifests: 5 chunks
-  bundling manifests: 6 chunks
-  bundling manifests: 7 chunks
-  bundling manifests: 8 chunks
-  bundling manifests: 9 chunks
-  bundling files: foo/Bar/file.txt 0 chunks
-  bundling files: foo/Bar/file.txt 1 chunks
-  bundling files: foo/Bar/file.txt 2 chunks
-  bundling files: foo/Bar/file.txt 3 chunks
-  bundling files: foo/file.txt 4 chunks
-  bundling files: foo/file.txt 5 chunks
-  bundling files: foo/file.txt 6 chunks
-  bundling files: foo/file.txt 7 chunks
-  bundling files: quux/file.py 8 chunks
-  bundling files: quux/file.py 9 chunks
-  bundling files: quux/file.py 10 chunks
-  bundling files: quux/file.py 11 chunks
+  bundling: 0 changesets
+  bundling: 0 changesets
+  bundling: 0 changesets
+  bundling: 1 changesets
+  bundling: 1 changesets
+  bundling: 1 changesets
+  bundling: 2 changesets
+  bundling: 2 changesets
+  bundling: 2 changesets
+  bundling: 3 changesets
+  bundling: 0/3 manifests (0.00%)
+  bundling: 0/3 manifests (0.00%)
+  bundling: 0/3 manifests (0.00%)
+  bundling: 1/3 manifests (33.33%)
+  bundling: 1/3 manifests (33.33%)
+  bundling: 1/3 manifests (33.33%)
+  bundling: 2/3 manifests (66.67%)
+  bundling: 2/3 manifests (66.67%)
+  bundling: 2/3 manifests (66.67%)
+  bundling: 3/3 manifests (100.00%)
+  bundling: foo/Bar/file.txt 0/3 files (0.00%)
+  bundling: foo/Bar/file.txt 0/3 files (0.00%)
+  bundling: foo/Bar/file.txt 0/3 files (0.00%)
+  bundling: foo/Bar/file.txt 0/3 files (0.00%)
+  bundling: foo/file.txt 1/3 files (33.33%)
+  bundling: foo/file.txt 1/3 files (33.33%)
+  bundling: foo/file.txt 1/3 files (33.33%)
+  bundling: foo/file.txt 1/3 files (33.33%)
+  bundling: quux/file.py 2/3 files (66.67%)
+  bundling: quux/file.py 2/3 files (66.67%)
+  bundling: quux/file.py 2/3 files (66.67%)
+  bundling: quux/file.py 2/3 files (66.67%)
   changesets: 1 chunks
   add changeset ef1ea85a6374
   changesets: 2 chunks
@@ -166,38 +166,38 @@
   f9cafe1212c8c6fa1120d14a556e18cc44ff8bdd
   911600dab2ae7a9baff75958b84fe606851ce955
   adding changesets
-  bundling changes: 0 chunks
-  bundling changes: 1 chunks
-  bundling changes: 2 chunks
-  bundling changes: 3 chunks
-  bundling changes: 4 chunks
-  bundling changes: 5 chunks
-  bundling changes: 6 chunks
-  bundling changes: 7 chunks
-  bundling changes: 8 chunks
-  bundling changes: 9 chunks
-  bundling manifests: 0 chunks
-  bundling manifests: 1 chunks
-  bundling manifests: 2 chunks
-  bundling manifests: 3 chunks
-  bundling manifests: 4 chunks
-  bundling manifests: 5 chunks
-  bundling manifests: 6 chunks
-  bundling manifests: 7 chunks
-  bundling manifests: 8 chunks
-  bundling manifests: 9 chunks
-  bundling files: foo/Bar/file.txt 0 chunks
-  bundling files: foo/Bar/file.txt 1 chunks
-  bundling files: foo/Bar/file.txt 2 chunks
-  bundling files: foo/Bar/file.txt 3 chunks
-  bundling files: foo/file.txt 4 chunks
-  bundling files: foo/file.txt 5 chunks
-  bundling files: foo/file.txt 6 chunks
-  bundling files: foo/file.txt 7 chunks
-  bundling files: quux/file.py 8 chunks
-  bundling files: quux/file.py 9 chunks
-  bundling files: quux/file.py 10 chunks
-  bundling files: quux/file.py 11 chunks
+  bundling: 0 changesets
+  bundling: 0 changesets
+  bundling: 0 changesets
+  bundling: 1 changesets
+  bundling: 1 changesets
+  bundling: 1 changesets
+  bundling: 2 changesets
+  bundling: 2 changesets
+  bundling: 2 changesets
+  bundling: 3 changesets
+  bundling: 0/3 manifests (0.00%)
+  bundling: 0/3 manifests (0.00%)
+  bundling: 0/3 manifests (0.00%)
+  bundling: 1/3 manifests (33.33%)
+  bundling: 1/3 manifests (33.33%)
+  bundling: 1/3 manifests (33.33%)
+  bundling: 2/3 manifests (66.67%)
+  bundling: 2/3 manifests (66.67%)
+  bundling: 2/3 manifests (66.67%)
+  bundling: 3/3 manifests (100.00%)
+  bundling: foo/Bar/file.txt 0/3 files (0.00%)
+  bundling: foo/Bar/file.txt 0/3 files (0.00%)
+  bundling: foo/Bar/file.txt 0/3 files (0.00%)
+  bundling: foo/Bar/file.txt 0/3 files (0.00%)
+  bundling: foo/file.txt 1/3 files (33.33%)
+  bundling: foo/file.txt 1/3 files (33.33%)
+  bundling: foo/file.txt 1/3 files (33.33%)
+  bundling: foo/file.txt 1/3 files (33.33%)
+  bundling: quux/file.py 2/3 files (66.67%)
+  bundling: quux/file.py 2/3 files (66.67%)
+  bundling: quux/file.py 2/3 files (66.67%)
+  bundling: quux/file.py 2/3 files (66.67%)
   changesets: 1 chunks
   add changeset ef1ea85a6374
   changesets: 2 chunks
@@ -245,38 +245,38 @@
   f9cafe1212c8c6fa1120d14a556e18cc44ff8bdd
   911600dab2ae7a9baff75958b84fe606851ce955
   adding changesets
-  bundling changes: 0 chunks
-  bundling changes: 1 chunks
-  bundling changes: 2 chunks
-  bundling changes: 3 chunks
-  bundling changes: 4 chunks
-  bundling changes: 5 chunks
-  bundling changes: 6 chunks
-  bundling changes: 7 chunks
-  bundling changes: 8 chunks
-  bundling changes: 9 chunks
-  bundling manifests: 0 chunks
-  bundling manifests: 1 chunks
-  bundling manifests: 2 chunks
-  bundling manifests: 3 chunks
-  bundling manifests: 4 chunks
-  bundling manifests: 5 chunks
-  bundling manifests: 6 chunks
-  bundling manifests: 7 chunks
-  bundling manifests: 8 chunks
-  bundling manifests: 9 chunks
-  bundling files: foo/Bar/file.txt 0 chunks
-  bundling files: foo/Bar/file.txt 1 chunks
-  bundling files: foo/Bar/file.txt 2 chunks
-  bundling files: foo/Bar/file.txt 3 chunks
-  bundling files: foo/file.txt 4 chunks
-  bundling files: foo/file.txt 5 chunks
-  bundling files: foo/file.txt 6 chunks
-  bundling files: foo/file.txt 7 chunks
-  bundling files: quux/file.py 8 chunks
-  bundling files: quux/file.py 9 chunks
-  bundling files: quux/file.py 10 chunks
-  bundling files: quux/file.py 11 chunks
+  bundling: 0 changesets
+  bundling: 0 changesets
+  bundling: 0 changesets
+  bundling: 1 changesets
+  bundling: 1 changesets
+  bundling: 1 changesets
+  bundling: 2 changesets
+  bundling: 2 changesets
+  bundling: 2 changesets
+  bundling: 3 changesets
+  bundling: 0/3 manifests (0.00%)
+  bundling: 0/3 manifests (0.00%)
+  bundling: 0/3 manifests (0.00%)
+  bundling: 1/3 manifests (33.33%)
+  bundling: 1/3 manifests (33.33%)
+  bundling: 1/3 manifests (33.33%)
+  bundling: 2/3 manifests (66.67%)
+  bundling: 2/3 manifests (66.67%)
+  bundling: 2/3 manifests (66.67%)
+  bundling: 3/3 manifests (100.00%)
+  bundling: foo/Bar/file.txt 0/3 files (0.00%)
+  bundling: foo/Bar/file.txt 0/3 files (0.00%)
+  bundling: foo/Bar/file.txt 0/3 files (0.00%)
+  bundling: foo/Bar/file.txt 0/3 files (0.00%)
+  bundling: foo/file.txt 1/3 files (33.33%)
+  bundling: foo/file.txt 1/3 files (33.33%)
+  bundling: foo/file.txt 1/3 files (33.33%)
+  bundling: foo/file.txt 1/3 files (33.33%)
+  bundling: quux/file.py 2/3 files (66.67%)
+  bundling: quux/file.py 2/3 files (66.67%)
+  bundling: quux/file.py 2/3 files (66.67%)
+  bundling: quux/file.py 2/3 files (66.67%)
   changesets: 1 chunks
   add changeset ef1ea85a6374
   changesets: 2 chunks
@@ -333,38 +333,38 @@
   f9cafe1212c8c6fa1120d14a556e18cc44ff8bdd
   911600dab2ae7a9baff75958b84fe606851ce955
   adding changesets
-  bundling changes: 0 chunks
-  bundling changes: 1 chunks
-  bundling changes: 2 chunks
-  bundling changes: 3 chunks
-  bundling changes: 4 chunks
-  bundling changes: 5 chunks
-  bundling changes: 6 chunks
-  bundling changes: 7 chunks
-  bundling changes: 8 chunks
-  bundling changes: 9 chunks
-  bundling manifests: 0 chunks
-  bundling manifests: 1 chunks
-  bundling manifests: 2 chunks
-  bundling manifests: 3 chunks
-  bundling manifests: 4 chunks
-  bundling manifests: 5 chunks
-  bundling manifests: 6 chunks
-  bundling manifests: 7 chunks
-  bundling manifests: 8 chunks
-  bundling manifests: 9 chunks
-  bundling files: foo/Bar/file.txt 0 chunks
-  bundling files: foo/Bar/file.txt 1 chunks
-  bundling files: foo/Bar/file.txt 2 chunks
-  bundling files: foo/Bar/file.txt 3 chunks
-  bundling files: foo/file.txt 4 chunks
-  bundling files: foo/file.txt 5 chunks
-  bundling files: foo/file.txt 6 chunks
-  bundling files: foo/file.txt 7 chunks
-  bundling files: quux/file.py 8 chunks
-  bundling files: quux/file.py 9 chunks
-  bundling files: quux/file.py 10 chunks
-  bundling files: quux/file.py 11 chunks
+  bundling: 0 changesets
+  bundling: 0 changesets
+  bundling: 0 changesets
+  bundling: 1 changesets
+  bundling: 1 changesets
+  bundling: 1 changesets
+  bundling: 2 changesets
+  bundling: 2 changesets
+  bundling: 2 changesets
+  bundling: 3 changesets
+  bundling: 0/3 manifests (0.00%)
+  bundling: 0/3 manifests (0.00%)
+  bundling: 0/3 manifests (0.00%)
+  bundling: 1/3 manifests (33.33%)
+  bundling: 1/3 manifests (33.33%)
+  bundling: 1/3 manifests (33.33%)
+  bundling: 2/3 manifests (66.67%)
+  bundling: 2/3 manifests (66.67%)
+  bundling: 2/3 manifests (66.67%)
+  bundling: 3/3 manifests (100.00%)
+  bundling: foo/Bar/file.txt 0/3 files (0.00%)
+  bundling: foo/Bar/file.txt 0/3 files (0.00%)
+  bundling: foo/Bar/file.txt 0/3 files (0.00%)
+  bundling: foo/Bar/file.txt 0/3 files (0.00%)
+  bundling: foo/file.txt 1/3 files (33.33%)
+  bundling: foo/file.txt 1/3 files (33.33%)
+  bundling: foo/file.txt 1/3 files (33.33%)
+  bundling: foo/file.txt 1/3 files (33.33%)
+  bundling: quux/file.py 2/3 files (66.67%)
+  bundling: quux/file.py 2/3 files (66.67%)
+  bundling: quux/file.py 2/3 files (66.67%)
+  bundling: quux/file.py 2/3 files (66.67%)
   changesets: 1 chunks
   add changeset ef1ea85a6374
   changesets: 2 chunks
@@ -420,38 +420,38 @@
   f9cafe1212c8c6fa1120d14a556e18cc44ff8bdd
   911600dab2ae7a9baff75958b84fe606851ce955
   adding changesets
-  bundling changes: 0 chunks
-  bundling changes: 1 chunks
-  bundling changes: 2 chunks
-  bundling changes: 3 chunks
-  bundling changes: 4 chunks
-  bundling changes: 5 chunks
-  bundling changes: 6 chunks
-  bundling changes: 7 chunks
-  bundling changes: 8 chunks
-  bundling changes: 9 chunks
-  bundling manifests: 0 chunks
-  bundling manifests: 1 chunks
-  bundling manifests: 2 chunks
-  bundling manifests: 3 chunks
-  bundling manifests: 4 chunks
-  bundling manifests: 5 chunks
-  bundling manifests: 6 chunks
-  bundling manifests: 7 chunks
-  bundling manifests: 8 chunks
-  bundling manifests: 9 chunks
-  bundling files: foo/Bar/file.txt 0 chunks
-  bundling files: foo/Bar/file.txt 1 chunks
-  bundling files: foo/Bar/file.txt 2 chunks
-  bundling files: foo/Bar/file.txt 3 chunks
-  bundling files: foo/file.txt 4 chunks
-  bundling files: foo/file.txt 5 chunks
-  bundling files: foo/file.txt 6 chunks
-  bundling files: foo/file.txt 7 chunks
-  bundling files: quux/file.py 8 chunks
-  bundling files: quux/file.py 9 chunks
-  bundling files: quux/file.py 10 chunks
-  bundling files: quux/file.py 11 chunks
+  bundling: 0 changesets
+  bundling: 0 changesets
+  bundling: 0 changesets
+  bundling: 1 changesets
+  bundling: 1 changesets
+  bundling: 1 changesets
+  bundling: 2 changesets
+  bundling: 2 changesets
+  bundling: 2 changesets
+  bundling: 3 changesets
+  bundling: 0/3 manifests (0.00%)
+  bundling: 0/3 manifests (0.00%)
+  bundling: 0/3 manifests (0.00%)
+  bundling: 1/3 manifests (33.33%)
+  bundling: 1/3 manifests (33.33%)
+  bundling: 1/3 manifests (33.33%)
+  bundling: 2/3 manifests (66.67%)
+  bundling: 2/3 manifests (66.67%)
+  bundling: 2/3 manifests (66.67%)
+  bundling: 3/3 manifests (100.00%)
+  bundling: foo/Bar/file.txt 0/3 files (0.00%)
+  bundling: foo/Bar/file.txt 0/3 files (0.00%)
+  bundling: foo/Bar/file.txt 0/3 files (0.00%)
+  bundling: foo/Bar/file.txt 0/3 files (0.00%)
+  bundling: foo/file.txt 1/3 files (33.33%)
+  bundling: foo/file.txt 1/3 files (33.33%)
+  bundling: foo/file.txt 1/3 files (33.33%)
+  bundling: foo/file.txt 1/3 files (33.33%)
+  bundling: quux/file.py 2/3 files (66.67%)
+  bundling: quux/file.py 2/3 files (66.67%)
+  bundling: quux/file.py 2/3 files (66.67%)
+  bundling: quux/file.py 2/3 files (66.67%)
   changesets: 1 chunks
   add changeset ef1ea85a6374
   changesets: 2 chunks
@@ -512,38 +512,38 @@
   f9cafe1212c8c6fa1120d14a556e18cc44ff8bdd
   911600dab2ae7a9baff75958b84fe606851ce955
   adding changesets
-  bundling changes: 0 chunks
-  bundling changes: 1 chunks
-  bundling changes: 2 chunks
-  bundling changes: 3 chunks
-  bundling changes: 4 chunks
-  bundling changes: 5 chunks
-  bundling changes: 6 chunks
-  bundling changes: 7 chunks
-  bundling changes: 8 chunks
-  bundling changes: 9 chunks
-  bundling manifests: 0 chunks
-  bundling manifests: 1 chunks
-  bundling manifests: 2 chunks
-  bundling manifests: 3 chunks
-  bundling manifests: 4 chunks
-  bundling manifests: 5 chunks
-  bundling manifests: 6 chunks
-  bundling manifests: 7 chunks
-  bundling manifests: 8 chunks
-  bundling manifests: 9 chunks
-  bundling files: foo/Bar/file.txt 0 chunks
-  bundling files: foo/Bar/file.txt 1 chunks
-  bundling files: foo/Bar/file.txt 2 chunks
-  bundling files: foo/Bar/file.txt 3 chunks
-  bundling files: foo/file.txt 4 chunks
-  bundling files: foo/file.txt 5 chunks
-  bundling files: foo/file.txt 6 chunks
-  bundling files: foo/file.txt 7 chunks
-  bundling files: quux/file.py 8 chunks
-  bundling files: quux/file.py 9 chunks
-  bundling files: quux/file.py 10 chunks
-  bundling files: quux/file.py 11 chunks
+  bundling: 0 changesets
+  bundling: 0 changesets
+  bundling: 0 changesets
+  bundling: 1 changesets
+  bundling: 1 changesets
+  bundling: 1 changesets
+  bundling: 2 changesets
+  bundling: 2 changesets
+  bundling: 2 changesets
+  bundling: 3 changesets
+  bundling: 0/3 manifests (0.00%)
+  bundling: 0/3 manifests (0.00%)
+  bundling: 0/3 manifests (0.00%)
+  bundling: 1/3 manifests (33.33%)
+  bundling: 1/3 manifests (33.33%)
+  bundling: 1/3 manifests (33.33%)
+  bundling: 2/3 manifests (66.67%)
+  bundling: 2/3 manifests (66.67%)
+  bundling: 2/3 manifests (66.67%)
+  bundling: 3/3 manifests (100.00%)
+  bundling: foo/Bar/file.txt 0/3 files (0.00%)
+  bundling: foo/Bar/file.txt 0/3 files (0.00%)
+  bundling: foo/Bar/file.txt 0/3 files (0.00%)
+  bundling: foo/Bar/file.txt 0/3 files (0.00%)
+  bundling: foo/file.txt 1/3 files (33.33%)
+  bundling: foo/file.txt 1/3 files (33.33%)
+  bundling: foo/file.txt 1/3 files (33.33%)
+  bundling: foo/file.txt 1/3 files (33.33%)
+  bundling: quux/file.py 2/3 files (66.67%)
+  bundling: quux/file.py 2/3 files (66.67%)
+  bundling: quux/file.py 2/3 files (66.67%)
+  bundling: quux/file.py 2/3 files (66.67%)
   changesets: 1 chunks
   add changeset ef1ea85a6374
   changesets: 2 chunks
@@ -601,38 +601,38 @@
   f9cafe1212c8c6fa1120d14a556e18cc44ff8bdd
   911600dab2ae7a9baff75958b84fe606851ce955
   adding changesets
-  bundling changes: 0 chunks
-  bundling changes: 1 chunks
-  bundling changes: 2 chunks
-  bundling changes: 3 chunks
-  bundling changes: 4 chunks
-  bundling changes: 5 chunks
-  bundling changes: 6 chunks
-  bundling changes: 7 chunks
-  bundling changes: 8 chunks
-  bundling changes: 9 chunks
-  bundling manifests: 0 chunks
-  bundling manifests: 1 chunks
-  bundling manifests: 2 chunks
-  bundling manifests: 3 chunks
-  bundling manifests: 4 chunks
-  bundling manifests: 5 chunks
-  bundling manifests: 6 chunks
-  bundling manifests: 7 chunks
-  bundling manifests: 8 chunks
-  bundling manifests: 9 chunks
-  bundling files: foo/Bar/file.txt 0 chunks
-  bundling files: foo/Bar/file.txt 1 chunks
-  bundling files: foo/Bar/file.txt 2 chunks
-  bundling files: foo/Bar/file.txt 3 chunks
-  bundling files: foo/file.txt 4 chunks
-  bundling files: foo/file.txt 5 chunks
-  bundling files: foo/file.txt 6 chunks
-  bundling files: foo/file.txt 7 chunks
-  bundling files: quux/file.py 8 chunks
-  bundling files: quux/file.py 9 chunks
-  bundling files: quux/file.py 10 chunks
-  bundling files: quux/file.py 11 chunks
+  bundling: 0 changesets
+  bundling: 0 changesets
+  bundling: 0 changesets
+  bundling: 1 changesets
+  bundling: 1 changesets
+  bundling: 1 changesets
+  bundling: 2 changesets
+  bundling: 2 changesets
+  bundling: 2 changesets
+  bundling: 3 changesets
+  bundling: 0/3 manifests (0.00%)
+  bundling: 0/3 manifests (0.00%)
+  bundling: 0/3 manifests (0.00%)
+  bundling: 1/3 manifests (33.33%)
+  bundling: 1/3 manifests (33.33%)
+  bundling: 1/3 manifests (33.33%)
+  bundling: 2/3 manifests (66.67%)
+  bundling: 2/3 manifests (66.67%)
+  bundling: 2/3 manifests (66.67%)
+  bundling: 3/3 manifests (100.00%)
+  bundling: foo/Bar/file.txt 0/3 files (0.00%)
+  bundling: foo/Bar/file.txt 0/3 files (0.00%)
+  bundling: foo/Bar/file.txt 0/3 files (0.00%)
+  bundling: foo/Bar/file.txt 0/3 files (0.00%)
+  bundling: foo/file.txt 1/3 files (33.33%)
+  bundling: foo/file.txt 1/3 files (33.33%)
+  bundling: foo/file.txt 1/3 files (33.33%)
+  bundling: foo/file.txt 1/3 files (33.33%)
+  bundling: quux/file.py 2/3 files (66.67%)
+  bundling: quux/file.py 2/3 files (66.67%)
+  bundling: quux/file.py 2/3 files (66.67%)
+  bundling: quux/file.py 2/3 files (66.67%)
   changesets: 1 chunks
   add changeset ef1ea85a6374
   changesets: 2 chunks
@@ -695,38 +695,38 @@
   f9cafe1212c8c6fa1120d14a556e18cc44ff8bdd
   911600dab2ae7a9baff75958b84fe606851ce955
   adding changesets
-  bundling changes: 0 chunks
-  bundling changes: 1 chunks
-  bundling changes: 2 chunks
-  bundling changes: 3 chunks
-  bundling changes: 4 chunks
-  bundling changes: 5 chunks
-  bundling changes: 6 chunks
-  bundling changes: 7 chunks
-  bundling changes: 8 chunks
-  bundling changes: 9 chunks
-  bundling manifests: 0 chunks
-  bundling manifests: 1 chunks
-  bundling manifests: 2 chunks
-  bundling manifests: 3 chunks
-  bundling manifests: 4 chunks
-  bundling manifests: 5 chunks
-  bundling manifests: 6 chunks
-  bundling manifests: 7 chunks
-  bundling manifests: 8 chunks
-  bundling manifests: 9 chunks
-  bundling files: foo/Bar/file.txt 0 chunks
-  bundling files: foo/Bar/file.txt 1 chunks
-  bundling files: foo/Bar/file.txt 2 chunks
-  bundling files: foo/Bar/file.txt 3 chunks
-  bundling files: foo/file.txt 4 chunks
-  bundling files: foo/file.txt 5 chunks
-  bundling files: foo/file.txt 6 chunks
-  bundling files: foo/file.txt 7 chunks
-  bundling files: quux/file.py 8 chunks
-  bundling files: quux/file.py 9 chunks
-  bundling files: quux/file.py 10 chunks
-  bundling files: quux/file.py 11 chunks
+  bundling: 0 changesets
+  bundling: 0 changesets
+  bundling: 0 changesets
+  bundling: 1 changesets
+  bundling: 1 changesets
+  bundling: 1 changesets
+  bundling: 2 changesets
+  bundling: 2 changesets
+  bundling: 2 changesets
+  bundling: 3 changesets
+  bundling: 0/3 manifests (0.00%)
+  bundling: 0/3 manifests (0.00%)
+  bundling: 0/3 manifests (0.00%)
+  bundling: 1/3 manifests (33.33%)
+  bundling: 1/3 manifests (33.33%)
+  bundling: 1/3 manifests (33.33%)
+  bundling: 2/3 manifests (66.67%)
+  bundling: 2/3 manifests (66.67%)
+  bundling: 2/3 manifests (66.67%)
+  bundling: 3/3 manifests (100.00%)
+  bundling: foo/Bar/file.txt 0/3 files (0.00%)
+  bundling: foo/Bar/file.txt 0/3 files (0.00%)
+  bundling: foo/Bar/file.txt 0/3 files (0.00%)
+  bundling: foo/Bar/file.txt 0/3 files (0.00%)
+  bundling: foo/file.txt 1/3 files (33.33%)
+  bundling: foo/file.txt 1/3 files (33.33%)
+  bundling: foo/file.txt 1/3 files (33.33%)
+  bundling: foo/file.txt 1/3 files (33.33%)
+  bundling: quux/file.py 2/3 files (66.67%)
+  bundling: quux/file.py 2/3 files (66.67%)
+  bundling: quux/file.py 2/3 files (66.67%)
+  bundling: quux/file.py 2/3 files (66.67%)
   changesets: 1 chunks
   add changeset ef1ea85a6374
   changesets: 2 chunks
@@ -786,38 +786,38 @@
   f9cafe1212c8c6fa1120d14a556e18cc44ff8bdd
   911600dab2ae7a9baff75958b84fe606851ce955
   adding changesets
-  bundling changes: 0 chunks
-  bundling changes: 1 chunks
-  bundling changes: 2 chunks
-  bundling changes: 3 chunks
-  bundling changes: 4 chunks
-  bundling changes: 5 chunks
-  bundling changes: 6 chunks
-  bundling changes: 7 chunks
-  bundling changes: 8 chunks
-  bundling changes: 9 chunks
-  bundling manifests: 0 chunks
-  bundling manifests: 1 chunks
-  bundling manifests: 2 chunks
-  bundling manifests: 3 chunks
-  bundling manifests: 4 chunks
-  bundling manifests: 5 chunks
-  bundling manifests: 6 chunks
-  bundling manifests: 7 chunks
-  bundling manifests: 8 chunks
-  bundling manifests: 9 chunks
-  bundling files: foo/Bar/file.txt 0 chunks
-  bundling files: foo/Bar/file.txt 1 chunks
-  bundling files: foo/Bar/file.txt 2 chunks
-  bundling files: foo/Bar/file.txt 3 chunks
-  bundling files: foo/file.txt 4 chunks
-  bundling files: foo/file.txt 5 chunks
-  bundling files: foo/file.txt 6 chunks
-  bundling files: foo/file.txt 7 chunks
-  bundling files: quux/file.py 8 chunks
-  bundling files: quux/file.py 9 chunks
-  bundling files: quux/file.py 10 chunks
-  bundling files: quux/file.py 11 chunks
+  bundling: 0 changesets
+  bundling: 0 changesets
+  bundling: 0 changesets
+  bundling: 1 changesets
+  bundling: 1 changesets
+  bundling: 1 changesets
+  bundling: 2 changesets
+  bundling: 2 changesets
+  bundling: 2 changesets
+  bundling: 3 changesets
+  bundling: 0/3 manifests (0.00%)
+  bundling: 0/3 manifests (0.00%)
+  bundling: 0/3 manifests (0.00%)
+  bundling: 1/3 manifests (33.33%)
+  bundling: 1/3 manifests (33.33%)
+  bundling: 1/3 manifests (33.33%)
+  bundling: 2/3 manifests (66.67%)
+  bundling: 2/3 manifests (66.67%)
+  bundling: 2/3 manifests (66.67%)
+  bundling: 3/3 manifests (100.00%)
+  bundling: foo/Bar/file.txt 0/3 files (0.00%)
+  bundling: foo/Bar/file.txt 0/3 files (0.00%)
+  bundling: foo/Bar/file.txt 0/3 files (0.00%)
+  bundling: foo/Bar/file.txt 0/3 files (0.00%)
+  bundling: foo/file.txt 1/3 files (33.33%)
+  bundling: foo/file.txt 1/3 files (33.33%)
+  bundling: foo/file.txt 1/3 files (33.33%)
+  bundling: foo/file.txt 1/3 files (33.33%)
+  bundling: quux/file.py 2/3 files (66.67%)
+  bundling: quux/file.py 2/3 files (66.67%)
+  bundling: quux/file.py 2/3 files (66.67%)
+  bundling: quux/file.py 2/3 files (66.67%)
   changesets: 1 chunks
   add changeset ef1ea85a6374
   changesets: 2 chunks
@@ -879,38 +879,38 @@
   f9cafe1212c8c6fa1120d14a556e18cc44ff8bdd
   911600dab2ae7a9baff75958b84fe606851ce955
   adding changesets
-  bundling changes: 0 chunks
-  bundling changes: 1 chunks
-  bundling changes: 2 chunks
-  bundling changes: 3 chunks
-  bundling changes: 4 chunks
-  bundling changes: 5 chunks
-  bundling changes: 6 chunks
-  bundling changes: 7 chunks
-  bundling changes: 8 chunks
-  bundling changes: 9 chunks
-  bundling manifests: 0 chunks
-  bundling manifests: 1 chunks
-  bundling manifests: 2 chunks
-  bundling manifests: 3 chunks
-  bundling manifests: 4 chunks
-  bundling manifests: 5 chunks
-  bundling manifests: 6 chunks
-  bundling manifests: 7 chunks
-  bundling manifests: 8 chunks
-  bundling manifests: 9 chunks
-  bundling files: foo/Bar/file.txt 0 chunks
-  bundling files: foo/Bar/file.txt 1 chunks
-  bundling files: foo/Bar/file.txt 2 chunks
-  bundling files: foo/Bar/file.txt 3 chunks
-  bundling files: foo/file.txt 4 chunks
-  bundling files: foo/file.txt 5 chunks
-  bundling files: foo/file.txt 6 chunks
-  bundling files: foo/file.txt 7 chunks
-  bundling files: quux/file.py 8 chunks
-  bundling files: quux/file.py 9 chunks
-  bundling files: quux/file.py 10 chunks
-  bundling files: quux/file.py 11 chunks
+  bundling: 0 changesets
+  bundling: 0 changesets
+  bundling: 0 changesets
+  bundling: 1 changesets
+  bundling: 1 changesets
+  bundling: 1 changesets
+  bundling: 2 changesets
+  bundling: 2 changesets
+  bundling: 2 changesets
+  bundling: 3 changesets
+  bundling: 0/3 manifests (0.00%)
+  bundling: 0/3 manifests (0.00%)
+  bundling: 0/3 manifests (0.00%)
+  bundling: 1/3 manifests (33.33%)
+  bundling: 1/3 manifests (33.33%)
+  bundling: 1/3 manifests (33.33%)
+  bundling: 2/3 manifests (66.67%)
+  bundling: 2/3 manifests (66.67%)
+  bundling: 2/3 manifests (66.67%)
+  bundling: 3/3 manifests (100.00%)
+  bundling: foo/Bar/file.txt 0/3 files (0.00%)
+  bundling: foo/Bar/file.txt 0/3 files (0.00%)
+  bundling: foo/Bar/file.txt 0/3 files (0.00%)
+  bundling: foo/Bar/file.txt 0/3 files (0.00%)
+  bundling: foo/file.txt 1/3 files (33.33%)
+  bundling: foo/file.txt 1/3 files (33.33%)
+  bundling: foo/file.txt 1/3 files (33.33%)
+  bundling: foo/file.txt 1/3 files (33.33%)
+  bundling: quux/file.py 2/3 files (66.67%)
+  bundling: quux/file.py 2/3 files (66.67%)
+  bundling: quux/file.py 2/3 files (66.67%)
+  bundling: quux/file.py 2/3 files (66.67%)
   changesets: 1 chunks
   add changeset ef1ea85a6374
   changesets: 2 chunks
@@ -974,38 +974,38 @@
   f9cafe1212c8c6fa1120d14a556e18cc44ff8bdd
   911600dab2ae7a9baff75958b84fe606851ce955
   adding changesets
-  bundling changes: 0 chunks
-  bundling changes: 1 chunks
-  bundling changes: 2 chunks
-  bundling changes: 3 chunks
-  bundling changes: 4 chunks
-  bundling changes: 5 chunks
-  bundling changes: 6 chunks
-  bundling changes: 7 chunks
-  bundling changes: 8 chunks
-  bundling changes: 9 chunks
-  bundling manifests: 0 chunks
-  bundling manifests: 1 chunks
-  bundling manifests: 2 chunks
-  bundling manifests: 3 chunks
-  bundling manifests: 4 chunks
-  bundling manifests: 5 chunks
-  bundling manifests: 6 chunks
-  bundling manifests: 7 chunks
-  bundling manifests: 8 chunks
-  bundling manifests: 9 chunks
-  bundling files: foo/Bar/file.txt 0 chunks
-  bundling files: foo/Bar/file.txt 1 chunks
-  bundling files: foo/Bar/file.txt 2 chunks
-  bundling files: foo/Bar/file.txt 3 chunks
-  bundling files: foo/file.txt 4 chunks
-  bundling files: foo/file.txt 5 chunks
-  bundling files: foo/file.txt 6 chunks
-  bundling files: foo/file.txt 7 chunks
-  bundling files: quux/file.py 8 chunks
-  bundling files: quux/file.py 9 chunks
-  bundling files: quux/file.py 10 chunks
-  bundling files: quux/file.py 11 chunks
+  bundling: 0 changesets
+  bundling: 0 changesets
+  bundling: 0 changesets
+  bundling: 1 changesets
+  bundling: 1 changesets
+  bundling: 1 changesets
+  bundling: 2 changesets
+  bundling: 2 changesets
+  bundling: 2 changesets
+  bundling: 3 changesets
+  bundling: 0/3 manifests (0.00%)
+  bundling: 0/3 manifests (0.00%)
+  bundling: 0/3 manifests (0.00%)
+  bundling: 1/3 manifests (33.33%)
+  bundling: 1/3 manifests (33.33%)
+  bundling: 1/3 manifests (33.33%)
+  bundling: 2/3 manifests (66.67%)
+  bundling: 2/3 manifests (66.67%)
+  bundling: 2/3 manifests (66.67%)
+  bundling: 3/3 manifests (100.00%)
+  bundling: foo/Bar/file.txt 0/3 files (0.00%)
+  bundling: foo/Bar/file.txt 0/3 files (0.00%)
+  bundling: foo/Bar/file.txt 0/3 files (0.00%)
+  bundling: foo/Bar/file.txt 0/3 files (0.00%)
+  bundling: foo/file.txt 1/3 files (33.33%)
+  bundling: foo/file.txt 1/3 files (33.33%)
+  bundling: foo/file.txt 1/3 files (33.33%)
+  bundling: foo/file.txt 1/3 files (33.33%)
+  bundling: quux/file.py 2/3 files (66.67%)
+  bundling: quux/file.py 2/3 files (66.67%)
+  bundling: quux/file.py 2/3 files (66.67%)
+  bundling: quux/file.py 2/3 files (66.67%)
   changesets: 1 chunks
   add changeset ef1ea85a6374
   changesets: 2 chunks
@@ -1074,38 +1074,38 @@
   f9cafe1212c8c6fa1120d14a556e18cc44ff8bdd
   911600dab2ae7a9baff75958b84fe606851ce955
   adding changesets
-  bundling changes: 0 chunks
-  bundling changes: 1 chunks
-  bundling changes: 2 chunks
-  bundling changes: 3 chunks
-  bundling changes: 4 chunks
-  bundling changes: 5 chunks
-  bundling changes: 6 chunks
-  bundling changes: 7 chunks
-  bundling changes: 8 chunks
-  bundling changes: 9 chunks
-  bundling manifests: 0 chunks
-  bundling manifests: 1 chunks
-  bundling manifests: 2 chunks
-  bundling manifests: 3 chunks
-  bundling manifests: 4 chunks
-  bundling manifests: 5 chunks
-  bundling manifests: 6 chunks
-  bundling manifests: 7 chunks
-  bundling manifests: 8 chunks
-  bundling manifests: 9 chunks
-  bundling files: foo/Bar/file.txt 0 chunks
-  bundling files: foo/Bar/file.txt 1 chunks
-  bundling files: foo/Bar/file.txt 2 chunks
-  bundling files: foo/Bar/file.txt 3 chunks
-  bundling files: foo/file.txt 4 chunks
-  bundling files: foo/file.txt 5 chunks
-  bundling files: foo/file.txt 6 chunks
-  bundling files: foo/file.txt 7 chunks
-  bundling files: quux/file.py 8 chunks
-  bundling files: quux/file.py 9 chunks
-  bundling files: quux/file.py 10 chunks
-  bundling files: quux/file.py 11 chunks
+  bundling: 0 changesets
+  bundling: 0 changesets
+  bundling: 0 changesets
+  bundling: 1 changesets
+  bundling: 1 changesets
+  bundling: 1 changesets
+  bundling: 2 changesets
+  bundling: 2 changesets
+  bundling: 2 changesets
+  bundling: 3 changesets
+  bundling: 0/3 manifests (0.00%)
+  bundling: 0/3 manifests (0.00%)
+  bundling: 0/3 manifests (0.00%)
+  bundling: 1/3 manifests (33.33%)
+  bundling: 1/3 manifests (33.33%)
+  bundling: 1/3 manifests (33.33%)
+  bundling: 2/3 manifests (66.67%)
+  bundling: 2/3 manifests (66.67%)
+  bundling: 2/3 manifests (66.67%)
+  bundling: 3/3 manifests (100.00%)
+  bundling: foo/Bar/file.txt 0/3 files (0.00%)
+  bundling: foo/Bar/file.txt 0/3 files (0.00%)
+  bundling: foo/Bar/file.txt 0/3 files (0.00%)
+  bundling: foo/Bar/file.txt 0/3 files (0.00%)
+  bundling: foo/file.txt 1/3 files (33.33%)
+  bundling: foo/file.txt 1/3 files (33.33%)
+  bundling: foo/file.txt 1/3 files (33.33%)
+  bundling: foo/file.txt 1/3 files (33.33%)
+  bundling: quux/file.py 2/3 files (66.67%)
+  bundling: quux/file.py 2/3 files (66.67%)
+  bundling: quux/file.py 2/3 files (66.67%)
+  bundling: quux/file.py 2/3 files (66.67%)
   changesets: 1 chunks
   add changeset ef1ea85a6374
   changesets: 2 chunks
@@ -1168,38 +1168,38 @@
   f9cafe1212c8c6fa1120d14a556e18cc44ff8bdd
   911600dab2ae7a9baff75958b84fe606851ce955
   adding changesets
-  bundling changes: 0 chunks
-  bundling changes: 1 chunks
-  bundling changes: 2 chunks
-  bundling changes: 3 chunks
-  bundling changes: 4 chunks
-  bundling changes: 5 chunks
-  bundling changes: 6 chunks
-  bundling changes: 7 chunks
-  bundling changes: 8 chunks
-  bundling changes: 9 chunks
-  bundling manifests: 0 chunks
-  bundling manifests: 1 chunks
-  bundling manifests: 2 chunks
-  bundling manifests: 3 chunks
-  bundling manifests: 4 chunks
-  bundling manifests: 5 chunks
-  bundling manifests: 6 chunks
-  bundling manifests: 7 chunks
-  bundling manifests: 8 chunks
-  bundling manifests: 9 chunks
-  bundling files: foo/Bar/file.txt 0 chunks
-  bundling files: foo/Bar/file.txt 1 chunks
-  bundling files: foo/Bar/file.txt 2 chunks
-  bundling files: foo/Bar/file.txt 3 chunks
-  bundling files: foo/file.txt 4 chunks
-  bundling files: foo/file.txt 5 chunks
-  bundling files: foo/file.txt 6 chunks
-  bundling files: foo/file.txt 7 chunks
-  bundling files: quux/file.py 8 chunks
-  bundling files: quux/file.py 9 chunks
-  bundling files: quux/file.py 10 chunks
-  bundling files: quux/file.py 11 chunks
+  bundling: 0 changesets
+  bundling: 0 changesets
+  bundling: 0 changesets
+  bundling: 1 changesets
+  bundling: 1 changesets
+  bundling: 1 changesets
+  bundling: 2 changesets
+  bundling: 2 changesets
+  bundling: 2 changesets
+  bundling: 3 changesets
+  bundling: 0/3 manifests (0.00%)
+  bundling: 0/3 manifests (0.00%)
+  bundling: 0/3 manifests (0.00%)
+  bundling: 1/3 manifests (33.33%)
+  bundling: 1/3 manifests (33.33%)
+  bundling: 1/3 manifests (33.33%)
+  bundling: 2/3 manifests (66.67%)
+  bundling: 2/3 manifests (66.67%)
+  bundling: 2/3 manifests (66.67%)
+  bundling: 3/3 manifests (100.00%)
+  bundling: foo/Bar/file.txt 0/3 files (0.00%)
+  bundling: foo/Bar/file.txt 0/3 files (0.00%)
+  bundling: foo/Bar/file.txt 0/3 files (0.00%)
+  bundling: foo/Bar/file.txt 0/3 files (0.00%)
+  bundling: foo/file.txt 1/3 files (33.33%)
+  bundling: foo/file.txt 1/3 files (33.33%)
+  bundling: foo/file.txt 1/3 files (33.33%)
+  bundling: foo/file.txt 1/3 files (33.33%)
+  bundling: quux/file.py 2/3 files (66.67%)
+  bundling: quux/file.py 2/3 files (66.67%)
+  bundling: quux/file.py 2/3 files (66.67%)
+  bundling: quux/file.py 2/3 files (66.67%)
   changesets: 1 chunks
   add changeset ef1ea85a6374
   changesets: 2 chunks
@@ -1274,38 +1274,38 @@
   f9cafe1212c8c6fa1120d14a556e18cc44ff8bdd
   911600dab2ae7a9baff75958b84fe606851ce955
   adding changesets
-  bundling changes: 0 chunks
-  bundling changes: 1 chunks
-  bundling changes: 2 chunks
-  bundling changes: 3 chunks
-  bundling changes: 4 chunks
-  bundling changes: 5 chunks
-  bundling changes: 6 chunks
-  bundling changes: 7 chunks
-  bundling changes: 8 chunks
-  bundling changes: 9 chunks
-  bundling manifests: 0 chunks
-  bundling manifests: 1 chunks
-  bundling manifests: 2 chunks
-  bundling manifests: 3 chunks
-  bundling manifests: 4 chunks
-  bundling manifests: 5 chunks
-  bundling manifests: 6 chunks
-  bundling manifests: 7 chunks
-  bundling manifests: 8 chunks
-  bundling manifests: 9 chunks
-  bundling files: foo/Bar/file.txt 0 chunks
-  bundling files: foo/Bar/file.txt 1 chunks
-  bundling files: foo/Bar/file.txt 2 chunks
-  bundling files: foo/Bar/file.txt 3 chunks
-  bundling files: foo/file.txt 4 chunks
-  bundling files: foo/file.txt 5 chunks
-  bundling files: foo/file.txt 6 chunks
-  bundling files: foo/file.txt 7 chunks
-  bundling files: quux/file.py 8 chunks
-  bundling files: quux/file.py 9 chunks
-  bundling files: quux/file.py 10 chunks
-  bundling files: quux/file.py 11 chunks
+  bundling: 0 changesets
+  bundling: 0 changesets
+  bundling: 0 changesets
+  bundling: 1 changesets
+  bundling: 1 changesets
+  bundling: 1 changesets
+  bundling: 2 changesets
+  bundling: 2 changesets
+  bundling: 2 changesets
+  bundling: 3 changesets
+  bundling: 0/3 manifests (0.00%)
+  bundling: 0/3 manifests (0.00%)
+  bundling: 0/3 manifests (0.00%)
+  bundling: 1/3 manifests (33.33%)
+  bundling: 1/3 manifests (33.33%)
+  bundling: 1/3 manifests (33.33%)
+  bundling: 2/3 manifests (66.67%)
+  bundling: 2/3 manifests (66.67%)
+  bundling: 2/3 manifests (66.67%)
+  bundling: 3/3 manifests (100.00%)
+  bundling: foo/Bar/file.txt 0/3 files (0.00%)
+  bundling: foo/Bar/file.txt 0/3 files (0.00%)
+  bundling: foo/Bar/file.txt 0/3 files (0.00%)
+  bundling: foo/Bar/file.txt 0/3 files (0.00%)
+  bundling: foo/file.txt 1/3 files (33.33%)
+  bundling: foo/file.txt 1/3 files (33.33%)
+  bundling: foo/file.txt 1/3 files (33.33%)
+  bundling: foo/file.txt 1/3 files (33.33%)
+  bundling: quux/file.py 2/3 files (66.67%)
+  bundling: quux/file.py 2/3 files (66.67%)
+  bundling: quux/file.py 2/3 files (66.67%)
+  bundling: quux/file.py 2/3 files (66.67%)
   changesets: 1 chunks
   add changeset ef1ea85a6374
   changesets: 2 chunks
@@ -1370,38 +1370,38 @@
   f9cafe1212c8c6fa1120d14a556e18cc44ff8bdd
   911600dab2ae7a9baff75958b84fe606851ce955
   adding changesets
-  bundling changes: 0 chunks
-  bundling changes: 1 chunks
-  bundling changes: 2 chunks
-  bundling changes: 3 chunks
-  bundling changes: 4 chunks
-  bundling changes: 5 chunks
-  bundling changes: 6 chunks
-  bundling changes: 7 chunks
-  bundling changes: 8 chunks
-  bundling changes: 9 chunks
-  bundling manifests: 0 chunks
-  bundling manifests: 1 chunks
-  bundling manifests: 2 chunks
-  bundling manifests: 3 chunks
-  bundling manifests: 4 chunks
-  bundling manifests: 5 chunks
-  bundling manifests: 6 chunks
-  bundling manifests: 7 chunks
-  bundling manifests: 8 chunks
-  bundling manifests: 9 chunks
-  bundling files: foo/Bar/file.txt 0 chunks
-  bundling files: foo/Bar/file.txt 1 chunks
-  bundling files: foo/Bar/file.txt 2 chunks
-  bundling files: foo/Bar/file.txt 3 chunks
-  bundling files: foo/file.txt 4 chunks
-  bundling files: foo/file.txt 5 chunks
-  bundling files: foo/file.txt 6 chunks
-  bundling files: foo/file.txt 7 chunks
-  bundling files: quux/file.py 8 chunks
-  bundling files: quux/file.py 9 chunks
-  bundling files: quux/file.py 10 chunks
-  bundling files: quux/file.py 11 chunks
+  bundling: 0 changesets
+  bundling: 0 changesets
+  bundling: 0 changesets
+  bundling: 1 changesets
+  bundling: 1 changesets
+  bundling: 1 changesets
+  bundling: 2 changesets
+  bundling: 2 changesets
+  bundling: 2 changesets
+  bundling: 3 changesets
+  bundling: 0/3 manifests (0.00%)
+  bundling: 0/3 manifests (0.00%)
+  bundling: 0/3 manifests (0.00%)
+  bundling: 1/3 manifests (33.33%)
+  bundling: 1/3 manifests (33.33%)
+  bundling: 1/3 manifests (33.33%)
+  bundling: 2/3 manifests (66.67%)
+  bundling: 2/3 manifests (66.67%)
+  bundling: 2/3 manifests (66.67%)
+  bundling: 3/3 manifests (100.00%)
+  bundling: foo/Bar/file.txt 0/3 files (0.00%)
+  bundling: foo/Bar/file.txt 0/3 files (0.00%)
+  bundling: foo/Bar/file.txt 0/3 files (0.00%)
+  bundling: foo/Bar/file.txt 0/3 files (0.00%)
+  bundling: foo/file.txt 1/3 files (33.33%)
+  bundling: foo/file.txt 1/3 files (33.33%)
+  bundling: foo/file.txt 1/3 files (33.33%)
+  bundling: foo/file.txt 1/3 files (33.33%)
+  bundling: quux/file.py 2/3 files (66.67%)
+  bundling: quux/file.py 2/3 files (66.67%)
+  bundling: quux/file.py 2/3 files (66.67%)
+  bundling: quux/file.py 2/3 files (66.67%)
   changesets: 1 chunks
   add changeset ef1ea85a6374
   changesets: 2 chunks
@@ -1462,38 +1462,38 @@
   f9cafe1212c8c6fa1120d14a556e18cc44ff8bdd
   911600dab2ae7a9baff75958b84fe606851ce955
   adding changesets
-  bundling changes: 0 chunks
-  bundling changes: 1 chunks
-  bundling changes: 2 chunks
-  bundling changes: 3 chunks
-  bundling changes: 4 chunks
-  bundling changes: 5 chunks
-  bundling changes: 6 chunks
-  bundling changes: 7 chunks
-  bundling changes: 8 chunks
-  bundling changes: 9 chunks
-  bundling manifests: 0 chunks
-  bundling manifests: 1 chunks
-  bundling manifests: 2 chunks
-  bundling manifests: 3 chunks
-  bundling manifests: 4 chunks
-  bundling manifests: 5 chunks
-  bundling manifests: 6 chunks
-  bundling manifests: 7 chunks
-  bundling manifests: 8 chunks
-  bundling manifests: 9 chunks
-  bundling files: foo/Bar/file.txt 0 chunks
-  bundling files: foo/Bar/file.txt 1 chunks
-  bundling files: foo/Bar/file.txt 2 chunks
-  bundling files: foo/Bar/file.txt 3 chunks
-  bundling files: foo/file.txt 4 chunks
-  bundling files: foo/file.txt 5 chunks
-  bundling files: foo/file.txt 6 chunks
-  bundling files: foo/file.txt 7 chunks
-  bundling files: quux/file.py 8 chunks
-  bundling files: quux/file.py 9 chunks
-  bundling files: quux/file.py 10 chunks
-  bundling files: quux/file.py 11 chunks
+  bundling: 0 changesets
+  bundling: 0 changesets
+  bundling: 0 changesets
+  bundling: 1 changesets
+  bundling: 1 changesets
+  bundling: 1 changesets
+  bundling: 2 changesets
+  bundling: 2 changesets
+  bundling: 2 changesets
+  bundling: 3 changesets
+  bundling: 0/3 manifests (0.00%)
+  bundling: 0/3 manifests (0.00%)
+  bundling: 0/3 manifests (0.00%)
+  bundling: 1/3 manifests (33.33%)
+  bundling: 1/3 manifests (33.33%)
+  bundling: 1/3 manifests (33.33%)
+  bundling: 2/3 manifests (66.67%)
+  bundling: 2/3 manifests (66.67%)
+  bundling: 2/3 manifests (66.67%)
+  bundling: 3/3 manifests (100.00%)
+  bundling: foo/Bar/file.txt 0/3 files (0.00%)
+  bundling: foo/Bar/file.txt 0/3 files (0.00%)
+  bundling: foo/Bar/file.txt 0/3 files (0.00%)
+  bundling: foo/Bar/file.txt 0/3 files (0.00%)
+  bundling: foo/file.txt 1/3 files (33.33%)
+  bundling: foo/file.txt 1/3 files (33.33%)
+  bundling: foo/file.txt 1/3 files (33.33%)
+  bundling: foo/file.txt 1/3 files (33.33%)
+  bundling: quux/file.py 2/3 files (66.67%)
+  bundling: quux/file.py 2/3 files (66.67%)
+  bundling: quux/file.py 2/3 files (66.67%)
+  bundling: quux/file.py 2/3 files (66.67%)
   changesets: 1 chunks
   add changeset ef1ea85a6374
   changesets: 2 chunks
@@ -1558,38 +1558,38 @@
   f9cafe1212c8c6fa1120d14a556e18cc44ff8bdd
   911600dab2ae7a9baff75958b84fe606851ce955
   adding changesets
-  bundling changes: 0 chunks
-  bundling changes: 1 chunks
-  bundling changes: 2 chunks
-  bundling changes: 3 chunks
-  bundling changes: 4 chunks
-  bundling changes: 5 chunks
-  bundling changes: 6 chunks
-  bundling changes: 7 chunks
-  bundling changes: 8 chunks
-  bundling changes: 9 chunks
-  bundling manifests: 0 chunks
-  bundling manifests: 1 chunks
-  bundling manifests: 2 chunks
-  bundling manifests: 3 chunks
-  bundling manifests: 4 chunks
-  bundling manifests: 5 chunks
-  bundling manifests: 6 chunks
-  bundling manifests: 7 chunks
-  bundling manifests: 8 chunks
-  bundling manifests: 9 chunks
-  bundling files: foo/Bar/file.txt 0 chunks
-  bundling files: foo/Bar/file.txt 1 chunks
-  bundling files: foo/Bar/file.txt 2 chunks
-  bundling files: foo/Bar/file.txt 3 chunks
-  bundling files: foo/file.txt 4 chunks
-  bundling files: foo/file.txt 5 chunks
-  bundling files: foo/file.txt 6 chunks
-  bundling files: foo/file.txt 7 chunks
-  bundling files: quux/file.py 8 chunks
-  bundling files: quux/file.py 9 chunks
-  bundling files: quux/file.py 10 chunks
-  bundling files: quux/file.py 11 chunks
+  bundling: 0 changesets
+  bundling: 0 changesets
+  bundling: 0 changesets
+  bundling: 1 changesets
+  bundling: 1 changesets
+  bundling: 1 changesets
+  bundling: 2 changesets
+  bundling: 2 changesets
+  bundling: 2 changesets
+  bundling: 3 changesets
+  bundling: 0/3 manifests (0.00%)
+  bundling: 0/3 manifests (0.00%)
+  bundling: 0/3 manifests (0.00%)
+  bundling: 1/3 manifests (33.33%)
+  bundling: 1/3 manifests (33.33%)
+  bundling: 1/3 manifests (33.33%)
+  bundling: 2/3 manifests (66.67%)
+  bundling: 2/3 manifests (66.67%)
+  bundling: 2/3 manifests (66.67%)
+  bundling: 3/3 manifests (100.00%)
+  bundling: foo/Bar/file.txt 0/3 files (0.00%)
+  bundling: foo/Bar/file.txt 0/3 files (0.00%)
+  bundling: foo/Bar/file.txt 0/3 files (0.00%)
+  bundling: foo/Bar/file.txt 0/3 files (0.00%)
+  bundling: foo/file.txt 1/3 files (33.33%)
+  bundling: foo/file.txt 1/3 files (33.33%)
+  bundling: foo/file.txt 1/3 files (33.33%)
+  bundling: foo/file.txt 1/3 files (33.33%)
+  bundling: quux/file.py 2/3 files (66.67%)
+  bundling: quux/file.py 2/3 files (66.67%)
+  bundling: quux/file.py 2/3 files (66.67%)
+  bundling: quux/file.py 2/3 files (66.67%)
   changesets: 1 chunks
   add changeset ef1ea85a6374
   changesets: 2 chunks
@@ -1651,38 +1651,38 @@
   f9cafe1212c8c6fa1120d14a556e18cc44ff8bdd
   911600dab2ae7a9baff75958b84fe606851ce955
   adding changesets
-  bundling changes: 0 chunks
-  bundling changes: 1 chunks
-  bundling changes: 2 chunks
-  bundling changes: 3 chunks
-  bundling changes: 4 chunks
-  bundling changes: 5 chunks
-  bundling changes: 6 chunks
-  bundling changes: 7 chunks
-  bundling changes: 8 chunks
-  bundling changes: 9 chunks
-  bundling manifests: 0 chunks
-  bundling manifests: 1 chunks
-  bundling manifests: 2 chunks
-  bundling manifests: 3 chunks
-  bundling manifests: 4 chunks
-  bundling manifests: 5 chunks
-  bundling manifests: 6 chunks
-  bundling manifests: 7 chunks
-  bundling manifests: 8 chunks
-  bundling manifests: 9 chunks
-  bundling files: foo/Bar/file.txt 0 chunks
-  bundling files: foo/Bar/file.txt 1 chunks
-  bundling files: foo/Bar/file.txt 2 chunks
-  bundling files: foo/Bar/file.txt 3 chunks
-  bundling files: foo/file.txt 4 chunks
-  bundling files: foo/file.txt 5 chunks
-  bundling files: foo/file.txt 6 chunks
-  bundling files: foo/file.txt 7 chunks
-  bundling files: quux/file.py 8 chunks
-  bundling files: quux/file.py 9 chunks
-  bundling files: quux/file.py 10 chunks
-  bundling files: quux/file.py 11 chunks
+  bundling: 0 changesets
+  bundling: 0 changesets
+  bundling: 0 changesets
+  bundling: 1 changesets
+  bundling: 1 changesets
+  bundling: 1 changesets
+  bundling: 2 changesets
+  bundling: 2 changesets
+  bundling: 2 changesets
+  bundling: 3 changesets
+  bundling: 0/3 manifests (0.00%)
+  bundling: 0/3 manifests (0.00%)
+  bundling: 0/3 manifests (0.00%)
+  bundling: 1/3 manifests (33.33%)
+  bundling: 1/3 manifests (33.33%)
+  bundling: 1/3 manifests (33.33%)
+  bundling: 2/3 manifests (66.67%)
+  bundling: 2/3 manifests (66.67%)
+  bundling: 2/3 manifests (66.67%)
+  bundling: 3/3 manifests (100.00%)
+  bundling: foo/Bar/file.txt 0/3 files (0.00%)
+  bundling: foo/Bar/file.txt 0/3 files (0.00%)
+  bundling: foo/Bar/file.txt 0/3 files (0.00%)
+  bundling: foo/Bar/file.txt 0/3 files (0.00%)
+  bundling: foo/file.txt 1/3 files (33.33%)
+  bundling: foo/file.txt 1/3 files (33.33%)
+  bundling: foo/file.txt 1/3 files (33.33%)
+  bundling: foo/file.txt 1/3 files (33.33%)
+  bundling: quux/file.py 2/3 files (66.67%)
+  bundling: quux/file.py 2/3 files (66.67%)
+  bundling: quux/file.py 2/3 files (66.67%)
+  bundling: quux/file.py 2/3 files (66.67%)
   changesets: 1 chunks
   add changeset ef1ea85a6374
   changesets: 2 chunks
--- a/tests/test-archive.t	Fri Dec 10 12:51:37 2010 +0100
+++ b/tests/test-archive.t	Mon Dec 20 12:08:56 2010 -0600
@@ -30,7 +30,6 @@
 
 check http return codes
 
-
   $ test_archtype gz tar.gz tar.bz2 zip
   % gz allowed should give 200
   200 Script output follows
@@ -150,9 +149,8 @@
   > print h1 == h2 or "md5 differ: " + repr((h1, h2))
   > EOF
 
-archive name is stored in the archive, so create similar
-
-archives and rename them afterwards.
+archive name is stored in the archive, so create similar archives and
+rename them afterwards.
 
   $ hg archive -t tgz tip.tar.gz
   $ mv tip.tar.gz tip1.tar.gz
@@ -208,6 +206,38 @@
   abort: unknown archive type 'bogus'
   [255]
 
+enable progress extension:
+
+  $ cp $HGRCPATH $HGRCPATH.no-progress
+  $ cat >> $HGRCPATH <<EOF
+  > [extensions]
+  > progress =
+  > [progress]
+  > assume-tty = 1
+  > format = topic bar number
+  > delay = 0
+  > refresh = 0
+  > width = 60
+  > EOF
+
+  $ hg archive ../with-progress 2>&1 | $TESTDIR/filtercr.py
+  
+  archiving [                                           ] 0/4
+  archiving [                                           ] 0/4
+  archiving [=========>                                 ] 1/4
+  archiving [=========>                                 ] 1/4
+  archiving [====================>                      ] 2/4
+  archiving [====================>                      ] 2/4
+  archiving [===============================>           ] 3/4
+  archiving [===============================>           ] 3/4
+  archiving [==========================================>] 4/4
+  archiving [==========================================>] 4/4
+                                                              \r (esc)
+
+cleanup after progress extension test:
+
+  $ cp $HGRCPATH.no-progress $HGRCPATH
+
 server errors
 
   $ cat errors.log
@@ -219,6 +249,7 @@
   $ hg archive ../test-empty
   abort: no working directory: please specify a revision
   [255]
+
 old file -- date clamped to 1980
 
   $ touch -t 197501010000 old
--- a/tests/test-bundle.t	Fri Dec 10 12:51:37 2010 +0100
+++ b/tests/test-bundle.t	Mon Dec 20 12:08:56 2010 -0600
@@ -543,26 +543,26 @@
   list of changesets:
   d2ae7f538514cd87c17547b0de4cea71fe1af9fb
   5ece8e77363e2b5269e27c66828b72da29e4341a
-  bundling changes: 0 chunks
-  bundling changes: 1 chunks
-  bundling changes: 2 chunks
-  bundling changes: 3 chunks
-  bundling changes: 4 chunks
-  bundling changes: 5 chunks
-  bundling changes: 6 chunks
-  bundling manifests: 0 chunks
-  bundling manifests: 1 chunks
-  bundling manifests: 2 chunks
-  bundling manifests: 3 chunks
-  bundling manifests: 4 chunks
-  bundling manifests: 5 chunks
-  bundling manifests: 6 chunks
-  bundling files: b 0 chunks
-  bundling files: b 1 chunks
-  bundling files: b 2 chunks
-  bundling files: b 3 chunks
-  bundling files: b1 4 chunks
-  bundling files: b1 5 chunks
-  bundling files: b1 6 chunks
-  bundling files: b1 7 chunks
+  bundling: 0 changesets
+  bundling: 0 changesets
+  bundling: 0 changesets
+  bundling: 1 changesets
+  bundling: 1 changesets
+  bundling: 1 changesets
+  bundling: 2 changesets
+  bundling: 0/2 manifests (0.00%)
+  bundling: 0/2 manifests (0.00%)
+  bundling: 0/2 manifests (0.00%)
+  bundling: 1/2 manifests (50.00%)
+  bundling: 1/2 manifests (50.00%)
+  bundling: 1/2 manifests (50.00%)
+  bundling: 2/2 manifests (100.00%)
+  bundling: b 0/2 files (0.00%)
+  bundling: b 0/2 files (0.00%)
+  bundling: b 0/2 files (0.00%)
+  bundling: b 0/2 files (0.00%)
+  bundling: b1 1/2 files (50.00%)
+  bundling: b1 1/2 files (50.00%)
+  bundling: b1 1/2 files (50.00%)
+  bundling: b1 1/2 files (50.00%)
 
--- a/tests/test-churn.t	Fri Dec 10 12:51:37 2010 +0100
+++ b/tests/test-churn.t	Mon Dec 20 12:08:56 2010 -0600
@@ -139,3 +139,22 @@
   $ hg churn
   test      0 
   $ cd ..
+
+Ignore trailing or leading spaces in emails
+
+  $ cd repo
+  $ touch bar
+  $ hg ci -Am'bar' -u 'user4 <user4@x.com>'
+  adding bar
+  $ touch foo
+  $ hg ci -Am'foo' -u 'user4 < user4@x.com >'
+  adding foo
+  $ hg log -l2 --template '[{author|email}]\n'
+  [ user4@x.com ]
+  [user4@x.com]
+  $ hg churn -c
+  user1            4 *********************************************************
+  user3            3 *******************************************
+  user4@x.com      2 *****************************
+  user2            2 *****************************
+  with space       1 **************
--- a/tests/test-command-template.t	Fri Dec 10 12:51:37 2010 +0100
+++ b/tests/test-command-template.t	Mon Dec 20 12:08:56 2010 -0600
@@ -570,7 +570,7 @@
 
 Keys work:
 
-  $ for key in author branches date desc file_adds file_dels file_mods \
+  $ for key in author branch branches date desc file_adds file_dels file_mods \
   >         file_copies file_copies_switch files \
   >         manifest node parents rev tags diffstat extras; do
   >     for mode in '' --verbose --debug; do
@@ -604,6 +604,33 @@
   author--debug: other@place
   author--debug: A. N. Other <other@place>
   author--debug: User Name <user@hostname>
+  branch: default
+  branch: default
+  branch: default
+  branch: default
+  branch: foo
+  branch: default
+  branch: default
+  branch: default
+  branch: default
+  branch--verbose: default
+  branch--verbose: default
+  branch--verbose: default
+  branch--verbose: default
+  branch--verbose: foo
+  branch--verbose: default
+  branch--verbose: default
+  branch--verbose: default
+  branch--verbose: default
+  branch--debug: default
+  branch--debug: default
+  branch--debug: default
+  branch--debug: default
+  branch--debug: foo
+  branch--debug: default
+  branch--debug: default
+  branch--debug: default
+  branch--debug: default
   branches: 
   branches: 
   branches: 
--- a/tests/test-convert-svn-move.t	Fri Dec 10 12:51:37 2010 +0100
+++ b/tests/test-convert-svn-move.t	Mon Dec 20 12:08:56 2010 -0600
@@ -167,83 +167,73 @@
   > [progress]
   > assume-tty = 1
   > delay = 0
+  > format = topic bar number
   > refresh = 0
-  > EOF
-  $ cat > filtercr.py <<EOF
-  > import sys, re
-  > for line in sys.stdin:
-  >     line = re.sub(r'\r+[^\n]', lambda m: '\n' + m.group()[-1:], line)
-  >     sys.stdout.write(line)
+  > width = 60
   > EOF
 
-  $ hg convert svn-repo hg-progress 2>&1 | python filtercr.py
+  $ hg convert svn-repo hg-progress 2>&1 | $TESTDIR/filtercr.py
   
-  scanning [ <=>                                                              ] 1
-  scanning [  <=>                                                             ] 2
-  scanning [   <=>                                                            ] 3
-  scanning [    <=>                                                           ] 4
-  scanning [     <=>                                                          ] 5
-  scanning [      <=>                                                         ] 6
-  scanning [       <=>                                                        ] 7
-                                                                                  
-  converting [                                                              ] 0/7
-  getting files [========>                                                  ] 1/6
-  getting files [==================>                                        ] 2/6
-  getting files [============================>                              ] 3/6
-  getting files [======================================>                    ] 4/6
-  getting files [================================================>          ] 5/6
-  getting files [==========================================================>] 6/6
-                                                                                  
-  converting [=======>                                                      ] 1/7
-  scanning paths [                                                          ] 0/1
-                                                                                  
-  getting files [==========================================================>] 1/1
-                                                                                  
-  converting [================>                                             ] 2/7
-  scanning paths [                                                          ] 0/2
-  scanning paths [============================>                             ] 1/2
-                                                                                  
-  getting files [=============>                                             ] 1/4
-  getting files [============================>                              ] 2/4
-  getting files [===========================================>               ] 3/4
-  getting files [==========================================================>] 4/4
-                                                                                  
-  converting [=========================>                                    ] 3/7
-  scanning paths [                                                          ] 0/1
-                                                                                  
-  getting files [==========================================================>] 1/1
-                                                                                  
-  converting [==================================>                           ] 4/7
-  scanning paths [                                                          ] 0/1
-                                                                                  
-  getting files [==========================================================>] 1/1
-                                                                                  
-  converting [===========================================>                  ] 5/7
-  scanning paths [                                                          ] 0/3
-  scanning paths [==================>                                       ] 1/3
-  scanning paths [=====================================>                    ] 2/3
-                                                                                  
-  getting files [======>                                                    ] 1/8
-  getting files [=============>                                             ] 2/8
-  getting files [=====================>                                     ] 3/8
-  getting files [============================>                              ] 4/8
-  getting files [===================================>                       ] 5/8
-  getting files [===========================================>               ] 6/8
-  getting files [==================================================>        ] 7/8
-  getting files [==========================================================>] 8/8
-                                                                                  
-  converting [====================================================>         ] 6/7
-  scanning paths [                                                          ] 0/1
-                                                                                  
-  getting files [======>                                                    ] 1/8
-  getting files [=============>                                             ] 2/8
-  getting files [=====================>                                     ] 3/8
-  getting files [============================>                              ] 4/8
-  getting files [===================================>                       ] 5/8
-  getting files [===========================================>               ] 6/8
-  getting files [==================================================>        ] 7/8
-  getting files [==========================================================>] 8/8
-                                                                                  
+  scanning [ <=>                                          ] 1
+  scanning [  <=>                                         ] 2
+  scanning [   <=>                                        ] 3
+  scanning [    <=>                                       ] 4
+  scanning [     <=>                                      ] 5
+  scanning [      <=>                                     ] 6
+  scanning [       <=>                                    ] 7
+                                                              
+  converting [                                          ] 0/7
+  getting files [=====>                                 ] 1/6
+  getting files [============>                          ] 2/6
+  getting files [==================>                    ] 3/6
+  getting files [=========================>             ] 4/6
+  getting files [===============================>       ] 5/6
+  getting files [======================================>] 6/6
+                                                              
+  converting [=====>                                    ] 1/7
+  scanning paths [                                      ] 0/1
+  getting files [======================================>] 1/1
+                                                              
+  converting [===========>                              ] 2/7
+  scanning paths [                                      ] 0/2
+  scanning paths [==================>                   ] 1/2
+  getting files [========>                              ] 1/4
+  getting files [==================>                    ] 2/4
+  getting files [============================>          ] 3/4
+  getting files [======================================>] 4/4
+                                                              
+  converting [=================>                        ] 3/7
+  scanning paths [                                      ] 0/1
+  getting files [======================================>] 1/1
+                                                              
+  converting [=======================>                  ] 4/7
+  scanning paths [                                      ] 0/1
+  getting files [======================================>] 1/1
+                                                              
+  converting [=============================>            ] 5/7
+  scanning paths [                                      ] 0/3
+  scanning paths [===========>                          ] 1/3
+  scanning paths [========================>             ] 2/3
+  getting files [===>                                   ] 1/8
+  getting files [========>                              ] 2/8
+  getting files [=============>                         ] 3/8
+  getting files [==================>                    ] 4/8
+  getting files [=======================>               ] 5/8
+  getting files [============================>          ] 6/8
+  getting files [=================================>     ] 7/8
+  getting files [======================================>] 8/8
+                                                              
+  converting [===================================>      ] 6/7
+  scanning paths [                                      ] 0/1
+  getting files [===>                                   ] 1/8
+  getting files [========>                              ] 2/8
+  getting files [=============>                         ] 3/8
+  getting files [==================>                    ] 4/8
+  getting files [=======================>               ] 5/8
+  getting files [============================>          ] 6/8
+  getting files [=================================>     ] 7/8
+  getting files [======================================>] 8/8
+                                                              
   initializing destination hg-progress repository
   scanning source...
   sorting...
@@ -255,3 +245,4 @@
   2 adddb
   1 branch
   0 clobberdir
+  
--- a/tests/test-hook.t	Fri Dec 10 12:51:37 2010 +0100
+++ b/tests/test-hook.t	Mon Dec 20 12:08:56 2010 -0600
@@ -73,8 +73,8 @@
   [1]
   $ hg cat b
   pre-cat hook: HG_ARGS=cat b HG_OPTS={'rev': '', 'decode': None, 'exclude': [], 'output': '', 'include': []} HG_PATS=['b'] 
+  b
   post-cat hook: HG_ARGS=cat b HG_OPTS={'rev': '', 'decode': None, 'exclude': [], 'output': '', 'include': []} HG_PATS=['b'] HG_RESULT=0 
-  b
 
   $ cd ../b
   $ hg pull ../a
--- a/tests/test-https.t	Fri Dec 10 12:51:37 2010 +0100
+++ b/tests/test-https.t	Mon Dec 20 12:08:56 2010 -0600
@@ -106,6 +106,7 @@
 clone via pull
 
   $ hg clone https://localhost:$HGPORT/ copy-pull
+  warning: localhost certificate not verified (check web.cacerts config setting)
   requesting all changes
   adding changesets
   adding manifests
@@ -131,6 +132,7 @@
   $ echo '[hooks]' >> .hg/hgrc
   $ echo "changegroup = python '$TESTDIR'/printenv.py changegroup" >> .hg/hgrc
   $ hg pull
+  warning: localhost certificate not verified (check web.cacerts config setting)
   changegroup hook: HG_NODE=5fed3813f7f5e1824344fdc9cf8f63bb662c292d HG_SOURCE=pull HG_URL=https://localhost:$HGPORT/ 
   pulling from https://localhost:$HGPORT/
   searching for changes
--- a/tests/test-issue619.t	Fri Dec 10 12:51:37 2010 +0100
+++ b/tests/test-issue619.t	Mon Dec 20 12:08:56 2010 -0600
@@ -19,7 +19,12 @@
   $ hg merge b
   1 files updated, 0 files merged, 0 files removed, 0 files unresolved
   (branch merge, don't forget to commit)
+  $ hg branch
+  default
+  $ hg parent --template '{rev}:{node|short} {branches}: {desc}\n'
+  1:06c2121185be b: b
   $ hg ci -Ammerge
+  created new head
 
 Bogus fast-forward should fail:
 
--- a/tests/test-newbranch.t	Fri Dec 10 12:51:37 2010 +0100
+++ b/tests/test-newbranch.t	Mon Dec 20 12:08:56 2010 -0600
@@ -208,12 +208,11 @@
   $ hg branch
   foo
   $ hg commit -m'Merge ff into foo'
+  created new head
   $ hg parents
-  changeset:   6:917eb54e1b4b
+  changeset:   6:6af8030670c9
   branch:      foo
   tag:         tip
-  parent:      4:98d14f698afe
-  parent:      5:6683a60370cb
   user:        test
   date:        Thu Jan 01 00:00:00 1970 +0000
   summary:     Merge ff into foo
--- a/tests/test-progress.t	Fri Dec 10 12:51:37 2010 +0100
+++ b/tests/test-progress.t	Mon Dec 20 12:08:56 2010 -0600
@@ -23,78 +23,127 @@
   > }
   > EOF
 
-  $ cat > filtercr.py <<EOF
-  > import sys, re
-  > for line in sys.stdin:
-  >     line = re.sub(r'\r+[^\n]', lambda m: '\n' + m.group()[-1:], line)
-  >     sys.stdout.write(line)
-  > print
-  > EOF
-
   $ echo "[extensions]" >> $HGRCPATH
   $ echo "progress=" >> $HGRCPATH
   $ echo "loop=`pwd`/loop.py" >> $HGRCPATH
   $ echo "[progress]" >> $HGRCPATH
+  $ echo  "format = topic bar number" >> $HGRCPATH
   $ echo "assume-tty=1" >> $HGRCPATH
+  $ echo "width=60" >> $HGRCPATH
 
 test default params, display nothing because of delay
 
-  $ hg -y loop 3 2>&1 | python filtercr.py
+  $ hg -y loop 3 2>&1 | $TESTDIR/filtercr.py
   
   $ echo "delay=0" >> $HGRCPATH
   $ echo "refresh=0" >> $HGRCPATH
 
 test with delay=0, refresh=0
 
-  $ hg -y loop 3 2>&1 | python filtercr.py
+  $ hg -y loop 3 2>&1 | $TESTDIR/filtercr.py
   
-  loop [                                                                    ] 0/3
-  loop [=====================>                                              ] 1/3
-  loop [============================================>                       ] 2/3
-                                                                                  \r (esc)
+  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 | python filtercr.py
+  $ hg -y --config progress.refresh=100 loop 3 2>&1 | $TESTDIR/filtercr.py
   
 
 test format options 1
 
-  $ hg -y --config 'progress.format=number topic item+2' loop 2 2>&1 | python filtercr.py
+  $ hg -y --config 'progress.format=number topic item+2' loop 2 2>&1 \
+  > | $TESTDIR/filtercr.py
   
   0/2 loop lo
   1/2 loop lo
-                                                                                  \r (esc)
+                                                              \r (esc)
 
 test format options 2
 
-  $ hg -y --config 'progress.format=number item-3 bar' loop 2 2>&1 | python filtercr.py
+  $ hg -y --config 'progress.format=number item-3 bar' loop 2 2>&1 \
+  > | $TESTDIR/filtercr.py
   
-  0/2 p.0 [                                                                     ]
-  1/2 p.1 [=================================>                                   ]
-                                                                                  \r (esc)
+  0/2 p.0 [                                                 ]
+  1/2 p.1 [=======================>                         ]
+                                                              \r (esc)
 
 test format options and indeterminate progress
 
-  $ hg -y --config 'progress.format=number item bar' loop -- -2 2>&1 | python filtercr.py
+  $ hg -y --config 'progress.format=number item bar' loop -- -2 2>&1 \
+  > | $TESTDIR/filtercr.py
   
-  0 loop.0               [ <=>                                                  ]
-  1 loop.1               [  <=>                                                 ]
-                                                                                  \r (esc)
+  0 loop.0               [ <=>                              ]
+  1 loop.1               [  <=>                             ]
+                                                              \r (esc)
 
 make sure things don't fall over if count > total
 
-  $ hg -y loop --total 4 6 2>&1 | python filtercr.py
+  $ hg -y loop --total 4 6 2>&1 | $TESTDIR/filtercr.py
   
-  loop [                                                                    ] 0/4
-  loop [================>                                                   ] 1/4
-  loop [=================================>                                  ] 2/4
-  loop [==================================================>                 ] 3/4
-  loop [===================================================================>] 4/4
-  loop [ <=>                                                                ] 5/4
-                                                                                  \r (esc)
+  loop [                                                ] 0/4
+  loop [===========>                                    ] 1/4
+  loop [=======================>                        ] 2/4
+  loop [===================================>            ] 3/4
+  loop [===============================================>] 4/4
+  loop [ <=>                                            ] 5/4
+                                                              \r (esc)
 
 test immediate progress completion
 
-  $ hg -y loop 0 2>&1 | python filtercr.py
+  $ hg -y loop 0 2>&1 | $TESTDIR/filtercr.py
   
+
+test delay time estimates
+
+  $ cat > mocktime.py <<EOF
+  > import os
+  > import time
+  > 
+  > class mocktime(object):
+  >     def __init__(self, increment):
+  >         self.time = 0
+  >         self.increment = increment
+  >     def __call__(self):
+  >         self.time += self.increment
+  >         return self.time
+  > 
+  > def uisetup(ui):
+  >     time.time = mocktime(int(os.environ.get('MOCKTIME', '11')))
+  > EOF
+
+  $ echo "[extensions]" > $HGRCPATH
+  $ echo "mocktime=`pwd`/mocktime.py" >> $HGRCPATH
+  $ echo "progress=" >> $HGRCPATH
+  $ echo "loop=`pwd`/loop.py" >> $HGRCPATH
+  $ echo "[progress]" >> $HGRCPATH
+  $ echo "assume-tty=1" >> $HGRCPATH
+  $ echo "delay=25" >> $HGRCPATH
+  $ echo "width=60" >> $HGRCPATH
+
+  $ hg -y loop 8 2>&1 | python $TESTDIR/filtercr.py
+  
+  loop [=========>                                ] 2/8 1m07s
+  loop [===============>                            ] 3/8 56s
+  loop [=====================>                      ] 4/8 45s
+  loop [==========================>                 ] 5/8 34s
+  loop [================================>           ] 6/8 23s
+  loop [=====================================>      ] 7/8 12s
+                                                              \r (esc)
+
+  $ MOCKTIME=10000 hg -y loop 4 2>&1 | python $TESTDIR/filtercr.py
+  
+  loop [                                                ] 0/4
+  loop [=========>                                ] 1/4 8h21m
+  loop [====================>                     ] 2/4 5h34m
+  loop [==============================>           ] 3/4 2h47m
+                                                              \r (esc)
+
+Time estimates should not fail when there's no end point:
+  $ hg -y loop -- -4 2>&1 | python $TESTDIR/filtercr.py
+  
+  loop [ <=>                                              ] 2
+  loop [  <=>                                             ] 3
+                                                              \r (esc)
--- a/tests/test-push-warn.t	Fri Dec 10 12:51:37 2010 +0100
+++ b/tests/test-push-warn.t	Mon Dec 20 12:08:56 2010 -0600
@@ -413,6 +413,7 @@
   (branch merge, don't forget to commit)
 
   $ hg -R k ci -m merge
+  created new head
 
   $ hg -R k push -r a j
   pushing to j
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tests/test-subrepo-empty-commit.t	Mon Dec 20 12:08:56 2010 -0600
@@ -0,0 +1,64 @@
+  $ hg init
+  $ hg init sub
+  $ echo 'sub = sub' > .hgsub
+  $ hg add .hgsub
+  $ echo c1 > f1
+  $ echo c2 > sub/f2
+  $ hg add -S
+  adding f1
+  adding sub/f2
+  $ hg commit -m0
+  committing subrepository sub
+
+Make .hgsubstate dirty:
+
+  $ echo '0000000000000000000000000000000000000000 sub' > .hgsubstate
+  $ hg diff --nodates
+  diff -r 853ea21970bb .hgsubstate
+  --- a/.hgsubstate
+  +++ b/.hgsubstate
+  @@ -1,1 +1,1 @@
+  -5bbc614a5b06ad7f3bf7c2463d74b005324f34c1 sub
+  +0000000000000000000000000000000000000000 sub
+
+trying to do an empty commit:
+
+  $ hg commit -m1
+  committing subrepository sub
+  nothing changed
+  [1]
+
+an okay update of .hgsubstate
+  $ cd sub
+  $ echo c3 > f2
+  $ hg commit -m "Sub commit"
+  $ cd ..
+  $ hg commit -m "Updated sub"
+  committing subrepository sub
+
+deleting again:
+  $ echo '' > .hgsub
+  $ hg commit -m2
+  $ cat .hgsub
+  
+  $ cat .hgsubstate
+
+an okay commit, but with a dirty .hgsubstate
+  $ echo 'sub = sub' > .hgsub
+  $ hg commit -m3
+  committing subrepository sub
+  $ echo '0000000000000000000000000000000000000000 sub' > .hgsubstate
+  $ hg diff --nodates
+  diff -r 41e1dee3d5d9 .hgsubstate
+  --- a/.hgsubstate
+  +++ b/.hgsubstate
+  @@ -1,1 +1,1 @@
+  -fe0229ee9a0a38b43163c756bb51b94228b118e7 sub
+  +0000000000000000000000000000000000000000 sub
+  $ echo c4 > f3
+  $ hg add f3
+  $ hg status 
+  M .hgsubstate
+  A f3
+  $ hg commit -m4
+  committing subrepository sub
--- a/tests/test-subrepo-git.t	Fri Dec 10 12:51:37 2010 +0100
+++ b/tests/test-subrepo-git.t	Mon Dec 20 12:08:56 2010 -0600
@@ -81,8 +81,7 @@
 
 update to previous substate
 
-  $ hg update 1 2>/dev/null
-  1 files updated, 0 files merged, 0 files removed, 0 files unresolved
+  $ hg update 1 -q
   $ cat s/g
   g
   $ hg debugsub
--- a/tests/test-subrepo-recursion.t	Fri Dec 10 12:51:37 2010 +0100
+++ b/tests/test-subrepo-recursion.t	Mon Dec 20 12:08:56 2010 -0600
@@ -221,9 +221,48 @@
    z1
   +z2
 
-Test archiving to a directory tree:
+Enable progress extension for archive tests:
+
+  $ cp $HGRCPATH $HGRCPATH.no-progress
+  $ cat >> $HGRCPATH <<EOF
+  > [extensions]
+  > progress =
+  > [progress]
+  > assume-tty = 1
+  > delay = 0
+  > format = topic bar number
+  > refresh = 0
+  > width = 60
+  > EOF
+
+Test archiving to a directory tree (the doubled lines in the output
+only show up in the test output, not in real usage):
 
-  $ hg archive --subrepos ../archive
+  $ hg archive --subrepos ../archive 2>&1 | $TESTDIR/filtercr.py
+  
+  archiving [                                           ] 0/3
+  archiving [                                           ] 0/3
+  archiving [=============>                             ] 1/3
+  archiving [=============>                             ] 1/3
+  archiving [===========================>               ] 2/3
+  archiving [===========================>               ] 2/3
+  archiving [==========================================>] 3/3
+  archiving [==========================================>] 3/3
+                                                              
+  archiving (foo) [                                     ] 0/3
+  archiving (foo) [                                     ] 0/3
+  archiving (foo) [===========>                         ] 1/3
+  archiving (foo) [===========>                         ] 1/3
+  archiving (foo) [=======================>             ] 2/3
+  archiving (foo) [=======================>             ] 2/3
+  archiving (foo) [====================================>] 3/3
+  archiving (foo) [====================================>] 3/3
+                                                              
+  archiving (foo/bar) [                                 ] 0/1
+  archiving (foo/bar) [                                 ] 0/1
+  archiving (foo/bar) [================================>] 1/1
+  archiving (foo/bar) [================================>] 1/1
+                                                              \r (esc)
   $ find ../archive | sort
   ../archive
   ../archive/.hg_archival.txt
@@ -239,7 +278,35 @@
 
 Test archiving to zip file (unzip output is unstable):
 
-  $ hg archive --subrepos ../archive.zip
+  $ hg archive --subrepos ../archive.zip 2>&1 | $TESTDIR/filtercr.py
+  
+  archiving [                                           ] 0/3
+  archiving [                                           ] 0/3
+  archiving [=============>                             ] 1/3
+  archiving [=============>                             ] 1/3
+  archiving [===========================>               ] 2/3
+  archiving [===========================>               ] 2/3
+  archiving [==========================================>] 3/3
+  archiving [==========================================>] 3/3
+                                                              
+  archiving (foo) [                                     ] 0/3
+  archiving (foo) [                                     ] 0/3
+  archiving (foo) [===========>                         ] 1/3
+  archiving (foo) [===========>                         ] 1/3
+  archiving (foo) [=======================>             ] 2/3
+  archiving (foo) [=======================>             ] 2/3
+  archiving (foo) [====================================>] 3/3
+  archiving (foo) [====================================>] 3/3
+                                                              
+  archiving (foo/bar) [                                 ] 0/1
+  archiving (foo/bar) [                                 ] 0/1
+  archiving (foo/bar) [================================>] 1/1
+  archiving (foo/bar) [================================>] 1/1
+                                                              \r (esc)
+
+Disable progress extension and cleanup:
+
+  $ mv $HGRCPATH.no-progress $HGRCPATH
 
 Clone and test outgoing:
 
--- a/tests/test-tag.t	Fri Dec 10 12:51:37 2010 +0100
+++ b/tests/test-tag.t	Mon Dec 20 12:08:56 2010 -0600
@@ -78,13 +78,20 @@
   $ cat .hg/localtags
   d4f0d2909abc9290e2773c08837d70c1794e3f5a bleah1
 
+tagging on a non-head revision
+
   $ hg update 0
   0 files updated, 0 files merged, 1 files removed, 0 files unresolved
+  $ hg tag -l localblah
   $ hg tag "foobar"
+  abort: not at a branch head (use -f to force)
+  [255]
+  $ hg tag -f "foobar"
   $ cat .hgtags
   acb14030fe0a21b60322c440ad2d20cf7685a376 foobar
   $ cat .hg/localtags
   d4f0d2909abc9290e2773c08837d70c1794e3f5a bleah1
+  acb14030fe0a21b60322c440ad2d20cf7685a376 localblah
 
   $ hg tag -l 'xx
   > newline'
@@ -102,6 +109,7 @@
   tag:         bleah
   tag:         bleah0
   tag:         foobar
+  tag:         localblah
   user:        test
   date:        Thu Jan 01 00:00:00 1970 +0000
   summary:     test
@@ -156,10 +164,10 @@
   > f = file('.hg/localtags', 'w'); f.write(last); f.close()
   > EOF
   $ cat .hg/localtags; echo
-  d4f0d2909abc9290e2773c08837d70c1794e3f5a bleah1
+  acb14030fe0a21b60322c440ad2d20cf7685a376 localblah
   $ hg tag -l localnewline
   $ cat .hg/localtags; echo
-  d4f0d2909abc9290e2773c08837d70c1794e3f5a bleah1
+  acb14030fe0a21b60322c440ad2d20cf7685a376 localblah
   c2899151f4e76890c602a2597a650a72666681bf localnewline
   
 
@@ -196,3 +204,77 @@
   $ hg log -l1 --template "{desc}\n"
   custom tag message
   second line
+
+
+local tag with .hgtags modified
+
+  $ hg tag hgtags-modified
+  $ hg rollback
+  rolling back to revision 11 (undo commit)
+  $ hg st
+  M .hgtags
+  ? .hgtags.orig
+  ? editor
+  $ hg tag --local baz
+  $ hg revert --no-backup .hgtags
+
+
+tagging when at named-branch-head that's not a topo-head
+
+  $ hg up default
+  1 files updated, 0 files merged, 0 files removed, 0 files unresolved
+  $ hg merge -t internal:local
+  0 files updated, 1 files merged, 0 files removed, 0 files unresolved
+  (branch merge, don't forget to commit)
+  $ hg ci -m 'merge named branch'
+  $ hg up 11
+  1 files updated, 0 files merged, 0 files removed, 0 files unresolved
+  $ hg tag new-topo-head
+
+
+tagging on null rev
+
+  $ hg up null
+  0 files updated, 0 files merged, 2 files removed, 0 files unresolved
+  $ hg tag nullrev
+  abort: not at a branch head (use -f to force)
+  [255]
+
+  $ hg init empty
+  $ hg tag -R empty nullrev
+
+  $ cd ..
+
+tagging on an uncommitted merge (issue2542)
+
+  $ hg init repo-tag-uncommitted-merge
+  $ cd repo-tag-uncommitted-merge
+  $ echo c1 > f1
+  $ hg ci -Am0
+  adding f1
+  $ echo c2 > f2
+  $ hg ci -Am1
+  adding f2
+  $ hg co -q 0
+  $ hg branch b1
+  marked working directory as branch b1
+  $ hg ci -m2
+  $ hg up default
+  1 files updated, 0 files merged, 0 files removed, 0 files unresolved
+  $ hg merge b1
+  0 files updated, 0 files merged, 0 files removed, 0 files unresolved
+  (branch merge, don't forget to commit)
+
+  $ hg tag t1
+  abort: uncommitted merge
+  [255]
+  $ hg status
+  $ hg tag --rev 1 t2
+  abort: uncommitted merge
+  [255]
+  $ hg tag --rev 1 --local t3
+  $ hg tags -v
+  tip                                2:8a8f787d0d5c
+  t3                                 1:c3adabd1a5f4 local
+
+  $ cd ..