changeset 17986:e853d27956fb

merge with stable
author Matt Mackall <mpm@selenic.com>
date Wed, 28 Nov 2012 18:08:51 -0600
parents c64e646af81e (diff) f94ead934067 (current diff)
children 1683bca31752
files mercurial/commands.py
diffstat 52 files changed, 1309 insertions(+), 725 deletions(-) [+]
line wrap: on
line diff
--- a/Makefile	Wed Nov 28 17:53:52 2012 -0600
+++ b/Makefile	Wed Nov 28 18:08:51 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/contrib/check-code.py	Wed Nov 28 17:53:52 2012 -0600
+++ b/contrib/check-code.py	Wed Nov 28 18:08:51 2012 -0600
@@ -211,11 +211,11 @@
     (r'\.strip\(\)\.split\(\)', "no need to strip before splitting"),
     (r'^\s*except\s*:', "warning: naked except clause", r'#.*re-raises'),
     (r':\n(    )*( ){1,3}[^ ]', "must indent 4 spaces"),
+    (r'ui\.(status|progress|write|note|warn)\([\'\"]x',
+     "missing _() in ui message (use () to hide false-positives)"),
   ],
   # warnings
   [
-    (r'ui\.(status|progress|write|note|warn)\([\'\"]x',
-     "warning: unwrapped ui message"),
   ]
 ]
 
--- a/contrib/hgk	Wed Nov 28 17:53:52 2012 -0600
+++ b/contrib/hgk	Wed Nov 28 18:08:51 2012 -0600
@@ -15,8 +15,43 @@
 # The whole snipped is activated only under windows, mouse wheel
 # bindings working already under MacOSX and Linux.
 
+if {[catch {package require Ttk}]} {
+    # use a shim
+    namespace eval ttk {
+        proc style args {}
+
+        proc entry args {
+            eval [linsert $args 0 ::entry] -relief flat
+        }
+    }
+
+    interp alias {} ttk::button {} button
+    interp alias {} ttk::frame {} frame
+    interp alias {} ttk::label {} label
+    interp alias {} ttk::scrollbar {} scrollbar
+    interp alias {} ttk::optionMenu {} tk_optionMenu
+} else {
+    proc ::ttk::optionMenu {w varName firstValue args} {
+        upvar #0 $varName var
+
+        if {![info exists var]} {
+            set var $firstValue
+        }
+        ttk::menubutton $w -textvariable $varName -menu $w.menu \
+                -direction flush
+        menu $w.menu -tearoff 0
+        $w.menu add radiobutton -label $firstValue -variable $varName
+        foreach i $args {
+            $w.menu add radiobutton -label $i -variable $varName
+        }
+        return $w.menu
+    }
+}
+
 if {[tk windowingsystem] eq "win32"} {
 
+ttk::style theme use xpnative
+
 set mw_classes [list Text Listbox Table TreeCtrl]
    foreach class $mw_classes { bind $class <MouseWheel> {} }
 
@@ -72,6 +107,12 @@
 bind all <MouseWheel> [list ::tk::MouseWheel %W %X %Y %D 0]
 
 # end of win32 section
+} else {
+
+if {[ttk::style theme use] eq "default"} {
+    ttk::style theme use clam
+}
+
 }
 
 
@@ -480,7 +521,7 @@
     wm transient $w .
     message $w.m -text $msg -justify center -aspect 400
     pack $w.m -side top -fill x -padx 20 -pady 20
-    button $w.ok -text OK -command "destroy $w"
+    ttk::button $w.ok -text OK -command "destroy $w"
     pack $w.ok -side bottom -fill x
     bind $w <Visibility> "grab $w; focus $w"
     tkwait window $w
@@ -526,11 +567,11 @@
 	set geometry(ctexth) [expr {($texth - 8) /
 				    [font metrics $textfont -linespace]}]
     }
-    frame .ctop.top
-    frame .ctop.top.bar
+    ttk::frame .ctop.top
+    ttk::frame .ctop.top.bar
     pack .ctop.top.bar -side bottom -fill x
     set cscroll .ctop.top.csb
-    scrollbar $cscroll -command {allcanvs yview} -highlightthickness 0
+    ttk::scrollbar $cscroll -command {allcanvs yview}
     pack $cscroll -side right -fill y
     panedwindow .ctop.top.clist -orient horizontal -sashpad 0 -handlesize 4
     pack .ctop.top.clist -side top -fill both -expand 1
@@ -538,15 +579,15 @@
     set canv .ctop.top.clist.canv
     canvas $canv -height $geometry(canvh) -width $geometry(canv1) \
 	-bg $bgcolor -bd 0 \
-	-yscrollincr $linespc -yscrollcommand "$cscroll set" -selectbackground grey
+	-yscrollincr $linespc -yscrollcommand "$cscroll set" -selectbackground "#c0c0c0"
     .ctop.top.clist add $canv
     set canv2 .ctop.top.clist.canv2
     canvas $canv2 -height $geometry(canvh) -width $geometry(canv2) \
-	-bg $bgcolor -bd 0 -yscrollincr $linespc -selectbackground grey
+	-bg $bgcolor -bd 0 -yscrollincr $linespc -selectbackground "#c0c0c0"
     .ctop.top.clist add $canv2
     set canv3 .ctop.top.clist.canv3
     canvas $canv3 -height $geometry(canvh) -width $geometry(canv3) \
-	-bg $bgcolor -bd 0 -yscrollincr $linespc -selectbackground grey
+	-bg $bgcolor -bd 0 -yscrollincr $linespc -selectbackground "#c0c0c0"
     .ctop.top.clist add $canv3
     bind .ctop.top.clist <Configure> {resizeclistpanes %W %w}
 
@@ -557,7 +598,7 @@
 	-command gotocommit -width 8
     $sha1but conf -disabledforeground [$sha1but cget -foreground]
     pack .ctop.top.bar.sha1label -side left
-    entry $sha1entry -width 40 -font $textfont -textvariable sha1string
+    ttk::entry $sha1entry -width 40 -font $textfont -textvariable sha1string
     trace add variable sha1string write sha1change
     pack $sha1entry -side left -pady 2
 
@@ -577,25 +618,25 @@
 	0x00, 0x38, 0xff, 0x7f, 0xff, 0x7f, 0xff, 0x7f, 0x00, 0x38, 0x00, 0x1c,
 	0x00, 0x0e, 0x00, 0x07, 0x80, 0x03, 0xc0, 0x01};
     }
-    button .ctop.top.bar.leftbut -image bm-left -command goback \
+    ttk::button .ctop.top.bar.leftbut -image bm-left -command goback \
 	-state disabled -width 26
     pack .ctop.top.bar.leftbut -side left -fill y
-    button .ctop.top.bar.rightbut -image bm-right -command goforw \
+    ttk::button .ctop.top.bar.rightbut -image bm-right -command goforw \
 	-state disabled -width 26
     pack .ctop.top.bar.rightbut -side left -fill y
 
-    button .ctop.top.bar.findbut -text "Find" -command dofind
+    ttk::button .ctop.top.bar.findbut -text "Find" -command dofind
     pack .ctop.top.bar.findbut -side left
     set findstring {}
     set fstring .ctop.top.bar.findstring
     lappend entries $fstring
-    entry $fstring -width 30 -font $textfont -textvariable findstring
+    ttk::entry $fstring -width 30 -font $textfont -textvariable findstring
     pack $fstring -side left -expand 1 -fill x
     set findtype Exact
-    set findtypemenu [tk_optionMenu .ctop.top.bar.findtype \
+    set findtypemenu [ttk::optionMenu .ctop.top.bar.findtype \
 			  findtype Exact IgnCase Regexp]
     set findloc "All fields"
-    tk_optionMenu .ctop.top.bar.findloc findloc "All fields" Headline \
+    ttk::optionMenu .ctop.top.bar.findloc findloc "All fields" Headline \
 	Comments Author Committer Files Pickaxe
     pack .ctop.top.bar.findloc -side right
     pack .ctop.top.bar.findtype -side right
@@ -604,14 +645,14 @@
 
     panedwindow .ctop.cdet -orient horizontal
     .ctop add .ctop.cdet
-    frame .ctop.cdet.left
+    ttk::frame .ctop.cdet.left
     set ctext .ctop.cdet.left.ctext
     text $ctext -fg $fgcolor -bg $bgcolor -state disabled -font $textfont \
 	-width $geometry(ctextw) -height $geometry(ctexth) \
 	-yscrollcommand ".ctop.cdet.left.sb set" \
 	-xscrollcommand ".ctop.cdet.left.hb set" -wrap none
-    scrollbar .ctop.cdet.left.sb -command "$ctext yview"
-    scrollbar .ctop.cdet.left.hb -orient horizontal -command "$ctext xview"
+    ttk::scrollbar .ctop.cdet.left.sb -command "$ctext yview"
+    ttk::scrollbar .ctop.cdet.left.hb -orient horizontal -command "$ctext xview"
     pack .ctop.cdet.left.sb -side right -fill y
     pack .ctop.cdet.left.hb -side bottom -fill x
     pack $ctext -side left -fill both -expand 1
@@ -643,12 +684,12 @@
 	$ctext tag conf found -back yellow
     }
 
-    frame .ctop.cdet.right
+    ttk::frame .ctop.cdet.right
     set cflist .ctop.cdet.right.cfiles
     listbox $cflist -fg $fgcolor -bg $bgcolor \
         -selectmode extended -width $geometry(cflistw) \
 	-yscrollcommand ".ctop.cdet.right.sb set"
-    scrollbar .ctop.cdet.right.sb -command "$cflist yview"
+    ttk::scrollbar .ctop.cdet.right.sb -command "$cflist yview"
     pack .ctop.cdet.right.sb -side right -fill y
     pack $cflist -side left -fill both -expand 1
     .ctop.cdet add .ctop.cdet.right
@@ -901,7 +942,7 @@
 Use and redistribute under the terms of the GNU General Public License} \
 	    -justify center -aspect 400
     pack $w.m -side top -fill x -padx 20 -pady 20
-    button $w.ok -text Close -command "destroy $w"
+    ttk::button $w.ok -text Close -command "destroy $w"
     pack $w.ok -side bottom
 }
 
