changeset 17932:c8ffde272653

Merge with crew-stable.
author Augie Fackler <raf@durin42.com>
date Tue, 13 Nov 2012 19:32:53 -0600
parents 7788b5e7d9ef (diff) 35ba170c0f82 (current diff)
children 8243dd66e0e3
files mercurial/cmdutil.py mercurial/commands.py
diffstat 22 files changed, 486 insertions(+), 288 deletions(-) [+]
line wrap: on
line diff
--- a/Makefile	Mon Nov 12 19:27:03 2012 +0200
+++ b/Makefile	Tue Nov 13 19:32:53 2012 -0600
@@ -34,7 +34,7 @@
 
 local:
 	$(PYTHON) setup.py $(PURE) build_py -c -d . build_ext -i build_hgexe -i build_mo
-	$(PYTHON) hg version
+	env HGRCPATH= $(PYTHON) hg version
 
 build:
 	$(PYTHON) setup.py $(PURE) build
--- a/hgext/convert/git.py	Mon Nov 12 19:27:03 2012 +0200
+++ b/hgext/convert/git.py	Tue Nov 13 19:32:53 2012 -0600
@@ -6,12 +6,24 @@
 # GNU General Public License version 2 or any later version.
 
 import os
-from mercurial import util
+from mercurial import util, config
 from mercurial.node import hex, nullid
 from mercurial.i18n import _
 
 from common import NoRepo, commit, converter_source, checktool
 
+class submodule(object):
+    def __init__(self, path, node, url):
+        self.path = path
+        self.node = node
+        self.url = url
+
+    def hgsub(self):
+        return "%s = [git]%s" % (self.path, self.url)
+
+    def hgsubstate(self):
+        return "%s %s" % (self.node, self.path)
+
 class convert_git(converter_source):
     # Windows does not support GIT_DIR= construct while other systems
     # cannot remove environment variable. Just assume none have
@@ -55,6 +67,7 @@
         checktool('git', 'git')
 
         self.path = path
+        self.submodules = []
 
     def getheads(self):
         if not self.rev:
@@ -76,16 +89,57 @@
         return data
 
     def getfile(self, name, rev):
-        data = self.catfile(rev, "blob")
-        mode = self.modecache[(name, rev)]
+        if name == '.hgsub':
+            data = '\n'.join([m.hgsub() for m in self.submoditer()])
+            mode = ''
+        elif name == '.hgsubstate':
+            data = '\n'.join([m.hgsubstate() for m in self.submoditer()])
+            mode = ''
+        else:
+            data = self.catfile(rev, "blob")
+            mode = self.modecache[(name, rev)]
         return data, mode
 
+    def submoditer(self):
+        null = hex(nullid)
+        for m in sorted(self.submodules, key=lambda p: p.path):
+            if m.node != null:
+                yield m
+
+    def parsegitmodules(self, content):
+        """Parse the formatted .gitmodules file, example file format:
+        [submodule "sub"]\n
+        \tpath = sub\n
+        \turl = git://giturl\n
+        """
+        self.submodules = []
+        c = config.config()
+        # Each item in .gitmodules starts with \t that cant be parsed
+        c.parse('.gitmodules', content.replace('\t',''))
+        for sec in c.sections():
+            s = c[sec]
+            if 'url' in s and 'path' in s:
+                self.submodules.append(submodule(s['path'], '', s['url']))
+
+    def retrievegitmodules(self, version):
+        modules, ret = self.gitread("git show %s:%s" % (version, '.gitmodules'))
+        if ret:
+            raise util.Abort(_('cannot read submodules config file in %s') %
+                             version)
+        self.parsegitmodules(modules)
+        for m in self.submodules:
+            node, ret = self.gitread("git rev-parse %s:%s" % (version, m.path))
+            if ret:
+                continue
+            m.node = node.strip()
+
     def getchanges(self, version):
         self.modecache = {}
         fh = self.gitopen("git diff-tree -z --root -m -r %s" % version)
         changes = []
         seen = set()
         entry = None
+        subexists = False
         for l in fh.read().split('\x00'):
             if not entry:
                 if not l.startswith(':'):
@@ -97,15 +151,24 @@
                 seen.add(f)
                 entry = entry.split()
                 h = entry[3]
-                if entry[1] == '160000':
-                    raise util.Abort('git submodules are not supported!')
                 p = (entry[1] == "100755")
                 s = (entry[1] == "120000")
-                self.modecache[(f, h)] = (p and "x") or (s and "l") or ""
-                changes.append((f, h))
+
+                if f == '.gitmodules':
+                    subexists = True
+                    changes.append(('.hgsub', ''))
+                elif entry[1] == '160000' or entry[0] == ':160000':
+                    subexists = True
+                else:
+                    self.modecache[(f, h)] = (p and "x") or (s and "l") or ""
+                    changes.append((f, h))
             entry = None
         if fh.close():
             raise util.Abort(_('cannot read changes in %s') % version)
+
+        if subexists:
+            self.retrievegitmodules(version)
+            changes.append(('.hgsubstate', ''))
         return (changes, {})
 
     def getcommit(self, version):
--- a/hgext/convert/hg.py	Mon Nov 12 19:27:03 2012 +0200
+++ b/hgext/convert/hg.py	Tue Nov 13 19:32:53 2012 -0600
@@ -219,9 +219,10 @@
             return
 
         self.ui.status(_("updating bookmarks\n"))
+        destmarks = self.repo._bookmarks
         for bookmark in updatedbookmark:
-            self.repo._bookmarks[bookmark] = bin(updatedbookmark[bookmark])
-            bookmarks.write(self.repo)
+            destmarks[bookmark] = bin(updatedbookmark[bookmark])
+        destmarks.write()
 
     def hascommit(self, rev):
         if rev not in self.repo and self.clonebranches:
--- a/hgext/histedit.py	Mon Nov 12 19:27:03 2012 +0200
+++ b/hgext/histedit.py	Tue Nov 13 19:32:53 2012 -0600
@@ -144,7 +144,6 @@
     import pickle
 import os
 
-from mercurial import bookmarks
 from mercurial import cmdutil
 from mercurial import discovery
 from mercurial import error
@@ -740,12 +739,13 @@
             # nothing to move
         moves.append((bk, new[-1]))
     if moves:
+        marks = repo._bookmarks
         for mark, new in moves:
-            old = repo._bookmarks[mark]
+            old = marks[mark]
             ui.note(_('histedit: moving bookmarks %s from %s to %s\n')
                     % (mark, node.short(old), node.short(new)))