@@ -1219,7 +1260,7 @@
         } else {
             # draw a head or other ref
             if {[incr nheads -1] >= 0} {
-                set col green
+                set col "#00ff00"
             } else {
                 set col "#ddddff"
             }
@@ -2417,8 +2458,7 @@
     set currentid $id
     $sha1entry delete 0 end
     $sha1entry insert 0 $id
-    $sha1entry selection from 0
-    $sha1entry selection to end
+    $sha1entry selection range 0 end
 
     $ctext conf -state normal
     $ctext delete 0.0 end
@@ -3675,36 +3715,36 @@
     set patchtop $top
     catch {destroy $top}
     toplevel $top
-    label $top.title -text "Generate patch"
+    ttk::label $top.title -text "Generate patch"
     grid $top.title - -pady 10
-    label $top.from -text "From:"
-    entry $top.fromsha1 -width 40 -relief flat
+    ttk::label $top.from -text "From:"
+    ttk::entry $top.fromsha1 -width 40
     $top.fromsha1 insert 0 $oldid
     $top.fromsha1 conf -state readonly
     grid $top.from $top.fromsha1 -sticky w
-    entry $top.fromhead -width 60 -relief flat
+    ttk::entry $top.fromhead -width 60
     $top.fromhead insert 0 $oldhead
     $top.fromhead conf -state readonly
     grid x $top.fromhead -sticky w
-    label $top.to -text "To:"
-    entry $top.tosha1 -width 40 -relief flat
+    ttk::label $top.to -text "To:"
+    ttk::entry $top.tosha1 -width 40
     $top.tosha1 insert 0 $newid
     $top.tosha1 conf -state readonly
     grid $top.to $top.tosha1 -sticky w
-    entry $top.tohead -width 60 -relief flat
+    ttk::entry $top.tohead -width 60
     $top.tohead insert 0 $newhead
     $top.tohead conf -state readonly
     grid x $top.tohead -sticky w
-    button $top.rev -text "Reverse" -command mkpatchrev -padx 5
+    ttk::button $top.rev -text "Reverse" -command mkpatchrev
     grid $top.rev x -pady 10
-    label $top.flab -text "Output file:"
-    entry $top.fname -width 60
+    ttk::label $top.flab -text "Output file:"
+    ttk::entry $top.fname -width 60
     $top.fname insert 0 [file normalize "patch$patchnum.patch"]
     incr patchnum
     grid $top.flab $top.fname -sticky w
-    frame $top.buts
-    button $top.buts.gen -text "Generate" -command mkpatchgo
-    button $top.buts.can -text "Cancel" -command mkpatchcan
+    ttk::frame $top.buts
+    ttk::button $top.buts.gen -text "Generate" -command mkpatchgo
+    ttk::button $top.buts.can -text "Cancel" -command mkpatchcan
     grid $top.buts.gen $top.buts.can
     grid columnconfigure $top.buts 0 -weight 1 -uniform a
     grid columnconfigure $top.buts 1 -weight 1 -uniform a
@@ -3755,23 +3795,23 @@
     set mktagtop $top
     catch {destroy $top}
     toplevel $top
-    label $top.title -text "Create tag"
+    ttk::label $top.title -text "Create tag"
     grid $top.title - -pady 10
-    label $top.id -text "ID:"
-    entry $top.sha1 -width 40 -relief flat
+    ttk::label $top.id -text "ID:"
+    ttk::entry $top.sha1 -width 40
     $top.sha1 insert 0 $rowmenuid
     $top.sha1 conf -state readonly
     grid $top.id $top.sha1 -sticky w
-    entry $top.head -width 60 -relief flat
+    ttk::entry $top.head -width 60
     $top.head insert 0 [lindex $commitinfo($rowmenuid) 0]
     $top.head conf -state readonly
     grid x $top.head -sticky w
-    label $top.tlab -text "Tag name:"
-    entry $top.tag -width 60
+    ttk::label $top.tlab -text "Tag name:"
+    ttk::entry $top.tag -width 60
     grid $top.tlab $top.tag -sticky w
-    frame $top.buts
-    button $top.buts.gen -text "Create" -command mktaggo
-    button $top.buts.can -text "Cancel" -command mktagcan
+    ttk::frame $top.buts
+    ttk::button $top.buts.gen -text "Create" -command mktaggo
+    ttk::button $top.buts.can -text "Cancel" -command mktagcan
     grid $top.buts.gen $top.buts.can
     grid columnconfigure $top.buts 0 -weight 1 -uniform a
     grid columnconfigure $top.buts 1 -weight 1 -uniform a
@@ -3835,27 +3875,27 @@
     set wrcomtop $top
     catch {destroy $top}
     toplevel $top
-    label $top.title -text "Write commit to file"
+    ttk::label $top.title -text "Write commit to file"
     grid $top.title - -pady 10
-    label $top.id -text "ID:"
-    entry $top.sha1 -width 40 -relief flat
+    ttk::label $top.id -text "ID:"
+    ttk::entry $top.sha1 -width 40
     $top.sha1 insert 0 $rowmenuid
     $top.sha1 conf -state readonly
     grid $top.id $top.sha1 -sticky w
-    entry $top.head -width 60 -relief flat
+    ttk::entry $top.head -width 60
     $top.head insert 0 [lindex $commitinfo($rowmenuid) 0]
     $top.head conf -state readonly
     grid x $top.head -sticky w
-    label $top.clab -text "Command:"
-    entry $top.cmd -width 60 -textvariable wrcomcmd
+    ttk::label $top.clab -text "Command:"
+    ttk::entry $top.cmd -width 60 -textvariable wrcomcmd
     grid $top.clab $top.cmd -sticky w -pady 10
-    label $top.flab -text "Output file:"
-    entry $top.fname -width 60
+    ttk::label $top.flab -text "Output file:"
+    ttk::entry $top.fname -width 60
     $top.fname insert 0 [file normalize "commit-[string range $rowmenuid 0 6]"]
     grid $top.flab $top.fname -sticky w
-    frame $top.buts
-    button $top.buts.gen -text "Write" -command wrcomgo
-    button $top.buts.can -text "Cancel" -command wrcomcan
+    ttk::frame $top.buts
+    ttk::button $top.buts.gen -text "Write" -command wrcomgo
+    ttk::button $top.buts.can -text "Cancel" -command wrcomcan
     grid $top.buts.gen $top.buts.can
     grid columnconfigure $top.buts 0 -weight 1 -uniform a
     grid columnconfigure $top.buts 1 -weight 1 -uniform a
--- a/hgext/convert/__init__.py	Wed Nov 28 17:53:52 2012 -0600
+++ b/hgext/convert/__init__.py	Wed Nov 28 18:08:51 2012 -0600
@@ -191,6 +191,10 @@
         branch indicated in the regex as the second parent of the
         changeset. Default is ``{{mergefrombranch ([-\\w]+)}}``
 
+    :convert.localtimezone: use local time (as determined by the TZ
+        environment variable) for changeset date/times. The default
+        is False (use UTC).
+
     :hook.cvslog: Specify a Python function to be called at the end of
         gathering the CVS log. The function is passed a list with the
         log entries, and can modify the entries in-place, or add or
@@ -231,6 +235,10 @@
     :convert.svn.trunk: specify the name of the trunk branch. The
         default is ``trunk``.
 
+    :convert.localtimezone: use local time (as determined by the TZ
+        environment variable) for changeset date/times. The default
+        is False (use UTC).
+
     Source history can be retrieved starting at a specific revision,
     instead of being integrally converted. Only single branch
     conversions are supported.
--- a/hgext/convert/common.py	Wed Nov 28 17:53:52 2012 -0600
+++ b/hgext/convert/common.py	Wed Nov 28 18:08:51 2012 -0600
@@ -5,7 +5,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 base64, errno, subprocess, os
+import base64, errno, subprocess, os, datetime
 import cPickle as pickle
 from mercurial import util
 from mercurial.i18n import _
@@ -446,3 +446,10 @@
         if e.errno != errno.ENOENT:
             raise
     return m
+
+def makedatetimestamp(t):
+    """Like util.makedate() but for time t instead of current time"""
+    delta = (datetime.datetime.utcfromtimestamp(t) -
+             datetime.datetime.fromtimestamp(t))
+    tz = delta.days * 86400 + delta.seconds
+    return t, tz
--- a/hgext/convert/cvs.py	Wed Nov 28 17:53:52 2012 -0600
+++ b/hgext/convert/cvs.py	Wed Nov 28 18:08:51 2012 -0600
@@ -11,6 +11,7 @@
 from mercurial.i18n import _
 
 from common import NoRepo, commit, converter_source, checktool
+from common import makedatetimestamp
 import cvsps
 
 class convert_cvs(converter_source):
@@ -70,6 +71,8 @@
                 cs.author = self.recode(cs.author)
                 self.lastbranch[cs.branch] = id
                 cs.comment = self.recode(cs.comment)
+                if self.ui.configbool('convert', 'localtimezone'):
+                    cs.date = makedatetimestamp(cs.date[0])
                 date = util.datestr(cs.date, '%Y-%m-%d %H:%M:%S %1%2')
                 self.tags.update(dict.fromkeys(cs.tags, id))
 
--- a/hgext/convert/cvsps.py	Wed Nov 28 17:53:52 2012 -0600
+++ b/hgext/convert/cvsps.py	Wed Nov 28 18:08:51 2012 -0600
@@ -801,22 +801,22 @@
             # Note: trailing spaces on several lines here are needed to have
             #       bug-for-bug compatibility with cvsps.
             ui.write('---------------------\n')
-            ui.write('PatchSet %d \n' % cs.id)
-            ui.write('Date: %s\n' % util.datestr(cs.date,
-                                                 '%Y/%m/%d %H:%M:%S %1%2'))
-            ui.write('Author: %s\n' % cs.author)
-            ui.write('Branch: %s\n' % (cs.branch or 'HEAD'))
-            ui.write('Tag%s: %s \n' % (['', 's'][len(cs.tags) > 1],
-                                  ','.join(cs.tags) or '(none)'))
+            ui.write(('PatchSet %d \n' % cs.id))
+            ui.write(('Date: %s\n' % util.datestr(cs.date,
+                                                 '%Y/%m/%d %H:%M:%S %1%2')))
+            ui.write(('Author: %s\n' % cs.author))
+            ui.write(('Branch: %s\n' % (cs.branch or 'HEAD')))
+            ui.write(('Tag%s: %s \n' % (['', 's'][len(cs.tags) > 1],
+                                  ','.join(cs.tags) or '(none)')))
             branchpoints = getattr(cs, 'branchpoints', None)
             if branchpoints:
-                ui.write('Branchpoints: %s \n' % ', '.join(branchpoints))
+                ui.write(('Branchpoints: %s \n' % ', '.join(branchpoints)))
             if opts["parents"] and cs.parents:
                 if len(cs.parents) > 1:
-                    ui.write('Parents: %s\n' %
-                             (','.join([str(p.id) for p in cs.parents])))
+                    ui.write(('Parents: %s\n' %
+                             (','.join([str(p.id) for p in cs.parents]))))
                 else:
-                    ui.write('Parent: %d\n' % cs.parents[0].id)
+                    ui.write(('Parent: %d\n' % cs.parents[0].id))
 
             if opts["ancestors"]:
                 b = cs.branch
@@ -825,11 +825,11 @@
                     b, c = ancestors[b]
                     r.append('%s:%d:%d' % (b or "HEAD", c, branches[b]))
                 if r:
-                    ui.write('Ancestors: %s\n' % (','.join(r)))
+                    ui.write(('Ancestors: %s\n' % (','.join(r))))
 
-            ui.write('Log:\n')
+            ui.write(('Log:\n'))
             ui.write('%s\n\n' % cs.comment)
-            ui.write('Members: \n')
+            ui.write(('Members: \n'))
             for f in cs.entries:
                 fn = f.file
                 if fn.startswith(opts["prefix"]):
--- a/hgext/convert/git.py	Wed Nov 28 17:53:52 2012 -0600
+++ b/hgext/convert/git.py	Wed Nov 28 18:08:51 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	Wed Nov 28 17:53:52 2012 -0600
+++ b/hgext/convert/hg.py	Wed Nov 28 18:08:51 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/convert/subversion.py	Wed Nov 28 17:53:52 2012 -0600
+++ b/hgext/convert/subversion.py	Wed Nov 28 18:08:51 2012 -0600
@@ -18,6 +18,7 @@
 
 from common import NoRepo, MissingTool, commit, encodeargs, decodeargs
 from common import commandline, converter_source, converter_sink, mapfile
+from common import makedatetimestamp
 
 try:
     from svn.core import SubversionException, Pool
@@ -802,6 +803,8 @@
             # ISO-8601 conformant
             # '2007-01-04T17:35:00.902377Z'
             date = util.parsedate(date[:19] + " UTC", ["%Y-%m-%dT%H:%M:%S"])
+            if self.ui.configbool('convert', 'localtimezone'):
+                date = makedatetimestamp(date[0])
 
             log = message and self.recode(message) or ''
             author = author and self.recode(author) or ''
--- a/hgext/eol.py	Wed Nov 28 17:53:52 2012 -0600
+++ b/hgext/eol.py	Wed Nov 28 18:08:51 2012 -0600
@@ -307,7 +307,7 @@
                 eolmtime = 0
 
             if eolmtime > cachemtime:
-                ui.debug("eol: detected change in .hgeol\n")
+                self.ui.debug("eol: detected change in .hgeol\n")
                 wlock = None
                 try:
                     wlock = self.wlock()
--- a/hgext/hgk.py	Wed Nov 28 17:53:52 2012 -0600
+++ b/hgext/hgk.py	Wed Nov 28 18:08:51 2012 -0600
@@ -98,9 +98,9 @@
     if ctx is None:
         ctx = repo[n]
     # use ctx.node() instead ??
-    ui.write("tree %s\n" % short(ctx.changeset()[0]))
+    ui.write(("tree %s\n" % short(ctx.changeset()[0])))
     for p in ctx.parents():
-        ui.write("parent %s\n" % p)
+        ui.write(("parent %s\n" % p))
 
     date = ctx.date()
     description = ctx.description().replace("\0", "")
@@ -108,12 +108,13 @@
     if lines and lines[-1].startswith('committer:'):
         committer = lines[-1].split(': ')[1].rstrip()
     else:
-        committer = ctx.user()
+        committer = ""
 
-    ui.write("author %s %s %s\n" % (ctx.user(), int(date[0]), date[1]))
-    ui.write("committer %s %s %s\n" % (committer, int(date[0]), date[1]))
-    ui.write("revision %d\n" % ctx.rev())
-    ui.write("branch %s\n\n" % ctx.branch())
+    ui.write(("author %s %s %s\n" % (ctx.user(), int(date[0]), date[1])))
+    if committer != '':
+        ui.write(("committer %s %s %s\n" % (committer, int(date[0]), date[1])))
+    ui.write(("revision %d\n" % ctx.rev()))
+    ui.write(("branch %s\n\n" % ctx.branch()))
 
     if prefix != "":
         ui.write("%s%s\n" % (prefix,
@@ -302,7 +303,7 @@
 def config(ui, repo, **opts):
     """print extension options"""
     def writeopt(name, value):
-        ui.write('k=%s\nv=%s\n' % (name, value))
+        ui.write(('k=%s\nv=%s\n' % (name, value)))
 
     writeopt('vdiff', ui.config('hgk', 'vdiff', ''))
 
--- a/hgext/histedit.py	Wed Nov 28 17:53:52 2012 -0600
+++ b/hgext/histedit.py	Wed Nov 28 18:08:51 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	Wed Nov 28 17:53:52 2012 -0600
+++ b/hgext/mq.py	Wed Nov 28 18:08:51 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,
@@ -3435,7 +3436,7 @@
                             outapplied.pop()
                 # looking for pushed and shared changeset
                 for node in outapplied:
-                    if repo[node].phase() < phases.secret:
+                    if self[node].phase() < phases.secret:
                         raise util.Abort(_('source has mq patches applied'))
                 # no non-secret patches pushed
             super(mqrepo, self).checkpush(force, revs)
--- a/hgext/patchbomb.py	Wed Nov 28 17:53:52 2012 -0600
+++ b/hgext/patchbomb.py	Wed Nov 28 18:08:51 2012 -0600
@@ -474,11 +474,11 @@
 
     if opts.get('diffstat') or opts.get('confirm'):
         ui.write(_('\nFinal summary:\n\n'))
-        ui.write('From: %s\n' % sender)
+        ui.write(('From: %s\n' % sender))
         for addr in showaddrs:
             ui.write('%s\n' % addr)
         for m, subj, ds in msgs:
-            ui.write('Subject: %s\n' % subj)
+            ui.write(('Subject: %s\n' % subj))
             if ds:
                 ui.write(ds)
         ui.write('\n')
--- a/hgext/rebase.py	Wed Nov 28 17:53:52 2012 -0600
+++ b/hgext/rebase.py	Wed Nov 28 18:08:51 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):
@@ -655,9 +656,12 @@
     #
     # The actual abort is handled by `defineparents`
     if len(root.parents()) <= 1:
-        # (strict) ancestors of <root> not ancestors of <dest>
-        detachset = repo.revs('::%d - ::%d - %d', root, commonbase, root)
+        # ancestors of <root> not ancestors of <dest>
+        detachset = repo.changelog.findmissingrevs([commonbase.rev()],
+                                                   [root.rev()])
         state.update(dict.fromkeys(detachset, nullmerge))
+        # detachset can have root, and we definitely want to rebase that
+        state[root.rev()] = nullrev
     return repo['.'].rev(), dest.rev(), state
 
 def clearrebased(ui, repo, state, collapsedas=None):
--- a/mercurial/ancestor.py	Wed Nov 28 17:53:52 2012 -0600
+++ b/mercurial/ancestor.py	Wed Nov 28 18:08:51 2012 -0600
@@ -6,6 +6,7 @@
 # GNU General Public License version 2 or any later version.
 
 import heapq
+from node import nullrev
 
 def ancestor(a, b, pfunc):
     """
@@ -89,3 +90,166 @@
                 gx = x.next()
     except StopIteration:
         return None
+
+def missingancestors(revs, bases, pfunc):
+    """Return all the ancestors of revs that are not ancestors of bases.
+
+    This may include elements from revs.
+
+    Equivalent to the revset (::revs - ::bases). Revs are returned in
+    revision number order, which is a topological order.
+
+    revs and bases should both be iterables. pfunc must return a list of
+    parent revs for a given revs.
+
+    graph is a dict of child->parent adjacency lists for this graph:
+    o  13
+    |
+    | o  12
+    | |
+    | | o    11
+    | | |\
+    | | | | o  10
+    | | | | |
+    | o---+ |  9
+    | | | | |
+    o | | | |  8
+     / / / /
+    | | o |  7
+    | | | |
+    o---+ |  6
+     / / /
+    | | o  5
+    | |/
+    | o  4
+    | |
+    o |  3
+    | |
+    | o  2
+    |/
+    o  1
+    |
+    o  0
+    >>> graph = {0: [-1], 1: [0], 2: [1], 3: [1], 4: [2], 5: [4], 6: [4],
+    ...          7: [4], 8: [-1], 9: [6, 7], 10: [5], 11: [3, 7], 12: [9],
+    ...          13: [8]}
+    >>> pfunc = graph.get
+
+    Empty revs
+    >>> missingancestors([], [1], pfunc)
+    []
+    >>> missingancestors([], [], pfunc)
+    []
+
+    If bases is empty, it's the same as if it were [nullrev]
+    >>> missingancestors([12], [], pfunc)
+    [0, 1, 2, 4, 6, 7, 9, 12]
+
+    Trivial case: revs == bases
+    >>> missingancestors([0], [0], pfunc)
+    []
+    >>> missingancestors([4, 5, 6], [6, 5, 4], pfunc)
+    []
+
+    With nullrev
+    >>> missingancestors([-1], [12], pfunc)
+    []
+    >>> missingancestors([12], [-1], pfunc)
+    [0, 1, 2, 4, 6, 7, 9, 12]
+
+    9 is a parent of 12. 7 is a parent of 9, so an ancestor of 12. 6 is an
+    ancestor of 12 but not of 7.
+    >>> missingancestors([12], [9], pfunc)
+    [12]
+    >>> missingancestors([9], [12], pfunc)
+    []
+    >>> missingancestors([12, 9], [7], pfunc)
+    [6, 9, 12]
+    >>> missingancestors([7, 6], [12], pfunc)
+    []
+
+    More complex cases
+    >>> missingancestors([10], [11, 12], pfunc)
+    [5, 10]
+    >>> missingancestors([11], [10], pfunc)
+    [3, 7, 11]
+    >>> missingancestors([11], [10, 12], pfunc)
+    [3, 11]
+    >>> missingancestors([12], [10], pfunc)
+    [6, 7, 9, 12]
+    >>> missingancestors([12], [11], pfunc)
+    [6, 9, 12]
+    >>> missingancestors([10, 11, 12], [13], pfunc)
+    [0, 1, 2, 3, 4, 5, 6, 7, 9, 10, 11, 12]
+    >>> missingancestors([13], [10, 11, 12], pfunc)
+    [8, 13]
+    """
+
+    revsvisit = set(revs)
+    basesvisit = set(bases)
+    if not revsvisit:
+        return []
+    if not basesvisit:
+        basesvisit.add(nullrev)
+    start = max(max(revsvisit), max(basesvisit))
+    bothvisit = revsvisit.intersection(basesvisit)
+    revsvisit.difference_update(bothvisit)
+    basesvisit.difference_update(bothvisit)
+    # At this point, we hold the invariants that:
+    # - revsvisit is the set of nodes we know are an ancestor of at least one
+    #   of the nodes in revs
+    # - basesvisit is the same for bases
+    # - bothvisit is the set of nodes we know are ancestors of at least one of
+    #   the nodes in revs and one of the nodes in bases
+    # - a node may be in none or one, but not more, of revsvisit, basesvisit
+    #   and bothvisit at any given time
+    # Now we walk down in reverse topo order, adding parents of nodes already
+    # visited to the sets while maintaining the invariants. When a node is
+    # found in both revsvisit and basesvisit, it is removed from them and
+    # added to bothvisit instead. When revsvisit becomes empty, there are no
+    # more ancestors of revs that aren't also ancestors of bases, so exit.
+
+    missing = []
+    for curr in xrange(start, nullrev, -1):
+        if not revsvisit:
+            break
+
+        if curr in bothvisit:
+            bothvisit.remove(curr)
+            # curr's parents might have made it into revsvisit or basesvisit
+            # through another path
+            for p in pfunc(curr):
+                revsvisit.discard(p)
+                basesvisit.discard(p)
+                bothvisit.add(p)
+            continue
+
+        # curr will never be in both revsvisit and basesvisit, since if it
+        # were it'd have been pushed to bothvisit
+        if curr in revsvisit:
+            missing.append(curr)
+            thisvisit = revsvisit
+            othervisit = basesvisit
+        elif curr in basesvisit:
+            thisvisit = basesvisit
+            othervisit = revsvisit
+        else:
+            # not an ancestor of revs or bases: ignore
+            continue
+
+        thisvisit.remove(curr)
+        for p in pfunc(curr):
+            if p == nullrev:
+                pass
+            elif p in othervisit or p in bothvisit:
+                # p is implicitly in thisvisit. This means p is or should be
+                # in bothvisit
+                revsvisit.discard(p)
+                basesvisit.discard(p)
+                bothvisit.add(p)
+            else:
+                # visit later
+                thisvisit.add(p)
+
+    missing.reverse()
+    return missing
--- a/mercurial/bookmarks.py	Wed Nov 28 17:53:52 2012 -0600
+++ b/mercurial/bookmarks.py	Wed Nov 28 18:08:51 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	Wed Nov 28 17:53:52 2012 -0600
+++ b/mercurial/bundlerepo.py	Wed Nov 28 18:08:51 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	Wed Nov 28 17:53:52 2012 -0600
+++ b/mercurial/cmdutil.py	Wed Nov 28 18:08:51 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	Wed Nov 28 17:53:52 2012 -0600
+++ b/mercurial/commands.py	Wed Nov 28 18:08:51 2012 -0600
@@ -549,6 +549,10 @@
           hg bisect --skip
           hg bisect --skip 23
 
+      - skip all revisions that do not touch directories ``foo`` or ``bar``
+
+          hg bisect --skip '!( file("path:foo") & file("path:bar") )'
+
       - forget the current bisection::
 
           hg bisect --reset
@@ -821,7 +825,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 +838,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 +852,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
@@ -1322,11 +1326,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'):
@@ -1513,7 +1518,7 @@
         ui.progress(_('building'), id, unit=_('revisions'), total=total)
         for type, data in dagparser.parsedag(text):
             if type == 'n':
-                ui.note('node %s\n' % str(data))
+                ui.note(('node %s\n' % str(data)))
                 id, ps = data
 
                 files = []
@@ -1574,10 +1579,10 @@
                 at = id
             elif type == 'l':
                 id, name = data
-                ui.note('tag %s\n' % name)
+                ui.note(('tag %s\n' % name))
                 tags.append("%s %s\n" % (hex(repo.changelog.node(id)), name))
             elif type == 'a':
-                ui.note('branch %s\n' % data)
+                ui.note(('branch %s\n' % data))
                 atbranch = data
             ui.progress(_('building'), id, unit=_('revisions'), total=total)
         tr.close()
@@ -1595,7 +1600,7 @@
     try:
         gen = changegroup.readbundle(f, bundlepath)
         if all:
-            ui.write("format: id, p1, p2, cset, delta base, len(delta)\n")
+            ui.write(("format: id, p1, p2, cset, delta base, len(delta)\n"))
 
             def showchunks(named):
                 ui.write("\n%s\n" % named)
@@ -1787,11 +1792,11 @@
         d = util.parsedate(date, util.extendeddateformats)
     else:
         d = util.parsedate(date)
-    ui.write("internal: %s %s\n" % d)
-    ui.write("standard: %s\n" % util.datestr(d))
+    ui.write(("internal: %s %s\n") % d)
+    ui.write(("standard: %s\n") % util.datestr(d))
     if range:
         m = util.matchdate(range)
-        ui.write("match: %s\n" % m(d[0]))
+        ui.write(("match: %s\n") % m(d[0]))
 
 @command('debugdiscovery',
     [('', 'old', None, _('use old-style discovery')),
@@ -1821,7 +1826,7 @@
                                                                 force=True)
             common = set(common)
             if not opts.get('nonheads'):
-                ui.write("unpruned common: %s\n" % " ".join([short(n)
+                ui.write(("unpruned common: %s\n") % " ".join([short(n)
                                                             for n in common]))
                 dag = dagutil.revlogdag(repo.changelog)
                 all = dag.ancestorset(dag.internalizeall(common))
@@ -1831,11 +1836,11 @@
         common = set(common)
         rheads = set(hds)
         lheads = set(repo.heads())
-        ui.write("common heads: %s\n" % " ".join([short(n) for n in common]))
+        ui.write(("common heads: %s\n") % " ".join([short(n) for n in common]))
         if lheads <= common:
-            ui.write("local is subset\n")
+            ui.write(("local is subset\n"))
         elif rheads <= common:
-            ui.write("remote is subset\n")
+            ui.write(("remote is subset\n"))
 
     serverlogs = opts.get('serverlog')
     if serverlogs:
@@ -1879,9 +1884,9 @@
 def debugfsinfo(ui, path = "."):
     """show information detected about current filesystem"""
     util.writefile('.debugfsinfo', '')
-    ui.write('exec: %s\n' % (util.checkexec(path) and 'yes' or 'no'))
-    ui.write('symlink: %s\n' % (util.checklink(path) and 'yes' or 'no'))
-    ui.write('case-sensitive: %s\n' % (util.checkcase('.debugfsinfo')
+    ui.write(('exec: %s\n') % (util.checkexec(path) and 'yes' or 'no'))
+    ui.write(('symlink: %s\n') % (util.checklink(path) and 'yes' or 'no'))
+    ui.write(('case-sensitive: %s\n') % (util.checkcase('.debugfsinfo')
                                 and 'yes' or 'no'))
     os.unlink('.debugfsinfo')
 
@@ -1979,7 +1984,7 @@
             r = filelog
     if not r:
         r = revlog.revlog(scmutil.opener(os.getcwd(), audit=False), file_)
-    ui.write("digraph G {\n")
+    ui.write(("digraph G {\n"))
     for i in r:
         node = r.node(i)
         pp = r.parents(node)
@@ -2325,52 +2330,54 @@
     def pcfmt(value, total):
         return (value, 100 * float(value) / total)
 
-    ui.write('format : %d\n' % format)
-    ui.write('flags  : %s\n' % ', '.join(flags))
+    ui.write(('format : %d\n') % format)
+    ui.write(('flags  : %s\n') % ', '.join(flags))
 
     ui.write('\n')
     fmt = pcfmtstr(totalsize)
     fmt2 = dfmtstr(totalsize)
-    ui.write('revisions     : ' + fmt2 % numrevs)
-    ui.write('    merges    : ' + fmt % pcfmt(nummerges, numrevs))
-    ui.write('    normal    : ' + fmt % pcfmt(numrevs - nummerges, numrevs))
-    ui.write('revisions     : ' + fmt2 % numrevs)
-    ui.write('    full      : ' + fmt % pcfmt(numfull, numrevs))
-    ui.write('    deltas    : ' + fmt % pcfmt(numdeltas, numrevs))
-    ui.write('revision size : ' + fmt2 % totalsize)
-    ui.write('    full      : ' + fmt % pcfmt(fulltotal, totalsize))
-    ui.write('    deltas    : ' + fmt % pcfmt(deltatotal, totalsize))
+    ui.write(('revisions     : ') + fmt2 % numrevs)
+    ui.write(('    merges    : ') + fmt % pcfmt(nummerges, numrevs))
+    ui.write(('    normal    : ') + fmt % pcfmt(numrevs - nummerges, numrevs))
+    ui.write(('revisions     : ') + fmt2 % numrevs)
+    ui.write(('    full      : ') + fmt % pcfmt(numfull, numrevs))
+    ui.write(('    deltas    : ') + fmt % pcfmt(numdeltas, numrevs))
+    ui.write(('revision size : ') + fmt2 % totalsize)
+    ui.write(('    full      : ') + fmt % pcfmt(fulltotal, totalsize))
+    ui.write(('    deltas    : ') + fmt % pcfmt(deltatotal, totalsize))
 
     ui.write('\n')
     fmt = dfmtstr(max(avgchainlen, compratio))
-    ui.write('avg chain length  : ' + fmt % avgchainlen)
-    ui.write('compression ratio : ' + fmt % compratio)
+    ui.write(('avg chain length  : ') + fmt % avgchainlen)
+    ui.write(('compression ratio : ') + fmt % compratio)
 
     if format > 0:
         ui.write('\n')
-        ui.write('uncompressed data size (min/max/avg) : %d / %d / %d\n'
+        ui.write(('uncompressed data size (min/max/avg) : %d / %d / %d\n')
                  % tuple(datasize))
-    ui.write('full revision size (min/max/avg)     : %d / %d / %d\n'
+    ui.write(('full revision size (min/max/avg)     : %d / %d / %d\n')
              % tuple(fullsize))
-    ui.write('delta size (min/max/avg)             : %d / %d / %d\n'
+    ui.write(('delta size (min/max/avg)             : %d / %d / %d\n')
              % tuple(deltasize))
 
     if numdeltas > 0:
         ui.write('\n')
         fmt = pcfmtstr(numdeltas)
         fmt2 = pcfmtstr(numdeltas, 4)
-        ui.write('deltas against prev  : ' + fmt % pcfmt(numprev, numdeltas))
+        ui.write(('deltas against prev  : ') + fmt % pcfmt(numprev, numdeltas))
         if numprev > 0:
-            ui.write('    where prev = p1  : ' + fmt2 % pcfmt(nump1prev,
+            ui.write(('    where prev = p1  : ') + fmt2 % pcfmt(nump1prev,
                                                               numprev))
-            ui.write('    where prev = p2  : ' + fmt2 % pcfmt(nump2prev,
+            ui.write(('    where prev = p2  : ') + fmt2 % pcfmt(nump2prev,
                                                               numprev))
-            ui.write('    other            : ' + fmt2 % pcfmt(numoprev,
+            ui.write(('    other            : ') + fmt2 % pcfmt(numoprev,
                                                               numprev))
         if gdelta:
-            ui.write('deltas against p1    : ' + fmt % pcfmt(nump1, numdeltas))
-            ui.write('deltas against p2    : ' + fmt % pcfmt(nump2, numdeltas))
-            ui.write('deltas against other : ' + fmt % pcfmt(numother,
+            ui.write(('deltas against p1    : ')
+                     + fmt % pcfmt(nump1, numdeltas))
+            ui.write(('deltas against p2    : ')
+                     + fmt % pcfmt(nump2, numdeltas))
+            ui.write(('deltas against other : ') + fmt % pcfmt(numother,
                                                              numdeltas))
 
 @command('debugrevspec', [], ('REVSPEC'))
@@ -2448,9 +2455,9 @@
 def debugsub(ui, repo, rev=None):
     ctx = scmutil.revsingle(repo, rev, None)
     for k, v in sorted(ctx.substate.items()):
-        ui.write('path %s\n' % k)
-        ui.write(' source   %s\n' % v[0])
-        ui.write(' revision %s\n' % v[1])
+        ui.write(('path %s\n') % k)
+        ui.write((' source   %s\n') % v[0])
+        ui.write((' revision %s\n') % v[1])
 
 @command('debugwalk', walkopts, _('[OPTION]... [FILE]...'))
 def debugwalk(ui, repo, *pats, **opts):
@@ -4207,6 +4214,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"))
@@ -4224,7 +4234,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:
@@ -4233,14 +4245,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')),
@@ -4666,11 +4681,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
 
@@ -5427,17 +5443,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')
@@ -5743,7 +5758,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
@@ -5752,27 +5767,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	Wed Nov 28 17:53:52 2012 -0600
+++ b/mercurial/formatter.py	Wed Nov 28 18:08:51 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/help/config.txt	Wed Nov 28 17:53:52 2012 -0600
+++ b/mercurial/help/config.txt	Wed Nov 28 18:08:51 2012 -0600
@@ -1295,6 +1295,10 @@
     (DEPRECATED) Whether to allow .zip downloading of repository
     revisions. Default is False. This feature creates temporary files.
 
+``archivesubrepos``
+    Whether to recurse into subrepositories when archiving. Default is
+    False.
+
 ``baseurl``
     Base URL to use when publishing URLs in other locations, so
     third-party tools like email notification hooks can construct
--- a/mercurial/hg.py	Wed Nov 28 17:53:52 2012 -0600
+++ b/mercurial/hg.py	Wed Nov 28 18:08:51 2012 -0600
@@ -171,11 +171,14 @@
     r = repository(ui, root)
 
     default = srcrepo.ui.config('paths', 'default')
-    if default:
-        fp = r.opener("hgrc", "w", text=True)
-        fp.write("[paths]\n")
-        fp.write("default = %s\n" % default)
-        fp.close()
+    if not default:
+        # set default to source for being able to clone subrepos
+        default = os.path.abspath(util.urllocalpath(origsource))
+    fp = r.opener("hgrc", "w", text=True)
+    fp.write("[paths]\n")
+    fp.write("default = %s\n" % default)
+    fp.close()
+    r.ui.setconfig('paths', 'default', default)
 
     if update:
         r.ui.status(_("updating working directory\n"))
@@ -391,14 +394,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/hgweb/webcommands.py	Wed Nov 28 17:53:52 2012 -0600
+++ b/mercurial/hgweb/webcommands.py	Wed Nov 28 18:08:51 2012 -0600
@@ -14,6 +14,7 @@
 from common import HTTP_OK, HTTP_FORBIDDEN, HTTP_NOT_FOUND
 from mercurial import graphmod, patch
 from mercurial import help as helpmod
+from mercurial import scmutil
 from mercurial.i18n import _
 
 # __all__ is populated with the allowed commands. Be sure to add to it if
@@ -799,7 +800,11 @@
         headers.append(('Content-Encoding', encoding))
     req.header(headers)
     req.respond(HTTP_OK)
-    archival.archive(web.repo, req, cnode, artype, prefix=name)
+
+    ctx = webutil.changectx(web.repo, req)
+    archival.archive(web.repo, req, cnode, artype, prefix=name,
+                     matchfn=scmutil.match(ctx, []),
+                     subrepos=web.configbool("web", "archivesubrepos"))
     return []
 
 
--- a/mercurial/localrepo.py	Wed Nov 28 17:53:52 2012 -0600
+++ b/mercurial/localrepo.py	Wed Nov 28 18:08:51 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/manifest.py	Wed Nov 28 17:53:52 2012 -0600
+++ b/mercurial/manifest.py	Wed Nov 28 18:08:51 2012 -0600
@@ -117,15 +117,23 @@
         # apply the changes collected during the bisect loop to our addlist
         # return a delta suitable for addrevision
         def addlistdelta(addlist, x):
-            # start from the bottom up
-            # so changes to the offsets don't mess things up.
-            for start, end, content in reversed(x):
+            # for large addlist arrays, building a new array is cheaper
+            # than repeatedly modifying the existing one
+            currentposition = 0
+            newaddlist = array.array('c')
+
+            for start, end, content in x:
+                newaddlist += addlist[currentposition:start]
                 if content:
-                    addlist[start:end] = array.array('c', content)
-                else:
-                    del addlist[start:end]
-            return "".join(struct.pack(">lll", start, end, len(content))
+                    newaddlist += array.array('c', content)
+
+                currentposition = end
+
+            newaddlist += addlist[currentposition:]
+
+            deltatext = "".join(struct.pack(">lll", start, end, len(content))
                            + content for start, end, content in x)
+            return deltatext, newaddlist
 
         def checkforbidden(l):
             for f in l:
@@ -194,7 +202,8 @@
             if dstart is not None:
                 delta.append([dstart, dend, "".join(dline)])
             # apply the delta to the addlist, and get a delta for addrevision
-            cachedelta = (self.rev(p1), addlistdelta(addlist, delta))
+            deltatext, addlist = addlistdelta(addlist, delta)
+            cachedelta = (self.rev(p1), deltatext)
             arraytext = addlist
             text = util.buffer(arraytext)
 
--- a/mercurial/mdiff.py	Wed Nov 28 17:53:52 2012 -0600
+++ b/mercurial/mdiff.py	Wed Nov 28 18:08:51 2012 -0600
@@ -7,7 +7,7 @@
 
 from i18n import _
 import bdiff, mpatch, util
-import re, struct
+import re, struct, base85, zlib
 
 def splitnewlines(text):
     '''like str.splitlines, but only split on newlines.'''
@@ -142,20 +142,7 @@
             yield s, type
         yield s1, '='
 
-def diffline(revs, a, b, opts):
-    parts = ['diff']
-    if opts.git:
-        parts.append('--git')
-    if revs and not opts.git:
-        parts.append(' '.join(["-r %s" % rev for rev in revs]))
-    if opts.git:
-        parts.append('a/%s' % a)
-        parts.append('b/%s' % b)
-    else:
-        parts.append(a)
-    return ' '.join(parts) + '\n'
-
-def unidiff(a, ad, b, bd, fn1, fn2, r=None, opts=defaultopts):
+def unidiff(a, ad, b, bd, fn1, fn2, opts=defaultopts):
     def datetag(date, fn=None):
         if not opts.git and not opts.nodates:
             return '\t%s\n' % date
@@ -206,9 +193,6 @@
         if l[ln][-1] != '\n':
             l[ln] += "\n\ No newline at end of file\n"
 
-    if r:
-        l.insert(0, diffline(r, fn1, fn2, opts))
-
     return "".join(l)
 
 # creates a headerless unified diff
@@ -314,6 +298,41 @@
         for x in yieldhunk(hunk):
             yield x
 
+def b85diff(to, tn):
+    '''print base85-encoded binary diff'''
+    def fmtline(line):
+        l = len(line)
+        if l <= 26:
+            l = chr(ord('A') + l - 1)
+        else:
+            l = chr(l - 26 + ord('a') - 1)
+        return '%c%s\n' % (l, base85.b85encode(line, True))
+
+    def chunk(text, csize=52):
+        l = len(text)
+        i = 0
+        while i < l:
+            yield text[i:i + csize]
+            i += csize
+
+    if to is None:
+        to = ''
+    if tn is None:
+        tn = ''
+
+    if to == tn:
+        return ''
+
+    # TODO: deltas
+    ret = []
+    ret.append('GIT binary patch\n')
+    ret.append('literal %s\n' % len(tn))
+    for l in chunk(zlib.compress(tn)):
+        ret.append(fmtline(l))
+    ret.append('\n')
+
+    return ''.join(ret)
+
 def patchtext(bin):
     pos = 0
     t = []
--- a/mercurial/patch.py	Wed Nov 28 17:53:52 2012 -0600
+++ b/mercurial/patch.py	Wed Nov 28 18:08:51 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.
 
-import cStringIO, email.Parser, os, errno, re
+import cStringIO, email.Parser, os, errno, re, posixpath
 import tempfile, zlib, shutil
 
 from i18n import _
@@ -1514,44 +1514,6 @@
     finally:
         fp.close()
 
-def b85diff(to, tn):
-    '''print base85-encoded binary diff'''
-    def gitindex(text):
-        if not text:
-            return hex(nullid)
-        l = len(text)
-        s = util.sha1('blob %d\0' % l)
-        s.update(text)
-        return s.hexdigest()
-
-    def fmtline(line):
-        l = len(line)
-        if l <= 26:
-            l = chr(ord('A') + l - 1)
-        else:
-            l = chr(l - 26 + ord('a') - 1)
-        return '%c%s\n' % (l, base85.b85encode(line, True))
-
-    def chunk(text, csize=52):
-        l = len(text)
-        i = 0
-        while i < l:
-            yield text[i:i + csize]
-            i += csize
-
-    tohash = gitindex(to)
-    tnhash = gitindex(tn)
-    if tohash == tnhash:
-        return ""
-
-    # TODO: deltas
-    ret = ['index %s..%s\nGIT binary patch\nliteral %s\n' %
-           (tohash, tnhash, len(tn))]
-    for l in chunk(zlib.compress(tn)):
-        ret.append(fmtline(l))
-    ret.append('\n')
-    return ''.join(ret)
-
 class GitDiffRequired(Exception):
     pass
 
@@ -1622,9 +1584,8 @@
         return []
 
     revs = None
-    if not repo.ui.quiet:
-        hexfunc = repo.ui.debugflag and hex or short
-        revs = [hexfunc(node) for node in [node1, node2] if node]
+    hexfunc = repo.ui.debugflag and hex or short
+    revs = [hexfunc(node) for node in [node1, node2] if node]
 
     copy = {}
     if opts.git or opts.upgrade:
@@ -1690,17 +1651,45 @@
     '''like diff(), but yields 2-tuples of (output, label) for ui.write()'''
     return difflabel(diff, *args, **kw)
 
-
-def _addmodehdr(header, omode, nmode):
-    if omode != nmode:
-        header.append('old mode %s\n' % omode)
-        header.append('new mode %s\n' % nmode)
-
 def trydiff(repo, revs, ctx1, ctx2, modified, added, removed,
             copy, getfilectx, opts, losedatafn, prefix):
 
     def join(f):
-        return os.path.join(prefix, f)
+        return posixpath.join(prefix, f)
+
+    def addmodehdr(header, omode, nmode):
+        if omode != nmode:
+            header.append('old mode %s\n' % omode)
+            header.append('new mode %s\n' % nmode)
+
+    def addindexmeta(meta, revs):
+        if opts.git:
+            i = len(revs)
+            if i==2:
+                meta.append('index %s..%s\n' % tuple(revs))
+            elif i==3:
+                meta.append('index %s,%s..%s\n' % tuple(revs))
+
+    def gitindex(text):
+        if not text:
+            return hex(nullid)
+        l = len(text)
+        s = util.sha1('blob %d\0' % l)
+        s.update(text)
+        return s.hexdigest()
+
+    def diffline(a, b, revs):
+        if opts.git:
+            line = 'diff --git a/%s b/%s\n' % (a, b)
+        elif not repo.ui.quiet:
+            if revs:
+                revinfo = ' '.join(["-r %s" % rev for rev in revs])
+                line = 'diff %s %s\n' % (revinfo, a)
+            else:
+                line = 'diff %s\n' % a
+        else:
+            line = ''
+        return line
 
     date1 = util.datestr(ctx1.date())
     man1 = ctx1.manifest()
@@ -1733,7 +1722,7 @@
                         else:
                             a = copyto[f]
                         omode = gitmode[man1.flags(a)]
-                        _addmodehdr(header, omode, mode)
+                        addmodehdr(header, omode, mode)
                         if a in removed and a not in gone:
                             op = 'rename'
                             gone.add(a)
@@ -1779,22 +1768,24 @@
                 nflag = ctx2.flags(f)
                 binary = util.binary(to) or util.binary(tn)
                 if opts.git:
-                    _addmodehdr(header, gitmode[oflag], gitmode[nflag])
+                    addmodehdr(header, gitmode[oflag], gitmode[nflag])
                     if binary:
                         dodiff = 'binary'
                 elif binary or nflag != oflag:
                     losedatafn(f)
-            if opts.git:
-                header.insert(0, mdiff.diffline(revs, join(a), join(b), opts))
 
         if dodiff:
+            if opts.git or revs:
+                header.insert(0, diffline(join(a), join(b), revs))
             if dodiff == 'binary':
-                text = b85diff(to, tn)
+                text = mdiff.b85diff(to, tn)
+                if text:
+                    addindexmeta(header, [gitindex(to), gitindex(tn)])
             else:
                 text = mdiff.unidiff(to, date1,
                                     # ctx2 date may be dynamic
                                     tn, util.datestr(ctx2.date()),
-                                    join(a), join(b), revs, opts=opts)
+                                    join(a), join(b), opts=opts)
             if header and (text or len(header) > 1):
                 yield ''.join(header)
             if text:
--- a/mercurial/repair.py	Wed Nov 28 17:53:52 2012 -0600
+++ b/mercurial/repair.py	Wed Nov 28 18:08:51 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/revlog.py	Wed Nov 28 17:53:52 2012 -0600
+++ b/mercurial/revlog.py	Wed Nov 28 18:08:51 2012 -0600
@@ -257,11 +257,14 @@
         return iter(xrange(len(self)))
     def revs(self, start=0, stop=None):
         """iterate over all rev in this revlog (from start to stop)"""
-        if stop is None:
-            stop = len(self)
+        step = 1
+        if stop is not None:
+            if start > stop:
+                step = -1
+            stop += step
         else:
-            stop += 1
-        return xrange(start, stop)
+            stop = len(self)
+        return xrange(start, stop, step)
 
     @util.propertycache
     def nodemap(self):
@@ -429,6 +432,29 @@
         missing.sort()
         return has, [self.node(r) for r in missing]
 
+    def findmissingrevs(self, common=None, heads=None):
+        """Return the revision numbers of the ancestors of heads that
+        are not ancestors of common.
+
+        More specifically, return a list of revision numbers corresponding to
+        nodes N such that every N satisfies the following constraints:
+
+          1. N is an ancestor of some node in 'heads'
+          2. N is not an ancestor of any node in 'common'
+
+        The list is sorted by revision number, meaning it is
+        topologically sorted.
+
+        'heads' and 'common' are both lists of revision numbers.  If heads is
+        not supplied, uses all of the revlog's heads.  If common is not
+        supplied, uses nullid."""
+        if common is None:
+            common = [nullrev]
+        if heads is None:
+            heads = self.headrevs()
+
+        return ancestor.missingancestors(heads, common, self.parentrevs)
+
     def findmissing(self, common=None, heads=None):
         """Return the ancestors of heads that are not ancestors of common.
 
@@ -444,8 +470,16 @@
         'heads' and 'common' are both lists of node IDs.  If heads is
         not supplied, uses all of the revlog's heads.  If common is not
         supplied, uses nullid."""
-        _common, missing = self.findcommonmissing(common, heads)
-        return missing
+        if common is None:
+            common = [nullid]
+        if heads is None:
+            heads = self.heads()
+
+        common = [self.rev(n) for n in common]
+        heads = [self.rev(n) for n in heads]
+
+        return [self.node(r) for r in
+                ancestor.missingancestors(heads, common, self.parentrevs)]
 
     def nodesbetween(self, roots=None, heads=None):
         """Return a topological path from 'roots' to 'heads'.
--- a/mercurial/revset.py	Wed Nov 28 17:53:52 2012 -0600
+++ b/mercurial/revset.py	Wed Nov 28 18:08:51 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/mercurial/scmutil.py	Wed Nov 28 17:53:52 2012 -0600
+++ b/mercurial/scmutil.py	Wed Nov 28 18:08:51 2012 -0600
@@ -279,37 +279,38 @@
             mode += "b" # for that other OS
 
         nlink = -1
-        dirname, basename = util.split(f)
-        # If basename is empty, then the path is malformed because it points
-        # to a directory. Let the posixfile() call below raise IOError.
-        if basename and mode not in ('r', 'rb'):
-            if atomictemp:
-                if not os.path.isdir(dirname):
-                    util.makedirs(dirname, self.createmode)
-                return util.atomictempfile(f, mode, self.createmode)
-            try:
-                if 'w' in mode:
-                    util.unlink(f)
+        if mode not in ('r', 'rb'):
+            dirname, basename = util.split(f)
+            # If basename is empty, then the path is malformed because it points
+            # to a directory. Let the posixfile() call below raise IOError.
+            if basename:
+                if atomictemp:
+                    if not os.path.isdir(dirname):
+                        util.makedirs(dirname, self.createmode)
+                    return util.atomictempfile(f, mode, self.createmode)
+                try:
+                    if 'w' in mode:
+                        util.unlink(f)
+                        nlink = 0
+                    else:
+                        # nlinks() may behave differently for files on Windows
+                        # shares if the file is open.
+                        fd = util.posixfile(f)
+                        nlink = util.nlinks(f)
+                        if nlink < 1:
+                            nlink = 2 # force mktempcopy (issue1922)
+                        fd.close()
+                except (OSError, IOError), e:
+                    if e.errno != errno.ENOENT:
+                        raise
                     nlink = 0
-                else:
-                    # nlinks() may behave differently for files on Windows
-                    # shares if the file is open.
-                    fd = util.posixfile(f)
-                    nlink = util.nlinks(f)
-                    if nlink < 1:
-                        nlink = 2 # force mktempcopy (issue1922)
-                    fd.close()
-            except (OSError, IOError), e:
-                if e.errno != errno.ENOENT:
-                    raise
-                nlink = 0
-                if not os.path.isdir(dirname):
-                    util.makedirs(dirname, self.createmode)
-            if nlink > 0:
-                if self._trustnlink is None:
-                    self._trustnlink = nlink > 1 or util.checknlink(f)
-                if nlink > 1 or not self._trustnlink:
-                    util.rename(util.mktempcopy(f), f)
+                    if not os.path.isdir(dirname):
+                        util.makedirs(dirname, self.createmode)
+                if nlink > 0:
+                    if self._trustnlink is None:
+                        self._trustnlink = nlink > 1 or util.checknlink(f)
+                    if nlink > 1 or not self._trustnlink:
+                        util.rename(util.mktempcopy(f), f)
         fp = util.posixfile(f, mode)
         if nlink == 0:
             self._fixfilemode(f)
--- a/mercurial/subrepo.py	Wed Nov 28 17:53:52 2012 -0600
+++ b/mercurial/subrepo.py	Wed Nov 28 18:08:51 2012 -0600
@@ -446,7 +446,7 @@
                 node2 = node.bin(node2)
             cmdutil.diffordiffstat(self._repo.ui, self._repo, diffopts,
                                    node1, node2, match,
-                                   prefix=os.path.join(prefix, self._path),
+                                   prefix=posixpath.join(prefix, self._path),
                                    listsubrepos=True, **opts)
         except error.RepoLookupError, inst:
             self._repo.ui.warn(_('warning: error "%s" in subrepository "%s"\n')
--- a/mercurial/templater.py	Wed Nov 28 17:53:52 2012 -0600
+++ b/mercurial/templater.py	Wed Nov 28 18:08:51 2012 -0600
@@ -8,6 +8,7 @@
 from i18n import _
 import sys, os, re
 import util, config, templatefilters, parser, error
+import types
 
 # template parsing
 
@@ -140,6 +141,10 @@
         v = context._defaults.get(key, '')
     if util.safehasattr(v, '__call__'):
         return v(**mapping)
+    if isinstance(v, types.GeneratorType):
+        v = list(v)
+        mapping[key] = v
+        return v
     return v
 
 def buildfilter(exp, context):
--- a/setup.py	Wed Nov 28 17:53:52 2012 -0600
+++ b/setup.py	Wed Nov 28 18:08:51 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/autodiff.py	Wed Nov 28 17:53:52 2012 -0600
+++ b/tests/autodiff.py	Wed Nov 28 18:08:51 2012 -0600
@@ -35,7 +35,7 @@
     for chunk in it:
         ui.write(chunk)
     for fn in sorted(brokenfiles):
-        ui.write('data lost for: %s\n' % fn)
+        ui.write(('data lost for: %s\n' % fn))
 
 cmdtable = {
     "autodiff":
--- a/tests/run-tests.py	Wed Nov 28 17:53:52 2012 -0600
+++ b/tests/run-tests.py	Wed Nov 28 18:08:51 2012 -0600
@@ -55,6 +55,7 @@
 import re
 import threading
 import killdaemons as killmod
+import cPickle as pickle
 
 processlock = threading.Lock()
 
@@ -162,6 +163,8 @@
     parser.add_option("-p", "--port", type="int",
         help="port on which servers should listen"
              " (default: $%s or %d)" % defaults['port'])
+    parser.add_option("--compiler", type="string",
+        help="compiler to build with")
     parser.add_option("--pure", action="store_true",
         help="use pure Python code instead of C extensions")
     parser.add_option("-R", "--restart", action="store_true",
@@ -175,6 +178,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 +268,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+')
@@ -364,6 +373,9 @@
 def installhg(options):
     vlog("# Performing temporary installation of HG")
     installerrs = os.path.join("tests", "install.err")
+    compiler = ''
+    if options.compiler:
+        compiler = '--compiler ' + options.compiler
     pure = options.pure and "--pure" or ""
 
     # Run installer in hg root
@@ -377,12 +389,14 @@
         # least on Windows for now, deal with .pydistutils.cfg bugs
         # when they happen.
         nohome = ''
-    cmd = ('%s setup.py %s clean --all'
-           ' build --build-base="%s"'
-           ' install --force --prefix="%s" --install-lib="%s"'
-           ' --install-scripts="%s" %s >%s 2>&1'
-           % (sys.executable, pure, os.path.join(HGTMP, "build"),
-              INST, PYTHONDIR, BINDIR, nohome, installerrs))
+    cmd = ('%(exe)s setup.py %(pure)s clean --all'
+           ' build %(compiler)s --build-base="%(base)s"'
+           ' install --force --prefix="%(prefix)s" --install-lib="%(libdir)s"'
+           ' --install-scripts="%(bindir)s" %(nohome)s >%(logfile)s 2>&1'
+           % dict(exe=sys.executable, pure=pure, compiler=compiler,
+                  base=os.path.join(HGTMP, "build"),
+                  prefix=INST, libdir=PYTHONDIR, bindir=BINDIR,
+                  nohome=nohome, logfile=installerrs))
     vlog("# Running", cmd)
     if os.system(cmd) == 0:
         if not options.verbose:
@@ -447,6 +461,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')
@@ -891,7 +913,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 +1083,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 +1121,20 @@
 
     _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 +1160,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 +1168,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 +1182,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 +1201,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	Wed Nov 28 17:53:52 2012 -0600
+++ b/tests/test-bundle.t	Wed Nov 28 18:08:51 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-check-code-hg.t	Wed Nov 28 17:53:52 2012 -0600
+++ b/tests/test-check-code-hg.t	Wed Nov 28 18:08:51 2012 -0600
@@ -5,163 +5,7 @@
   >     echo "skipped: not a Mercurial working dir" >&2
   >     exit 80
   > fi
-  $ hg manifest | xargs "$check_code" || echo 'FAILURE IS NOT AN OPTION!!!'
 
-  $ hg manifest | xargs "$check_code" --warnings --nolineno --per-file=0 || true
-  hgext/convert/cvsps.py:0:
-   >                     ui.write('Ancestors: %s\n' % (','.join(r)))
-   warning: unwrapped ui message
-  hgext/convert/cvsps.py:0:
-   >                     ui.write('Parent: %d\n' % cs.parents[0].id)
-   warning: unwrapped ui message
-  hgext/convert/cvsps.py:0:
-   >                     ui.write('Parents: %s\n' %
-   warning: unwrapped ui message
-  hgext/convert/cvsps.py:0:
-   >                 ui.write('Branchpoints: %s \n' % ', '.join(branchpoints))
-   warning: unwrapped ui message
-  hgext/convert/cvsps.py:0:
-   >             ui.write('Author: %s\n' % cs.author)
-   warning: unwrapped ui message
-  hgext/convert/cvsps.py:0:
-   >             ui.write('Branch: %s\n' % (cs.branch or 'HEAD'))
-   warning: unwrapped ui message
-  hgext/convert/cvsps.py:0:
-   >             ui.write('Date: %s\n' % util.datestr(cs.date,
-   warning: unwrapped ui message
-  hgext/convert/cvsps.py:0:
-   >             ui.write('Log:\n')
-   warning: unwrapped ui message
-  hgext/convert/cvsps.py:0:
-   >             ui.write('Members: \n')
-   warning: unwrapped ui message
-  hgext/convert/cvsps.py:0:
-   >             ui.write('PatchSet %d \n' % cs.id)
-   warning: unwrapped ui message
-  hgext/convert/cvsps.py:0:
-   >             ui.write('Tag%s: %s \n' % (['', 's'][len(cs.tags) > 1],
-   warning: unwrapped ui message
-  hgext/hgk.py:0:
-   >         ui.write("parent %s\n" % p)
-   warning: unwrapped ui message
-  hgext/hgk.py:0:
-   >         ui.write('k=%s\nv=%s\n' % (name, value))
-   warning: unwrapped ui message
-  hgext/hgk.py:0:
-   >     ui.write("author %s %s %s\n" % (ctx.user(), int(date[0]), date[1]))
-   warning: unwrapped ui message
-  hgext/hgk.py:0:
-   >     ui.write("branch %s\n\n" % ctx.branch())
-   warning: unwrapped ui message
-  hgext/hgk.py:0:
-   >     ui.write("committer %s %s %s\n" % (committer, int(date[0]), date[1]))
-   warning: unwrapped ui message
-  hgext/hgk.py:0:
-   >     ui.write("revision %d\n" % ctx.rev())
-   warning: unwrapped ui message
-  hgext/hgk.py:0:
-   >     ui.write("tree %s\n" % short(ctx.changeset()[0]))
-   warning: unwrapped ui message
-  hgext/patchbomb.py:0:
-   >             ui.write('Subject: %s\n' % subj)
-   warning: unwrapped ui message
-  hgext/patchbomb.py:0:
-   >         ui.write('From: %s\n' % sender)
-   warning: unwrapped ui message
-  mercurial/commands.py:0:
-   >                 ui.note('branch %s\n' % data)
-   warning: unwrapped ui message
-  mercurial/commands.py:0:
-   >                 ui.note('node %s\n' % str(data))
-   warning: unwrapped ui message
-  mercurial/commands.py:0:
-   >                 ui.note('tag %s\n' % name)
-   warning: unwrapped ui message
-  mercurial/commands.py:0:
-   >                 ui.write("unpruned common: %s\n" % " ".join([short(n)
-   warning: unwrapped ui message
-  mercurial/commands.py:0:
-   >             ui.write("format: id, p1, p2, cset, delta base, len(delta)\n")
-   warning: unwrapped ui message
-  mercurial/commands.py:0:
-   >             ui.write("local is subset\n")
-   warning: unwrapped ui message
-  mercurial/commands.py:0:
-   >             ui.write("remote is subset\n")
-   warning: unwrapped ui message
-  mercurial/commands.py:0:
-   >             ui.write('deltas against other : ' + fmt % pcfmt(numother,
-   warning: unwrapped ui message
-  mercurial/commands.py:0:
-   >             ui.write('deltas against p1    : ' + fmt % pcfmt(nump1, numdeltas))
-   warning: unwrapped ui message
-  mercurial/commands.py:0:
-   >             ui.write('deltas against p2    : ' + fmt % pcfmt(nump2, numdeltas))
-   warning: unwrapped ui message
-  mercurial/commands.py:0:
-   >         ui.write("common heads: %s\n" % " ".join([short(n) for n in common]))
-   warning: unwrapped ui message
-  mercurial/commands.py:0:
-   >         ui.write("match: %s\n" % m(d[0]))
-   warning: unwrapped ui message
-  mercurial/commands.py:0:
-   >         ui.write('deltas against prev  : ' + fmt % pcfmt(numprev, numdeltas))
-   warning: unwrapped ui message
-  mercurial/commands.py:0:
-   >         ui.write('path %s\n' % k)
-   warning: unwrapped ui message
-  mercurial/commands.py:0:
-   >         ui.write('uncompressed data size (min/max/avg) : %d / %d / %d\n'
-   warning: unwrapped ui message
-  mercurial/commands.py:0:
-   >     ui.write("digraph G {\n")
-   warning: unwrapped ui message
-  mercurial/commands.py:0:
-   >     ui.write("internal: %s %s\n" % d)
-   warning: unwrapped ui message
-  mercurial/commands.py:0:
-   >     ui.write("standard: %s\n" % util.datestr(d))
-   warning: unwrapped ui message
-  mercurial/commands.py:0:
-   >     ui.write('avg chain length  : ' + fmt % avgchainlen)
-   warning: unwrapped ui message
-  mercurial/commands.py:0:
-   >     ui.write('case-sensitive: %s\n' % (util.checkcase('.debugfsinfo')
-   warning: unwrapped ui message
-  mercurial/commands.py:0:
-   >     ui.write('compression ratio : ' + fmt % compratio)
-   warning: unwrapped ui message
-  mercurial/commands.py:0:
-   >     ui.write('delta size (min/max/avg)             : %d / %d / %d\n'
-   warning: unwrapped ui message
-  mercurial/commands.py:0:
-   >     ui.write('exec: %s\n' % (util.checkexec(path) and 'yes' or 'no'))
-   warning: unwrapped ui message
-  mercurial/commands.py:0:
-   >     ui.write('flags  : %s\n' % ', '.join(flags))
-   warning: unwrapped ui message
-  mercurial/commands.py:0:
-   >     ui.write('format : %d\n' % format)
-   warning: unwrapped ui message
-  mercurial/commands.py:0:
-   >     ui.write('full revision size (min/max/avg)     : %d / %d / %d\n'
-   warning: unwrapped ui message
-  mercurial/commands.py:0:
-   >     ui.write('revision size : ' + fmt2 % totalsize)
-   warning: unwrapped ui message
-  mercurial/commands.py:0:
-   >     ui.write('revisions     : ' + fmt2 % numrevs)
-   warning: unwrapped ui message
-   warning: unwrapped ui message
-  mercurial/commands.py:0:
-   >     ui.write('symlink: %s\n' % (util.checklink(path) and 'yes' or 'no'))
-   warning: unwrapped ui message
-  tests/autodiff.py:0:
-   >         ui.write('data lost for: %s\n' % fn)
-   warning: unwrapped ui message
-  tests/test-ui-color.py:0:
-   > testui.warn('warning\n')
-   warning: unwrapped ui message
-  tests/test-ui-color.py:0:
-   > testui.write('buffered\n')
-   warning: unwrapped ui message
+New errors are not allowed. Warnings are strongly discouraged.
+
+  $ hg manifest | xargs "$check_code" --warnings --nolineno --per-file=0
--- a/tests/test-convert-cvs.t	Wed Nov 28 17:53:52 2012 -0600
+++ b/tests/test-convert-cvs.t	Wed Nov 28 18:08:51 2012 -0600
@@ -69,9 +69,16 @@
   $TESTTMP/cvsrepo/src/b/c,v  <--  *c (glob)
   $ cd ..
 
-convert fresh repo
+convert fresh repo and also check localtimezone option
+
+NOTE: This doesn't check all time zones -- it merely determines that
+the configuration option is taking effect.
 
-  $ hg convert src src-hg
+An arbitrary (U.S.) time zone is used here.  TZ=US/Hawaii is selected
+since it does not use DST (unlike other U.S. time zones) and is always
+a fixed difference from UTC.
+
+  $ TZ=US/Hawaii hg convert --config convert.localtimezone=True src src-hg
   initializing destination src-hg repository
   connecting to $TESTTMP/cvsrepo
   scanning source...
@@ -161,7 +168,7 @@
 
 convert again
 
-  $ hg convert src src-hg
+  $ TZ=US/Hawaii hg convert --config convert.localtimezone=True src src-hg
   connecting to $TESTTMP/cvsrepo
   scanning source...
   collecting CVS rlog
@@ -221,7 +228,7 @@
 
 convert again
 
-  $ hg convert src src-hg
+  $ TZ=US/Hawaii hg convert --config convert.localtimezone=True src src-hg
   connecting to $TESTTMP/cvsrepo
   scanning source...
   collecting CVS rlog
@@ -239,7 +246,7 @@
 
 convert again with --filemap
 
-  $ hg convert --filemap filemap src src-filemap
+  $ TZ=US/Hawaii hg convert --config convert.localtimezone=True --filemap filemap src src-filemap
   connecting to $TESTTMP/cvsrepo
   scanning source...
   collecting CVS rlog
@@ -286,7 +293,7 @@
 
 convert again
 
-  $ hg convert --config convert.cvsps.fuzz=2 src src-hg
+  $ TZ=US/Hawaii hg convert --config convert.cvsps.fuzz=2 --config convert.localtimezone=True src src-hg
   connecting to $TESTTMP/cvsrepo
   scanning source...
   collecting CVS rlog
@@ -300,25 +307,25 @@
   2 funny
   1 fuzzy
   0 fuzzy
-  $ hg -R src-hg glog --template '{rev} ({branches}) {desc} files: {files}\n'
-  o  8 (branch) fuzzy files: b/c
+  $ hg -R src-hg glog --template '{rev} ({branches}) {desc} date: {date|date} files: {files}\n'
+  o  8 (branch) fuzzy date: * -1000 files: b/c (glob)
   |
-  o  7 (branch) fuzzy files: a
+  o  7 (branch) fuzzy date: * -1000 files: a (glob)
   |
   o  6 (branch) funny
   |  ----------------------------
-  |  log message files: a
-  o  5 (branch) ci2 files: b/c
+  |  log message date: * -1000 files: a (glob)
+  o  5 (branch) ci2 date: * -1000 files: b/c (glob)
   
-  o  4 () ci1 files: a b/c
+  o  4 () ci1 date: * -1000 files: a b/c (glob)
   |
-  o  3 () update tags files: .hgtags
+  o  3 () update tags date: * +0000 files: .hgtags (glob)
   |
-  o  2 () ci0 files: b/c
+  o  2 () ci0 date: * -1000 files: b/c (glob)
   |
-  | o  1 (INITIAL) import files:
+  | o  1 (INITIAL) import date: * -1000 files: (glob)
   |/
-  o  0 () Initial revision files: a b/c
+  o  0 () Initial revision date: * -1000 files: a b/c (glob)
   
 
 testing debugcvsps
--- a/tests/test-convert-git.t	Wed Nov 28 17:53:52 2012 -0600
+++ b/tests/test-convert-git.t	Wed Nov 28 18:08:51 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-convert-svn-source.t	Wed Nov 28 17:53:52 2012 -0600
+++ b/tests/test-convert-svn-source.t	Wed Nov 28 18:08:51 2012 -0600
@@ -63,9 +63,16 @@
   Committed revision 5.
   $ cd ..
 
-Convert to hg once
+Convert to hg once and also test localtimezone option
+
+NOTE: This doesn't check all time zones -- it merely determines that
+the configuration option is taking effect.
 
-  $ hg convert "$SVNREPOURL/proj%20B" B-hg
+An arbitrary (U.S.) time zone is used here.  TZ=US/Hawaii is selected
+since it does not use DST (unlike other U.S. time zones) and is always
+a fixed difference from UTC.
+
+  $ TZ=US/Hawaii hg convert --config convert.localtimezone=True "$SVNREPOURL/proj%20B" B-hg
   initializing destination B-hg repository
   scanning source...
   sorting...
@@ -109,7 +116,7 @@
 
 Test incremental conversion
 
-  $ hg convert "$SVNREPOURL/proj%20B" B-hg
+  $ TZ=US/Hawaii hg convert --config convert.localtimezone=True "$SVNREPOURL/proj%20B" B-hg
   scanning source...
   sorting...
   converting...
@@ -118,22 +125,22 @@
   updating tags
 
   $ cd B-hg
-  $ hg glog --template '{rev} {desc|firstline} files: {files}\n'
-  o  7 update tags files: .hgtags
+  $ hg glog --template '{rev} {desc|firstline} date: {date|date} files: {files}\n'
+  o  7 update tags date: * +0000 files: .hgtags (glob)
   |
-  o  6 work in progress files: letter2.txt
+  o  6 work in progress date: * -1000 files: letter2.txt (glob)
   |
-  o  5 second letter files: letter .txt letter2.txt
+  o  5 second letter date: * -1000 files: letter .txt letter2.txt (glob)
   |
-  o  4 update tags files: .hgtags
+  o  4 update tags date: * +0000 files: .hgtags (glob)
   |
-  o  3 nice day files: letter .txt
+  o  3 nice day date: * -1000 files: letter .txt (glob)
   |
-  o  2 world files: letter .txt
+  o  2 world date: * -1000 files: letter .txt (glob)
   |
-  o  1 hello files: letter .txt
+  o  1 hello date: * -1000 files: letter .txt (glob)
   |
-  o  0 init projB files:
+  o  0 init projB date: * -1000 files: (glob)
   
   $ hg tags -q
   tip
--- a/tests/test-convert.t	Wed Nov 28 17:53:52 2012 -0600
+++ b/tests/test-convert.t	Wed Nov 28 18:08:51 2012 -0600
@@ -172,6 +172,10 @@
                     will add the most recent revision on the branch indicated in
                     the regex as the second parent of the changeset. Default is
                     "{{mergefrombranch ([-\w]+)}}"
+      convert.localtimezone
+                    use local time (as determined by the TZ environment
+                    variable) for changeset date/times. The default is False
+                    (use UTC).
       hook.cvslog   Specify a Python function to be called at the end of
                     gathering the CVS log. The function is passed a list with
                     the log entries, and can modify the entries in-place, or add
@@ -211,6 +215,10 @@
       convert.svn.trunk
                     specify the name of the trunk branch. The default is
                     "trunk".
+      convert.localtimezone
+                    use local time (as determined by the TZ environment
+                    variable) for changeset date/times. The default is False
+                    (use UTC).
   
       Source history can be retrieved starting at a specific revision, instead
       of being integrally converted. Only single branch conversions are
--- a/tests/test-eolfilename.t	Wed Nov 28 17:53:52 2012 -0600
+++ b/tests/test-eolfilename.t	Wed Nov 28 18:08:51 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-hgk.t	Wed Nov 28 17:53:52 2012 -0600
+++ b/tests/test-hgk.t	Wed Nov 28 18:08:51 2012 -0600
@@ -11,7 +11,6 @@
   tree a0c8bcbbb45c
   parent 000000000000
   author test 0 0
-  committer test 0 0
   revision 0
   branch default
   
--- a/tests/test-mq-qrefresh.t	Wed Nov 28 17:53:52 2012 -0600
+++ b/tests/test-mq-qrefresh.t	Wed Nov 28 18:08:51 2012 -0600
@@ -209,6 +209,7 @@
   $ hg add orphanchild
   $ hg qrefresh nonexistentfilename # clear patch
   nonexistentfilename: * (glob)
+  $ hg diff -c qtip
   $ hg qrefresh --short 1/base
   $ hg qrefresh --short 2/base
 
--- a/tests/test-mq.t	Wed Nov 28 17:53:52 2012 -0600
+++ b/tests/test-mq.t	Wed Nov 28 18:08:51 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
 
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tests/test-pathencode.py	Wed Nov 28 18:08:51 2012 -0600
@@ -0,0 +1,193 @@
+# This is a randomized test that generates different pathnames every
+# time it is invoked, and tests the encoding of those pathnames.
+#
+# It uses a simple probabilistic model to generate valid pathnames
+# that have proven likely to expose bugs and divergent behaviour in
+# different encoding implementations.
+
+from mercurial import parsers
+from mercurial import store
+import binascii, itertools, math, os, random, sys, time
+import collections
+
+if sys.version_info[:2] < (2, 6):
+    sys.exit(0)
+
+def hybridencode(path):
+    return store._hybridencode(path, True)
+
+validchars = set(map(chr, range(0, 256)))
+alphanum = range(ord('A'), ord('Z'))
+
+for c in '\0/':
+    validchars.remove(c)
+
+winreserved = ('aux con prn nul'.split() +
+               ['com%d' % i for i in xrange(1, 10)] +
+               ['lpt%d' % i for i in xrange(1, 10)])
+
+def casecombinations(names):
+    '''Build all case-diddled combinations of names.'''
+
+    combos = set()
+
+    for r in names:
+        for i in xrange(len(r) + 1):
+            for c in itertools.combinations(xrange(len(r)), i):
+                d = r
+                for j in c:
+                    d = ''.join((d[:j], d[j].upper(), d[j + 1:]))
+                combos.add(d)
+    return sorted(combos)
+
+def buildprobtable(fp, cmd='hg manifest tip'):
+    '''Construct and print a table of probabilities for path name
+    components.  The numbers are percentages.'''
+
+    counts = collections.defaultdict(lambda: 0)
+    for line in os.popen(cmd).read().splitlines():
+        if line[-2:] in ('.i', '.d'):
+            line = line[:-2]
+        if line.startswith('data/'):
+            line = line[5:]
+        for c in line:
+            counts[c] += 1
+    for c in '\r/\n':
+        counts.pop(c, None)
+    t = sum(counts.itervalues()) / 100.0
+    fp.write('probtable = (')
+    for i, (k, v) in enumerate(sorted(counts.iteritems(), key=lambda x: x[1],
+                                      reverse=True)):
+        if (i % 5) == 0:
+            fp.write('\n    ')
+        vt = v / t
+        if vt < 0.0005:
+            break
+        fp.write('(%r, %.03f), ' % (k, vt))
+    fp.write('\n    )\n')
+
+# A table of character frequencies (as percentages), gleaned by
+# looking at filelog names from a real-world, very large repo.
+
+probtable = (
+    ('t', 9.828), ('e', 9.042), ('s', 8.011), ('a', 6.801), ('i', 6.618),
+    ('g', 5.053), ('r', 5.030), ('o', 4.887), ('p', 4.363), ('n', 4.258),
+    ('l', 3.830), ('h', 3.693), ('_', 3.659), ('.', 3.377), ('m', 3.194),
+    ('u', 2.364), ('d', 2.296), ('c', 2.163), ('b', 1.739), ('f', 1.625),
+    ('6', 0.666), ('j', 0.610), ('y', 0.554), ('x', 0.487), ('w', 0.477),
+    ('k', 0.476), ('v', 0.473), ('3', 0.336), ('1', 0.335), ('2', 0.326),
+    ('4', 0.310), ('5', 0.305), ('9', 0.302), ('8', 0.300), ('7', 0.299),
+    ('q', 0.298), ('0', 0.250), ('z', 0.223), ('-', 0.118), ('C', 0.095),
+    ('T', 0.087), ('F', 0.085), ('B', 0.077), ('S', 0.076), ('P', 0.076),
+    ('L', 0.059), ('A', 0.058), ('N', 0.051), ('D', 0.049), ('M', 0.046),
+    ('E', 0.039), ('I', 0.035), ('R', 0.035), ('G', 0.028), ('U', 0.026),
+    ('W', 0.025), ('O', 0.017), ('V', 0.015), ('H', 0.013), ('Q', 0.011),
+    ('J', 0.007), ('K', 0.005), ('+', 0.004), ('X', 0.003), ('Y', 0.001),
+    )
+
+for c, _ in probtable:
+    validchars.remove(c)
+validchars = list(validchars)
+
+def pickfrom(rng, table):
+    c = 0
+    r = rng.random() * sum(i[1] for i in table)
+    for i, p in table:
+        c += p
+        if c >= r:
+            return i
+
+reservedcombos = casecombinations(winreserved)
+
+# The first component of a name following a slash.
+
+firsttable = (
+    (lambda rng: pickfrom(rng, probtable), 90),
+    (lambda rng: rng.choice(validchars), 5),
+    (lambda rng: rng.choice(reservedcombos), 5),
+    )
+
+# Components of a name following the first.
+
+resttable = firsttable[:-1]
+
+# Special suffixes.
+
+internalsuffixcombos = casecombinations('.hg .i .d'.split())
+
+# The last component of a path, before a slash or at the end of a name.
+
+lasttable = resttable + (
+    (lambda rng: '', 95),
+    (lambda rng: rng.choice(internalsuffixcombos), 5),
+    )
+
+def makepart(rng, k):
+    '''Construct a part of a pathname, without slashes.'''
+
+    p = pickfrom(rng, firsttable)(rng)
+    l = len(p)
+    ps = [p]
+    while l <= k:
+        p = pickfrom(rng, resttable)(rng)
+        l += len(p)
+        ps.append(p)
+    ps.append(pickfrom(rng, lasttable)(rng))
+    return ''.join(ps)
+
+def makepath(rng, j, k):
+    '''Construct a complete pathname.'''
+
+    return ('data/' + '/'.join(makepart(rng, k) for _ in xrange(j)) +
+            rng.choice(['.d', '.i']))
+
+def genpath(rng, count):
+    '''Generate random pathnames with gradually increasing lengths.'''
+
+    mink, maxk = 1, 4096
+    def steps():
+        x, k = 0, mink
+        for i in xrange(count):
+            yield mink + int(round(math.sqrt((maxk - mink) * float(i) / count)))
+    for k in steps():
+        x = rng.randint(1, k)
+        y = rng.randint(1, k)
+        yield makepath(rng, x, y)
+
+def runtests(rng, seed, count):
+    nerrs = 0
+    for p in genpath(rng, count):
+        hybridencode(p)
+    return nerrs
+
+def main():
+    import getopt
+
+    # Empirically observed to take about a second to run
+    count = 100
+    seed = None
+    opts, args = getopt.getopt(sys.argv[1:], 'c:s:',
+                               ['build', 'count=', 'seed='])
+    for o, a in opts:
+        if o in ('-c', '--count'):
+            count = int(a)
+        elif o in ('-s', '--seed'):
+            seed = long(a)
+        elif o == '--build':
+            buildprobtable(sys.stdout,
+                           'find .hg/store/data -type f && '
+                           'cat .hg/store/fncache 2>/dev/null')
+            sys.exit(0)
+
+    if seed is None:
+        try:
+            seed = long(binascii.hexlify(os.urandom(16)), 16)
+        except AttributeError:
+            seed = long(time.time() * 1000)
+
+    rng = random.Random(seed)
+    if runtests(rng, seed, count):
+        sys.exit(1)
+
+if __name__ == '__main__':
+    main()
--- a/tests/test-status-color.t	Wed Nov 28 17:53:52 2012 -0600
+++ b/tests/test-status-color.t	Wed Nov 28 18:08:51 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.
--- a/tests/test-subrepo.t	Wed Nov 28 17:53:52 2012 -0600
+++ b/tests/test-subrepo.t	Wed Nov 28 18:08:51 2012 -0600
@@ -718,6 +718,14 @@
   committing subrepository subrepo-2
   $ hg st subrepo-2/file
 
+Check that share works with subrepo
+  $ hg --config extensions.share= share . ../shared
+  updating working directory
+  cloning subrepo subrepo-2 from $TESTTMP/subrepo-status/subrepo-2
+  2 files updated, 0 files merged, 0 files removed, 0 files unresolved
+  $ test -f ../shared/subrepo-1/.hg/sharedpath
+  [1]
+
 Check hg update --clean
   $ cd $TESTTMP/t
   $ rm -r t/t.orig
--- a/tests/test-ui-color.py	Wed Nov 28 17:53:52 2012 -0600
+++ b/tests/test-ui-color.py	Wed Nov 28 18:08:51 2012 -0600
@@ -5,8 +5,8 @@
 # ensure errors aren't buffered
 testui = color.colorui()
 testui.pushbuffer()
-testui.write('buffered\n')
-testui.warn('warning\n')
+testui.write(('buffered\n'))
+testui.warn(('warning\n'))
 testui.write_err('error\n')
 print repr(testui.popbuffer())