-            repo._bookmarks[mark] = new
-        bookmarks.write(repo)
+            marks[mark] = new
+        marks.write()
 
 def cleanupnode(ui, repo, name, nodes):
     """strip a group of nodes from the repository
--- a/hgext/mq.py	Mon Nov 12 19:27:03 2012 +0200
+++ b/hgext/mq.py	Tue Nov 13 19:32:53 2012 -0600
@@ -63,7 +63,7 @@
 from mercurial.node import bin, hex, short, nullid, nullrev
 from mercurial.lock import release
 from mercurial import commands, cmdutil, hg, scmutil, util, revset
-from mercurial import repair, extensions, error, phases, bookmarks
+from mercurial import repair, extensions, error, phases
 from mercurial import patch as patchmod
 import os, re, errno, shutil
 
@@ -1675,9 +1675,10 @@
                     patchf.write(chunk)
                 patchf.close()
 
+                marks = repo._bookmarks
                 for bm in bmlist:
-                    repo._bookmarks[bm] = n
-                bookmarks.write(repo)
+                    marks[bm] = n
+                marks.write()
 
                 self.applied.append(statusentry(n, patchfn))
             except: # re-raises
@@ -2999,7 +3000,7 @@
             revs.update(set(rsrevs))
         if not revs:
             del marks[mark]
-            repo._writebookmarks(mark)
+            marks.write()
             ui.write(_("bookmark '%s' deleted\n") % mark)
 
     if not revs:
@@ -3049,7 +3050,7 @@
 
     if opts.get('bookmark'):
         del marks[mark]
-        repo._writebookmarks(marks)
+        marks.write()
         ui.write(_("bookmark '%s' deleted\n") % mark)
 
     repo.mq.strip(repo, revs, backup=backup, update=update,
--- a/hgext/rebase.py	Mon Nov 12 19:27:03 2012 +0200
+++ b/hgext/rebase.py	Tue Nov 13 19:32:53 2012 -0600
@@ -479,13 +479,14 @@
 
 def updatebookmarks(repo, nstate, originalbookmarks, **opts):
     'Move bookmarks to their correct changesets'
+    marks = repo._bookmarks
     for k, v in originalbookmarks.iteritems():
         if v in nstate:
             if nstate[v] != nullmerge:
                 # update the bookmarks for revs that have moved
-                repo._bookmarks[k] = nstate[v]
+                marks[k] = nstate[v]
 
-    bookmarks.write(repo)
+    marks.write()
 
 def storestatus(repo, originalwd, target, state, collapse, keep, keepbranches,
                                                                 external):
--- a/mercurial/bookmarks.py	Mon Nov 12 19:27:03 2012 +0200
+++ b/mercurial/bookmarks.py	Tue Nov 13 19:32:53 2012 -0600
@@ -7,35 +7,75 @@
 
 from mercurial.i18n import _
 from mercurial.node import hex
-from mercurial import encoding, error, util, obsolete, phases
+from mercurial import encoding, error, util, obsolete
 import errno, os
 
-def read(repo):
-    '''Parse .hg/bookmarks file and return a dictionary
+class bmstore(dict):
+    """Storage for bookmarks.
+
+    This object should do all bookmark reads and writes, so that it's
+    fairly simple to replace the storage underlying bookmarks without
+    having to clone the logic surrounding bookmarks.
+
+    This particular bmstore implementation stores bookmarks as
+    {hash}\s{name}\n (the same format as localtags) in
+    .hg/bookmarks. The mapping is stored as {name: nodeid}.
+
+    This class does NOT handle the "current" bookmark state at this
+    time.
+    """
 
-    Bookmarks are stored as {HASH}\\s{NAME}\\n (localtags format) values
-    in the .hg/bookmarks file.
-    Read the file and return a (name=>nodeid) dictionary
-    '''
-    bookmarks = {}
-    try:
-        for line in repo.opener('bookmarks'):
-            line = line.strip()
-            if not line:
-                continue
-            if ' ' not in line:
-                repo.ui.warn(_('malformed line in .hg/bookmarks: %r\n') % line)
-                continue
-            sha, refspec = line.split(' ', 1)
-            refspec = encoding.tolocal(refspec)
+    def __init__(self, repo):
+        dict.__init__(self)
+        self._repo = repo
+        try:
+            for line in repo.vfs('bookmarks'):
+                line = line.strip()
+                if not line:
+                    continue
+                if ' ' not in line:
+                    repo.ui.warn(_('malformed line in .hg/bookmarks: %r\n')
+                                 % line)
+                    continue
+                sha, refspec = line.split(' ', 1)
+                refspec = encoding.tolocal(refspec)
+                try:
+                    self[refspec] = repo.changelog.lookup(sha)
+                except LookupError:
+                    pass
+        except IOError, inst:
+            if inst.errno != errno.ENOENT:
+                raise
+
+    def write(self):
+        '''Write bookmarks
+
+        Write the given bookmark => hash dictionary to the .hg/bookmarks file
+        in a format equal to those of localtags.
+
+        We also store a backup of the previous state in undo.bookmarks that
+        can be copied back on rollback.
+        '''
+        repo = self._repo
+        if repo._bookmarkcurrent not in self:
+            setcurrent(repo, None)
+
+        wlock = repo.wlock()
+        try:
+
+            file = repo.vfs('bookmarks', 'w', atomictemp=True)
+            for name, node in self.iteritems():
+                file.write("%s %s\n" % (hex(node), encoding.fromlocal(name)))
+            file.close()
+
+            # touch 00changelog.i so hgweb reloads bookmarks (no lock needed)
             try:
-                bookmarks[refspec] = repo.changelog.lookup(sha)
-            except LookupError:
+                os.utime(repo.sjoin('00changelog.i'), None)
+            except OSError:
                 pass
-    except IOError, inst:
-        if inst.errno != errno.ENOENT:
-            raise
-    return bookmarks
+
+        finally:
+            wlock.release()
 
 def readcurrent(repo):
     '''Get the current bookmark
@@ -60,37 +100,6 @@
         file.close()
     return mark
 
-def write(repo):
-    '''Write bookmarks
-
-    Write the given bookmark => hash dictionary to the .hg/bookmarks file
-    in a format equal to those of localtags.
-
-    We also store a backup of the previous state in undo.bookmarks that
-    can be copied back on rollback.
-    '''
-    refs = repo._bookmarks
-
-    if repo._bookmarkcurrent not in refs:
-        setcurrent(repo, None)
-
-    wlock = repo.wlock()
-    try:
-
-        file = repo.opener('bookmarks', 'w', atomictemp=True)
-        for refspec, node in refs.iteritems():
-            file.write("%s %s\n" % (hex(node), encoding.fromlocal(refspec)))
-        file.close()
-
-        # touch 00changelog.i so hgweb reloads bookmarks (no lock needed)
-        try:
-            os.utime(repo.sjoin('00changelog.i'), None)
-        except OSError:
-            pass
-
-    finally:
-        wlock.release()
-
 def setcurrent(repo, mark):
     '''Set the name of the bookmark that we are currently on
 
@@ -152,7 +161,7 @@
             if mark != cur:
                 del marks[mark]
     if update:
-        repo._writebookmarks(marks)
+        marks.write()
     return update
 
 def listbookmarks(repo):
@@ -179,7 +188,7 @@
             if new not in repo:
                 return False
             marks[key] = repo[new].node()
-        write(repo)
+        marks.write()
         return True
     finally:
         w.release()
@@ -188,16 +197,17 @@
     ui.debug("checking for updated bookmarks\n")
     rb = remote.listkeys('bookmarks')
     changed = False
+    localmarks = repo._bookmarks
     for k in rb.keys():
-        if k in repo._bookmarks:
-            nr, nl = rb[k], repo._bookmarks[k]
+        if k in localmarks:
+            nr, nl = rb[k], localmarks[k]
             if nr in repo:
                 cr = repo[nr]
                 cl = repo[nl]
                 if cl.rev() >= cr.rev():
                     continue
                 if validdest(repo, cl, cr):
-                    repo._bookmarks[k] = cr.node()
+                    localmarks[k] = cr.node()
                     changed = True
                     ui.status(_("updating bookmark %s\n") % k)
                 else:
@@ -208,7 +218,7 @@
                     # find a unique @ suffix
                     for x in range(1, 100):
                         n = '%s@%d' % (kd, x)
-                        if n not in repo._bookmarks:
+                        if n not in localmarks:
                             break
                     # try to use an @pathalias suffix
                     # if an @pathalias already exists, we overwrite (update) it
@@ -216,17 +226,17 @@
                         if path == u:
                             n = '%s@%s' % (kd, p)
 
-                    repo._bookmarks[n] = cr.node()
+                    localmarks[n] = cr.node()
                     changed = True
                     ui.warn(_("divergent bookmark %s stored as %s\n") % (k, n))
         elif rb[k] in repo:
             # add remote bookmarks for changes we already have
-            repo._bookmarks[k] = repo[rb[k]].node()
+            localmarks[k] = repo[rb[k]].node()
             changed = True
             ui.status(_("adding remote bookmark %s\n") % k)
 
     if changed:
-        write(repo)
+        localmarks.write()
 
 def diff(ui, dst, src):
     ui.status(_("searching for changed bookmarks\n"))
@@ -263,14 +273,10 @@
         while len(validdests) != plen:
             plen = len(validdests)
             succs = set(c.node() for c in validdests)
-            for c in validdests:
-                if c.phase() > phases.public:
-                    # obsolescence marker does not apply to public changeset
-                    succs.update(obsolete.allsuccessors(repo.obsstore,
-                                                        [c.node()]))
+            mutable = [c.node() for c in validdests if c.mutable()]
+            succs.update(obsolete.allsuccessors(repo.obsstore, mutable))
             known = (n for n in succs if n in nm)
             validdests = set(repo.set('%ln::', known))
-        validdests.remove(old)
         return new in validdests
     else:
         return old.descendant(new)
--- a/mercurial/bundlerepo.py	Mon Nov 12 19:27:03 2012 +0200
+++ b/mercurial/bundlerepo.py	Tue Nov 13 19:32:53 2012 -0600
@@ -33,6 +33,7 @@
         self.basemap = {}
         n = len(self)
         chain = None
+        self.bundlenodes = []
         while True:
             chunkdata = bundle.deltachunk(chain)
             if not chunkdata:
@@ -48,6 +49,7 @@
             start = bundle.tell() - size
 
             link = linkmapper(cs)
+            self.bundlenodes.append(node)
             if node in self.nodemap:
                 # this can happen if two branches make the same change
                 chain = node
--- a/mercurial/cmdutil.py	Mon Nov 12 19:27:03 2012 +0200
+++ b/mercurial/cmdutil.py	Tue Nov 13 19:32:53 2012 -0600
@@ -10,7 +10,7 @@
 import os, sys, errno, re, tempfile
 import util, scmutil, templater, patch, error, templatekw, revlog, copies
 import match as matchmod
-import subrepo, context, repair, bookmarks, graphmod, revset, phases, obsolete
+import subrepo, context, repair, graphmod, revset, phases, obsolete
 import changelog
 import lock as lockmod
 
@@ -1759,9 +1759,10 @@
                 # Move bookmarks from old parent to amend commit
                 bms = repo.nodebookmarks(old.node())
                 if bms:
+                    marks = repo._bookmarks
                     for bm in bms:
-                        repo._bookmarks[bm] = newid
-                    bookmarks.write(repo)
+                        marks[bm] = newid
+                    marks.write()
             #commit the whole amend process
             if obsolete._enabled and newid != old.node():
                 # mark the new changeset as successor of the rewritten one
--- a/mercurial/commands.py	Mon Nov 12 19:27:03 2012 +0200
+++ b/mercurial/commands.py	Tue Nov 13 19:32:53 2012 -0600
@@ -821,7 +821,7 @@
         if mark == repo._bookmarkcurrent:
             bookmarks.setcurrent(repo, None)
         del marks[mark]
-        bookmarks.write(repo)
+        marks.write()
 
     elif rename:
         if mark is None:
@@ -834,7 +834,7 @@
         if repo._bookmarkcurrent == rename and not inactive:
             bookmarks.setcurrent(repo, mark)
         del marks[rename]
-        bookmarks.write(repo)
+        marks.write()
 
     elif mark is not None:
         mark = checkformat(mark)
@@ -848,7 +848,7 @@
             marks[mark] = cur
         if not inactive and cur == marks[mark]:
             bookmarks.setcurrent(repo, mark)
-        bookmarks.write(repo)
+        marks.write()
 
     # Same message whether trying to deactivate the current bookmark (-i
     # with no NAME) or listing bookmarks
@@ -1321,11 +1321,12 @@
         elif marks:
             ui.debug('moving bookmarks %r from %s to %s\n' %
                      (marks, old.hex(), hex(node)))
+            newmarks = repo._bookmarks
             for bm in marks:
-                repo._bookmarks[bm] = node
+                newmarks[bm] = node
                 if bm == current:
                     bookmarks.setcurrent(repo, bm)
-            bookmarks.write(repo)
+            newmarks.write()
     else:
         e = cmdutil.commiteditor
         if opts.get('force_editor'):
@@ -4206,6 +4207,9 @@
 
     Returns 0 on success.
     """
+
+    fm = ui.formatter('manifest', opts)
+
     if opts.get('all'):
         if rev or node:
             raise util.Abort(_("can't specify a revision with --all"))
@@ -4223,7 +4227,9 @@
         finally:
             lock.release()
         for f in res:
-            ui.write("%s\n" % f)
+            fm.startitem()
+            fm.write("path", '%s\n', f)
+        fm.end()
         return
 
     if rev and node:
@@ -4232,14 +4238,17 @@
     if not node:
         node = rev
 
-    decor = {'l':'644 @ ', 'x':'755 * ', '':'644   '}
+    char = {'l': '@', 'x': '*', '': ''}
+    mode = {'l': '644', 'x': '755', '': '644'}
     ctx = scmutil.revsingle(repo, node)
+    mf = ctx.manifest()
     for f in ctx:
-        if ui.debugflag:
-            ui.write("%40s " % hex(ctx.manifest()[f]))
-        if ui.verbose:
-            ui.write(decor[ctx.flags(f)])
-        ui.write("%s\n" % f)
+        fm.startitem()
+        fl = ctx[f].flags()
+        fm.condwrite(ui.debugflag, 'hash', '%s ', hex(mf[f]))
+        fm.condwrite(ui.verbose, 'mode type', '%s %1s ', mode[fl], char[fl])
+        fm.write('path', '%s\n', f)
+    fm.end()
 
 @command('^merge',
     [('f', 'force', None, _('force a merge with outstanding changes')),
@@ -4665,11 +4674,12 @@
 
     # update specified bookmarks
     if opts.get('bookmark'):
+        marks = repo._bookmarks
         for b in opts['bookmark']:
             # explicit pull overrides local bookmark if any
             ui.status(_("importing bookmark %s\n") % b)
-            repo._bookmarks[b] = repo[rb[b]].node()
-        bookmarks.write(repo)
+            marks[b] = repo[rb[b]].node()
+        marks.write()
 
     return ret
 
@@ -5426,17 +5436,16 @@
         copy = copies.pathcopies(repo[node1], repo[node2])
 
     fm = ui.formatter('status', opts)
-    format = '%s %s' + end
-    if opts.get('no_status'):
-        format = '%.0s%s' + end
+    fmt = '%s' + end
+    showchar = not opts.get('no_status')
 
     for state, char, files in changestates:
         if state in show:
             label = 'status.' + state
             for f in files:
                 fm.startitem()
-                fm.write("status path", format, char,
-                         repo.pathto(f, cwd), label=label)
+                fm.condwrite(showchar, 'status', '%s ', char, label=label)
+                fm.write('path', fmt, repo.pathto(f, cwd), label=label)
                 if f in copy:
                     fm.write("copy", '  %s' + end, repo.pathto(copy[f], cwd),
                              label='status.copied')
@@ -5742,7 +5751,7 @@
         release(lock, wlock)
 
 @command('tags', [], '')
-def tags(ui, repo):
+def tags(ui, repo, **opts):
     """list repository tags
 
     This lists both regular and local tags. When the -v/--verbose
@@ -5751,27 +5760,27 @@
     Returns 0 on success.
     """
 
+    fm = ui.formatter('tags', opts)
     hexfunc = ui.debugflag and hex or short
     tagtype = ""
 
     for t, n in reversed(repo.tagslist()):
-        if ui.quiet:
-            ui.write("%s\n" % t, label='tags.normal')
-            continue
-
         hn = hexfunc(n)
-        r = "%5d:%s" % (repo.changelog.rev(n), hn)
-        rev = ui.label(r, 'log.changeset changeset.%s' % repo[n].phasestr())
-        spaces = " " * (30 - encoding.colwidth(t))
-
-        tag = ui.label(t, 'tags.normal')
-        if ui.verbose:
-            if repo.tagtype(t) == 'local':
-                tagtype = " local"
-                tag = ui.label(t, 'tags.local')
-            else:
-                tagtype = ""
-        ui.write("%s%s %s%s\n" % (tag, spaces, rev, tagtype))
+        label = 'tags.normal'
+        tagtype = ''
+        if repo.tagtype(t) == 'local':
+            label = 'tags.local'
+            tagtype = 'local'
+
+        fm.startitem()
+        fm.write('tag', '%s', t, label=label)
+        fmt = " " * (30 - encoding.colwidth(t)) + ' %5d:%s'
+        fm.condwrite(not ui.quiet, 'rev id', fmt,
+                     repo.changelog.rev(n), hn, label=label)
+        fm.condwrite(ui.verbose and tagtype, 'type', ' %s',
+                     tagtype, label=label)
+        fm.plain('\n')
+    fm.end()
 
 @command('tip',
     [('p', 'patch', None, _('show patch')),
--- a/mercurial/formatter.py	Mon Nov 12 19:27:03 2012 +0200
+++ b/mercurial/formatter.py	Tue Nov 13 19:32:53 2012 -0600
@@ -31,6 +31,10 @@
         '''do default text output while assigning data to item'''
         for k, v in zip(fields.split(), fielddata):
             self._item[k] = v
+    def condwrite(self, cond, fields, deftext, *fielddata, **opts):
+        '''do conditional write (primarily for plain formatter)'''
+        for k, v in zip(fields.split(), fielddata):
+            self._item[k] = v
     def plain(self, text, **opts):
         '''show raw text for non-templated mode'''
         pass
@@ -51,6 +55,10 @@
         pass
     def write(self, fields, deftext, *fielddata, **opts):
         self._ui.write(deftext % fielddata, **opts)
+    def condwrite(self, cond, fields, deftext, *fielddata, **opts):
+        '''do conditional write'''
+        if cond:
+            self._ui.write(deftext % fielddata, **opts)
     def plain(self, text, **opts):
         self._ui.write(text, **opts)
     def end(self):
--- a/mercurial/hg.py	Mon Nov 12 19:27:03 2012 +0200
+++ b/mercurial/hg.py	Tue Nov 13 19:32:53 2012 -0600
@@ -391,14 +391,15 @@
         destrepo = destpeer.local()
         if destrepo and srcpeer.capable("pushkey"):
             rb = srcpeer.listkeys('bookmarks')
+            marks = destrepo._bookmarks
             for k, n in rb.iteritems():
                 try:
                     m = destrepo.lookup(n)
-                    destrepo._bookmarks[k] = m
+                    marks[k] = m
                 except error.RepoLookupError:
                     pass
             if rb:
-                bookmarks.write(destrepo)
+                marks.write()
         elif srcrepo and destpeer.capable("pushkey"):
             for k, n in srcrepo._bookmarks.iteritems():
                 destpeer.pushkey('bookmarks', k, '', hex(n))
--- a/mercurial/localrepo.py	Mon Nov 12 19:27:03 2012 +0200
+++ b/mercurial/localrepo.py	Tue Nov 13 19:32:53 2012 -0600
@@ -265,15 +265,12 @@
 
     @filecache('bookmarks')
     def _bookmarks(self):
-        return bookmarks.read(self)
+        return bookmarks.bmstore(self)
 
     @filecache('bookmarks.current')
     def _bookmarkcurrent(self):
         return bookmarks.readcurrent(self)
 
-    def _writebookmarks(self, marks):
-        bookmarks.write(self)
-
     def bookmarkheads(self, bookmark):
         name = bookmark.split('@', 1)[0]
         heads = []
--- a/mercurial/repair.py	Mon Nov 12 19:27:03 2012 +0200
+++ b/mercurial/repair.py	Tue Nov 13 19:32:53 2012 -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.
 
-from mercurial import changegroup, bookmarks
+from mercurial import changegroup
 from mercurial.node import short
 from mercurial.i18n import _
 import os
@@ -181,7 +181,7 @@
 
         for m in updatebm:
             bm[m] = repo[newbmtarget].node()
-        bookmarks.write(repo)
+        bm.write()
     except: # re-raises
         if backupfile:
             ui.warn(_("strip failed, full bundle stored in '%s'\n")
--- a/mercurial/revset.py	Mon Nov 12 19:27:03 2012 +0200
+++ b/mercurial/revset.py	Tue Nov 13 19:32:53 2012 -0600
@@ -442,6 +442,19 @@
     bumped = obsmod.getrevs(repo, 'bumped')
     return [r for r in subset if r in bumped]
 
+def bundle(repo, subset, x):
+    """``bundle()``
+    Changesets in the bundle.
+
+    Bundle must be specified by the -R option."""
+
+    try:
+        bundlenodes = repo.changelog.bundlenodes
+    except AttributeError:
+        raise util.Abort(_("no bundle provided - specify with -R"))
+    revs = set(repo[n].rev() for n in bundlenodes)
+    return [r for r in subset if r in revs]
+
 def checkstatus(repo, subset, pat, field):
     m = None
     s = []
@@ -1513,6 +1526,7 @@
     "branch": branch,
     "branchpoint": branchpoint,
     "bumped": bumped,
+    "bundle": bundle,
     "children": children,
     "closed": closed,
     "contains": contains,
--- a/setup.py	Mon Nov 12 19:27:03 2012 +0200
+++ b/setup.py	Tue Nov 13 19:32:53 2012 -0600
@@ -151,6 +151,8 @@
            if not e.startswith(b('Not trusting file')) \
               and not e.startswith(b('warning: Not importing'))]
     if err:
+        print >> sys.stderr, "stderr from '%s':" % (' '.join(cmd))
+        print >> sys.stderr, '\n'.join(['  ' + e for e in err])
         return ''
     return out
 
--- a/tests/run-tests.py	Mon Nov 12 19:27:03 2012 +0200
+++ b/tests/run-tests.py	Tue Nov 13 19:32:53 2012 -0600
@@ -55,6 +55,7 @@
 import re
 import threading
 import killdaemons as killmod
+import cPickle as pickle
 
 processlock = threading.Lock()
 
@@ -175,6 +176,8 @@
     parser.add_option("-t", "--timeout", type="int",
         help="kill errant tests after TIMEOUT seconds"
              " (default: $%s or %d)" % defaults['timeout'])
+    parser.add_option("--time", action="store_true",
+        help="time how long each test takes")
     parser.add_option("--tmpdir", type="string",
         help="run tests in the given temporary directory"
              " (implies --keep-tmpdir)")
@@ -263,6 +266,10 @@
             sys.stderr.write(
                 'warning: --timeout option ignored with --debug\n')
         options.timeout = 0
+        if options.time:
+            sys.stderr.write(
+                'warning: --time option ignored with --debug\n')
+        options.time = False
     if options.py3k_warnings:
         if sys.version_info[:2] < (2, 6) or sys.version_info[:2] >= (3, 0):
             parser.error('--py3k-warnings can only be used on Python 2.6+')
@@ -447,6 +454,14 @@
         fn = os.path.join(INST, '..', '.coverage')
         os.environ['COVERAGE_FILE'] = fn
 
+def outputtimes(options):
+    vlog('# Producing time report')
+    times.sort(key=lambda t: (t[1], t[0]), reverse=True)
+    cols = '%7.3f   %s'
+    print '\n%-7s   %s' % ('Time', 'Test')
+    for test, timetaken in times:
+        print cols % (timetaken, test)
+
 def outputcoverage(options):
 
     vlog('# Producing coverage report')
@@ -753,18 +768,13 @@
     True -> passed
     False -> failed'''
 
-    global results, resultslock, iolock
+    global results, iolock
 
     testpath = os.path.join(TESTDIR, test)
 
-    def result(l, e):
-        resultslock.acquire()
-        results[l].append(e)
-        resultslock.release()
-
     def skip(msg):
         if not options.verbose:
-            result('s', (test, msg))
+            results['s'].append((test, msg))
         else:
             iolock.acquire()
             print "\nSkipping %s: %s" % (testpath, msg)
@@ -787,15 +797,15 @@
                     rename(testpath + ".err", testpath)
                 else:
                     rename(testpath + ".err", testpath + ".out")
-                result('p', test)
+                success(test)
                 return
-        result('f', (test, msg))
+        results['f'].append((test, msg))
 
     def success():
-        result('p', test)
+        results['p'].append(test)
 
     def ignore(msg):
-        result('i', (test, msg))
+        results['i'].append((test, msg))
 
     if (os.path.basename(test).startswith("test-") and '~' not in test and
         ('.' not in test or test.endswith('.py') or
@@ -891,7 +901,12 @@
         replacements.append((re.escape(testtmp), '$TESTTMP'))
 
     os.mkdir(testtmp)
+    if options.time:
+        starttime = time.time()
     ret, out = runner(testpath, testtmp, options, replacements)
+    if options.time:
+        endtime = time.time()
+        times.append((test, endtime - starttime))
     vlog("# Ret was:", ret)
 
     mark = '.'
@@ -1056,29 +1071,30 @@
         childopts += ['--tmpdir', childtmp]
         cmdline = [PYTHON, sys.argv[0]] + opts + childopts + job
         vlog(' '.join(cmdline))
-        fps[os.spawnvp(os.P_NOWAIT, cmdline[0], cmdline)] = os.fdopen(rfd, 'r')
+        fps[os.spawnvp(os.P_NOWAIT, cmdline[0], cmdline)] = os.fdopen(rfd, 'rb')
         os.close(wfd)
     signal.signal(signal.SIGINT, signal.SIG_IGN)
     failures = 0
-    tested, skipped, failed = 0, 0, 0
+    passed, skipped, failed = 0, 0, 0
     skips = []
     fails = []
     while fps:
         pid, status = os.wait()
         fp = fps.pop(pid)
-        l = fp.read().splitlines()
         try:
-            test, skip, fail = map(int, l[:3])
-        except ValueError:
-            test, skip, fail = 0, 0, 0
-        split = -fail or len(l)
-        for s in l[3:split]:
-            skips.append(s.split(" ", 1))
-        for s in l[split:]:
-            fails.append(s.split(" ", 1))
-        tested += test
-        skipped += skip
-        failed += fail
+            childresults = pickle.load(fp)
+        except pickle.UnpicklingError:
+            pass
+        else:
+            passed += len(childresults['p'])
+            skipped += len(childresults['s'])
+            failed += len(childresults['f'])
+            skips.extend(childresults['s'])
+            fails.extend(childresults['f'])
+        if options.time:
+            childtimes = pickle.load(fp)
+            times.extend(childtimes)
+
         vlog('pid %d exited, status %d' % (pid, status))
         failures |= status
     print
@@ -1093,17 +1109,19 @@
 
     _checkhglib("Tested")
     print "# Ran %d tests, %d skipped, %d failed." % (
-        tested, skipped, failed)
+        passed + failed, skipped, failed)
 
+    if options.time:
+        outputtimes(options)
     if options.anycoverage:
         outputcoverage(options)
     sys.exit(failures != 0)
 
 results = dict(p=[], f=[], s=[], i=[])
-resultslock = threading.Lock()
+times = []
 iolock = threading.Lock()
 
-def runqueue(options, tests, results):
+def runqueue(options, tests):
     for test in tests:
         ret = runone(options, test)
         if options.first and ret is not None and not ret:
@@ -1129,7 +1147,7 @@
                 print "running all tests"
                 tests = orig
 
-        runqueue(options, tests, results)
+        runqueue(options, tests)
 
         failed = len(results['f'])
         tested = len(results['p']) + failed
@@ -1137,12 +1155,10 @@
         ignored = len(results['i'])
 
         if options.child:
-            fp = os.fdopen(options.child, 'w')
-            fp.write('%d\n%d\n%d\n' % (tested, skipped, failed))
-            for s in results['s']:
-                fp.write("%s %s\n" % s)
-            for s in results['f']:
-                fp.write("%s %s\n" % s)
+            fp = os.fdopen(options.child, 'wb')
+            pickle.dump(results, fp, pickle.HIGHEST_PROTOCOL)
+            if options.time:
+                pickle.dump(times, fp, pickle.HIGHEST_PROTOCOL)
             fp.close()
         else:
             print
@@ -1153,6 +1169,8 @@
             _checkhglib("Tested")
             print "# Ran %d tests, %d skipped, %d failed." % (
                 tested, skipped + ignored, failed)
+            if options.time:
+                outputtimes(options)
 
         if options.anycoverage:
             outputcoverage(options)
@@ -1170,9 +1188,9 @@
 
         checktools()
 
-    if len(args) == 0:
-        args = os.listdir(".")
-    args.sort()
+        if len(args) == 0:
+            args = os.listdir(".")
+        args.sort()
 
     tests = args
 
--- a/tests/test-bundle.t	Mon Nov 12 19:27:03 2012 +0200
+++ b/tests/test-bundle.t	Tue Nov 13 19:32:53 2012 -0600
@@ -444,6 +444,33 @@
   added 1 changesets with 1 changes to 1 files
   1 files updated, 0 files merged, 0 files removed, 0 files unresolved
 
+View full contents of the bundle
+  $ hg -R test bundle --base null -r 3  ../partial.hg
+  4 changesets found
+  $ cd test
+  $ hg -R ../../partial.hg log -r "bundle()"
+  changeset:   0:f9ee2f85a263
+  user:        test
+  date:        Thu Jan 01 00:00:00 1970 +0000
+  summary:     0.0
+  
+  changeset:   1:34c2bf6b0626
+  user:        test
+  date:        Thu Jan 01 00:00:00 1970 +0000
+  summary:     0.1
+  
+  changeset:   2:e38ba6f5b7e0
+  user:        test
+  date:        Thu Jan 01 00:00:00 1970 +0000
+  summary:     0.2
+  
+  changeset:   3:eebf5a27f8ca
+  user:        test
+  date:        Thu Jan 01 00:00:00 1970 +0000
+  summary:     0.3
+  
+  $ cd ..
+
 test for 540d1059c802
 
 test for 540d1059c802
--- a/tests/test-convert-git.t	Mon Nov 12 19:27:03 2012 +0200
+++ b/tests/test-convert-git.t	Tue Nov 13 19:32:53 2012 -0600
@@ -298,3 +298,50 @@
   $ hg convert git-repo4 git-repo4-broken-hg 2>&1 | \
   >     grep 'abort:' | sed 's/abort:.*/abort:/g'
   abort:
+
+test sub modules
+
+  $ mkdir git-repo5
+  $ cd git-repo5
+  $ git init-db >/dev/null 2>/dev/null
+  $ echo 'sub' >> foo
+  $ git add foo
+  $ commit -a -m 'addfoo'
+  $ BASE=${PWD}
+  $ cd ..
+  $ mkdir git-repo6
+  $ cd git-repo6
+  $ git init-db >/dev/null 2>/dev/null
+  $ git submodule add ${BASE} >/dev/null 2>/dev/null
+  $ commit -a -m 'addsubmodule' >/dev/null 2>/dev/null
+  $ cd ..
+
+convert sub modules
+  $ hg convert git-repo6 git-repo6-hg
+  initializing destination git-repo6-hg repository
+  scanning source...
+  sorting...
+  converting...
+  0 addsubmodule
+  updating bookmarks
+  $ hg -R git-repo6-hg log -v
+  changeset:   0:* (glob)
+  bookmark:    master
+  tag:         tip
+  user:        nottest <test@example.org>
+  date:        Mon Jan 01 00:00:23 2007 +0000
+  files:       .hgsub .hgsubstate
+  description:
+  addsubmodule
+  
+  committer: test <test@example.org>
+  
+  
+
+  $ cd git-repo6-hg
+  $ hg up >/dev/null 2>/dev/null
+  $ cat .hgsubstate
+  * git-repo5 (glob)
+  $ cd git-repo5
+  $ cat foo
+  sub
--- a/tests/test-eolfilename.t	Mon Nov 12 19:27:03 2012 +0200
+++ b/tests/test-eolfilename.t	Tue Nov 13 19:32:53 2012 -0600
@@ -68,9 +68,9 @@
   $ touch "$A"
   $ touch "$B"
   $ hg status --color=always
-  \x1b[0;35;1;4m? foo\x1b[0m (esc)
+  \x1b[0;35;1;4m? \x1b[0m\x1b[0;35;1;4mfoo\x1b[0m (esc)
   \x1b[0;35;1;4mbar\x1b[0m (esc)
-  \x1b[0;35;1;4m? foo\x1b[0m (esc)
+  \x1b[0;35;1;4m? \x1b[0m\x1b[0;35;1;4mfoo\x1b[0m (esc)
   \x1b[0;35;1;4mbar.baz\x1b[0m (esc)
 
   $ cd ..
--- a/tests/test-mq.t	Mon Nov 12 19:27:03 2012 +0200
+++ b/tests/test-mq.t	Tue Nov 13 19:32:53 2012 -0600
@@ -198,11 +198,11 @@
 status --mq with color (issue2096)
 
   $ hg status --mq --config extensions.color= --config color.mode=ansi --color=always
-  \x1b[0;32;1mA .hgignore\x1b[0m (esc)
-  \x1b[0;32;1mA A\x1b[0m (esc)
-  \x1b[0;32;1mA B\x1b[0m (esc)
-  \x1b[0;32;1mA series\x1b[0m (esc)
-  \x1b[0;35;1;4m? flaf\x1b[0m (esc)
+  \x1b[0;32;1mA \x1b[0m\x1b[0;32;1m.hgignore\x1b[0m (esc)
+  \x1b[0;32;1mA \x1b[0m\x1b[0;32;1mA\x1b[0m (esc)
+  \x1b[0;32;1mA \x1b[0m\x1b[0;32;1mB\x1b[0m (esc)
+  \x1b[0;32;1mA \x1b[0m\x1b[0;32;1mseries\x1b[0m (esc)
+  \x1b[0;35;1;4m? \x1b[0m\x1b[0;35;1;4mflaf\x1b[0m (esc)
 
 try the --mq option on a command provided by an extension
 
--- a/tests/test-status-color.t	Mon Nov 12 19:27:03 2012 +0200
+++ b/tests/test-status-color.t	Tue Nov 13 19:32:53 2012 -0600
@@ -15,100 +15,100 @@
 hg status in repo root:
 
   $ hg status --color=always
-  \x1b[0;35;1;4m? a/1/in_a_1\x1b[0m (esc)
-  \x1b[0;35;1;4m? a/in_a\x1b[0m (esc)
-  \x1b[0;35;1;4m? b/1/in_b_1\x1b[0m (esc)
-  \x1b[0;35;1;4m? b/2/in_b_2\x1b[0m (esc)
-  \x1b[0;35;1;4m? b/in_b\x1b[0m (esc)
-  \x1b[0;35;1;4m? in_root\x1b[0m (esc)
+  \x1b[0;35;1;4m? \x1b[0m\x1b[0;35;1;4ma/1/in_a_1\x1b[0m (esc)
+  \x1b[0;35;1;4m? \x1b[0m\x1b[0;35;1;4ma/in_a\x1b[0m (esc)
+  \x1b[0;35;1;4m? \x1b[0m\x1b[0;35;1;4mb/1/in_b_1\x1b[0m (esc)
+  \x1b[0;35;1;4m? \x1b[0m\x1b[0;35;1;4mb/2/in_b_2\x1b[0m (esc)
+  \x1b[0;35;1;4m? \x1b[0m\x1b[0;35;1;4mb/in_b\x1b[0m (esc)
+  \x1b[0;35;1;4m? \x1b[0m\x1b[0;35;1;4min_root\x1b[0m (esc)
 
 hg status . in repo root:
 
   $ hg status --color=always .
-  \x1b[0;35;1;4m? a/1/in_a_1\x1b[0m (esc)
-  \x1b[0;35;1;4m? a/in_a\x1b[0m (esc)
-  \x1b[0;35;1;4m? b/1/in_b_1\x1b[0m (esc)
-  \x1b[0;35;1;4m? b/2/in_b_2\x1b[0m (esc)
-  \x1b[0;35;1;4m? b/in_b\x1b[0m (esc)
-  \x1b[0;35;1;4m? in_root\x1b[0m (esc)
+  \x1b[0;35;1;4m? \x1b[0m\x1b[0;35;1;4ma/1/in_a_1\x1b[0m (esc)
+  \x1b[0;35;1;4m? \x1b[0m\x1b[0;35;1;4ma/in_a\x1b[0m (esc)
+  \x1b[0;35;1;4m? \x1b[0m\x1b[0;35;1;4mb/1/in_b_1\x1b[0m (esc)
+  \x1b[0;35;1;4m? \x1b[0m\x1b[0;35;1;4mb/2/in_b_2\x1b[0m (esc)
+  \x1b[0;35;1;4m? \x1b[0m\x1b[0;35;1;4mb/in_b\x1b[0m (esc)
+  \x1b[0;35;1;4m? \x1b[0m\x1b[0;35;1;4min_root\x1b[0m (esc)
 
   $ hg status --color=always --cwd a
-  \x1b[0;35;1;4m? a/1/in_a_1\x1b[0m (esc)
-  \x1b[0;35;1;4m? a/in_a\x1b[0m (esc)
-  \x1b[0;35;1;4m? b/1/in_b_1\x1b[0m (esc)
-  \x1b[0;35;1;4m? b/2/in_b_2\x1b[0m (esc)
-  \x1b[0;35;1;4m? b/in_b\x1b[0m (esc)
-  \x1b[0;35;1;4m? in_root\x1b[0m (esc)
+  \x1b[0;35;1;4m? \x1b[0m\x1b[0;35;1;4ma/1/in_a_1\x1b[0m (esc)
+  \x1b[0;35;1;4m? \x1b[0m\x1b[0;35;1;4ma/in_a\x1b[0m (esc)
+  \x1b[0;35;1;4m? \x1b[0m\x1b[0;35;1;4mb/1/in_b_1\x1b[0m (esc)
+  \x1b[0;35;1;4m? \x1b[0m\x1b[0;35;1;4mb/2/in_b_2\x1b[0m (esc)
+  \x1b[0;35;1;4m? \x1b[0m\x1b[0;35;1;4mb/in_b\x1b[0m (esc)
+  \x1b[0;35;1;4m? \x1b[0m\x1b[0;35;1;4min_root\x1b[0m (esc)
   $ hg status --color=always --cwd a .
-  \x1b[0;35;1;4m? 1/in_a_1\x1b[0m (esc)
-  \x1b[0;35;1;4m? in_a\x1b[0m (esc)
+  \x1b[0;35;1;4m? \x1b[0m\x1b[0;35;1;4m1/in_a_1\x1b[0m (esc)
+  \x1b[0;35;1;4m? \x1b[0m\x1b[0;35;1;4min_a\x1b[0m (esc)
   $ hg status --color=always --cwd a ..
-  \x1b[0;35;1;4m? 1/in_a_1\x1b[0m (esc)
-  \x1b[0;35;1;4m? in_a\x1b[0m (esc)
-  \x1b[0;35;1;4m? ../b/1/in_b_1\x1b[0m (esc)
-  \x1b[0;35;1;4m? ../b/2/in_b_2\x1b[0m (esc)
-  \x1b[0;35;1;4m? ../b/in_b\x1b[0m (esc)
-  \x1b[0;35;1;4m? ../in_root\x1b[0m (esc)
+  \x1b[0;35;1;4m? \x1b[0m\x1b[0;35;1;4m1/in_a_1\x1b[0m (esc)
+  \x1b[0;35;1;4m? \x1b[0m\x1b[0;35;1;4min_a\x1b[0m (esc)
+  \x1b[0;35;1;4m? \x1b[0m\x1b[0;35;1;4m../b/1/in_b_1\x1b[0m (esc)
+  \x1b[0;35;1;4m? \x1b[0m\x1b[0;35;1;4m../b/2/in_b_2\x1b[0m (esc)
+  \x1b[0;35;1;4m? \x1b[0m\x1b[0;35;1;4m../b/in_b\x1b[0m (esc)
+  \x1b[0;35;1;4m? \x1b[0m\x1b[0;35;1;4m../in_root\x1b[0m (esc)
 
   $ hg status --color=always --cwd b
-  \x1b[0;35;1;4m? a/1/in_a_1\x1b[0m (esc)
-  \x1b[0;35;1;4m? a/in_a\x1b[0m (esc)
-  \x1b[0;35;1;4m? b/1/in_b_1\x1b[0m (esc)
-  \x1b[0;35;1;4m? b/2/in_b_2\x1b[0m (esc)
-  \x1b[0;35;1;4m? b/in_b\x1b[0m (esc)
-  \x1b[0;35;1;4m? in_root\x1b[0m (esc)
+  \x1b[0;35;1;4m? \x1b[0m\x1b[0;35;1;4ma/1/in_a_1\x1b[0m (esc)
+  \x1b[0;35;1;4m? \x1b[0m\x1b[0;35;1;4ma/in_a\x1b[0m (esc)
+  \x1b[0;35;1;4m? \x1b[0m\x1b[0;35;1;4mb/1/in_b_1\x1b[0m (esc)
+  \x1b[0;35;1;4m? \x1b[0m\x1b[0;35;1;4mb/2/in_b_2\x1b[0m (esc)
+  \x1b[0;35;1;4m? \x1b[0m\x1b[0;35;1;4mb/in_b\x1b[0m (esc)
+  \x1b[0;35;1;4m? \x1b[0m\x1b[0;35;1;4min_root\x1b[0m (esc)
   $ hg status --color=always --cwd b .
-  \x1b[0;35;1;4m? 1/in_b_1\x1b[0m (esc)
-  \x1b[0;35;1;4m? 2/in_b_2\x1b[0m (esc)
-  \x1b[0;35;1;4m? in_b\x1b[0m (esc)
+  \x1b[0;35;1;4m? \x1b[0m\x1b[0;35;1;4m1/in_b_1\x1b[0m (esc)
+  \x1b[0;35;1;4m? \x1b[0m\x1b[0;35;1;4m2/in_b_2\x1b[0m (esc)
+  \x1b[0;35;1;4m? \x1b[0m\x1b[0;35;1;4min_b\x1b[0m (esc)
   $ hg status --color=always --cwd b ..
-  \x1b[0;35;1;4m? ../a/1/in_a_1\x1b[0m (esc)
-  \x1b[0;35;1;4m? ../a/in_a\x1b[0m (esc)
-  \x1b[0;35;1;4m? 1/in_b_1\x1b[0m (esc)
-  \x1b[0;35;1;4m? 2/in_b_2\x1b[0m (esc)
-  \x1b[0;35;1;4m? in_b\x1b[0m (esc)
-  \x1b[0;35;1;4m? ../in_root\x1b[0m (esc)
+  \x1b[0;35;1;4m? \x1b[0m\x1b[0;35;1;4m../a/1/in_a_1\x1b[0m (esc)
+  \x1b[0;35;1;4m? \x1b[0m\x1b[0;35;1;4m../a/in_a\x1b[0m (esc)
+  \x1b[0;35;1;4m? \x1b[0m\x1b[0;35;1;4m1/in_b_1\x1b[0m (esc)
+  \x1b[0;35;1;4m? \x1b[0m\x1b[0;35;1;4m2/in_b_2\x1b[0m (esc)
+  \x1b[0;35;1;4m? \x1b[0m\x1b[0;35;1;4min_b\x1b[0m (esc)
+  \x1b[0;35;1;4m? \x1b[0m\x1b[0;35;1;4m../in_root\x1b[0m (esc)
 
   $ hg status --color=always --cwd a/1
-  \x1b[0;35;1;4m? a/1/in_a_1\x1b[0m (esc)
-  \x1b[0;35;1;4m? a/in_a\x1b[0m (esc)
-  \x1b[0;35;1;4m? b/1/in_b_1\x1b[0m (esc)
-  \x1b[0;35;1;4m? b/2/in_b_2\x1b[0m (esc)
-  \x1b[0;35;1;4m? b/in_b\x1b[0m (esc)
-  \x1b[0;35;1;4m? in_root\x1b[0m (esc)
+  \x1b[0;35;1;4m? \x1b[0m\x1b[0;35;1;4ma/1/in_a_1\x1b[0m (esc)
+  \x1b[0;35;1;4m? \x1b[0m\x1b[0;35;1;4ma/in_a\x1b[0m (esc)
+  \x1b[0;35;1;4m? \x1b[0m\x1b[0;35;1;4mb/1/in_b_1\x1b[0m (esc)
+  \x1b[0;35;1;4m? \x1b[0m\x1b[0;35;1;4mb/2/in_b_2\x1b[0m (esc)
+  \x1b[0;35;1;4m? \x1b[0m\x1b[0;35;1;4mb/in_b\x1b[0m (esc)
+  \x1b[0;35;1;4m? \x1b[0m\x1b[0;35;1;4min_root\x1b[0m (esc)
   $ hg status --color=always --cwd a/1 .
-  \x1b[0;35;1;4m? in_a_1\x1b[0m (esc)
+  \x1b[0;35;1;4m? \x1b[0m\x1b[0;35;1;4min_a_1\x1b[0m (esc)
   $ hg status --color=always --cwd a/1 ..
-  \x1b[0;35;1;4m? in_a_1\x1b[0m (esc)
-  \x1b[0;35;1;4m? ../in_a\x1b[0m (esc)
+  \x1b[0;35;1;4m? \x1b[0m\x1b[0;35;1;4min_a_1\x1b[0m (esc)
+  \x1b[0;35;1;4m? \x1b[0m\x1b[0;35;1;4m../in_a\x1b[0m (esc)
 
   $ hg status --color=always --cwd b/1
-  \x1b[0;35;1;4m? a/1/in_a_1\x1b[0m (esc)
-  \x1b[0;35;1;4m? a/in_a\x1b[0m (esc)
-  \x1b[0;35;1;4m? b/1/in_b_1\x1b[0m (esc)
-  \x1b[0;35;1;4m? b/2/in_b_2\x1b[0m (esc)
-  \x1b[0;35;1;4m? b/in_b\x1b[0m (esc)
-  \x1b[0;35;1;4m? in_root\x1b[0m (esc)
+  \x1b[0;35;1;4m? \x1b[0m\x1b[0;35;1;4ma/1/in_a_1\x1b[0m (esc)
+  \x1b[0;35;1;4m? \x1b[0m\x1b[0;35;1;4ma/in_a\x1b[0m (esc)
+  \x1b[0;35;1;4m? \x1b[0m\x1b[0;35;1;4mb/1/in_b_1\x1b[0m (esc)
+  \x1b[0;35;1;4m? \x1b[0m\x1b[0;35;1;4mb/2/in_b_2\x1b[0m (esc)
+  \x1b[0;35;1;4m? \x1b[0m\x1b[0;35;1;4mb/in_b\x1b[0m (esc)
+  \x1b[0;35;1;4m? \x1b[0m\x1b[0;35;1;4min_root\x1b[0m (esc)
   $ hg status --color=always --cwd b/1 .
-  \x1b[0;35;1;4m? in_b_1\x1b[0m (esc)
+  \x1b[0;35;1;4m? \x1b[0m\x1b[0;35;1;4min_b_1\x1b[0m (esc)
   $ hg status --color=always --cwd b/1 ..
-  \x1b[0;35;1;4m? in_b_1\x1b[0m (esc)
-  \x1b[0;35;1;4m? ../2/in_b_2\x1b[0m (esc)
-  \x1b[0;35;1;4m? ../in_b\x1b[0m (esc)
+  \x1b[0;35;1;4m? \x1b[0m\x1b[0;35;1;4min_b_1\x1b[0m (esc)
+  \x1b[0;35;1;4m? \x1b[0m\x1b[0;35;1;4m../2/in_b_2\x1b[0m (esc)
+  \x1b[0;35;1;4m? \x1b[0m\x1b[0;35;1;4m../in_b\x1b[0m (esc)
 
   $ hg status --color=always --cwd b/2
-  \x1b[0;35;1;4m? a/1/in_a_1\x1b[0m (esc)
-  \x1b[0;35;1;4m? a/in_a\x1b[0m (esc)
-  \x1b[0;35;1;4m? b/1/in_b_1\x1b[0m (esc)
-  \x1b[0;35;1;4m? b/2/in_b_2\x1b[0m (esc)
-  \x1b[0;35;1;4m? b/in_b\x1b[0m (esc)
-  \x1b[0;35;1;4m? in_root\x1b[0m (esc)
+  \x1b[0;35;1;4m? \x1b[0m\x1b[0;35;1;4ma/1/in_a_1\x1b[0m (esc)
+  \x1b[0;35;1;4m? \x1b[0m\x1b[0;35;1;4ma/in_a\x1b[0m (esc)
+  \x1b[0;35;1;4m? \x1b[0m\x1b[0;35;1;4mb/1/in_b_1\x1b[0m (esc)
+  \x1b[0;35;1;4m? \x1b[0m\x1b[0;35;1;4mb/2/in_b_2\x1b[0m (esc)
+  \x1b[0;35;1;4m? \x1b[0m\x1b[0;35;1;4mb/in_b\x1b[0m (esc)
+  \x1b[0;35;1;4m? \x1b[0m\x1b[0;35;1;4min_root\x1b[0m (esc)
   $ hg status --color=always --cwd b/2 .
-  \x1b[0;35;1;4m? in_b_2\x1b[0m (esc)
+  \x1b[0;35;1;4m? \x1b[0m\x1b[0;35;1;4min_b_2\x1b[0m (esc)
   $ hg status --color=always --cwd b/2 ..
-  \x1b[0;35;1;4m? ../1/in_b_1\x1b[0m (esc)
-  \x1b[0;35;1;4m? in_b_2\x1b[0m (esc)
-  \x1b[0;35;1;4m? ../in_b\x1b[0m (esc)
+  \x1b[0;35;1;4m? \x1b[0m\x1b[0;35;1;4m../1/in_b_1\x1b[0m (esc)
+  \x1b[0;35;1;4m? \x1b[0m\x1b[0;35;1;4min_b_2\x1b[0m (esc)
+  \x1b[0;35;1;4m? \x1b[0m\x1b[0;35;1;4m../in_b\x1b[0m (esc)
   $ cd ..
 
   $ hg init repo2
@@ -128,59 +128,59 @@
 hg status:
 
   $ hg status --color=always
-  \x1b[0;32;1mA added\x1b[0m (esc)
-  \x1b[0;31;1mR removed\x1b[0m (esc)
-  \x1b[0;36;1;4m! deleted\x1b[0m (esc)
-  \x1b[0;35;1;4m? unknown\x1b[0m (esc)
+  \x1b[0;32;1mA \x1b[0m\x1b[0;32;1madded\x1b[0m (esc)
+  \x1b[0;31;1mR \x1b[0m\x1b[0;31;1mremoved\x1b[0m (esc)
+  \x1b[0;36;1;4m! \x1b[0m\x1b[0;36;1;4mdeleted\x1b[0m (esc)
+  \x1b[0;35;1;4m? \x1b[0m\x1b[0;35;1;4munknown\x1b[0m (esc)
 
 hg status modified added removed deleted unknown never-existed ignored:
 
   $ hg status --color=always modified added removed deleted unknown never-existed ignored
   never-existed: * (glob)
-  \x1b[0;32;1mA added\x1b[0m (esc)
-  \x1b[0;31;1mR removed\x1b[0m (esc)
-  \x1b[0;36;1;4m! deleted\x1b[0m (esc)
-  \x1b[0;35;1;4m? unknown\x1b[0m (esc)
+  \x1b[0;32;1mA \x1b[0m\x1b[0;32;1madded\x1b[0m (esc)
+  \x1b[0;31;1mR \x1b[0m\x1b[0;31;1mremoved\x1b[0m (esc)
+  \x1b[0;36;1;4m! \x1b[0m\x1b[0;36;1;4mdeleted\x1b[0m (esc)
+  \x1b[0;35;1;4m? \x1b[0m\x1b[0;35;1;4munknown\x1b[0m (esc)
 
   $ hg copy modified copied
 
 hg status -C:
 
   $ hg status --color=always -C
-  \x1b[0;32;1mA added\x1b[0m (esc)
-  \x1b[0;32;1mA copied\x1b[0m (esc)
+  \x1b[0;32;1mA \x1b[0m\x1b[0;32;1madded\x1b[0m (esc)
+  \x1b[0;32;1mA \x1b[0m\x1b[0;32;1mcopied\x1b[0m (esc)
   \x1b[0;0m  modified\x1b[0m (esc)
-  \x1b[0;31;1mR removed\x1b[0m (esc)
-  \x1b[0;36;1;4m! deleted\x1b[0m (esc)
-  \x1b[0;35;1;4m? unknown\x1b[0m (esc)
+  \x1b[0;31;1mR \x1b[0m\x1b[0;31;1mremoved\x1b[0m (esc)
+  \x1b[0;36;1;4m! \x1b[0m\x1b[0;36;1;4mdeleted\x1b[0m (esc)
+  \x1b[0;35;1;4m? \x1b[0m\x1b[0;35;1;4munknown\x1b[0m (esc)
 
 hg status -A:
 
   $ hg status --color=always -A
-  \x1b[0;32;1mA added\x1b[0m (esc)
-  \x1b[0;32;1mA copied\x1b[0m (esc)
+  \x1b[0;32;1mA \x1b[0m\x1b[0;32;1madded\x1b[0m (esc)
+  \x1b[0;32;1mA \x1b[0m\x1b[0;32;1mcopied\x1b[0m (esc)
   \x1b[0;0m  modified\x1b[0m (esc)
-  \x1b[0;31;1mR removed\x1b[0m (esc)
-  \x1b[0;36;1;4m! deleted\x1b[0m (esc)
-  \x1b[0;35;1;4m? unknown\x1b[0m (esc)
-  \x1b[0;30;1mI ignored\x1b[0m (esc)
-  \x1b[0;0mC .hgignore\x1b[0m (esc)
-  \x1b[0;0mC modified\x1b[0m (esc)
+  \x1b[0;31;1mR \x1b[0m\x1b[0;31;1mremoved\x1b[0m (esc)
+  \x1b[0;36;1;4m! \x1b[0m\x1b[0;36;1;4mdeleted\x1b[0m (esc)
+  \x1b[0;35;1;4m? \x1b[0m\x1b[0;35;1;4munknown\x1b[0m (esc)
+  \x1b[0;30;1mI \x1b[0m\x1b[0;30;1mignored\x1b[0m (esc)
+  \x1b[0;0mC \x1b[0m\x1b[0;0m.hgignore\x1b[0m (esc)
+  \x1b[0;0mC \x1b[0m\x1b[0;0mmodified\x1b[0m (esc)
 
 hg status -A (with terminfo color):
 
   $ mkdir "$TESTTMP/terminfo"
   $ TERMINFO="$TESTTMP/terminfo" tic "$TESTDIR/hgterm.ti"
   $ TERM=hgterm TERMINFO="$TESTTMP/terminfo" hg status --config color.mode=terminfo --color=always -A
-  \x1b[30m\x1b[32m\x1b[1mA added\x1b[30m (esc)
-  \x1b[30m\x1b[32m\x1b[1mA copied\x1b[30m (esc)
+  \x1b[30m\x1b[32m\x1b[1mA \x1b[30m\x1b[30m\x1b[32m\x1b[1madded\x1b[30m (esc)
+  \x1b[30m\x1b[32m\x1b[1mA \x1b[30m\x1b[30m\x1b[32m\x1b[1mcopied\x1b[30m (esc)
   \x1b[30m\x1b[30m  modified\x1b[30m (esc)
-  \x1b[30m\x1b[31m\x1b[1mR removed\x1b[30m (esc)
-  \x1b[30m\x1b[36m\x1b[1m\x1b[4m! deleted\x1b[30m (esc)
-  \x1b[30m\x1b[35m\x1b[1m\x1b[4m? unknown\x1b[30m (esc)
-  \x1b[30m\x1b[30m\x1b[1mI ignored\x1b[30m (esc)
-  \x1b[30m\x1b[30mC .hgignore\x1b[30m (esc)
-  \x1b[30m\x1b[30mC modified\x1b[30m (esc)
+  \x1b[30m\x1b[31m\x1b[1mR \x1b[30m\x1b[30m\x1b[31m\x1b[1mremoved\x1b[30m (esc)
+  \x1b[30m\x1b[36m\x1b[1m\x1b[4m! \x1b[30m\x1b[30m\x1b[36m\x1b[1m\x1b[4mdeleted\x1b[30m (esc)
+  \x1b[30m\x1b[35m\x1b[1m\x1b[4m? \x1b[30m\x1b[30m\x1b[35m\x1b[1m\x1b[4munknown\x1b[30m (esc)
+  \x1b[30m\x1b[30m\x1b[1mI \x1b[30m\x1b[30m\x1b[30m\x1b[1mignored\x1b[30m (esc)
+  \x1b[30m\x1b[30mC \x1b[30m\x1b[30m\x1b[30m.hgignore\x1b[30m (esc)
+  \x1b[30m\x1b[30mC \x1b[30m\x1b[30m\x1b[30mmodified\x1b[30m (esc)
 
 
   $ echo "^ignoreddir$" > .hgignore
@@ -194,7 +194,7 @@
 hg status -i ignoreddir/file:
 
   $ hg status --color=always -i ignoreddir/file
-  \x1b[0;30;1mI ignoreddir/file\x1b[0m (esc)
+  \x1b[0;30;1mI \x1b[0m\x1b[0;30;1mignoreddir/file\x1b[0m (esc)
   $ cd ..
 
 check 'status -q' and some combinations
@@ -220,11 +220,11 @@
   $ hg --config color.status.modified=periwinkle status --color=always
   ignoring unknown color/effect 'periwinkle' (configured in color.status.modified)
   M modified
-  \x1b[0;32;1mA added\x1b[0m (esc)
-  \x1b[0;32;1mA copied\x1b[0m (esc)
-  \x1b[0;31;1mR removed\x1b[0m (esc)
-  \x1b[0;36;1;4m! deleted\x1b[0m (esc)
-  \x1b[0;35;1;4m? unknown\x1b[0m (esc)
+  \x1b[0;32;1mA \x1b[0m\x1b[0;32;1madded\x1b[0m (esc)
+  \x1b[0;32;1mA \x1b[0m\x1b[0;32;1mcopied\x1b[0m (esc)
+  \x1b[0;31;1mR \x1b[0m\x1b[0;31;1mremoved\x1b[0m (esc)
+  \x1b[0;36;1;4m! \x1b[0m\x1b[0;36;1;4mdeleted\x1b[0m (esc)
+  \x1b[0;35;1;4m? \x1b[0m\x1b[0;35;1;4munknown\x1b[0m (esc)
 
 Run status with 2 different flags.
 Check if result is the same or different.