changeset 18453:f5fbe15ca744 stable 2.5-rc

merge default into stable for 2.5 code freeze
author Matt Mackall <mpm@selenic.com>
date Sat, 19 Jan 2013 17:24:33 -0600
parents 7648b87e76db (current diff) 8bd338c7c4c9 (diff)
children a9475e8936bc
files tests/test-inotify-issue1208.t tests/test-mq-caches.t
diffstat 308 files changed, 7572 insertions(+), 3124 deletions(-) [+]
line wrap: on
line diff
--- a/Makefile	Mon Jan 14 23:14:45 2013 +0900
+++ b/Makefile	Sat Jan 19 17:24:33 2013 -0600
@@ -11,6 +11,9 @@
 PYFILES:=$(shell find mercurial hgext doc -name '*.py')
 DOCFILES=mercurial/help/*.txt
 
+# Set this to e.g. "mingw32" to use a non-default compiler.
+COMPILER=
+
 help:
 	@echo 'Commonly used make targets:'
 	@echo '  all          - build program and documentation'
@@ -33,11 +36,15 @@
 all: build doc
 
 local:
-	$(PYTHON) setup.py $(PURE) build_py -c -d . build_ext -i build_hgexe -i build_mo
-	$(PYTHON) hg version
+	$(PYTHON) setup.py $(PURE) \
+	  build_py -c -d . \
+	  build_ext $(COMPILER:%=-c %) -i \
+	  build_hgexe $(COMPILER:%=-c %) -i \
+	  build_mo
+	env HGRCPATH= $(PYTHON) hg version
 
 build:
-	$(PYTHON) setup.py $(PURE) build
+	$(PYTHON) setup.py $(PURE) build $(COMPILER:%=-c %)
 
 doc:
 	$(MAKE) -C doc
--- a/contrib/check-code.py	Mon Jan 14 23:14:45 2013 +0900
+++ b/contrib/check-code.py	Sat Jan 19 17:24:33 2013 -0600
@@ -129,13 +129,14 @@
     (r'(?<!def)\s+(cmp)\(', "cmp is not available in Python 3+"),
     (r'\breduce\s*\(.*', "reduce is not available in Python 3+"),
     (r'\.has_key\b', "dict.has_key is not available in Python 3+"),
+    (r'\s<>\s', '<> operator is not available in Python 3+, use !='),
     (r'^\s*\t', "don't use tabs"),
     (r'\S;\s*\n', "semicolon"),
     (r'[^_]_\("[^"]+"\s*%', "don't use % inside _()"),
     (r"[^_]_\('[^']+'\s*%", "don't use % inside _()"),
-    (r'\w,\w', "missing whitespace after ,"),
-    (r'\w[+/*\-<>]\w', "missing whitespace in expression"),
-    (r'^\s+\w+=\w+[^,)\n]$', "missing whitespace in assignment"),
+    (r'(\w|\)),\w', "missing whitespace after ,"),
+    (r'(\w|\))[+/*\-<>]\w', "missing whitespace in expression"),
+    (r'^\s+(\w|\.)+=\w[^,()\n]*$', "missing whitespace in assignment"),
     (r'(\s+)try:\n((?:\n|\1\s.*\n)+?)\1except.*?:\n'
      r'((?:\n|\1\s.*\n)+?)\1finally:', 'no try/except/finally in Python 2.4'),
     (r'(\s+)try:\n((?:\n|\1\s.*\n)*?)\1\s*yield\b.*?'
@@ -185,6 +186,8 @@
     (r'[^^+=*/!<>&| %-](\s=|=\s)[^= ]',
      "wrong whitespace around ="),
     (r'raise Exception', "don't raise generic exceptions"),
+    (r'raise [^,(]+, (\([^\)]+\)|[^,\(\)]+)$',
+     "don't use old-style two-argument raise, use Exception(message)"),
     (r' is\s+(not\s+)?["\'0-9-]', "object comparison with literal"),
     (r' [=!]=\s+(True|False|None)',
      "comparison with singleton, use 'is' or 'is not' instead"),
@@ -211,11 +214,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	Mon Jan 14 23:14:45 2013 +0900
+++ b/contrib/hgk	Sat Jan 19 17:24:33 2013 -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/contrib/mergetools.hgrc	Mon Jan 14 23:14:45 2013 +0900
+++ b/contrib/mergetools.hgrc	Sat Jan 19 17:24:33 2013 -0600
@@ -19,7 +19,7 @@
 vimdiff.check=changed
 vimdiff.priority=-10
 
-merge.checkconflicts=True
+merge.check=conflicts
 merge.priority=-100
 
 gpyfm.gui=True
@@ -43,7 +43,7 @@
 diffmerge.regname=Location
 diffmerge.priority=-7
 diffmerge.args=-nosplash -merge -title1=local -title2=merged -title3=other $local $base $other -result=$output
-diffmerge.checkchanged=True
+diffmerge.check=changed
 diffmerge.gui=True
 diffmerge.diffargs=--nosplash --title1='$plabel1' --title2='$clabel' $parent $child
 
@@ -59,7 +59,7 @@
 tortoisemerge.args=/base:$base /mine:$local /theirs:$other /merged:$output
 tortoisemerge.regkey=Software\TortoiseSVN
 tortoisemerge.regkeyalt=Software\Wow6432Node\TortoiseSVN
-tortoisemerge.checkchanged=True
+tortoisemerge.check=changed
 tortoisemerge.gui=True
 tortoisemerge.priority=-8
 tortoisemerge.diffargs=/base:$parent /mine:$child /basename:'$plabel1' /minename:'$clabel'
@@ -93,7 +93,7 @@
 winmerge.regkey=Software\Thingamahoochie\WinMerge
 winmerge.regkeyalt=Software\Wow6432Node\Thingamahoochie\WinMerge\
 winmerge.regname=Executable
-winmerge.checkchanged=True
+winmerge.check=changed
 winmerge.gui=True
 winmerge.priority=-10
 winmerge.diffargs=/r /e /x /ub /wl /dl '$plabel1' /dr '$clabel' $parent $child
@@ -119,6 +119,5 @@
 UltraCompare.priority = -2
 UltraCompare.gui = True
 UltraCompare.binary = True
-UltraCompare.checkconflicts = True
-UltraCompare.checkchanged = True
+UltraCompare.check = conflicts,changed
 UltraCompare.diffargs=$child $parent -title1 $clabel -title2 $plabel1
--- a/contrib/perf.py	Mon Jan 14 23:14:45 2013 +0900
+++ b/contrib/perf.py	Sat Jan 19 17:24:33 2013 -0600
@@ -1,9 +1,13 @@
 # perf.py - performance test routines
 '''helper extension to measure performance'''
 
-from mercurial import cmdutil, scmutil, util, match, commands
+from mercurial import cmdutil, scmutil, util, match, commands, obsolete
+from mercurial import repoview, branchmap
 import time, os, sys
 
+cmdtable = {}
+command = cmdutil.command(cmdtable)
+
 def timer(func, title=None):
     results = []
     begin = time.time()
@@ -29,6 +33,7 @@
     sys.stderr.write("! wall %f comb %f user %f sys %f (best of %d)\n"
                      % (m[0], m[1] + m[2], m[1], m[2], count))
 
+@command('perfwalk')
 def perfwalk(ui, repo, *pats):
     try:
         m = scmutil.match(repo[None], pats, {})
@@ -40,11 +45,14 @@
         except Exception:
             timer(lambda: len(list(cmdutil.walk(repo, pats, {}))))
 
-def perfstatus(ui, repo, *pats):
+@command('perfstatus',
+         [('u', 'unknown', False,
+           'ask status to look for unknown files')])
+def perfstatus(ui, repo, **opts):
     #m = match.always(repo.root, repo.getcwd())
     #timer(lambda: sum(map(len, repo.dirstate.status(m, [], False, False,
     #                                                False))))
-    timer(lambda: sum(map(len, repo.status())))
+    timer(lambda: sum(map(len, repo.status(**opts))))
 
 def clearcaches(cl):
     # behave somewhat consistently across internal API changes
@@ -55,6 +63,7 @@
         cl._nodecache = {nullid: nullrev}
         cl._nodepos = None
 
+@command('perfheads')
 def perfheads(ui, repo):
     cl = repo.changelog
     def d():
@@ -62,6 +71,7 @@
         clearcaches(cl)
     timer(d)
 
+@command('perftags')
 def perftags(ui, repo):
     import mercurial.changelog, mercurial.manifest
     def t():
@@ -71,6 +81,7 @@
         return len(repo.tags())
     timer(t)
 
+@command('perfancestors')
 def perfancestors(ui, repo):
     heads = repo.changelog.headrevs()
     def d():
@@ -78,6 +89,17 @@
             pass
     timer(d)
 
+@command('perfancestorset')
+def perfancestorset(ui, repo, revset):
+    revs = repo.revs(revset)
+    heads = repo.changelog.headrevs()
+    def d():
+        s = repo.changelog.ancestors(heads)
+        for rev in revs:
+            rev in s
+    timer(d)
+
+@command('perfdirstate')
 def perfdirstate(ui, repo):
     "a" in repo.dirstate
     def d():
@@ -85,6 +107,7 @@
         "a" in repo.dirstate
     timer(d)
 
+@command('perfdirstatedirs')
 def perfdirstatedirs(ui, repo):
     "a" in repo.dirstate
     def d():
@@ -92,6 +115,7 @@
         del repo.dirstate._dirs
     timer(d)
 
+@command('perfdirstatewrite')
 def perfdirstatewrite(ui, repo):
     ds = repo.dirstate
     "a" in ds
@@ -100,6 +124,7 @@
         ds.write()
     timer(d)
 
+@command('perfmanifest')
 def perfmanifest(ui, repo):
     def d():
         t = repo.manifest.tip()
@@ -108,6 +133,7 @@
         repo.manifest._cache = None
     timer(d)
 
+@command('perfchangeset')
 def perfchangeset(ui, repo, rev):
     n = repo[rev].node()
     def d():
@@ -115,6 +141,7 @@
         #repo.changelog._cache = None
     timer(d)
 
+@command('perfindex')
 def perfindex(ui, repo):
     import mercurial.revlog
     mercurial.revlog._prereadsize = 2**24 # disable lazy parser in old hg
@@ -124,12 +151,14 @@
         cl.rev(n)
     timer(d)
 
+@command('perfstartup')
 def perfstartup(ui, repo):
     cmd = sys.argv[0]
     def d():
         os.system("HGRCPATH= %s version -q > /dev/null" % cmd)
     timer(d)
 
+@command('perfparents')
 def perfparents(ui, repo):
     nl = [repo.changelog.node(i) for i in xrange(1000)]
     def d():
@@ -137,22 +166,16 @@
             repo.changelog.parents(n)
     timer(d)
 
+@command('perflookup')
 def perflookup(ui, repo, rev):
     timer(lambda: len(repo.lookup(rev)))
 
+@command('perfrevrange')
 def perfrevrange(ui, repo, *specs):
     revrange = scmutil.revrange
     timer(lambda: len(revrange(repo, specs)))
 
-def perfnodelookup(ui, repo, rev):
-    import mercurial.revlog
-    mercurial.revlog._prereadsize = 2**24 # disable lazy parser in old hg
-    n = repo[rev].node()
-    def d():
-        cl = mercurial.revlog.revlog(repo.sopener, "00changelog.i")
-        cl.rev(n)
-    timer(d)
-
+@command('perfnodelookup')
 def perfnodelookup(ui, repo, rev):
     import mercurial.revlog
     mercurial.revlog._prereadsize = 2**24 # disable lazy parser in old hg
@@ -163,12 +186,15 @@
         clearcaches(cl)
     timer(d)
 
+@command('perflog',
+         [('', 'rename', False, 'ask log to follow renames')])
 def perflog(ui, repo, **opts):
     ui.pushbuffer()
     timer(lambda: commands.log(ui, repo, rev=[], date='', user='',
                                copies=opts.get('rename')))
     ui.popbuffer()
 
+@command('perftemplating')
 def perftemplating(ui, repo):
     ui.pushbuffer()
     timer(lambda: commands.log(ui, repo, rev=[], date='', user='',
@@ -176,15 +202,18 @@
                                ' {author|person}: {desc|firstline}\n'))
     ui.popbuffer()
 
+@command('perfcca')
 def perfcca(ui, repo):
     timer(lambda: scmutil.casecollisionauditor(ui, False, repo.dirstate))
 
+@command('perffncacheload')
 def perffncacheload(ui, repo):
     s = repo.store
     def d():
         s.fncache._load()
     timer(d)
 
+@command('perffncachewrite')
 def perffncachewrite(ui, repo):
     s = repo.store
     s.fncache._load()
@@ -193,6 +222,7 @@
         s.fncache.write()
     timer(d)
 
+@command('perffncacheencode')
 def perffncacheencode(ui, repo):
     s = repo.store
     s.fncache._load()
@@ -201,6 +231,7 @@
             s.encode(p)
     timer(d)
 
+@command('perfdiffwd')
 def perfdiffwd(ui, repo):
     """Profile diff of working directory changes"""
     options = {
@@ -218,6 +249,9 @@
         title = 'diffopts: %s' % (diffopt and ('-' + diffopt) or 'none')
         timer(d, title)
 
+@command('perfrevlog',
+         [('d', 'dist', 100, 'distance between the revisions')],
+         "[INDEXFILE]")
 def perfrevlog(ui, repo, file_, **opts):
     from mercurial import revlog
     dist = opts['dist']
@@ -228,32 +262,105 @@
 
     timer(d)
 
-cmdtable = {
-    'perfcca': (perfcca, []),
-    'perffncacheload': (perffncacheload, []),
-    'perffncachewrite': (perffncachewrite, []),
-    'perffncacheencode': (perffncacheencode, []),
-    'perflookup': (perflookup, []),
-    'perfrevrange': (perfrevrange, []),
-    'perfnodelookup': (perfnodelookup, []),
-    'perfparents': (perfparents, []),
-    'perfstartup': (perfstartup, []),
-    'perfstatus': (perfstatus, []),
-    'perfwalk': (perfwalk, []),
-    'perfmanifest': (perfmanifest, []),
-    'perfchangeset': (perfchangeset, []),
-    'perfindex': (perfindex, []),
-    'perfheads': (perfheads, []),
-    'perftags': (perftags, []),
-    'perfancestors': (perfancestors, []),
-    'perfdirstate': (perfdirstate, []),
-    'perfdirstatedirs': (perfdirstate, []),
-    'perfdirstatewrite': (perfdirstatewrite, []),
-    'perflog': (perflog,
-                [('', 'rename', False, 'ask log to follow renames')]),
-    'perftemplating': (perftemplating, []),
-    'perfdiffwd': (perfdiffwd, []),
-    'perfrevlog': (perfrevlog,
-                   [('d', 'dist', 100, 'distance between the revisions')],
-                   "[INDEXFILE]"),
-}
+@command('perfrevset',
+         [('C', 'clear', False, 'clear volatile cache between each call.')],
+         "REVSET")
+def perfrevset(ui, repo, expr, clear=False):
+    """benchmark the execution time of a revset
+
+    Use the --clean option if need to evaluate the impact of build volative
+    revisions set cache on the revset execution. Volatile cache hold filtered
+    and obsolete related cache."""
+    def d():
+        if clear:
+            repo.invalidatevolatilesets()
+        repo.revs(expr)
+    timer(d)
+
+@command('perfvolatilesets')
+def perfvolatilesets(ui, repo, *names):
+    """benchmark the computation of various volatile set
+
+    Volatile set computes element related to filtering and obsolescence."""
+    repo = repo.unfiltered()
+
+    def getobs(name):
+        def d():
+            repo.invalidatevolatilesets()
+            obsolete.getrevs(repo, name)
+        return d
+
+    allobs = sorted(obsolete.cachefuncs)
+    if names:
+        allobs = [n for n in allobs if n in names]
+
+    for name in allobs:
+        timer(getobs(name), title=name)
+
+    def getfiltered(name):
+        def d():
+            repo.invalidatevolatilesets()
+            repoview.filteredrevs(repo, name)
+        return d
+
+    allfilter = sorted(repoview.filtertable)
+    if names:
+        allfilter = [n for n in allfilter if n in names]
+
+    for name in allfilter:
+        timer(getfiltered(name), title=name)
+
+@command('perfbranchmap',
+         [('f', 'full', False,
+           'Includes build time of subset'),
+         ])
+def perfbranchmap(ui, repo, full=False):
+    """benchmark the update of a branchmap
+
+    This benchmarks the full repo.branchmap() call with read and write disabled
+    """
+    def getbranchmap(filtername):
+        """generate a benchmark function for the filtername"""
+        if filtername is None:
+            view = repo
+        else:
+            view = repo.filtered(filtername)
+        def d():
+            if full:
+                view._branchcaches.clear()
+            else:
+                view._branchcaches.pop(filtername, None)
+            view.branchmap()
+        return d
+    # add filter in smaller subset to bigger subset
+    possiblefilters = set(repoview.filtertable)
+    allfilters = []
+    while possiblefilters:
+        for name in possiblefilters:
+            subset = repoview.subsettable.get(name)
+            if subset not in possiblefilters:
+                break
+        else:
+            assert False, 'subset cycle %s!' % possiblefilters
+        allfilters.append(name)
+        possiblefilters.remove(name)
+
+    # warm the cache
+    if not full:
+        for name in allfilters:
+            repo.filtered(name).branchmap()
+    # add unfiltered
+    allfilters.append(None)
+    oldread = branchmap.read
+    oldwrite = branchmap.branchcache.write
+    try:
+        branchmap.read = lambda repo: None
+        branchmap.write = lambda repo: None
+        for name in allfilters:
+            timer(getbranchmap(name), title=str(name))
+    finally:
+        branchmap.read = oldread
+        branchmap.branchcache.write = oldwrite
+
+
+
--- a/contrib/synthrepo.py	Mon Jan 14 23:14:45 2013 +0900
+++ b/contrib/synthrepo.py	Sat Jan 19 17:24:33 2013 -0600
@@ -231,6 +231,8 @@
     fp.close()
 
     def cdf(l):
+        if not l:
+            return [], []
         vals, probs = zip(*sorted(l, key=lambda x: x[1], reverse=True))
         t = float(sum(probs, 0))
         s, cdfs = 0, []
--- a/contrib/vim/hgtest.vim	Mon Jan 14 23:14:45 2013 +0900
+++ b/contrib/vim/hgtest.vim	Sat Jan 19 17:24:33 2013 -0600
@@ -2,7 +2,8 @@
 " Language: Mercurial unified tests
 " Author: Steve Losh (steve@stevelosh.com)
 "
-" Add the following line to your ~/.vimrc to enable:
+" Place this file in ~/.vim/syntax/ and add the following line to your
+" ~/.vimrc to enable:
 " au BufNewFile,BufRead *.t set filetype=hgtest
 "
 " If you want folding you'll need the following line as well:
--- a/contrib/zsh_completion	Mon Jan 14 23:14:45 2013 +0900
+++ b/contrib/zsh_completion	Sat Jan 19 17:24:33 2013 -0600
@@ -174,11 +174,10 @@
 
   _hg_cmd tags | while read tag
   do
-    tags+=(${tag/ #    [0-9]#:*})
+    tags+=(${tag/ #[0-9]#:*})
   done
-  (( $#tags )) && _describe -t tags 'tags' tags
+  (( $#tags )) && _describe -t tags 'tags' tags
 }
-
 _hg_bookmarks() {
   typeset -a bookmark bookmarks
 
@@ -198,7 +197,7 @@
 
   _hg_cmd branches | while read branch
   do
-    branches+=(${branch/ #    [0-9]#:*})
+    branches+=(${branch/ #[0-9]#:*})
   done
   (( $#branches )) && _describe -t branches 'branches' branches
 }
@@ -208,12 +207,19 @@
   typeset -a heads
   local myrev
 
-  heads=(${(f)"$(_hg_cmd heads --template '{rev}\\n')"})
+  heads=(${(f)"$(_hg_cmd heads --template '{rev}:{branch}\\n')"})
   # exclude own revision
-  myrev=$(_hg_cmd log -r . --template '{rev}\\n')
+  myrev=$(_hg_cmd log -r . --template '{rev}:{branch}\\n')
   heads=(${heads:#$myrev})
 
   (( $#heads )) && _describe -t heads 'heads' heads
+
+  branches=(${(f)"$(_hg_cmd heads --template '{branch}\\n')"})
+  # exclude own revision
+  myrev=$(_hg_cmd log -r . --template '{branch}\\n')
+  branches=(${branches:#$myrev})
+
+  (( $#branches )) && _describe -t branches 'branches' branches
 }
 
 _hg_files() {
--- a/doc/hgmanpage.py	Mon Jan 14 23:14:45 2013 +0900
+++ b/doc/hgmanpage.py	Sat Jan 19 17:24:33 2013 -0600
@@ -146,7 +146,7 @@
                 text.extend(cell)
                 if not text[-1].endswith('\n'):
                     text[-1] += '\n'
-                if i < len(row)-1:
+                if i < len(row) - 1:
                     text.append('T}'+self._tab_char+'T{\n')
                 else:
                     text.append('T}\n')
@@ -258,7 +258,7 @@
             # ensure we get a ".TH" as viewers require it.
             self.head.append(self.header())
         # filter body
-        for i in xrange(len(self.body)-1, 0, -1):
+        for i in xrange(len(self.body) - 1, 0, -1):
             # remove superfluous vertical gaps.
             if self.body[i] == '.sp\n':
                 if self.body[i - 1][:4] in ('.BI ','.IP '):
@@ -880,7 +880,7 @@
         self.context[-3] = '.BI' # bold/italic alternate
         if node['delimiter'] != ' ':
             self.body.append('\\fB%s ' % node['delimiter'])
-        elif self.body[len(self.body)-1].endswith('='):
+        elif self.body[len(self.body) - 1].endswith('='):
             # a blank only means no blank in output, just changing font
             self.body.append(' ')
         else:
--- a/hgext/churn.py	Mon Jan 14 23:14:45 2013 +0900
+++ b/hgext/churn.py	Sat Jan 19 17:24:33 2013 -0600
@@ -144,8 +144,10 @@
     if not rate:
         return
 
-    sortkey = ((not opts.get('sort')) and (lambda x: -sum(x[1])) or None)
-    rate.sort(key=sortkey)
+    if opts.get('sort'):
+        rate.sort()
+    else:
+        rate.sort(key=lambda x: (-sum(x[1]), x))
 
     # Be careful not to have a zero maxcount (issue833)
     maxcount = float(max(sum(v) for k, v in rate)) or 1.0
--- a/hgext/color.py	Mon Jan 14 23:14:45 2013 +0900
+++ b/hgext/color.py	Sat Jan 19 17:24:33 2013 -0600
@@ -103,6 +103,7 @@
 import os
 
 from mercurial import commands, dispatch, extensions, ui as uimod, util
+from mercurial import templater
 from mercurial.i18n import _
 
 testedwith = 'internal'
@@ -354,6 +355,28 @@
                               for s in msg.split('\n')])
         return msg
 
+def templatelabel(context, mapping, args):
+    if len(args) != 2:
+        # i18n: "label" is a keyword
+        raise error.ParseError(_("label expects two arguments"))
+
+    thing = templater.stringify(args[1][0](context, mapping, args[1][1]))
+    thing = templater.runtemplate(context, mapping,
+                                  templater.compiletemplate(thing, context))
+
+    # apparently, repo could be a string that is the favicon?
+    repo = mapping.get('repo', '')
+    if isinstance(repo, str):
+        return thing
+
+    label = templater.stringify(args[0][0](context, mapping, args[0][1]))
+    label = templater.runtemplate(context, mapping,
+                                  templater.compiletemplate(label, context))
+
+    thing = templater.stringify(thing)
+    label = templater.stringify(label)
+
+    return repo.ui.label(thing, label)
 
 def uisetup(ui):
     global _terminfo_params
@@ -370,6 +393,7 @@
             configstyles(ui_)
         return orig(ui_, opts, cmd, cmdfunc)
     extensions.wrapfunction(dispatch, '_runcommand', colorcmd)
+    templater.funcs['label'] = templatelabel
 
 def extsetup(ui):
     commands.globalopts.append(
--- a/hgext/convert/__init__.py	Mon Jan 14 23:14:45 2013 +0900
+++ b/hgext/convert/__init__.py	Sat Jan 19 17:24:33 2013 -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).
+
     :hooks.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	Mon Jan 14 23:14:45 2013 +0900
+++ b/hgext/convert/common.py	Sat Jan 19 17:24:33 2013 -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/convcmd.py	Mon Jan 14 23:14:45 2013 +0900
+++ b/hgext/convert/convcmd.py	Sat Jan 19 17:24:33 2013 -0600
@@ -147,7 +147,7 @@
         map contains valid revision identifiers and merge the new
         links in the source graph.
         """
-        for c in splicemap:
+        for c in sorted(splicemap):
             if c not in parents:
                 if not self.dest.hascommit(self.map.get(c, c)):
                     # Could be in source but not converted during this run
@@ -175,7 +175,7 @@
             revisions without parents. 'parents' must be a mapping of revision
             identifier to its parents ones.
             """
-            visit = parents.keys()
+            visit = sorted(parents)
             seen = set()
             children = {}
             roots = []
--- a/hgext/convert/cvs.py	Mon Jan 14 23:14:45 2013 +0900
+++ b/hgext/convert/cvs.py	Sat Jan 19 17:24:33 2013 -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	Mon Jan 14 23:14:45 2013 +0900
+++ b/hgext/convert/cvsps.py	Sat Jan 19 17:24:33 2013 -0600
@@ -19,6 +19,7 @@
         .branch    - name of branch this revision is on
         .branches  - revision tuple of branches starting at this revision
         .comment   - commit message
+        .commitid  - CVS commitid or None
         .date      - the commit date as a (time, tz) tuple
         .dead      - true if file revision is dead
         .file      - Name of file
@@ -28,19 +29,17 @@
         .revision  - revision number as tuple
         .tags      - list of tags on the file
         .synthetic - is this a synthetic "file ... added on ..." revision?
-        .mergepoint- the branch that has been merged from
-                     (if present in rlog output)
-        .branchpoints- the branches that start at the current entry
+        .mergepoint - the branch that has been merged from (if present in
+                      rlog output) or None
+        .branchpoints - the branches that start at the current entry or empty
     '''
     def __init__(self, **entries):
         self.synthetic = False
         self.__dict__.update(entries)
 
     def __repr__(self):
-        return "<%s at 0x%x: %s %s>" % (self.__class__.__name__,
-                                        id(self),
-                                        self.file,
-                                        ".".join(map(str, self.revision)))
+        items = ("%s=%r"%(k, self.__dict__[k]) for k in sorted(self.__dict__))
+        return "%s(%s)"%(type(self).__name__, ", ".join(items))
 
 class logerror(Exception):
     pass
@@ -113,6 +112,7 @@
     re_50 = re.compile('revision ([\\d.]+)(\s+locked by:\s+.+;)?$')
     re_60 = re.compile(r'date:\s+(.+);\s+author:\s+(.+);\s+state:\s+(.+?);'
                        r'(\s+lines:\s+(\+\d+)?\s+(-\d+)?;)?'
+                       r'(\s+commitid:\s+([^;]+);)?'
                        r'(.*mergepoint:\s+([^;]+);)?')
     re_70 = re.compile('branches: (.+);$')
 
@@ -171,6 +171,14 @@
         try:
             ui.note(_('reading cvs log cache %s\n') % cachefile)
             oldlog = pickle.load(open(cachefile))
+            for e in oldlog:
+                if not (util.safehasattr(e, 'branchpoints') and
+                        util.safehasattr(e, 'commitid') and
+                        util.safehasattr(e, 'mergepoint')):
+                    ui.status(_('ignoring old cache\n'))
+                    oldlog = []
+                    break
+
             ui.note(_('cache has %d log entries\n') % len(oldlog))
         except Exception, e:
             ui.note(_('error reading cache: %r\n') % e)
@@ -296,9 +304,16 @@
             # as this state is re-entered for subsequent revisions of a file.
             match = re_50.match(line)
             assert match, _('expected revision number')
-            e = logentry(rcs=scache(rcs), file=scache(filename),
-                    revision=tuple([int(x) for x in match.group(1).split('.')]),
-                    branches=[], parent=None)
+            e = logentry(rcs=scache(rcs),
+                         file=scache(filename),
+                         revision=tuple([int(x) for x in
+                                         match.group(1).split('.')]),
+                         branches=[],
+                         parent=None,
+                         commitid=None,
+                         mergepoint=None,
+                         branchpoints=set())
+
             state = 6
 
         elif state == 6:
@@ -329,8 +344,11 @@
             else:
                 e.lines = None
 
-            if match.group(7): # cvsnt mergepoint
-                myrev = match.group(8).split('.')
+            if match.group(7): # cvs 1.12 commitid
+                e.commitid = match.group(8)
+
+            if match.group(9): # cvsnt mergepoint
+                myrev = match.group(10).split('.')
                 if len(myrev) == 2: # head
                     e.mergepoint = 'HEAD'
                 else:
@@ -339,8 +357,7 @@
                     assert len(branches) == 1, ('unknown branch: %s'
                                                 % e.mergepoint)
                     e.mergepoint = branches[0]
-            else:
-                e.mergepoint = None
+
             e.comment = []
             state = 7
 
@@ -469,23 +486,22 @@
         .author    - author name as CVS knows it
         .branch    - name of branch this changeset is on, or None
         .comment   - commit message
+        .commitid  - CVS commitid or None
         .date      - the commit date as a (time,tz) tuple
         .entries   - list of logentry objects in this changeset
         .parents   - list of one or two parent changesets
         .tags      - list of tags on this changeset
         .synthetic - from synthetic revision "file ... added on branch ..."
-        .mergepoint- the branch that has been merged from
-                     (if present in rlog output)
-        .branchpoints- the branches that start at the current entry
+        .mergepoint- the branch that has been merged from or None
+        .branchpoints- the branches that start at the current entry or empty
     '''
     def __init__(self, **entries):
         self.synthetic = False
         self.__dict__.update(entries)
 
     def __repr__(self):
-        return "<%s at 0x%x: %s>" % (self.__class__.__name__,
-                                     id(self),
-                                     getattr(self, 'id', "(no id)"))
+        items = ("%s=%r"%(k, self.__dict__[k]) for k in sorted(self.__dict__))
+        return "%s(%s)"%(type(self).__name__, ", ".join(items))
 
 def createchangeset(ui, log, fuzz=60, mergefrom=None, mergeto=None):
     '''Convert log into changesets.'''
@@ -493,8 +509,8 @@
     ui.status(_('creating changesets\n'))
 
     # Merge changesets
-
-    log.sort(key=lambda x: (x.comment, x.author, x.branch, x.date))
+    log.sort(key=lambda x: (x.commitid, x.comment, x.author, x.branch, x.date,
+                            x.branchpoints))
 
     changesets = []
     files = set()
@@ -517,22 +533,24 @@
         # first changeset and bar the next and MYBRANCH and MYBRANCH2
         # should both start off of the bar changeset. No provisions are
         # made to ensure that this is, in fact, what happens.
-        if not (c and
-                  e.comment == c.comment and
-                  e.author == c.author and
-                  e.branch == c.branch and
-                  (not util.safehasattr(e, 'branchpoints') or
-                    not util.safehasattr (c, 'branchpoints') or
-                    e.branchpoints == c.branchpoints) and
-                  ((c.date[0] + c.date[1]) <=
-                   (e.date[0] + e.date[1]) <=
-                   (c.date[0] + c.date[1]) + fuzz) and
-                  e.file not in files):
+        if not (c and e.branchpoints == c.branchpoints and
+                (# cvs commitids
+                 (e.commitid is not None and e.commitid == c.commitid) or
+                 (# no commitids, use fuzzy commit detection
+                  (e.commitid is None or c.commitid is None) and
+                   e.comment == c.comment and
+                   e.author == c.author and
+                   e.branch == c.branch and
+                   ((c.date[0] + c.date[1]) <=
+                    (e.date[0] + e.date[1]) <=
+                    (c.date[0] + c.date[1]) + fuzz) and
+                   e.file not in files))):
             c = changeset(comment=e.comment, author=e.author,
-                          branch=e.branch, date=e.date, entries=[],
-                          mergepoint=getattr(e, 'mergepoint', None),
-                          branchpoints=getattr(e, 'branchpoints', set()))
+                          branch=e.branch, date=e.date,
+                          entries=[], mergepoint=e.mergepoint,
+                          branchpoints=e.branchpoints, commitid=e.commitid)
             changesets.append(c)
+
             files = set()
             if len(changesets) % 100 == 0:
                 t = '%d %s' % (len(changesets), repr(e.comment)[1:-1])
@@ -801,22 +819,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)'))
-            branchpoints = getattr(cs, 'branchpoints', None)
-            if branchpoints:
-                ui.write('Branchpoints: %s \n' % ', '.join(branchpoints))
+            ui.write(('PatchSet %d \n' % cs.id))
+            ui.write(('Date: %s\n' % util.datestr(cs.date,
+                                                 '%Y/%m/%d %H:%M:%S %1%2')))
+            ui.write(('Author: %s\n' % cs.author))
+            ui.write(('Branch: %s\n' % (cs.branch or 'HEAD')))
+            ui.write(('Tag%s: %s \n' % (['', 's'][len(cs.tags) > 1],
+                                  ','.join(cs.tags) or '(none)')))
+            if cs.branchpoints:
+                ui.write(('Branchpoints: %s \n') %
+                         ', '.join(sorted(cs.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 +843,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	Mon Jan 14 23:14:45 2013 +0900
+++ b/hgext/convert/git.py	Sat Jan 19 17:24:33 2013 -0600
@@ -6,12 +6,24 @@
 # GNU General Public License version 2 or any later version.
 
 import os
-from mercurial import util
+from mercurial import util, config
 from mercurial.node import hex, nullid
 from mercurial.i18n import _
 
 from common import NoRepo, commit, converter_source, checktool
 
+class submodule(object):
+    def __init__(self, path, node, url):
+        self.path = path
+        self.node = node
+        self.url = url
+
+    def hgsub(self):
+        return "%s = [git]%s" % (self.path, self.url)
+
+    def hgsubstate(self):
+        return "%s %s" % (self.node, self.path)
+
 class convert_git(converter_source):
     # Windows does not support GIT_DIR= construct while other systems
     # cannot remove environment variable. Just assume none have
@@ -55,6 +67,7 @@
         checktool('git', 'git')
 
         self.path = path
+        self.submodules = []
 
     def getheads(self):
         if not self.rev:
@@ -76,16 +89,57 @@
         return data
 
     def getfile(self, name, rev):
-        data = self.catfile(rev, "blob")
-        mode = self.modecache[(name, rev)]
+        if name == '.hgsub':
+            data = '\n'.join([m.hgsub() for m in self.submoditer()])
+            mode = ''
+        elif name == '.hgsubstate':
+            data = '\n'.join([m.hgsubstate() for m in self.submoditer()])
+            mode = ''
+        else:
+            data = self.catfile(rev, "blob")
+            mode = self.modecache[(name, rev)]
         return data, mode
 
+    def submoditer(self):
+        null = hex(nullid)
+        for m in sorted(self.submodules, key=lambda p: p.path):
+            if m.node != null:
+                yield m
+
+    def parsegitmodules(self, content):
+        """Parse the formatted .gitmodules file, example file format:
+        [submodule "sub"]\n
+        \tpath = sub\n
+        \turl = git://giturl\n
+        """
+        self.submodules = []
+        c = config.config()
+        # Each item in .gitmodules starts with \t that cant be parsed
+        c.parse('.gitmodules', content.replace('\t',''))
+        for sec in c.sections():
+            s = c[sec]
+            if 'url' in s and 'path' in s:
+                self.submodules.append(submodule(s['path'], '', s['url']))
+
+    def retrievegitmodules(self, version):
+        modules, ret = self.gitread("git show %s:%s" % (version, '.gitmodules'))
+        if ret:
+            raise util.Abort(_('cannot read submodules config file in %s') %
+                             version)
+        self.parsegitmodules(modules)
+        for m in self.submodules:
+            node, ret = self.gitread("git rev-parse %s:%s" % (version, m.path))
+            if ret:
+                continue
+            m.node = node.strip()
+
     def getchanges(self, version):
         self.modecache = {}
         fh = self.gitopen("git diff-tree -z --root -m -r %s" % version)
         changes = []
         seen = set()
         entry = None
+        subexists = False
         for l in fh.read().split('\x00'):
             if not entry:
                 if not l.startswith(':'):
@@ -97,15 +151,24 @@
                 seen.add(f)
                 entry = entry.split()
                 h = entry[3]
-                if entry[1] == '160000':
-                    raise util.Abort('git submodules are not supported!')
                 p = (entry[1] == "100755")
                 s = (entry[1] == "120000")
-                self.modecache[(f, h)] = (p and "x") or (s and "l") or ""
-                changes.append((f, h))
+
+                if f == '.gitmodules':
+                    subexists = True
+                    changes.append(('.hgsub', ''))
+                elif entry[1] == '160000' or entry[0] == ':160000':
+                    subexists = True
+                else:
+                    self.modecache[(f, h)] = (p and "x") or (s and "l") or ""
+                    changes.append((f, h))
             entry = None
         if fh.close():
             raise util.Abort(_('cannot read changes in %s') % version)
+
+        if subexists:
+            self.retrievegitmodules(version)
+            changes.append(('.hgsubstate', ''))
         return (changes, {})
 
     def getcommit(self, version):
--- a/hgext/convert/hg.py	Mon Jan 14 23:14:45 2013 +0900
+++ b/hgext/convert/hg.py	Sat Jan 19 17:24:33 2013 -0600
@@ -110,7 +110,7 @@
 
         if missings:
             self.after()
-            for pbranch, heads in missings.iteritems():
+            for pbranch, heads in sorted(missings.iteritems()):
                 pbranchpath = os.path.join(self.path, pbranch)
                 prepo = hg.peer(self.ui, {}, pbranchpath)
                 self.ui.note(_('pulling from %s into %s\n') % (pbranch, branch))
@@ -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	Mon Jan 14 23:14:45 2013 +0900
+++ b/hgext/convert/subversion.py	Sat Jan 19 17:24:33 2013 -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
@@ -376,7 +377,7 @@
             rpath = self.url.strip('/')
             branchnames = svn.client.ls(rpath + '/' + quote(branches),
                                         rev, False, self.ctx)
-            for branch in branchnames.keys():
+            for branch in sorted(branchnames):
                 module = '%s/%s/%s' % (oldmodule, branches, branch)
                 if not isdir(module, self.last_changed):
                     continue
@@ -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	Mon Jan 14 23:14:45 2013 +0900
+++ b/hgext/eol.py	Sat Jan 19 17:24:33 2013 -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/graphlog.py	Mon Jan 14 23:14:45 2013 +0900
+++ b/hgext/graphlog.py	Sat Jan 19 17:24:33 2013 -0600
@@ -39,7 +39,6 @@
      _('show changesets within the given named branch'), _('BRANCH')),
     ('P', 'prune', [],
      _('do not display revision or any of its ancestors'), _('REV')),
-    ('', 'hidden', False, _('show hidden changesets (DEPRECATED)')),
     ] + commands.logopts + commands.walkopts,
     _('[OPTION]... [FILE]'))
 def graphlog(ui, repo, *pats, **opts):
--- a/hgext/hgk.py	Mon Jan 14 23:14:45 2013 +0900
+++ b/hgext/hgk.py	Sat Jan 19 17:24:33 2013 -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/highlight/highlight.py	Mon Jan 14 23:14:45 2013 +0900
+++ b/hgext/highlight/highlight.py	Sat Jan 19 17:24:33 2013 -0600
@@ -50,7 +50,7 @@
     colorized = highlight(text, lexer, formatter)
     # strip wrapping div
     colorized = colorized[:colorized.find('\n</pre>')]
-    colorized = colorized[colorized.find('<pre>')+5:]
+    colorized = colorized[colorized.find('<pre>') + 5:]
     coloriter = (s.encode(encoding.encoding, 'replace')
                  for s in colorized.splitlines())
 
--- a/hgext/histedit.py	Mon Jan 14 23:14:45 2013 +0900
+++ b/hgext/histedit.py	Sat Jan 19 17:24:33 2013 -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
@@ -177,6 +176,31 @@
 #
 """)
 
+def commitfuncfor(repo, src):
+    """Build a commit function for the replacement of <src>
+
+    This function ensure we apply the same treatement to all changesets.
+
+    - Add a 'histedit_source' entry in extra.
+
+    Note that fold have its own separated logic because its handling is a bit
+    different and not easily factored out of the fold method.
+    """
+    phasemin = src.phase()
+    def commitfunc(**kwargs):
+        phasebackup = repo.ui.backupconfig('phases', 'new-commit')
+        try:
+            repo.ui.setconfig('phases', 'new-commit', phasemin)
+            extra = kwargs.get('extra', {}).copy()
+            extra['histedit_source'] = src.hex()
+            kwargs['extra'] = extra
+            return repo.commit(**kwargs)
+        finally:
+            repo.ui.restoreconfig(phasebackup)
+    return commitfunc
+
+
+
 def applychanges(ui, repo, ctx, opts):
     """Merge changeset from ctx (only) in the current working directory"""
     wcpar = repo.dirstate.parents()[0]
@@ -255,7 +279,7 @@
         message = first.description()
     user = commitopts.get('user')
     date = commitopts.get('date')
-    extra = first.extra()
+    extra = commitopts.get('extra')
 
     parents = (first.p1().node(), first.p2().node())
     new = context.memctx(repo,
@@ -280,8 +304,9 @@
         raise util.Abort(_('Fix up the change and run '
                            'hg histedit --continue'))
     # drop the second merge parent
-    n = repo.commit(text=oldctx.description(), user=oldctx.user(),
-                    date=oldctx.date(), extra=oldctx.extra())
+    commit = commitfuncfor(repo, oldctx)
+    n = commit(text=oldctx.description(), user=oldctx.user(),
+               date=oldctx.date(), extra=oldctx.extra())
     if n is None:
         ui.warn(_('%s: empty changeset\n')
                      % node.hex(ha))
@@ -332,7 +357,19 @@
     commitopts['message'] = newmessage
     # date
     commitopts['date'] = max(ctx.date(), oldctx.date())
-    n = collapse(repo, ctx, repo[newnode], commitopts)
+    extra = ctx.extra().copy()
+    # histedit_source
+    # note: ctx is likely a temporary commit but that the best we can do here
+    #       This is sufficient to solve issue3681 anyway
+    extra['histedit_source'] = '%s,%s' % (ctx.hex(), oldctx.hex())
+    commitopts['extra'] = extra
+    phasebackup = repo.ui.backupconfig('phases', 'new-commit')
+    try:
+        phasemin = max(ctx.phase(), oldctx.phase())
+        repo.ui.setconfig('phases', 'new-commit', phasemin)
+        n = collapse(repo, ctx, repo[newnode], commitopts)
+    finally:
+        repo.ui.restoreconfig(phasebackup)
     if n is None:
         return ctx, []
     hg.update(repo, n)
@@ -357,8 +394,9 @@
                            'hg histedit --continue'))
     message = oldctx.description() + '\n'
     message = ui.edit(message, ui.username())
-    new = repo.commit(text=message, user=oldctx.user(), date=oldctx.date(),
-                      extra=oldctx.extra())
+    commit = commitfuncfor(repo, oldctx)
+    new = commit(text=message, user=oldctx.user(), date=oldctx.date(),
+                 extra=oldctx.extra())
     newctx = repo[new]
     if oldctx.node() != newctx.node():
         return newctx, [(oldctx.node(), (new,))]
@@ -559,9 +597,10 @@
             editor = cmdutil.commitforceeditor
         else:
             editor = False
-        new = repo.commit(text=message, user=ctx.user(),
-                          date=ctx.date(), extra=ctx.extra(),
-                          editor=editor)
+        commit = commitfuncfor(repo, ctx)
+        new = commit(text=message, user=ctx.user(),
+                     date=ctx.date(), extra=ctx.extra(),
+                     editor=editor)
         if new is not None:
             newchildren.append(new)
 
@@ -594,7 +633,8 @@
     When keep is false, the specified set can't have children."""
     ctxs = list(repo.set('%n::%n', old, new))
     if ctxs and not keep:
-        if repo.revs('(%ld::) - (%ld + hidden())', ctxs, ctxs):
+        if (not obsolete._enabled and
+            repo.revs('(%ld::) - (%ld)', ctxs, ctxs)):
             raise util.Abort(_('cannot edit history that would orphan nodes'))
         root = ctxs[0] # list is already sorted by repo.set
         if not root.phase():
@@ -720,9 +760,9 @@
         # if nothing got rewritten there is not purpose for this function
         return
     moves = []
-    for bk, old in repo._bookmarks.iteritems():
+    for bk, old in sorted(repo._bookmarks.iteritems()):
         if old == oldtopmost:
-            # special case ensure bookmark stay on tip. 
+            # special case ensure bookmark stay on tip.
             #
             # This is arguably a feature and we may only want that for the
             # active bookmark. But the behavior is kept compatible with the old
@@ -740,12 +780,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/inotify/linux/watcher.py	Mon Jan 14 23:14:45 2013 +0900
+++ b/hgext/inotify/linux/watcher.py	Sat Jan 19 17:24:33 2013 -0600
@@ -72,7 +72,7 @@
 
     def __repr__(self):
         r = repr(self.raw)
-        return 'event(path=' + repr(self.path) + ', ' + r[r.find('(')+1:]
+        return 'event(path=' + repr(self.path) + ', ' + r[r.find('(') + 1:]
 
 
 _event_props = {
--- a/hgext/inotify/linuxserver.py	Mon Jan 14 23:14:45 2013 +0900
+++ b/hgext/inotify/linuxserver.py	Sat Jan 19 17:24:33 2013 -0600
@@ -405,14 +405,7 @@
 
     def shutdown(self):
         self.sock.close()
-        try:
-            os.unlink(self.sockpath)
-            if self.realsockpath:
-                os.unlink(self.realsockpath)
-                os.rmdir(os.path.dirname(self.realsockpath))
-        except OSError, err:
-            if err.errno != errno.ENOENT:
-                raise
+        self.sock.cleanup()
 
     def answer_stat_query(self, cs):
         if self.repowatcher.timeout:
--- a/hgext/inotify/server.py	Mon Jan 14 23:14:45 2013 +0900
+++ b/hgext/inotify/server.py	Sat Jan 19 17:24:33 2013 -0600
@@ -6,7 +6,7 @@
 # GNU General Public License version 2 or any later version.
 
 from mercurial.i18n import _
-from mercurial import cmdutil, osutil, util
+from mercurial import cmdutil, posix, osutil, util
 import common
 
 import errno
@@ -15,7 +15,6 @@
 import stat
 import struct
 import sys
-import tempfile
 
 class AlreadyStartedException(Exception):
     pass
@@ -330,42 +329,15 @@
     def __init__(self, ui, root, repowatcher, timeout):
         self.ui = ui
         self.repowatcher = repowatcher
-        self.sock = socket.socket(socket.AF_UNIX)
-        self.sockpath = join(root, '.hg/inotify.sock')
-
-        self.realsockpath = self.sockpath
-        if os.path.islink(self.sockpath):
-            if os.path.exists(self.sockpath):
-                self.realsockpath = os.readlink(self.sockpath)
-            else:
-                raise util.Abort('inotify-server: cannot start: '
-                                '.hg/inotify.sock is a broken symlink')
         try:
-            self.sock.bind(self.realsockpath)
-        except socket.error, err:
+            self.sock = posix.unixdomainserver(
+                lambda p: os.path.join(root, '.hg', p),
+                'inotify')
+        except (OSError, socket.error), err:
             if err.args[0] == errno.EADDRINUSE:
-                raise AlreadyStartedException(_('cannot start: socket is '
-                                                'already bound'))
-            if err.args[0] == "AF_UNIX path too long":
-                tempdir = tempfile.mkdtemp(prefix="hg-inotify-")
-                self.realsockpath = os.path.join(tempdir, "inotify.sock")
-                try:
-                    self.sock.bind(self.realsockpath)
-                    os.symlink(self.realsockpath, self.sockpath)
-                except (OSError, socket.error), inst:
-                    try:
-                        os.unlink(self.realsockpath)
-                    except OSError:
-                        pass
-                    os.rmdir(tempdir)
-                    if inst.errno == errno.EEXIST:
-                        raise AlreadyStartedException(_('cannot start: tried '
-                            'linking .hg/inotify.sock to a temporary socket but'
-                            ' .hg/inotify.sock already exists'))
-                    raise
-            else:
-                raise
-        self.sock.listen(5)
+                raise AlreadyStartedException(_('cannot start: '
+                                                'socket is already bound'))
+            raise
         self.fileno = self.sock.fileno
 
     def answer_stat_query(self, cs):
--- a/hgext/largefiles/basestore.py	Mon Jan 14 23:14:45 2013 +0900
+++ b/hgext/largefiles/basestore.py	Sat Jan 19 17:24:33 2013 -0600
@@ -26,14 +26,8 @@
         self.detail = detail
 
     def longmessage(self):
-        if self.url:
-            return ('%s: %s\n'
-                    '(failed URL: %s)\n'
-                    % (self.filename, self.detail, self.url))
-        else:
-            return ('%s: %s\n'
-                    '(no default or default-push path set in hgrc)\n'
-                    % (self.filename, self.detail))
+        return (_("error getting %s from %s for %s: %s\n") %
+                 (self.hash, self.url, self.filename, self.detail))
 
     def __str__(self):
         return "%s: %s" % (self.url, self.detail)
--- a/hgext/largefiles/lfcommands.py	Mon Jan 14 23:14:45 2013 +0900
+++ b/hgext/largefiles/lfcommands.py	Sat Jan 19 17:24:33 2013 -0600
@@ -383,6 +383,13 @@
     store = basestore._openstore(repo)
     return store.verify(revs, contents=contents)
 
+def debugdirstate(ui, repo):
+    '''Show basic information for the largefiles dirstate'''
+    lfdirstate = lfutil.openlfdirstate(ui, repo)
+    for file_, ent in sorted(lfdirstate._map.iteritems()):
+        mode = '%3o' % (ent[1] & 0777 & ~util.umask)
+        ui.write("%c %s %10d %s\n" % (ent[0], mode, ent[2], file_))
+
 def cachelfiles(ui, repo, node, filelist=None):
     '''cachelfiles ensures that all largefiles needed by the specified revision
     are present in the repository's largefile cache.
--- a/hgext/largefiles/lfutil.py	Mon Jan 14 23:14:45 2013 +0900
+++ b/hgext/largefiles/lfutil.py	Sat Jan 19 17:24:33 2013 -0600
@@ -18,43 +18,10 @@
 from mercurial.i18n import _
 
 shortname = '.hglf'
+shortnameslash = shortname + '/'
 longname = 'largefiles'
 
 
-# -- Portability wrappers ----------------------------------------------
-
-def dirstatewalk(dirstate, matcher, unknown=False, ignored=False):
-    return dirstate.walk(matcher, [], unknown, ignored)
-
-def repoadd(repo, list):
-    add = repo[None].add
-    return add(list)
-
-def reporemove(repo, list, unlink=False):
-    def remove(list, unlink):
-        wlock = repo.wlock()
-        try:
-            if unlink:
-                for f in list:
-                    try:
-                        util.unlinkpath(repo.wjoin(f))
-                    except OSError, inst:
-                        if inst.errno != errno.ENOENT:
-                            raise
-            repo[None].forget(list)
-        finally:
-            wlock.release()
-    return remove(list, unlink=unlink)
-
-def repoforget(repo, list):
-    forget = repo[None].forget
-    return forget(list)
-
-def findoutgoing(repo, remote, force):
-    from mercurial import discovery
-    outgoing = discovery.findcommonoutgoing(repo, remote.peer(), force=force)
-    return outgoing.missing
-
 # -- Private worker functions ------------------------------------------
 
 def getminsize(ui, assumelfiles, opt, default=10):
@@ -139,24 +106,26 @@
         return super(largefilesdirstate, self).forget(unixpath(f))
     def normallookup(self, f):
         return super(largefilesdirstate, self).normallookup(unixpath(f))
+    def _ignore(self):
+        return False
 
 def openlfdirstate(ui, repo, create=True):
     '''
     Return a dirstate object that tracks largefiles: i.e. its root is
     the repo root, but it is saved in .hg/largefiles/dirstate.
     '''
-    admin = repo.join(longname)
-    opener = scmutil.opener(admin)
+    lfstoredir = repo.join(longname)
+    opener = scmutil.opener(lfstoredir)
     lfdirstate = largefilesdirstate(opener, ui, repo.root,
                                      repo.dirstate._validate)
 
     # If the largefiles dirstate does not exist, populate and create
     # it. This ensures that we create it on the first meaningful
     # largefiles operation in a new clone.
-    if create and not os.path.exists(os.path.join(admin, 'dirstate')):
-        util.makedirs(admin)
+    if create and not os.path.exists(os.path.join(lfstoredir, 'dirstate')):
+        util.makedirs(lfstoredir)
         matcher = getstandinmatcher(repo)
-        for standin in dirstatewalk(repo.dirstate, matcher):
+        for standin in repo.dirstate.walk(matcher, [], False, False):
             lfile = splitstandin(standin)
             hash = readstandin(repo, lfile)
             lfdirstate.normallookup(lfile)
@@ -173,8 +142,11 @@
     s = lfdirstate.status(match, [], False, False, False)
     unsure, modified, added, removed, missing, unknown, ignored, clean = s
     for lfile in unsure:
-        if repo[rev][standin(lfile)].data().strip() != \
-                hashfile(repo.wjoin(lfile)):
+        try:
+            fctx = repo[rev][standin(lfile)]
+        except LookupError:
+            fctx = None
+        if not fctx or fctx.data().strip() != hashfile(repo.wjoin(lfile)):
             modified.append(lfile)
         else:
             clean.append(lfile)
@@ -250,7 +222,7 @@
 
 def getstandinmatcher(repo, pats=[], opts={}):
     '''Return a match object that applies pats to the standin directory'''
-    standindir = repo.pathto(shortname)
+    standindir = repo.wjoin(shortname)
     if pats:
         # patterns supplied: search standin directory relative to current dir
         cwd = repo.getcwd()
@@ -264,19 +236,11 @@
         pats = [standindir]
     else:
         # no patterns and no standin dir: return matcher that matches nothing
-        match = match_.match(repo.root, None, [], exact=True)
-        match.matchfn = lambda f: False
-        return match
-    return getmatcher(repo, pats, opts, showbad=False)
+        return match_.match(repo.root, None, [], exact=True)
 
-def getmatcher(repo, pats=[], opts={}, showbad=True):
-    '''Wrapper around scmutil.match() that adds showbad: if false,
-    neuter the match object's bad() method so it does not print any
-    warnings about missing files or directories.'''
+    # no warnings about missing files or directories
     match = scmutil.match(repo[None], pats, opts)
-
-    if not showbad:
-        match.bad = lambda f, msg: None
+    match.bad = lambda f, msg: None
     return match
 
 def composestandinmatcher(repo, rmatcher):
@@ -296,17 +260,17 @@
     file.'''
     # Notes:
     # 1) Some callers want an absolute path, but for instance addlargefiles
-    #    needs it repo-relative so it can be passed to repoadd().  So leave
-    #    it up to the caller to use repo.wjoin() to get an absolute path.
+    #    needs it repo-relative so it can be passed to repo[None].add().  So
+    #    leave it up to the caller to use repo.wjoin() to get an absolute path.
     # 2) Join with '/' because that's what dirstate always uses, even on
     #    Windows. Change existing separator to '/' first in case we are
     #    passed filenames from an external source (like the command line).
-    return shortname + '/' + util.pconvert(filename)
+    return shortnameslash + util.pconvert(filename)
 
 def isstandin(filename):
     '''Return true if filename is a big file standin. filename must be
     in Mercurial's internal form (slash-separated).'''
-    return filename.startswith(shortname + '/')
+    return filename.startswith(shortnameslash)
 
 def splitstandin(filename):
     # Split on / because that's what dirstate always uses, even on Windows.
@@ -435,7 +399,7 @@
 
 def islfilesrepo(repo):
     if ('largefiles' in repo.requirements and
-            util.any(shortname + '/' in f[0] for f in repo.store.datafiles())):
+            util.any(shortnameslash in f[0] for f in repo.store.datafiles())):
         return True
 
     return util.any(openlfdirstate(repo.ui, repo, False))
@@ -455,9 +419,13 @@
 def getstandinsstate(repo):
     standins = []
     matcher = getstandinmatcher(repo)
-    for standin in dirstatewalk(repo.dirstate, matcher):
+    for standin in repo.dirstate.walk(matcher, [], False, False):
         lfile = splitstandin(standin)
-        standins.append((lfile, readstandin(repo, lfile)))
+        try:
+            hash = readstandin(repo, lfile)
+        except IOError:
+            hash = None
+        standins.append((lfile, hash))
     return standins
 
 def getlfilestoupdate(oldstandins, newstandins):
--- a/hgext/largefiles/localstore.py	Mon Jan 14 23:14:45 2013 +0900
+++ b/hgext/largefiles/localstore.py	Sat Jan 19 17:24:33 2013 -0600
@@ -22,9 +22,8 @@
     the user cache.'''
 
     def __init__(self, ui, repo, remote):
-        url = os.path.join(remote.local().path, '.hg', lfutil.longname)
-        super(localstore, self).__init__(ui, repo, util.expandpath(url))
         self.remote = remote.local()
+        super(localstore, self).__init__(ui, repo, self.remote.url())
 
     def put(self, source, hash):
         util.makedirs(os.path.dirname(lfutil.storepath(self.remote, hash)))
@@ -46,7 +45,7 @@
         elif lfutil.inusercache(self.ui, hash):
             path = lfutil.usercachepath(self.ui, hash)
         else:
-            raise basestore.StoreError(filename, hash, '',
+            raise basestore.StoreError(filename, hash, self.url,
                 _("can't get file locally"))
         fd = open(path, 'rb')
         try:
--- a/hgext/largefiles/overrides.py	Mon Jan 14 23:14:45 2013 +0900
+++ b/hgext/largefiles/overrides.py	Sat Jan 19 17:24:33 2013 -0600
@@ -12,7 +12,7 @@
 import copy
 
 from mercurial import hg, commands, util, cmdutil, scmutil, match as match_, \
-    node, archival, error, merge
+    node, archival, error, merge, discovery
 from mercurial.i18n import _
 from mercurial.node import hex
 from hgext import rebase
@@ -116,7 +116,7 @@
                     lfdirstate.add(f)
             lfdirstate.write()
             bad += [lfutil.splitstandin(f)
-                    for f in lfutil.repoadd(repo, standins)
+                    for f in repo[None].add(standins)
                     if f in m.files()]
     finally:
         wlock.release()
@@ -137,21 +137,23 @@
                                         if lfutil.standin(f) in manifest]
                                        for list in [s[0], s[1], s[3], s[6]]]
 
-    def warn(files, reason):
+    def warn(files, msg):
         for f in files:
-            ui.warn(_('not removing %s: %s (use forget to undo)\n')
-                    % (m.rel(f), reason))
+            ui.warn(msg % m.rel(f))
         return int(len(files) > 0)
 
     result = 0
 
     if after:
         remove, forget = deleted, []
-        result = warn(modified + added + clean, _('file still exists'))
+        result = warn(modified + added + clean,
+                      _('not removing %s: file still exists\n'))
     else:
         remove, forget = deleted + clean, []
-        result = warn(modified, _('file is modified'))
-        result = warn(added, _('file has been marked for add')) or result
+        result = warn(modified, _('not removing %s: file is modified (use -f'
+                                  ' to force removal)\n'))
+        result = warn(added, _('not removing %s: file has been marked for add'
+                               ' (use forget to undo)\n')) or result
 
     for f in sorted(remove + forget):
         if ui.verbose or not m.exact(f):
@@ -168,19 +170,18 @@
                 # are removing the file.
                 if getattr(repo, "_isaddremove", False):
                     ui.status(_('removing %s\n') % f)
-                if os.path.exists(repo.wjoin(f)):
-                    util.unlinkpath(repo.wjoin(f))
+                util.unlinkpath(repo.wjoin(f), ignoremissing=True)
             lfdirstate.remove(f)
         lfdirstate.write()
         forget = [lfutil.standin(f) for f in forget]
         remove = [lfutil.standin(f) for f in remove]
-        lfutil.repoforget(repo, forget)
+        repo[None].forget(forget)
         # If this is being called by addremove, let the original addremove
         # function handle this.
         if not getattr(repo, "_isaddremove", False):
-            lfutil.reporemove(repo, remove, unlink=True)
-        else:
-            lfutil.reporemove(repo, remove, unlink=False)
+            for f in remove:
+                util.unlinkpath(repo.wjoin(f), ignoremissing=True)
+        repo[None].forget(remove)
     finally:
         wlock.release()
 
@@ -238,11 +239,34 @@
         repo._repo.lfstatus = False
 
 def overridelog(orig, ui, repo, *pats, **opts):
+    def overridematch(ctx, pats=[], opts={}, globbed=False,
+            default='relpath'):
+        """Matcher that merges root directory with .hglf, suitable for log.
+        It is still possible to match .hglf directly.
+        For any listed files run log on the standin too.
+        matchfn tries both the given filename and with .hglf stripped.
+        """
+        match = oldmatch(ctx, pats, opts, globbed, default)
+        m = copy.copy(match)
+        standins = [lfutil.standin(f) for f in m._files]
+        m._files.extend(standins)
+        m._fmap = set(m._files)
+        origmatchfn = m.matchfn
+        def lfmatchfn(f):
+            lf = lfutil.splitstandin(f)
+            if lf is not None and origmatchfn(lf):
+                return True
+            r = origmatchfn(f)
+            return r
+        m.matchfn = lfmatchfn
+        return m
+    oldmatch = installmatchfn(overridematch)
     try:
         repo.lfstatus = True
         return orig(ui, repo, *pats, **opts)
     finally:
         repo.lfstatus = False
+        restorematchfn()
 
 def overrideverify(orig, ui, repo, *pats, **opts):
     large = opts.pop('large', False)
@@ -254,6 +278,13 @@
         result = result or lfcommands.verifylfiles(ui, repo, all, contents)
     return result
 
+def overridedebugstate(orig, ui, repo, *pats, **opts):
+    large = opts.pop('large', False)
+    if large:
+        lfcommands.debugdirstate(ui, repo)
+    else:
+        orig(ui, repo, *pats, **opts)
+
 # Override needs to refresh standins so that update's normal merge
 # will go through properly. Then the other update hook (overriding repo.update)
 # will get the new files. Filemerge is also overridden so that the merge
@@ -746,7 +777,7 @@
         # .hg/largefiles, and the standin matcher won't match anything anyway.)
         if 'largefiles' in repo.requirements:
             if opts.get('noupdate'):
-                util.makedirs(repo.pathto(lfutil.shortname))
+                util.makedirs(repo.wjoin(lfutil.shortname))
                 util.makedirs(repo.join(lfutil.longname))
 
         # Caching is implicitly limited to 'rev' option, since the dest repo was
@@ -839,7 +870,7 @@
         write(f, 'x' in ff and 0755 or 0644, 'l' in ff, getdata)
 
     if subrepos:
-        for subpath in ctx.substate:
+        for subpath in sorted(ctx.substate):
             sub = ctx.sub(subpath)
             submatch = match_.narrowmatcher(subpath, matchfn)
             sub.archive(repo.ui, archiver, prefix, submatch)
@@ -886,7 +917,7 @@
 
         write(f, 'x' in ff and 0755 or 0644, 'l' in ff, getdata)
 
-    for subpath in ctx.substate:
+    for subpath in sorted(ctx.substate):
         sub = ctx.sub(subpath)
         submatch = match_.narrowmatcher(subpath, match)
         sub.archive(ui, archiver, os.path.join(prefix, repo._path) + '/',
@@ -949,8 +980,10 @@
             else:
                 lfdirstate.remove(f)
         lfdirstate.write()
-        lfutil.reporemove(repo, [lfutil.standin(f) for f in forget],
-            unlink=True)
+        standins = [lfutil.standin(f) for f in forget]
+        for f in standins:
+            util.unlinkpath(repo.wjoin(f), ignoremissing=True)
+        repo[None].forget(standins)
     finally:
         wlock.release()
 
@@ -967,10 +1000,10 @@
         remote = hg.peer(repo, opts, dest)
     except error.RepoError:
         return None
-    o = lfutil.findoutgoing(repo, remote, False)
-    if not o:
-        return o
-    o = repo.changelog.nodesbetween(o, revs)[0]
+    outgoing = discovery.findcommonoutgoing(repo, remote.peer(), force=False)
+    if not outgoing.missing:
+        return outgoing.missing
+    o = repo.changelog.nodesbetween(outgoing.missing, revs)[0]
     if opts.get('newest_first'):
         o.reverse()
 
@@ -994,7 +1027,7 @@
                     files.add(f)
         toupload = toupload.union(
             set([f for f in files if lfutil.isstandin(f) and f in ctx]))
-    return toupload
+    return sorted(toupload)
 
 def overrideoutgoing(orig, ui, repo, dest=None, **opts):
     result = orig(ui, repo, dest, **opts)
@@ -1065,6 +1098,9 @@
 # Calling purge with --all will cause the largefiles to be deleted.
 # Override repo.status to prevent this from happening.
 def overridepurge(orig, ui, repo, *dirs, **opts):
+    # XXX large file status is buggy when used on repo proxy.
+    # XXX this needs to be investigate.
+    repo = repo.unfiltered()
     oldstatus = repo.status
     def overridestatus(node1='.', node2=None, match=None, ignored=False,
                         clean=False, unknown=False, listsubrepos=False):
--- a/hgext/largefiles/proto.py	Mon Jan 14 23:14:45 2013 +0900
+++ b/hgext/largefiles/proto.py	Sat Jan 19 17:24:33 2013 -0600
@@ -140,19 +140,6 @@
 def capabilities(repo, proto):
     return capabilitiesorig(repo, proto) + ' largefiles=serve'
 
-# duplicate what Mercurial's new out-of-band errors mechanism does, because
-# clients old and new alike both handle it well
-def webprotorefuseclient(self, message):
-    self.req.header([('Content-Type', 'application/hg-error')])
-    return message
-
-def sshprotorefuseclient(self, message):
-    self.ui.write_err('%s\n-\n' % message)
-    self.fout.write('\n')
-    self.fout.flush()
-
-    return ''
-
 def heads(repo, proto):
     if lfutil.islfilesrepo(repo):
         return wireproto.ooberror(LARGEFILES_REQUIRED_MSG)
--- a/hgext/largefiles/reposetup.py	Mon Jan 14 23:14:45 2013 +0900
+++ b/hgext/largefiles/reposetup.py	Sat Jan 19 17:24:33 2013 -0600
@@ -11,9 +11,11 @@
 import types
 import os
 
-from mercurial import context, error, manifest, match as match_, util
+from mercurial import context, error, manifest, match as match_, util, \
+    discovery
 from mercurial import node as node_
 from mercurial.i18n import _
+from mercurial import localrepo
 
 import lfcommands
 import proto
@@ -88,6 +90,9 @@
         # appropriate list in the result. Also removes standin files
         # from the listing. Revert to the original status if
         # self.lfstatus is False.
+        # XXX large file status is buggy when used on repo proxy.
+        # XXX this needs to be investigated.
+        @localrepo.unfilteredmethod
         def status(self, node1='.', node2=None, match=None, ignored=False,
                 clean=False, unknown=False, listsubrepos=False):
             listignored, listclean, listunknown = ignored, clean, unknown
@@ -153,78 +158,54 @@
                             newfiles.append(f)
                     return newfiles
 
-                # Create a function that we can use to override what is
-                # normally the ignore matcher.  We've already checked
-                # for ignored files on the first dirstate walk, and
-                # unnecessarily re-checking here causes a huge performance
-                # hit because lfdirstate only knows about largefiles
-                def _ignoreoverride(self):
-                    return False
-
                 m = copy.copy(match)
                 m._files = tostandins(m._files)
 
                 result = super(lfilesrepo, self).status(node1, node2, m,
                     ignored, clean, unknown, listsubrepos)
                 if working:
-                    try:
-                        # Any non-largefiles that were explicitly listed must be
-                        # taken out or lfdirstate.status will report an error.
-                        # The status of these files was already computed using
-                        # super's status.
-                        # Override lfdirstate's ignore matcher to not do
-                        # anything
-                        origignore = lfdirstate._ignore
-                        lfdirstate._ignore = _ignoreoverride
+
+                    def sfindirstate(f):
+                        sf = lfutil.standin(f)
+                        dirstate = self.dirstate
+                        return sf in dirstate or sf in dirstate.dirs()
 
-                        def sfindirstate(f):
-                            sf = lfutil.standin(f)
-                            dirstate = self.dirstate
-                            return sf in dirstate or sf in dirstate.dirs()
-                        match._files = [f for f in match._files
-                                        if sfindirstate(f)]
-                        # Don't waste time getting the ignored and unknown
-                        # files again; we already have them
-                        s = lfdirstate.status(match, [], False,
-                                listclean, False)
-                        (unsure, modified, added, removed, missing, unknown,
-                                ignored, clean) = s
-                        # Replace the list of ignored and unknown files with
-                        # the previously calculated lists, and strip out the
-                        # largefiles
-                        lfiles = set(lfdirstate._map)
-                        ignored = set(result[5]).difference(lfiles)
-                        unknown = set(result[4]).difference(lfiles)
-                        if parentworking:
-                            for lfile in unsure:
-                                standin = lfutil.standin(lfile)
-                                if standin not in ctx1:
-                                    # from second parent
-                                    modified.append(lfile)
-                                elif ctx1[standin].data().strip() \
-                                        != lfutil.hashfile(self.wjoin(lfile)):
+                    match._files = [f for f in match._files
+                                    if sfindirstate(f)]
+                    # Don't waste time getting the ignored and unknown
+                    # files from lfdirstate
+                    s = lfdirstate.status(match, [], False,
+                            listclean, False)
+                    (unsure, modified, added, removed, missing, _unknown,
+                            _ignored, clean) = s
+                    if parentworking:
+                        for lfile in unsure:
+                            standin = lfutil.standin(lfile)
+                            if standin not in ctx1:
+                                # from second parent
+                                modified.append(lfile)
+                            elif ctx1[standin].data().strip() \
+                                    != lfutil.hashfile(self.wjoin(lfile)):
+                                modified.append(lfile)
+                            else:
+                                clean.append(lfile)
+                                lfdirstate.normal(lfile)
+                    else:
+                        tocheck = unsure + modified + added + clean
+                        modified, added, clean = [], [], []
+
+                        for lfile in tocheck:
+                            standin = lfutil.standin(lfile)
+                            if inctx(standin, ctx1):
+                                if ctx1[standin].data().strip() != \
+                                        lfutil.hashfile(self.wjoin(lfile)):
                                     modified.append(lfile)
                                 else:
                                     clean.append(lfile)
-                                    lfdirstate.normal(lfile)
-                        else:
-                            tocheck = unsure + modified + added + clean
-                            modified, added, clean = [], [], []
+                            else:
+                                added.append(lfile)
 
-                            for lfile in tocheck:
-                                standin = lfutil.standin(lfile)
-                                if inctx(standin, ctx1):
-                                    if ctx1[standin].data().strip() != \
-                                            lfutil.hashfile(self.wjoin(lfile)):
-                                        modified.append(lfile)
-                                    else:
-                                        clean.append(lfile)
-                                else:
-                                    added.append(lfile)
-                    finally:
-                        # Replace the original ignore function
-                        lfdirstate._ignore = origignore
-
+                    # Standins no longer found in lfdirstate has been removed
                     for standin in ctx1.manifest():
                         if not lfutil.isstandin(standin):
                             continue
@@ -239,20 +220,17 @@
 
                     # Largefiles are not really removed when they're
                     # still in the normal dirstate. Likewise, normal
-                    # files are not really removed if it's still in
+                    # files are not really removed if they are still in
                     # lfdirstate. This happens in merges where files
                     # change type.
                     removed = [f for f in removed if f not in self.dirstate]
                     result[2] = [f for f in result[2] if f not in lfdirstate]
 
+                    lfiles = set(lfdirstate._map)
                     # Unknown files
-                    unknown = set(unknown).difference(ignored)
-                    result[4] = [f for f in unknown
-                                 if (self.dirstate[f] == '?' and
-                                     not lfutil.isstandin(f))]
-                    # Ignored files were calculated earlier by the dirstate,
-                    # and we already stripped out the largefiles from the list
-                    result[5] = ignored
+                    result[4] = set(result[4]).difference(lfiles)
+                    # Ignored files
+                    result[5] = set(result[5]).difference(lfiles)
                     # combine normal files and largefiles
                     normals = [[fn for fn in filelist
                                 if not lfutil.isstandin(fn)]
@@ -361,7 +339,7 @@
                 # Case 2: user calls commit with specified patterns: refresh
                 # any matching big files.
                 smatcher = lfutil.composestandinmatcher(self, match)
-                standins = lfutil.dirstatewalk(self.dirstate, smatcher)
+                standins = self.dirstate.walk(smatcher, [], False, False)
 
                 # No matching big files: get out of the way and pass control to
                 # the usual commit() method.
@@ -377,7 +355,7 @@
                 lfdirstate = lfutil.openlfdirstate(ui, self)
                 for standin in standins:
                     lfile = lfutil.splitstandin(standin)
-                    if lfdirstate[lfile] <> 'r':
+                    if lfdirstate[lfile] != 'r':
                         lfutil.updatestandin(self, standin)
                         lfdirstate.normal(lfile)
                     else:
@@ -427,10 +405,11 @@
                 wlock.release()
 
         def push(self, remote, force=False, revs=None, newbranch=False):
-            o = lfutil.findoutgoing(self, remote, force)
-            if o:
+            outgoing = discovery.findcommonoutgoing(repo, remote.peer(),
+                                                    force=force)
+            if outgoing.missing:
                 toupload = set()
-                o = self.changelog.nodesbetween(o, revs)[0]
+                o = self.changelog.nodesbetween(outgoing.missing, revs)[0]
                 for n in o:
                     parents = [p for p in self.changelog.parents(n)
                                if p != node_.nullid]
--- a/hgext/largefiles/uisetup.py	Mon Jan 14 23:14:45 2013 +0900
+++ b/hgext/largefiles/uisetup.py	Sat Jan 19 17:24:33 2013 -0600
@@ -9,9 +9,9 @@
 '''setup for largefiles extension: uisetup'''
 
 from mercurial import archival, cmdutil, commands, extensions, filemerge, hg, \
-    httppeer, localrepo, merge, scmutil, sshpeer, sshserver, wireproto
+    httppeer, localrepo, merge, scmutil, sshpeer, wireproto
 from mercurial.i18n import _
-from mercurial.hgweb import hgweb_mod, protocol, webcommands
+from mercurial.hgweb import hgweb_mod, webcommands
 from mercurial.subrepo import hgsubrepo
 
 import overrides
@@ -59,6 +59,11 @@
                      _('verify largefile contents not just existence'))]
     entry[1].extend(verifyopt)
 
+    entry = extensions.wrapcommand(commands.table, 'debugstate',
+                                   overrides.overridedebugstate)
+    debugstateopt = [('', 'large', None, _('display largefiles dirstate'))]
+    entry[1].extend(debugstateopt)
+
     entry = extensions.wrapcommand(commands.table, 'outgoing',
         overrides.overrideoutgoing)
     outgoingopt = [('', 'large', None, _('display outgoing largefiles'))]
@@ -139,11 +144,6 @@
     proto.capabilitiesorig = wireproto.capabilities
     wireproto.capabilities = proto.capabilities
 
-    # these let us reject non-largefiles clients and make them display
-    # our error messages
-    protocol.webproto.refuseclient = proto.webprotorefuseclient
-    sshserver.sshserver.refuseclient = proto.sshprotorefuseclient
-
     # can't do this in reposetup because it needs to have happened before
     # wirerepo.__init__ is called
     proto.ssholdcallstream = sshpeer.sshpeer._callstream
--- a/hgext/mq.py	Mon Jan 14 23:14:45 2013 +0900
+++ b/hgext/mq.py	Sat Jan 19 17:24:33 2013 -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
 
@@ -275,6 +275,7 @@
     It should be used instead of repo.commit inside the mq source for operation
     creating new changeset.
     """
+    repo = repo.unfiltered()
     if phase is None:
         if repo.ui.configbool('mq', 'secret', False):
             phase = phases.secret
@@ -826,7 +827,11 @@
             if r:
                 r[None].forget(patches)
             for p in patches:
-                os.unlink(self.join(p))
+                try:
+                    os.unlink(self.join(p))
+                except OSError, inst:
+                    if inst.errno != errno.ENOENT:
+                        raise
 
         qfinished = []
         if numrevs:
@@ -924,11 +929,11 @@
         self._cleanup(realpatches, numrevs, opts.get('keep'))
 
     def checktoppatch(self, repo):
+        '''check that working directory is at qtip'''
         if self.applied:
             top = self.applied[-1].node
             patch = self.applied[-1].name
-            pp = repo.dirstate.parents()
-            if top not in pp:
+            if repo.dirstate.p1() != top:
                 raise util.Abort(_("working directory revision is not qtip"))
             return top, patch
         return None, None
@@ -942,7 +947,7 @@
             bctx = repo[baserev]
         else:
             bctx = wctx.parents()[0]
-        for s in wctx.substate:
+        for s in sorted(wctx.substate):
             if wctx.sub(s).dirty(True):
                 raise util.Abort(
                     _("uncommitted changes in subrepository %s") % s)
@@ -1146,7 +1151,7 @@
                 return matches[0]
             if self.series and self.applied:
                 if s == 'qtip':
-                    return self.series[self.seriesend(True)-1]
+                    return self.series[self.seriesend(True) - 1]
                 if s == 'qbase':
                     return self.series[0]
             return None
@@ -1324,11 +1329,7 @@
                 # created while patching
                 for f in all_files:
                     if f not in repo.dirstate:
-                        try:
-                            util.unlinkpath(repo.wjoin(f))
-                        except OSError, inst:
-                            if inst.errno != errno.ENOENT:
-                                raise
+                        util.unlinkpath(repo.wjoin(f), ignoremissing=True)
                 self.ui.warn(_('done\n'))
                 raise
 
@@ -1405,8 +1406,6 @@
             self.applieddirty = True
             end = len(self.applied)
             rev = self.applied[start].node
-            if update:
-                top = self.checktoppatch(repo)[0]
 
             try:
                 heads = repo.changelog.heads(rev)
@@ -1427,7 +1426,7 @@
             if update:
                 qp = self.qparents(repo, rev)
                 ctx = repo[qp]
-                m, a, r, d = repo.status(qp, top)[:4]
+                m, a, r, d = repo.status(qp, '.')[:4]
                 if d:
                     raise util.Abort(_("deletions found between repo revs"))
 
@@ -1437,11 +1436,7 @@
                 self.backup(repo, tobackup)
 
                 for f in a:
-                    try:
-                        util.unlinkpath(repo.wjoin(f))
-                    except OSError, e:
-                        if e.errno != errno.ENOENT:
-                            raise
+                    util.unlinkpath(repo.wjoin(f), ignoremissing=True)
                     repo.dirstate.drop(f)
                 for f in m + r:
                     fctx = ctx[f]
@@ -1625,7 +1620,7 @@
                 # if the patch excludes a modified file, mark that
                 # file with mtime=0 so status can see it.
                 mm = []
-                for i in xrange(len(m)-1, -1, -1):
+                for i in xrange(len(m) - 1, -1, -1):
                     if not matchfn(m[i]):
                         mm.append(m[i])
                         del m[i]
@@ -1675,9 +1670,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 +2995,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:
@@ -3036,7 +3032,7 @@
             del q.applied[start:end]
             q.savedirty()
 
-    revs = list(rootnodes)
+    revs = sorted(rootnodes)
     if update and opts.get('keep'):
         wlock = repo.wlock()
         try:
@@ -3049,7 +3045,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 +3431,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)
@@ -3451,7 +3447,8 @@
             mqtags = [(patch.node, patch.name) for patch in q.applied]
 
             try:
-                self.changelog.rev(mqtags[-1][0])
+                # for now ignore filtering business
+                self.unfiltered().changelog.rev(mqtags[-1][0])
             except error.LookupError:
                 self.ui.warn(_('mq status file refers to unknown node %s\n')
                              % short(mqtags[-1][0]))
@@ -3470,41 +3467,6 @@
 
             return result
 
-        def _branchtags(self, partial, lrev):
-            q = self.mq
-            cl = self.changelog
-            qbase = None
-            if not q.applied:
-                if getattr(self, '_committingpatch', False):
-                    # Committing a new patch, must be tip
-                    qbase = len(cl) - 1
-            else:
-                qbasenode = q.applied[0].node
-                try:
-                    qbase = cl.rev(qbasenode)
-                except error.LookupError:
-                    self.ui.warn(_('mq status file refers to unknown node %s\n')
-                                 % short(qbasenode))
-            if qbase is None:
-                return super(mqrepo, self)._branchtags(partial, lrev)
-
-            start = lrev + 1
-            if start < qbase:
-                # update the cache (excluding the patches) and save it
-                ctxgen = (self[r] for r in xrange(lrev + 1, qbase))
-                self._updatebranchcache(partial, ctxgen)
-                self._writebranchcache(partial, cl.node(qbase - 1), qbase - 1)
-                start = qbase
-            # if start = qbase, the cache is as updated as it should be.
-            # if start > qbase, the cache includes (part of) the patches.
-            # we might as well use it, but we won't save it.
-
-            # update the cache up to the tip
-            ctxgen = (self[r] for r in xrange(start, len(cl)))
-            self._updatebranchcache(partial, ctxgen)
-
-            return partial
-
     if repo.local():
         repo.__class__ = mqrepo
 
--- a/hgext/patchbomb.py	Mon Jan 14 23:14:45 2013 +0900
+++ b/hgext/patchbomb.py	Sat Jan 19 17:24:33 2013 -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	Mon Jan 14 23:14:45 2013 +0900
+++ b/hgext/rebase.py	Sat Jan 19 17:24:33 2013 -0600
@@ -23,6 +23,7 @@
 import os, errno
 
 nullmerge = -2
+revignored = -3
 
 cmdtable = {}
 command = cmdutil.command(cmdtable)
@@ -184,8 +185,6 @@
                 rebaseset = repo.revs(
                     '(children(ancestor(%ld, %d)) and ::(%ld))::',
                     base, dest, base)
-            # temporary top level filtering of extinct revisions
-            rebaseset = repo.revs('%ld - hidden()', rebaseset)
             if rebaseset:
                 root = min(rebaseset)
             else:
@@ -194,8 +193,9 @@
             if not rebaseset:
                 repo.ui.debug('base is ancestor of destination\n')
                 result = None
-            elif not keepf and repo.revs('first(children(%ld) - %ld)-hidden()',
-                                         rebaseset, rebaseset):
+            elif (not (keepf or obsolete._enabled)
+                  and repo.revs('first(children(%ld) - %ld)',
+                                rebaseset, rebaseset)):
                 raise util.Abort(
                     _("can't remove original changesets with"
                       " unrebased descendants"),
@@ -214,8 +214,8 @@
             else:
                 originalwd, target, state = result
                 if collapsef:
-                    targetancestors = set(repo.changelog.ancestors([target]))
-                    targetancestors.add(target)
+                    targetancestors = repo.changelog.ancestors([target],
+                                                               inclusive=True)
                     external = checkexternal(repo, state, targetancestors)
 
         if keepbranchesf:
@@ -233,8 +233,7 @@
 
         # Rebase
         if not targetancestors:
-            targetancestors = set(repo.changelog.ancestors([target]))
-            targetancestors.add(target)
+            targetancestors = repo.changelog.ancestors([target], inclusive=True)
 
         # Keep track of the current bookmarks in order to reset them later
         currentbookmarks = repo._bookmarks.copy()
@@ -294,7 +293,7 @@
             else:
                 commitmsg = 'Collapsed revision'
                 for rebased in state:
-                    if rebased not in skipped and state[rebased] != nullmerge:
+                    if rebased not in skipped and state[rebased] > nullmerge:
                         commitmsg += '\n* %s' % repo[rebased].description()
                 commitmsg = ui.edit(commitmsg, repo.ui.username())
             newrev = concludenode(repo, rev, p1, external, commitmsg=commitmsg,
@@ -307,22 +306,21 @@
             # Nodeids are needed to reset bookmarks
             nstate = {}
             for k, v in state.iteritems():
-                if v != nullmerge:
+                if v > nullmerge:
                     nstate[repo[k].node()] = repo[v].node()
 
         if not keepf:
             collapsedas = None
             if collapsef:
                 collapsedas = newrev
-            clearrebased(ui, repo, state, collapsedas)
+            clearrebased(ui, repo, state, skipped, collapsedas)
 
         if currentbookmarks:
             updatebookmarks(repo, nstate, currentbookmarks, **opts)
 
         clearstatus(repo)
         ui.note(_("rebase completed\n"))
-        if os.path.exists(repo.sjoin('undo')):
-            util.unlinkpath(repo.sjoin('undo'))
+        util.unlinkpath(repo.sjoin('undo'), ignoremissing=True)
         if skipped:
             ui.note(_("%d revisions have been skipped\n") % len(skipped))
 
@@ -395,6 +393,15 @@
     # have to allow merging with it.
     return merge.update(repo, rev, True, True, False, base, collapse)
 
+def nearestrebased(repo, rev, state):
+    """return the nearest ancestors of rev in the rebase result"""
+    rebased = [r for r in state if state[r] > nullmerge]
+    candidates = repo.revs('max(%ld  and (::%d))', rebased, rev)
+    if candidates:
+        return state[candidates[0]]
+    else:
+        return None
+
 def defineparents(repo, rev, target, state, targetancestors):
     'Return the new parent relationship of the revision that will be rebased'
     parents = repo[rev].parents()
@@ -406,6 +413,10 @@
     elif P1n in state:
         if state[P1n] == nullmerge:
             p1 = target
+        elif state[P1n] == revignored:
+            p1 = nearestrebased(repo, P1n, state)
+            if p1 is None:
+                p1 = target
         else:
             p1 = state[P1n]
     else: # P1n external
@@ -418,6 +429,11 @@
         if P2n in state:
             if p1 == target: # P1n in targetancestors or external
                 p1 = state[P2n]
+            elif state[P2n] == revignored:
+                p2 = nearestrebased(repo, P2n, state)
+                if p2 is None:
+                    # no ancestors rebased yet, detach
+                    p2 = target
             else:
                 p2 = state[P2n]
         else: # P2n external
@@ -479,13 +495,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:
+            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):
@@ -499,7 +516,7 @@
     f.write('%d\n' % int(keepbranches))
     for d, v in state.iteritems():
         oldrev = repo[d].hex()
-        if v != nullmerge:
+        if v > nullmerge:
             newrev = repo[v].hex()
         else:
             newrev = v
@@ -509,8 +526,7 @@
 
 def clearstatus(repo):
     'Remove the status files'
-    if os.path.exists(repo.join("rebasestate")):
-        util.unlinkpath(repo.join("rebasestate"))
+    util.unlinkpath(repo.join("rebasestate"), ignoremissing=True)
 
 def restorestatus(repo):
     'Restore a previously stored status'
@@ -535,10 +551,10 @@
                 keepbranches = bool(int(l))
             else:
                 oldrev, newrev = l.split(':')
-                if newrev != str(nullmerge):
+                if newrev in (str(nullmerge), str(revignored)):
+                    state[repo[oldrev].rev()] = int(newrev)
+                else:
                     state[repo[oldrev].rev()] = repo[newrev].rev()
-                else:
-                    state[repo[oldrev].rev()] = int(newrev)
         skipped = set()
         # recompute the set of skipped revs
         if not collapse:
@@ -577,9 +593,9 @@
         merge.update(repo, repo[originalwd].rev(), False, True, False)
         rebased = filter(lambda x: x > -1 and x != target, state.values())
         if rebased:
-            strippoint = min(rebased)
+            strippoints = [c.node()  for c in repo.set('roots(%ld)', rebased)]
             # no backup of rebased cset versions needed
-            repair.strip(repo.ui, repo, repo[strippoint].node())
+            repair.strip(repo.ui, repo, strippoints)
         clearstatus(repo)
         repo.ui.warn(_('rebase aborted\n'))
         return 0
@@ -602,65 +618,77 @@
     roots = list(repo.set('roots(%ld)', rebaseset))
     if not roots:
         raise util.Abort(_('no matching revisions'))
-    if len(roots) > 1:
-        raise util.Abort(_("can't rebase multiple roots"))
-    root = roots[0]
-
-    commonbase = root.ancestor(dest)
-    if commonbase == root:
-        raise util.Abort(_('source is ancestor of destination'))
-    if commonbase == dest:
-        samebranch = root.branch() == dest.branch()
-        if not collapse and samebranch and root in dest.children():
-            repo.ui.debug('source is a child of destination\n')
-            return None
+    roots.sort()
+    state = {}
+    detachset = set()
+    for root in roots:
+        commonbase = root.ancestor(dest)
+        if commonbase == root:
+            raise util.Abort(_('source is ancestor of destination'))
+        if commonbase == dest:
+            samebranch = root.branch() == dest.branch()
+            if not collapse and samebranch and root in dest.children():
+                repo.ui.debug('source is a child of destination\n')
+                return None
 
-    repo.ui.debug('rebase onto %d starting from %d\n' % (dest, root))
-    state = dict.fromkeys(rebaseset, nullrev)
-    # Rebase tries to turn <dest> into a parent of <root> while
-    # preserving the number of parents of rebased changesets:
-    #
-    # - A changeset with a single parent will always be rebased as a
-    #   changeset with a single parent.
-    #
-    # - A merge will be rebased as merge unless its parents are both
-    #   ancestors of <dest> or are themselves in the rebased set and
-    #   pruned while rebased.
-    #
-    # If one parent of <root> is an ancestor of <dest>, the rebased
-    # version of this parent will be <dest>. This is always true with
-    # --base option.
-    #
-    # Otherwise, we need to *replace* the original parents with
-    # <dest>. This "detaches" the rebased set from its former location
-    # and rebases it onto <dest>. Changes introduced by ancestors of
-    # <root> not common with <dest> (the detachset, marked as
-    # nullmerge) are "removed" from the rebased changesets.
-    #
-    # - If <root> has a single parent, set it to <dest>.
-    #
-    # - If <root> is a merge, we cannot decide which parent to
-    #   replace, the rebase operation is not clearly defined.
-    #
-    # The table below sums up this behavior:
-    #
-    # +--------------------+----------------------+-------------------------+
-    # |                    |     one parent       |  merge                  |
-    # +--------------------+----------------------+-------------------------+
-    # | parent in ::<dest> | new parent is <dest> | parents in ::<dest> are |
-    # |                    |                      | remapped to <dest>      |
-    # +--------------------+----------------------+-------------------------+
-    # | unrelated source   | new parent is <dest> | ambiguous, abort        |
-    # +--------------------+----------------------+-------------------------+
-    #
-    # 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)
-        state.update(dict.fromkeys(detachset, nullmerge))
+        repo.ui.debug('rebase onto %d starting from %s\n' % (dest, roots))
+        state.update(dict.fromkeys(rebaseset, nullrev))
+        # Rebase tries to turn <dest> into a parent of <root> while
+        # preserving the number of parents of rebased changesets:
+        #
+        # - A changeset with a single parent will always be rebased as a
+        #   changeset with a single parent.
+        #
+        # - A merge will be rebased as merge unless its parents are both
+        #   ancestors of <dest> or are themselves in the rebased set and
+        #   pruned while rebased.
+        #
+        # If one parent of <root> is an ancestor of <dest>, the rebased
+        # version of this parent will be <dest>. This is always true with
+        # --base option.
+        #
+        # Otherwise, we need to *replace* the original parents with
+        # <dest>. This "detaches" the rebased set from its former location
+        # and rebases it onto <dest>. Changes introduced by ancestors of
+        # <root> not common with <dest> (the detachset, marked as
+        # nullmerge) are "removed" from the rebased changesets.
+        #
+        # - If <root> has a single parent, set it to <dest>.
+        #
+        # - If <root> is a merge, we cannot decide which parent to
+        #   replace, the rebase operation is not clearly defined.
+        #
+        # The table below sums up this behavior:
+        #
+        # +------------------+----------------------+-------------------------+
+        # |                  |     one parent       |  merge                  |
+        # +------------------+----------------------+-------------------------+
+        # | parent in        | new parent is <dest> | parents in ::<dest> are |
+        # | ::<dest>         |                      | remapped to <dest>      |
+        # +------------------+----------------------+-------------------------+
+        # | unrelated source | new parent is <dest> | ambiguous, abort        |
+        # +------------------+----------------------+-------------------------+
+        #
+        # The actual abort is handled by `defineparents`
+        if len(root.parents()) <= 1:
+            # ancestors of <root> not ancestors of <dest>
+            detachset.update(repo.changelog.findmissingrevs([commonbase.rev()],
+                                                            [root.rev()]))
+    for r in detachset:
+        if r not in state:
+            state[r] = nullmerge
+    if len(roots) > 1:
+        # If we have multiple roots, we may have "hole" in the rebase set.
+        # Rebase roots that descend from those "hole" should not be detached as
+        # other root are. We use the special `revignored` to inform rebase that
+        # the revision should be ignored but that `defineparent` should search
+        # a rebase destination that make sense regarding rebaset topology.
+        rebasedomain = set(repo.revs('%ld::%ld', rebaseset, rebaseset))
+        for ignored in set(rebasedomain) - set(rebaseset):
+            state[ignored] = revignored
     return repo['.'].rev(), dest.rev(), state
 
-def clearrebased(ui, repo, state, collapsedas=None):
+def clearrebased(ui, repo, state, skipped, collapsedas=None):
     """dispose of rebased revision at the end of the rebase
 
     If `collapsedas` is not None, the rebase was a collapse whose result if the
@@ -669,20 +697,28 @@
         markers = []
         for rev, newrev in sorted(state.items()):
             if newrev >= 0:
-                if collapsedas is not None:
-                    newrev = collapsedas
-                markers.append((repo[rev], (repo[newrev],)))
+                if rev in skipped:
+                    succs = ()
+                elif collapsedas is not None:
+                    succs = (repo[collapsedas],)
+                else:
+                    succs = (repo[newrev],)
+                markers.append((repo[rev], succs))
         if markers:
             obsolete.createmarkers(repo, markers)
     else:
-        rebased = [rev for rev in state if state[rev] != nullmerge]
+        rebased = [rev for rev in state if state[rev] > nullmerge]
         if rebased:
-            if set(repo.changelog.descendants([min(rebased)])) - set(state):
-                ui.warn(_("warning: new changesets detected "
-                          "on source branch, not stripping\n"))
-            else:
+            stripped = []
+            for root in repo.set('roots(%ld)', rebased):
+                if set(repo.changelog.descendants([root.rev()])) - set(state):
+                    ui.warn(_("warning: new changesets detected "
+                              "on source branch, not stripping\n"))
+                else:
+                    stripped.append(root.node())
+            if stripped:
                 # backup the old csets by default
-                repair.strip(ui, repo, repo[min(rebased)].node(), "all")
+                repair.strip(ui, repo, stripped, "all")
 
 
 def pullrebase(orig, ui, repo, *args, **opts):
--- a/hgext/record.py	Mon Jan 14 23:14:45 2013 +0900
+++ b/hgext/record.py	Sat Jan 19 17:24:33 2013 -0600
@@ -8,7 +8,7 @@
 '''commands to interactively select changes for commit/qrefresh'''
 
 from mercurial.i18n import gettext, _
-from mercurial import cmdutil, commands, extensions, hg, mdiff, patch
+from mercurial import cmdutil, commands, extensions, hg, patch
 from mercurial import util
 import copy, cStringIO, errno, os, re, shutil, tempfile
 
@@ -520,11 +520,11 @@
                                '(use "hg commit" instead)'))
 
         changes = repo.status(match=match)[:3]
-        diffopts = mdiff.diffopts(
+        diffopts = patch.diffopts(ui, opts=dict(
             git=True, nodates=True,
             ignorews=opts.get('ignore_all_space'),
             ignorewsamount=opts.get('ignore_space_change'),
-            ignoreblanklines=opts.get('ignore_blank_lines'))
+            ignoreblanklines=opts.get('ignore_blank_lines')))
         chunks = patch.diff(repo, changes=changes, opts=diffopts)
         fp = cStringIO.StringIO()
         fp.write(''.join(chunks))
--- a/hgext/transplant.py	Mon Jan 14 23:14:45 2013 +0900
+++ b/hgext/transplant.py	Sat Jan 19 17:24:33 2013 -0600
@@ -94,7 +94,8 @@
             parentrev = repo.changelog.rev(parent)
         if hasnode(repo, node):
             rev = repo.changelog.rev(node)
-            reachable = repo.changelog.incancestors([parentrev], rev)
+            reachable = repo.changelog.ancestors([parentrev], rev,
+                                                 inclusive=True)
             if rev in reachable:
                 return True
         for t in self.transplants.get(node):
@@ -103,7 +104,8 @@
                 self.transplants.remove(t)
                 return False
             lnoderev = repo.changelog.rev(t.lnode)
-            if lnoderev in repo.changelog.incancestors([parentrev], lnoderev):
+            if lnoderev in repo.changelog.ancestors([parentrev], lnoderev,
+                                                    inclusive=True):
                 return True
         return False
 
--- a/hgext/win32text.py	Mon Jan 14 23:14:45 2013 +0900
+++ b/hgext/win32text.py	Sat Jan 19 17:24:33 2013 -0600
@@ -121,7 +121,7 @@
     # changegroup that contains an unacceptable commit followed later
     # by a commit that fixes the problem.
     tip = repo['tip']
-    for rev in xrange(len(repo)-1, repo[node].rev()-1, -1):
+    for rev in xrange(len(repo) - 1, repo[node].rev() - 1, -1):
         c = repo[rev]
         for f in c.files():
             if f in seen or f not in tip or f not in c:
--- a/mercurial/ancestor.py	Mon Jan 14 23:14:45 2013 +0900
+++ b/mercurial/ancestor.py	Sat Jan 19 17:24:33 2013 -0600
@@ -5,7 +5,8 @@
 # This software may be used and distributed according to the terms of the
 # GNU General Public License version 2 or any later version.
 
-import heapq
+import heapq, util
+from node import nullrev
 
 def ancestor(a, b, pfunc):
     """
@@ -89,3 +90,175 @@
                 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.
+    """
+
+    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
+
+class lazyancestors(object):
+    def __init__(self, cl, revs, stoprev=0, inclusive=False):
+        """Create a new object generating ancestors for the given revs. Does
+        not generate revs lower than stoprev.
+
+        This is computed lazily starting from revs. The object supports
+        iteration and membership.
+
+        cl should be a changelog and revs should be an iterable. inclusive is
+        a boolean that indicates whether revs should be included. Revs lower
+        than stoprev will not be generated.
+
+        Result does not include the null revision."""
+        self._parentrevs = cl.parentrevs
+        self._initrevs = revs
+        self._stoprev = stoprev
+        self._inclusive = inclusive
+
+        # Initialize data structures for __contains__.
+        # For __contains__, we use a heap rather than a deque because
+        # (a) it minimizes the number of parentrevs calls made
+        # (b) it makes the loop termination condition obvious
+        # Python's heap is a min-heap. Multiply all values by -1 to convert it
+        # into a max-heap.
+        self._containsvisit = [-rev for rev in revs]
+        heapq.heapify(self._containsvisit)
+        if inclusive:
+            self._containsseen = set(revs)
+        else:
+            self._containsseen = set()
+
+    def __iter__(self):
+        """Generate the ancestors of _initrevs in reverse topological order.
+
+        If inclusive is False, yield a sequence of revision numbers starting
+        with the parents of each revision in revs, i.e., each revision is *not*
+        considered an ancestor of itself.  Results are in breadth-first order:
+        parents of each rev in revs, then parents of those, etc.
+
+        If inclusive is True, yield all the revs first (ignoring stoprev),
+        then yield all the ancestors of revs as when inclusive is False.
+        If an element in revs is an ancestor of a different rev it is not
+        yielded again."""
+        seen = set()
+        revs = self._initrevs
+        if self._inclusive:
+            for rev in revs:
+                yield rev
+            seen.update(revs)
+
+        parentrevs = self._parentrevs
+        stoprev = self._stoprev
+        visit = util.deque(revs)
+
+        while visit:
+            for parent in parentrevs(visit.popleft()):
+                if parent >= stoprev and parent not in seen:
+                    visit.append(parent)
+                    seen.add(parent)
+                    yield parent
+
+    def __contains__(self, target):
+        """Test whether target is an ancestor of self._initrevs."""
+        # Trying to do both __iter__ and __contains__ using the same visit
+        # heap and seen set is complex enough that it slows down both. Keep
+        # them separate.
+        seen = self._containsseen
+        if target in seen:
+            return True
+
+        parentrevs = self._parentrevs
+        visit = self._containsvisit
+        stoprev = self._stoprev
+        heappop = heapq.heappop
+        heappush = heapq.heappush
+
+        targetseen = False
+
+        while visit and -visit[0] > target and not targetseen:
+            for parent in parentrevs(-heappop(visit)):
+                if parent < stoprev or parent in seen:
+                    continue
+                # We need to make sure we push all parents into the heap so
+                # that we leave it in a consistent state for future calls.
+                heappush(visit, -parent)
+                seen.add(parent)
+                if parent == target:
+                    targetseen = True
+
+        return targetseen
--- a/mercurial/archival.py	Mon Jan 14 23:14:45 2013 +0900
+++ b/mercurial/archival.py	Sat Jan 19 17:24:33 2013 -0600
@@ -74,8 +74,11 @@
         def _write_gzip_header(self):
             self.fileobj.write('\037\213')             # magic header
             self.fileobj.write('\010')                 # compression method
-            # Python 2.6 deprecates self.filename
-            fname = getattr(self, 'name', None) or self.filename
+            # Python 2.6 introduced self.name and deprecated self.filename
+            try:
+                fname = self.name
+            except AttributeError:
+                fname = self.filename
             if fname and fname.endswith('.gz'):
                 fname = fname[:-3]
             flags = 0
@@ -103,7 +106,6 @@
                 self.fileobj = gzfileobj
                 return tarfile.TarFile.taropen(name, mode, gzfileobj)
             else:
-                self.fileobj = fileobj
                 return tarfile.open(name, mode + kind, fileobj)
 
         if isinstance(dest, str):
@@ -191,7 +193,7 @@
                                0x5455,     # block type: "extended-timestamp"
                                1 + 4,      # size of this block
                                1,          # "modification time is present"
-                               self.mtime) # time of last modification (UTC)
+                               int(self.mtime)) # last modification (UTC)
         self.z.writestr(i, data)
 
     def done(self):
@@ -297,7 +299,7 @@
     repo.ui.progress(_('archiving'), None)
 
     if subrepos:
-        for subpath in ctx.substate:
+        for subpath in sorted(ctx.substate):
             sub = ctx.sub(subpath)
             submatch = matchmod.narrowmatcher(subpath, matchfn)
             sub.archive(repo.ui, archiver, prefix, submatch)
--- a/mercurial/bookmarks.py	Mon Jan 14 23:14:45 2013 +0900
+++ b/mercurial/bookmarks.py	Sat Jan 19 17:24:33 2013 -0600
@@ -7,40 +7,80 @@
 
 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
 
-    If we use gittishsh branches we have a current bookmark that
+    If we use gittish branches we have a current bookmark that
     we are on. This function returns the name of the bookmark. It
     is stored in .hg/bookmarks.current
     '''
@@ -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
-    for k in rb.keys():
-        if k in repo._bookmarks:
-            nr, nl = rb[k], repo._bookmarks[k]
+    localmarks = repo._bookmarks
+    for k in sorted(rb):
+        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"))
@@ -246,6 +256,7 @@
 
 def validdest(repo, old, new):
     """Is the new bookmark destination a valid update from the old one"""
+    repo = repo.unfiltered()
     if old == new:
         # Old == new -> nothing to update.
         return False
@@ -263,14 +274,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)
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/mercurial/branchmap.py	Sat Jan 19 17:24:33 2013 -0600
@@ -0,0 +1,223 @@
+# branchmap.py - logic to computes, maintain and stores branchmap for local repo
+#
+# Copyright 2005-2007 Matt Mackall <mpm@selenic.com>
+#
+# This software may be used and distributed according to the terms of the
+# GNU General Public License version 2 or any later version.
+
+from node import bin, hex, nullid, nullrev
+import encoding
+import util, repoview
+
+def _filename(repo):
+    """name of a branchcache file for a given repo or repoview"""
+    filename = "cache/branchheads"
+    if repo.filtername:
+        filename = '%s-%s' % (filename, repo.filtername)
+    return filename
+
+def read(repo):
+    try:
+        f = repo.opener(_filename(repo))
+        lines = f.read().split('\n')
+        f.close()
+    except (IOError, OSError):
+        return None
+
+    try:
+        cachekey = lines.pop(0).split(" ", 2)
+        last, lrev = cachekey[:2]
+        last, lrev = bin(last), int(lrev)
+        filteredhash = None
+        if len(cachekey) > 2:
+            filteredhash = bin(cachekey[2])
+        partial = branchcache(tipnode=last, tiprev=lrev,
+                              filteredhash=filteredhash)
+        if not partial.validfor(repo):
+            # invalidate the cache
+            raise ValueError('tip differs')
+        for l in lines:
+            if not l:
+                continue
+            node, label = l.split(" ", 1)
+            label = encoding.tolocal(label.strip())
+            if not node in repo:
+                raise ValueError('node %s does not exist' % node)
+            partial.setdefault(label, []).append(bin(node))
+    except KeyboardInterrupt:
+        raise
+    except Exception, inst:
+        if repo.ui.debugflag:
+            msg = 'invalid branchheads cache'
+            if repo.filtername is not None:
+                msg += ' (%s)' % repo.filtername
+            msg += ': %s\n'
+            repo.ui.warn(msg % inst)
+        partial = None
+    return partial
+
+
+
+def updatecache(repo):
+    cl = repo.changelog
+    filtername = repo.filtername
+    partial = repo._branchcaches.get(filtername)
+
+    revs = []
+    if partial is None or not partial.validfor(repo):
+        partial = read(repo)
+        if partial is None:
+            subsetname = repoview.subsettable.get(filtername)
+            if subsetname is None:
+                partial = branchcache()
+            else:
+                subset = repo.filtered(subsetname)
+                partial = subset.branchmap().copy()
+                extrarevs = subset.changelog.filteredrevs - cl.filteredrevs
+                revs.extend(r for  r in extrarevs if r <= partial.tiprev)
+    revs.extend(cl.revs(start=partial.tiprev + 1))
+    if revs:
+        partial.update(repo, revs)
+        partial.write(repo)
+    assert partial.validfor(repo), filtername
+    repo._branchcaches[repo.filtername] = partial
+
+class branchcache(dict):
+    """A dict like object that hold branches heads cache"""
+
+    def __init__(self, entries=(), tipnode=nullid, tiprev=nullrev,
+                 filteredhash=None):
+        super(branchcache, self).__init__(entries)
+        self.tipnode = tipnode
+        self.tiprev = tiprev
+        self.filteredhash = filteredhash
+
+    def _hashfiltered(self, repo):
+        """build hash of revision filtered in the current cache
+
+        Tracking tipnode and tiprev is not enough to ensure validaty of the
+        cache as they do not help to distinct cache that ignored various
+        revision bellow tiprev.
+
+        To detect such difference, we build a cache of all ignored revisions.
+        """
+        cl = repo.changelog
+        if not cl.filteredrevs:
+            return None
+        key = None
+        revs = sorted(r for r in cl.filteredrevs if r <= self.tiprev)
+        if revs:
+            s = util.sha1()
+            for rev in revs:
+                s.update('%s;' % rev)
+            key = s.digest()
+        return key
+
+    def validfor(self, repo):
+        """Is the cache content valide regarding a repo
+
+        - False when cached tipnode are unknown or if we detect a strip.
+        - True when cache is up to date or a subset of current repo."""
+        try:
+            return ((self.tipnode == repo.changelog.node(self.tiprev))
+                    and (self.filteredhash == self._hashfiltered(repo)))
+        except IndexError:
+            return False
+
+    def copy(self):
+        """return an deep copy of the branchcache object"""
+        return branchcache(self, self.tipnode, self.tiprev, self.filteredhash)
+
+    def write(self, repo):
+        try:
+            f = repo.opener(_filename(repo), "w", atomictemp=True)
+            cachekey = [hex(self.tipnode), str(self.tiprev)]
+            if self.filteredhash is not None:
+                cachekey.append(hex(self.filteredhash))
+            f.write(" ".join(cachekey) + '\n')
+            for label, nodes in sorted(self.iteritems()):
+                for node in nodes:
+                    f.write("%s %s\n" % (hex(node), encoding.fromlocal(label)))
+            f.close()
+        except (IOError, OSError, util.Abort):
+            # Abort may be raise by read only opener
+            pass
+
+    def update(self, repo, revgen):
+        """Given a branchhead cache, self, that may have extra nodes or be
+        missing heads, and a generator of nodes that are at least a superset of
+        heads missing, this function updates self to be correct.
+        """
+        cl = repo.changelog
+        # collect new branch entries
+        newbranches = {}
+        getbranch = cl.branch
+        for r in revgen:
+            newbranches.setdefault(getbranch(r), []).append(cl.node(r))
+        # if older branchheads are reachable from new ones, they aren't
+        # really branchheads. Note checking parents is insufficient:
+        # 1 (branch a) -> 2 (branch b) -> 3 (branch a)
+        for branch, newnodes in newbranches.iteritems():
+            bheads = self.setdefault(branch, [])
+            # Remove candidate heads that no longer are in the repo (e.g., as
+            # the result of a strip that just happened).  Avoid using 'node in
+            # self' here because that dives down into branchcache code somewhat
+            # recursively.
+            bheadrevs = [cl.rev(node) for node in bheads
+                         if cl.hasnode(node)]
+            newheadrevs = [cl.rev(node) for node in newnodes
+                           if cl.hasnode(node)]
+            ctxisnew = bheadrevs and min(newheadrevs) > max(bheadrevs)
+            # Remove duplicates - nodes that are in newheadrevs and are already
+            # in bheadrevs.  This can happen if you strip a node whose parent
+            # was already a head (because they're on different branches).
+            bheadrevs = sorted(set(bheadrevs).union(newheadrevs))
+
+            # Starting from tip means fewer passes over reachable.  If we know
+            # the new candidates are not ancestors of existing heads, we don't
+            # have to examine ancestors of existing heads
+            if ctxisnew:
+                iterrevs = sorted(newheadrevs)
+            else:
+                iterrevs = list(bheadrevs)
+
+            # This loop prunes out two kinds of heads - heads that are
+            # superseded by a head in newheadrevs, and newheadrevs that are not
+            # heads because an existing head is their descendant.
+            while iterrevs:
+                latest = iterrevs.pop()
+                if latest not in bheadrevs:
+                    continue
+                ancestors = set(cl.ancestors([latest],
+                                                         bheadrevs[0]))
+                if ancestors:
+                    bheadrevs = [b for b in bheadrevs if b not in ancestors]
+            self[branch] = [cl.node(rev) for rev in bheadrevs]
+            tiprev = max(bheadrevs)
+            if tiprev > self.tiprev:
+                self.tipnode = cl.node(tiprev)
+                self.tiprev = tiprev
+
+        # There may be branches that cease to exist when the last commit in the
+        # branch was stripped.  This code filters them out.  Note that the
+        # branch that ceased to exist may not be in newbranches because
+        # newbranches is the set of candidate heads, which when you strip the
+        # last commit in a branch will be the parent branch.
+        droppednodes = []
+        for branch in self.keys():
+            nodes = [head for head in self[branch]
+                     if cl.hasnode(head)]
+            if not nodes:
+                droppednodes.extend(nodes)
+                del self[branch]
+        if ((not self.validfor(repo)) or (self.tipnode in droppednodes)):
+
+            # cache key are not valid anymore
+            self.tipnode = nullid
+            self.tiprev = nullrev
+            for heads in self.values():
+                tiprev = max(cl.rev(node) for node in heads)
+                if tiprev > self.tiprev:
+                    self.tipnode = cl.node(tiprev)
+                    self.tiprev = tiprev
+        self.filteredhash = self._hashfiltered(repo)
--- a/mercurial/bundlerepo.py	Mon Jan 14 23:14:45 2013 +0900
+++ b/mercurial/bundlerepo.py	Sat Jan 19 17:24:33 2013 -0600
@@ -14,25 +14,28 @@
 from node import nullid
 from i18n import _
 import os, tempfile, shutil
-import changegroup, util, mdiff, discovery, cmdutil
+import changegroup, util, mdiff, discovery, cmdutil, scmutil
 import localrepo, changelog, manifest, filelog, revlog, error
 
 class bundlerevlog(revlog.revlog):
     def __init__(self, opener, indexfile, bundle, linkmapper):
         # How it works:
-        # to retrieve a revision, we need to know the offset of
-        # the revision in the bundle (an unbundle object).
+        # To retrieve a revision, we need to know the offset of the revision in
+        # the bundle (an unbundle object). We store this offset in the index
+        # (start).
         #
-        # We store this offset in the index (start), to differentiate a
-        # rev in the bundle and from a rev in the revlog, we check
-        # len(index[r]). If the tuple is bigger than 7, it is a bundle
-        # (it is bigger since we store the node to which the delta is)
+        # basemap is indexed with revisions coming from the bundle, and it
+        # maps to the revision that is the base of the corresponding delta.
         #
+        # To differentiate a rev in the bundle from a rev in the revlog, we
+        # check revision against basemap.
+        opener = scmutil.readonlyvfs(opener)
         revlog.revlog.__init__(self, opener, indexfile)
         self.bundle = bundle
-        self.basemap = {}
+        self.basemap = {} # mapping rev to delta base rev
         n = len(self)
         chain = None
+        self.bundlerevs = set() # used by 'bundle()' revset expression
         while True:
             chunkdata = bundle.deltachunk(chain)
             if not chunkdata:
@@ -51,49 +54,50 @@
             if node in self.nodemap:
                 # this can happen if two branches make the same change
                 chain = node
+                self.bundlerevs.add(self.nodemap[node])
                 continue
 
             for p in (p1, p2):
                 if p not in self.nodemap:
                     raise error.LookupError(p, self.indexfile,
                                             _("unknown parent"))
+
+            if deltabase not in self.nodemap:
+                raise LookupError(deltabase, self.indexfile,
+                                  _('unknown delta base'))
+
+            baserev = self.rev(deltabase)
             # start, size, full unc. size, base (unused), link, p1, p2, node
             e = (revlog.offset_type(start, 0), size, -1, -1, link,
                  self.rev(p1), self.rev(p2), node)
-            self.basemap[n] = deltabase
+            self.basemap[n] = baserev
             self.index.insert(-1, e)
             self.nodemap[node] = n
+            self.bundlerevs.add(n)
             chain = node
             n += 1
 
-    def inbundle(self, rev):
-        """is rev from the bundle"""
-        if rev < 0:
-            return False
-        return rev in self.basemap
-    def bundlebase(self, rev):
-        return self.basemap[rev]
     def _chunk(self, rev):
-        # Warning: in case of bundle, the diff is against bundlebase,
+        # Warning: in case of bundle, the diff is against self.basemap,
         # not against rev - 1
         # XXX: could use some caching
-        if not self.inbundle(rev):
+        if rev not in self.basemap:
             return revlog.revlog._chunk(self, rev)
         self.bundle.seek(self.start(rev))
         return self.bundle.read(self.length(rev))
 
     def revdiff(self, rev1, rev2):
         """return or calculate a delta between two revisions"""
-        if self.inbundle(rev1) and self.inbundle(rev2):
+        if rev1 in self.basemap and rev2 in self.basemap:
             # hot path for bundle
-            revb = self.rev(self.bundlebase(rev2))
+            revb = self.basemap[rev2]
             if revb == rev1:
                 return self._chunk(rev2)
-        elif not self.inbundle(rev1) and not self.inbundle(rev2):
+        elif rev1 not in self.basemap and rev2 not in self.basemap:
             return revlog.revlog.revdiff(self, rev1, rev2)
 
         return mdiff.textdiff(self.revision(self.node(rev1)),
-                         self.revision(self.node(rev2)))
+                              self.revision(self.node(rev2)))
 
     def revision(self, nodeorrev):
         """return an uncompressed revision of a given node or revision
@@ -111,28 +115,23 @@
 
         text = None
         chain = []
-        iter_node = node
+        iterrev = rev
         # reconstruct the revision if it is from a changegroup
-        while self.inbundle(rev):
-            if self._cache and self._cache[0] == iter_node:
+        while iterrev in self.basemap:
+            if self._cache and self._cache[1] == iterrev:
                 text = self._cache[2]
                 break
-            chain.append(rev)
-            iter_node = self.bundlebase(rev)
-            rev = self.rev(iter_node)
+            chain.append(iterrev)
+            iterrev = self.basemap[iterrev]
         if text is None:
-            text = revlog.revlog.revision(self, iter_node)
+            text = revlog.revlog.revision(self, iterrev)
 
         while chain:
             delta = self._chunk(chain.pop())
             text = mdiff.patches(text, [delta])
 
-        p1, p2 = self.parents(node)
-        if node != revlog.hash(text, p1, p2):
-            raise error.RevlogError(_("integrity check failed on %s:%d")
-                                     % (self.datafile, self.rev(node)))
-
-        self._cache = (node, self.rev(node), text)
+        self._checkhash(text, node, rev)
+        self._cache = (node, rev, text)
         return text
 
     def addrevision(self, text, transaction, link, p1=None, p2=None, d=None):
@@ -212,7 +211,7 @@
         # dict with the mapping 'filename' -> position in the bundle
         self.bundlefilespos = {}
 
-    @util.propertycache
+    @localrepo.unfilteredpropertycache
     def changelog(self):
         # consume the header if it exists
         self.bundle.changelogheader()
@@ -220,7 +219,7 @@
         self.manstart = self.bundle.tell()
         return c
 
-    @util.propertycache
+    @localrepo.unfilteredpropertycache
     def manifest(self):
         self.bundle.seek(self.manstart)
         # consume the header if it exists
@@ -229,12 +228,12 @@
         self.filestart = self.bundle.tell()
         return m
 
-    @util.propertycache
+    @localrepo.unfilteredpropertycache
     def manstart(self):
         self.changelog
         return self.manstart
 
-    @util.propertycache
+    @localrepo.unfilteredpropertycache
     def filestart(self):
         self.manifest
         return self.filestart
@@ -256,8 +255,6 @@
                     if not c:
                         break
 
-        if f[0] == '/':
-            f = f[1:]
         if f in self.bundlefilespos:
             self.bundle.seek(self.bundlefilespos[f])
             return bundlefilelog(self.sopener, f, self.bundle,
@@ -282,9 +279,6 @@
     def getcwd(self):
         return os.getcwd() # always outside the repo
 
-    def _writebranchcache(self, branches, tip, tiprev):
-        # don't overwrite the disk cache with bundle-augmented data
-        pass
 
 def instance(ui, path, create):
     if create:
@@ -384,4 +378,3 @@
         other.close()
 
     return (localrepo, csets, cleanup)
-
--- a/mercurial/changelog.py	Mon Jan 14 23:14:45 2013 +0900
+++ b/mercurial/changelog.py	Sat Jan 19 17:24:33 2013 -0600
@@ -27,10 +27,13 @@
 
 def decodeextra(text):
     """
-    >>> decodeextra(encodeextra({'foo': 'bar', 'baz': chr(0) + '2'}))
-    {'foo': 'bar', 'baz': '\\x002', 'branch': 'default'}
-    >>> decodeextra(encodeextra({'foo': 'bar', 'baz': chr(92) + chr(0) + '2'}))
-    {'foo': 'bar', 'baz': '\\\\\\x002', 'branch': 'default'}
+    >>> sorted(decodeextra(encodeextra({'foo': 'bar', 'baz': chr(0) + '2'})
+    ...                    ).iteritems())
+    [('baz', '\\x002'), ('branch', 'default'), ('foo', 'bar')]
+    >>> sorted(decodeextra(encodeextra({'foo': 'bar',
+    ...                                 'baz': chr(92) + chr(0) + '2'})
+    ...                    ).iteritems())
+    [('baz', '\\\\\\x002'), ('branch', 'default'), ('foo', 'bar')]
     """
     extra = _defaultextra.copy()
     for l in text.split('\0'):
@@ -124,7 +127,7 @@
         self._realopener = opener
         self._delayed = False
         self._divert = False
-        self.filteredrevs = ()
+        self.filteredrevs = frozenset()
 
     def tip(self):
         """filtered version of revlog.tip"""
@@ -337,3 +340,10 @@
         l = [hex(manifest), user, parseddate] + sorted(files) + ["", desc]
         text = "\n".join(l)
         return self.addrevision(text, transaction, len(self), p1, p2)
+
+    def branch(self, rev):
+        """return the branch of a revision
+
+        This function exists because creating a changectx object
+        just to access this is costly."""
+        return encoding.tolocal(self.read(rev)[5].get("branch"))
--- a/mercurial/cmdutil.py	Mon Jan 14 23:14:45 2013 +0900
+++ b/mercurial/cmdutil.py	Sat Jan 19 17:24:33 2013 -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
 
@@ -85,7 +85,7 @@
     if modified or added or removed or deleted:
         raise util.Abort(_("outstanding uncommitted changes"))
     ctx = repo[None]
-    for s in ctx.substate:
+    for s in sorted(ctx.substate):
         if ctx.sub(s).dirty():
             raise util.Abort(_("uncommitted changes in subrepo %s") % s)
 
@@ -1137,8 +1137,8 @@
             for path in match.files():
                 if path == '.' or path in repo.store:
                     break
-                else:
-                    return []
+            else:
+                return []
 
     if slowpath:
         # We have to read the changelog to match filenames against
@@ -1399,39 +1399,18 @@
     callable taking a revision number and returning a match objects
     filtering the files to be detailed when displaying the revision.
     """
-    def increasingrevs(repo, revs, matcher):
-        # The sorted input rev sequence is chopped in sub-sequences
-        # which are sorted in ascending order and passed to the
-        # matcher. The filtered revs are sorted again as they were in
-        # the original sub-sequence. This achieve several things:
-        #
-        # - getlogrevs() now returns a generator which behaviour is
-        #   adapted to log need. First results come fast, last ones
-        #   are batched for performances.
-        #
-        # - revset matchers often operate faster on revision in
-        #   changelog order, because most filters deal with the
-        #   changelog.
-        #
-        # - revset matchers can reorder revisions. "A or B" typically
-        #   returns returns the revision matching A then the revision
-        #   matching B. We want to hide this internal implementation
-        #   detail from the caller, and sorting the filtered revision
-        #   again achieves this.
-        for i, window in increasingwindows(0, len(revs), windowsize=1):
-            orevs = revs[i:i + window]
-            nrevs = set(matcher(repo, sorted(orevs)))
-            for rev in orevs:
-                if rev in nrevs:
-                    yield rev
-
     if not len(repo):
-        return iter([]), None, None
+        return [], None, None
+    limit = loglimit(opts)
     # Default --rev value depends on --follow but --follow behaviour
     # depends on revisions resolved from --rev...
     follow = opts.get('follow') or opts.get('follow_first')
+    possiblyunsorted = False # whether revs might need sorting
     if opts.get('rev'):
         revs = scmutil.revrange(repo, opts['rev'])
+        # Don't sort here because _makegraphlogrevset might depend on the
+        # order of revs
+        possiblyunsorted = True
     else:
         if follow and len(repo) > 0:
             revs = repo.revs('reverse(:.)')
@@ -1439,17 +1418,23 @@
             revs = list(repo.changelog)
             revs.reverse()
     if not revs:
-        return iter([]), None, None
+        return [], None, None
     expr, filematcher = _makegraphlogrevset(repo, pats, opts, revs)
+    if possiblyunsorted:
+        revs.sort(reverse=True)
     if expr:
+        # Revset matchers often operate faster on revisions in changelog
+        # order, because most filters deal with the changelog.
+        revs.reverse()
         matcher = revset.match(repo.ui, expr)
-        revs = increasingrevs(repo, revs, matcher)
-    if not opts.get('hidden'):
-        # --hidden is still experimental and not worth a dedicated revset
-        # yet. Fortunately, filtering revision number is fast.
-        revs = (r for r in revs if r not in repo.hiddenrevs)
-    else:
-        revs = iter(revs)
+        # Revset matches can reorder revisions. "A or B" typically returns
+        # returns the revision matching A then the revision matching B. Sort
+        # again to fix that.
+        revs = matcher(repo, revs)
+        revs.sort(reverse=True)
+    if limit is not None:
+        revs = revs[:limit]
+
     return revs, expr, filematcher
 
 def displaygraph(ui, dag, displayer, showparents, edgefn, getrenamed=None,
@@ -1484,10 +1469,6 @@
 def graphlog(ui, repo, *pats, **opts):
     # Parameters are identical to log command ones
     revs, expr, filematcher = getgraphlogrevs(repo, pats, opts)
-    revs = sorted(revs, reverse=1)
-    limit = loglimit(opts)
-    if limit is not None:
-        revs = revs[:limit]
     revdag = graphmod.dagwalker(repo, revs)
 
     getrenamed = None
@@ -1534,7 +1515,7 @@
             if ui.verbose or not exact:
                 ui.status(_('adding %s\n') % match.rel(join(f)))
 
-    for subpath in wctx.substate:
+    for subpath in sorted(wctx.substate):
         sub = wctx.sub(subpath)
         try:
             submatch = matchmod.narrowmatcher(subpath, match)
@@ -1565,7 +1546,7 @@
     if explicitonly:
         forget = [f for f in forget if match.exact(f)]
 
-    for subpath in wctx.substate:
+    for subpath in sorted(wctx.substate):
         sub = wctx.sub(subpath)
         try:
             submatch = matchmod.narrowmatcher(subpath, match)
@@ -1762,9 +1743,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
@@ -1875,7 +1857,7 @@
                 names[abs] = m.rel(abs), m.exact(abs)
 
         # get the list of subrepos that must be reverted
-        targetsubs = [s for s in ctx.substate if m(s)]
+        targetsubs = sorted(s for s in ctx.substate if m(s))
         m = scmutil.matchfiles(repo, names)
         changes = repo.status(match=m)[:4]
         modified, added, removed, deleted = map(set, changes)
@@ -2015,12 +1997,12 @@
     '''returns a function object bound to table which can be used as
     a decorator for populating table as a command table'''
 
-    def cmd(name, options, synopsis=None):
+    def cmd(name, options=(), synopsis=None):
         def decorator(func):
             if synopsis:
-                table[name] = func, options[:], synopsis
+                table[name] = func, list(options), synopsis
             else:
-                table[name] = func, options[:]
+                table[name] = func, list(options)
             return func
         return decorator
 
--- a/mercurial/commands.py	Mon Jan 14 23:14:45 2013 +0900
+++ b/mercurial/commands.py	Sat Jan 19 17:24:33 2013 -0600
@@ -49,6 +49,7 @@
     ('', 'profile', None, _('print command execution profile')),
     ('', 'version', None, _('output version information and exit')),
     ('h', 'help', None, _('display help and exit')),
+    ('', 'hidden', False, _('consider hidden changesets')),
 ]
 
 dryrunopts = [('n', 'dry-run', None,
@@ -549,6 +550,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
@@ -754,7 +759,7 @@
             cmdutil.bailifchanged(repo)
             return hg.clean(repo, node)
 
-@command('bookmarks',
+@command('bookmarks|bookmark',
     [('f', 'force', False, _('force')),
     ('r', 'rev', '', _('revision'), _('REV')),
     ('d', 'delete', False, _('delete a given bookmark')),
@@ -821,7 +826,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 +839,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 +853,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
@@ -924,7 +929,7 @@
                                        ' exists'),
                                      # i18n: "it" refers to an existing branch
                                      hint=_("use 'hg update' to switch to it"))
-            scmutil.checknewlabel(None, label, 'branch')
+            scmutil.checknewlabel(repo, label, 'branch')
             repo.dirstate.setbranch(label)
             ui.status(_('marked working directory as branch %s\n') % label)
             ui.status(_('(branches are permanent and global, '
@@ -1292,7 +1297,7 @@
             raise util.Abort(_('cannot amend merge changesets'))
         if len(repo[None].parents()) > 1:
             raise util.Abort(_('cannot amend while merging'))
-        if old.children():
+        if (not obsolete._enabled) and old.children():
             raise util.Abort(_('cannot amend changeset with children'))
 
         e = cmdutil.commiteditor
@@ -1322,11 +1327,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 +1519,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 = []
@@ -1526,7 +1532,8 @@
                     if len(ps) > 1:
                         p2 = repo[ps[1]]
                         pa = p1.ancestor(p2)
-                        base, local, other = [x[fn].data() for x in pa, p1, p2]
+                        base, local, other = [x[fn].data() for x in (pa, p1,
+                                                                     p2)]
                         m3 = simplemerge.Merge3Text(base, local, other)
                         ml = [l.strip() for l in m3.merge_lines()]
                         ml.append("")
@@ -1574,10 +1581,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 +1602,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 +1794,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,8 +1828,8 @@
                                                                 force=True)
             common = set(common)
             if not opts.get('nonheads'):
-                ui.write("unpruned common: %s\n" % " ".join([short(n)
-                                                            for n in common]))
+                ui.write(("unpruned common: %s\n") %
+                         " ".join(sorted(short(n) for n in common)))
                 dag = dagutil.revlogdag(repo.changelog)
                 all = dag.ancestorset(dag.internalizeall(common))
                 common = dag.externalizeall(dag.headsetofconnecteds(all))
@@ -1831,11 +1838,12 @@
         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(sorted(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 +1887,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 +1987,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)
@@ -2128,7 +2136,8 @@
                 ui.write(' ')
                 ui.write(hex(repl))
             ui.write(' %X ' % m._data[2])
-            ui.write(m.metadata())
+            ui.write('{%s}' % (', '.join('%r: %r' % t for t in
+                                         sorted(m.metadata().items()))))
             ui.write('\n')
 
 @command('debugpushkey', [], _('REPO NAMESPACE [KEY OLD NEW]'))
@@ -2148,7 +2157,7 @@
         ui.status(str(r) + '\n')
         return not r
     else:
-        for k, v in target.listkeys(namespace).iteritems():
+        for k, v in sorted(target.listkeys(namespace).iteritems()):
             ui.write("%s\t%s\n" % (k.encode('string-escape'),
                                    v.encode('string-escape')))
 
@@ -2325,52 +2334,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 +2459,63 @@
 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('debugsuccessorssets',
+    [],
+    _('[REV]'))
+def debugsuccessorssets(ui, repo, *revs):
+    """show set of successors for revision
+
+    A successors set of changeset A is a consistent group of revisions that
+    succeed A. It contains non-obsolete changesets only.
+
+    In most cases a changeset A has a single successors set containing a single
+    successors (changeset A replaced by A').
+
+    A changeset that is made obsolete with no successors are called "pruned".
+    Such changesets have no successors sets at all.
+
+    A changeset that has been "split" will have a successors set containing
+    more than one successors.
+
+    A changeset that has been rewritten in multiple different ways is called
+    "divergent". Such changesets have multiple successor sets (each of which
+    may also be split, i.e. have multiple successors).
+
+    Results are displayed as follows::
+
+        <rev1>
+            <successors-1A>
+        <rev2>
+            <successors-2A>
+            <successors-2B1> <successors-2B2> <successors-2B3>
+
+    Here rev2 has two possible (i.e. divergent) successors sets. The first
+    holds one element, whereas the second holds three (i.e. the changeset has
+    been split).
+    """
+    # passed to successorssets caching computation from one call to another
+    cache = {}
+    ctx2str = str
+    node2str = short
+    if ui.debug():
+        def ctx2str(ctx):
+            return ctx.hex()
+        node2str = hex
+    for rev in scmutil.revrange(repo, revs):
+        ctx = repo[rev]
+        ui.write('%s\n'% ctx2str(ctx))
+        for succsset in obsolete.successorssets(repo, ctx.node(), cache):
+            if succsset:
+                ui.write('    ')
+                ui.write(node2str(succsset[0]))
+                for node in succsset[1:]:
+                    ui.write(' ')
+                    ui.write(node2str(node))
+            ui.write('\n')
 
 @command('debugwalk', walkopts, _('[OPTION]... [FILE]...'))
 def debugwalk(ui, repo, *pats, **opts):
@@ -2823,13 +2888,27 @@
 
     wlock = repo.wlock()
     try:
+        current = repo['.']
         for pos, ctx in enumerate(repo.set("%ld", revs)):
-            current = repo['.']
 
             ui.status(_('grafting revision %s\n') % ctx.rev())
             if opts.get('dry_run'):
                 continue
 
+            source = ctx.extra().get('source')
+            if not source:
+                source = ctx.hex()
+            extra = {'source': source}
+            user = ctx.user()
+            if opts.get('user'):
+                user = opts['user']
+            date = ctx.date()
+            if opts.get('date'):
+                date = opts['date']
+            message = ctx.description()
+            if opts.get('log'):
+                message += '\n(grafted from %s)' % ctx.hex()
+
             # we don't merge the first commit when continuing
             if not cont:
                 # perform the graft merge with p1(rev) as 'ancestor'
@@ -2858,29 +2937,18 @@
             cmdutil.duplicatecopies(repo, ctx.rev(), ctx.p1().rev())
 
             # commit
-            source = ctx.extra().get('source')
-            if not source:
-                source = ctx.hex()
-            extra = {'source': source}
-            user = ctx.user()
-            if opts.get('user'):
-                user = opts['user']
-            date = ctx.date()
-            if opts.get('date'):
-                date = opts['date']
-            message = ctx.description()
-            if opts.get('log'):
-                message += '\n(grafted from %s)' % ctx.hex()
             node = repo.commit(text=message, user=user,
                         date=date, extra=extra, editor=editor)
             if node is None:
                 ui.status(_('graft for revision %s is empty\n') % ctx.rev())
+            else:
+                current = repo[node]
     finally:
         wlock.release()
 
     # remove state when we complete successfully
-    if not opts.get('dry_run') and os.path.exists(repo.join('graftstate')):
-        util.unlinkpath(repo.join('graftstate'))
+    if not opts.get('dry_run'):
+        util.unlinkpath(repo.join('graftstate'), ignoremissing=True)
 
     return 0
 
@@ -3564,7 +3632,7 @@
                 bms = [bm for bm, bmr in peer.listkeys('bookmarks').iteritems()
                        if bmr == hexremoterev]
 
-            return bms
+            return sorted(bms)
 
         if bookmarks:
             output.extend(getbms())
@@ -4024,7 +4092,6 @@
      _('show changesets within the given named branch'), _('BRANCH')),
     ('P', 'prune', [],
      _('do not display revision or any of its ancestors'), _('REV')),
-    ('', 'hidden', False, _('show hidden changesets (DEPRECATED)')),
     ] + logopts + walkopts,
     _('[OPTION]... [FILE]'))
 def log(ui, repo, *pats, **opts):
@@ -4140,8 +4207,6 @@
             return
         if opts.get('branch') and ctx.branch() not in opts['branch']:
             return
-        if not opts.get('hidden') and ctx.hidden():
-            return
         if df and not df(ctx.date()[0]):
             return
 
@@ -4207,6 +4272,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 +4292,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 +4303,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')),
@@ -4556,10 +4629,14 @@
                 phases.retractboundary(repo, targetphase, nodes)
         finally:
             lock.release()
-        newdata = repo._phasecache.getphaserevs(repo)
+        # moving revision from public to draft may hide them
+        # We have to check result on an unfiltered repository
+        unfi = repo.unfiltered()
+        newdata = repo._phasecache.getphaserevs(unfi)
         changes = sum(o != newdata[i] for i, o in enumerate(olddata))
+        cl = unfi.changelog
         rejected = [n for n in nodes
-                    if newdata[repo[n].rev()] < targetphase]
+                    if newdata[cl.rev(n)] < targetphase]
         if rejected:
             ui.warn(_('cannot move %i changesets to a more permissive '
                       'phase, use --force\n') % len(rejected))
@@ -4666,11 +4743,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
 
@@ -4861,8 +4939,7 @@
     elif after:
         list = deleted
         for f in modified + added + clean:
-            ui.warn(_('not removing %s: file still exists (use -f'
-                      ' to force removal)\n') % m.rel(f))
+            ui.warn(_('not removing %s: file still exists\n') % m.rel(f))
             ret = 1
     else:
         list = deleted + clean
@@ -4885,11 +4962,7 @@
             for f in list:
                 if f in added:
                     continue # we never unlink added files on remove
-                try:
-                    util.unlinkpath(repo.wjoin(f))
-                except OSError, inst:
-                    if inst.errno != errno.ENOENT:
-                        raise
+                util.unlinkpath(repo.wjoin(f), ignoremissing=True)
         repo[None].forget(list)
     finally:
         wlock.release()
@@ -5427,17 +5500,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 +5815,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 +5824,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/commandserver.py	Mon Jan 14 23:14:45 2013 +0900
+++ b/mercurial/commandserver.py	Sat Jan 19 17:24:33 2013 -0600
@@ -42,7 +42,7 @@
 
     def __getattr__(self, attr):
         if attr in ('isatty', 'fileno'):
-            raise AttributeError, attr
+            raise AttributeError(attr)
         return getattr(self.in_, attr)
 
 class channeledinput(object):
@@ -122,7 +122,7 @@
 
     def __getattr__(self, attr):
         if attr in ('isatty', 'fileno'):
-            raise AttributeError, attr
+            raise AttributeError(attr)
         return getattr(self.in_, attr)
 
 class server(object):
@@ -220,7 +220,7 @@
                     'getencoding' : getencoding}
 
     def serve(self):
-        hellomsg = 'capabilities: ' + ' '.join(self.capabilities.keys())
+        hellomsg = 'capabilities: ' + ' '.join(sorted(self.capabilities))
         hellomsg += '\n'
         hellomsg += 'encoding: ' + encoding.encoding
 
--- a/mercurial/context.py	Mon Jan 14 23:14:45 2013 +0900
+++ b/mercurial/context.py	Sat Jan 19 17:24:33 2013 -0600
@@ -12,6 +12,7 @@
 import match as matchmod
 import os, errno, stat
 import obsolete as obsmod
+import repoview
 
 propertycache = util.propertycache
 
@@ -25,8 +26,12 @@
         self._repo = repo
 
         if isinstance(changeid, int):
+            try:
+                self._node = repo.changelog.node(changeid)
+            except IndexError:
+                raise error.RepoLookupError(
+                    _("unknown revision '%s'") % changeid)
             self._rev = changeid
-            self._node = repo.changelog.node(changeid)
             return
         if isinstance(changeid, long):
             changeid = str(changeid)
@@ -62,7 +67,7 @@
             self._rev = r
             self._node = repo.changelog.node(r)
             return
-        except (ValueError, OverflowError):
+        except (ValueError, OverflowError, IndexError):
             pass
 
         if len(changeid) == 40:
@@ -95,7 +100,10 @@
 
         # lookup failed
         # check if it might have come from damaged dirstate
-        if changeid in repo.dirstate.parents():
+        #
+        # XXX we could avoid the unfiltered if we had a recognizable exception
+        # for filtered changeset access
+        if changeid in repo.unfiltered().dirstate.parents():
             raise error.Abort(_("working directory has unknown parent '%s'!")
                               % short(changeid))
         try:
@@ -204,7 +212,7 @@
     def mutable(self):
         return self.phase() > phases.public
     def hidden(self):
-        return self._rev in self._repo.hiddenrevs
+        return self._rev in repoview.filterrevs(self._repo, 'visible')
 
     def parents(self):
         """return contexts for each parent changeset"""
@@ -250,6 +258,34 @@
         """
         return self.rev() in obsmod.getrevs(self._repo, 'bumped')
 
+    def divergent(self):
+        """Is a successors of a changeset with multiple possible successors set
+
+        Only non-public and non-obsolete changesets may be divergent.
+        """
+        return self.rev() in obsmod.getrevs(self._repo, 'divergent')
+
+    def troubled(self):
+        """True if the changeset is either unstable, bumped or divergent"""
+        return self.unstable() or self.bumped() or self.divergent()
+
+    def troubles(self):
+        """return the list of troubles affecting this changesets.
+
+        Troubles are returned as strings. possible values are:
+        - unstable,
+        - bumped,
+        - divergent.
+        """
+        troubles = []
+        if self.unstable():
+            troubles.append('unstable')
+        if self.bumped():
+            troubles.append('bumped')
+        if self.divergent():
+            troubles.append('divergent')
+        return troubles
+
     def _fileinfo(self, path):
         if '_manifest' in self.__dict__:
             try:
@@ -352,6 +388,9 @@
     def dirs(self):
         return self._dirs
 
+    def dirty(self):
+        return False
+
 class filectx(object):
     """A filecontext object makes access to data related to a particular
        filerevision convenient."""
@@ -380,7 +419,26 @@
 
     @propertycache
     def _changectx(self):
-        return changectx(self._repo, self._changeid)
+        try:
+            return changectx(self._repo, self._changeid)
+        except error.RepoLookupError:
+            # Linkrev may point to any revision in the repository.  When the
+            # repository is filtered this may lead to `filectx` trying to build
+            # `changectx` for filtered revision. In such case we fallback to
+            # creating `changectx` on the unfiltered version of the reposition.
+            # This fallback should not be an issue because`changectx` from
+            # `filectx` are not used in complexe operation that care about
+            # filtering.
+            #
+            # This fallback is a cheap and dirty fix that prevent several
+            # crash. It does not ensure the behavior is correct. However the
+            # behavior was not correct before filtering either and "incorrect
+            # behavior" is seen as better as "crash"
+            #
+            # Linkrevs have several serious troubles with filtering that are
+            # complicated to solve. Proper handling of the issue here should be
+            # considered when solving linkrev issue are on the table.
+            return changectx(self._repo.unfiltered(), self._changeid)
 
     @propertycache
     def _filelog(self):
@@ -977,13 +1035,13 @@
         return self._parents[0].ancestor(c2) # punt on two parents for now
 
     def walk(self, match):
-        return sorted(self._repo.dirstate.walk(match, self.substate.keys(),
+        return sorted(self._repo.dirstate.walk(match, sorted(self.substate),
                                                True, False))
 
     def dirty(self, missing=False, merge=True, branch=True):
         "check whether a working directory is modified"
         # check subrepos first
-        for s in self.substate:
+        for s in sorted(self.substate):
             if self.sub(s).dirty():
                 return True
         # check current working dir
--- a/mercurial/copies.py	Mon Jan 14 23:14:45 2013 +0900
+++ b/mercurial/copies.py	Sat Jan 19 17:24:33 2013 -0600
@@ -145,12 +145,16 @@
 
     return cm
 
-def _backwardcopies(a, b):
-    # because the forward mapping is 1:n, we can lose renames here
-    # in particular, we find renames better than copies
+def _backwardrenames(a, b):
+    # Even though we're not taking copies into account, 1:n rename situations
+    # can still exist (e.g. hg cp a b; hg mv a c). In those cases we
+    # arbitrarily pick one of the renames.
     f = _forwardcopies(b, a)
     r = {}
-    for k, v in f.iteritems():
+    for k, v in sorted(f.iteritems()):
+        # remove copies
+        if v in a:
+            continue
         r[v] = k
     return r
 
@@ -162,19 +166,25 @@
     if a == x:
         return _forwardcopies(x, y)
     if a == y:
-        return _backwardcopies(x, y)
-    return _chain(x, y, _backwardcopies(x, a), _forwardcopies(a, y))
+        return _backwardrenames(x, y)
+    return _chain(x, y, _backwardrenames(x, a), _forwardcopies(a, y))
 
 def mergecopies(repo, c1, c2, ca):
     """
     Find moves and copies between context c1 and c2 that are relevant
     for merging.
 
-    Returns two dicts, "copy" and "diverge".
+    Returns four dicts: "copy", "movewithdir", "diverge", and
+    "renamedelete".
 
     "copy" is a mapping from destination name -> source name,
     where source is in c1 and destination is in c2 or vice-versa.
 
+    "movewithdir" is a mapping from source name -> destination name,
+    where the file at source present in one context but not the other
+    needs to be moved to destination by the merge process, because the
+    other context moved the directory it is in.
+
     "diverge" is a mapping of source name -> list of destination names
     for divergent renames.
 
@@ -183,16 +193,16 @@
     """
     # avoid silly behavior for update from empty dir
     if not c1 or not c2 or c1 == c2:
-        return {}, {}, {}
+        return {}, {}, {}, {}
 
     # avoid silly behavior for parent -> working dir
     if c2.node() is None and c1.node() == repo.dirstate.p1():
-        return repo.dirstate.copies(), {}, {}
+        return repo.dirstate.copies(), {}, {}, {}
 
     limit = _findlimit(repo, c1.rev(), c2.rev())
     if limit is None:
         # no common ancestor, no copies
-        return {}, {}, {}
+        return {}, {}, {}, {}
     m1 = c1.manifest()
     m2 = c2.manifest()
     ma = ca.manifest()
@@ -206,6 +216,7 @@
 
     ctx = util.lrucachefunc(makectx)
     copy = {}
+    movewithdir = {}
     fullcopy = {}
     diverge = {}
 
@@ -303,7 +314,7 @@
     if fullcopy:
         repo.ui.debug("  all copies found (* = to merge, ! = divergent, "
                       "% = renamed and deleted):\n")
-        for f in fullcopy:
+        for f in sorted(fullcopy):
             note = ""
             if f in copy:
                 note += "*"
@@ -311,11 +322,12 @@
                 note += "!"
             if f in renamedelete2:
                 note += "%"
-            repo.ui.debug("   %s -> %s %s\n" % (f, fullcopy[f], note))
+            repo.ui.debug("   src: '%s' -> dst: '%s' %s\n" % (fullcopy[f], f,
+                                                              note))
     del diverge2
 
     if not fullcopy:
-        return copy, diverge, renamedelete
+        return copy, movewithdir, diverge, renamedelete
 
     repo.ui.debug("  checking for directory renames\n")
 
@@ -352,10 +364,11 @@
     del d1, d2, invalid
 
     if not dirmove:
-        return copy, diverge, renamedelete
+        return copy, movewithdir, diverge, renamedelete
 
     for d in dirmove:
-        repo.ui.debug("  dir %s -> %s\n" % (d, dirmove[d]))
+        repo.ui.debug("   discovered dir src: '%s' -> dst: '%s'\n" %
+                      (d, dirmove[d]))
 
     # check unaccounted nonoverlapping files against directory moves
     for f in u1 + u2:
@@ -365,8 +378,9 @@
                     # new file added in a directory that was moved, move it
                     df = dirmove[d] + f[len(d):]
                     if df not in copy:
-                        copy[f] = df
-                        repo.ui.debug("  file %s -> %s\n" % (f, copy[f]))
+                        movewithdir[f] = df
+                        repo.ui.debug(("   pending file src: '%s' -> "
+                                       "dst: '%s'\n") % (f, df))
                     break
 
-    return copy, diverge, renamedelete
+    return copy, movewithdir, diverge, renamedelete
--- a/mercurial/dirstate.py	Mon Jan 14 23:14:45 2013 +0900
+++ b/mercurial/dirstate.py	Sat Jan 19 17:24:33 2013 -0600
@@ -265,6 +265,12 @@
         try:
             f.write(self._branch + '\n')
             f.close()
+
+            # make sure filecache has the correct stat info for _branch after
+            # replacing the underlying file
+            ce = self._filecache['_branch']
+            if ce:
+                ce.refresh()
         except: # re-raises
             f.discard()
             raise
@@ -607,7 +613,7 @@
             normalize = self._normalize
             skipstep3 = False
         else:
-            normalize = lambda x, y, z: x
+            normalize = None
 
         files = sorted(match.files())
         subrepos.sort()
@@ -628,7 +634,10 @@
 
         # step 1: find all explicit files
         for ff in files:
-            nf = normalize(normpath(ff), False, True)
+            if normalize:
+                nf = normalize(normpath(ff), False, True)
+            else:
+                nf = normpath(ff)
             if nf in results:
                 continue
 
@@ -678,7 +687,10 @@
                     continue
                 raise
             for f, kind, st in entries:
-                nf = normalize(nd and (nd + "/" + f) or f, True, True)
+                if normalize:
+                    nf = normalize(nd and (nd + "/" + f) or f, True, True)
+                else:
+                    nf = nd and (nd + "/" + f) or f
                 if nf not in results:
                     if kind == dirkind:
                         if not ignore(nf):
@@ -698,11 +710,9 @@
         # step 3: report unseen items in the dmap hash
         if not skipstep3 and not exact:
             visit = sorted([f for f in dmap if f not in results and matchfn(f)])
-            for nf, st in zip(visit, util.statfiles([join(i) for i in visit])):
-                if (not st is None and
-                    getkind(st.st_mode) not in (regkind, lnkkind)):
-                    st = None
-                results[nf] = st
+            nf = iter(visit).next
+            for st in util.statfiles([join(i) for i in visit]):
+                results[nf()] = st
         for s in subrepos:
             del results[s]
         del results['.hg']
@@ -748,13 +758,19 @@
         radd = removed.append
         dadd = deleted.append
         cadd = clean.append
+        mexact = match.exact
+        dirignore = self._dirignore
+        checkexec = self._checkexec
+        checklink = self._checklink
+        copymap = self._copymap
+        lastnormaltime = self._lastnormaltime
 
         lnkkind = stat.S_IFLNK
 
         for fn, st in self.walk(match, subrepos, listunknown,
                                 listignored).iteritems():
             if fn not in dmap:
-                if (listignored or match.exact(fn)) and self._dirignore(fn):
+                if (listignored or mexact(fn)) and dirignore(fn):
                     if listignored:
                         iadd(fn)
                 elif listunknown:
@@ -773,15 +789,15 @@
                 mtime = int(st.st_mtime)
                 if (size >= 0 and
                     ((size != st.st_size and size != st.st_size & _rangemask)
-                     or ((mode ^ st.st_mode) & 0100 and self._checkexec))
-                    and (mode & lnkkind != lnkkind or self._checklink)
+                     or ((mode ^ st.st_mode) & 0100 and checkexec))
+                    and (mode & lnkkind != lnkkind or checklink)
                     or size == -2 # other parent
-                    or fn in self._copymap):
+                    or fn in copymap):
                     madd(fn)
                 elif ((time != mtime and time != mtime & _rangemask)
-                      and (mode & lnkkind != lnkkind or self._checklink)):
+                      and (mode & lnkkind != lnkkind or checklink)):
                     ladd(fn)
-                elif mtime == self._lastnormaltime:
+                elif mtime == lastnormaltime:
                     # fn may have been changed in the same timeslot without
                     # changing its size. This can happen if we quickly do
                     # multiple commits in a single transaction.
--- a/mercurial/discovery.py	Mon Jan 14 23:14:45 2013 +0900
+++ b/mercurial/discovery.py	Sat Jan 19 17:24:33 2013 -0600
@@ -8,6 +8,7 @@
 from node import nullid, short
 from i18n import _
 import util, setdiscovery, treediscovery, phases, obsolete, bookmarks
+import branchmap
 
 def findcommonincoming(repo, remote, heads=None, force=False):
     """Return a tuple (common, anyincoming, heads) used to identify the common
@@ -114,7 +115,7 @@
         og.missingheads = onlyheads or repo.heads()
     elif onlyheads is None:
         # use visible heads as it should be cached
-        og.missingheads = visibleheads(repo)
+        og.missingheads = repo.filtered("served").heads()
         og.excluded = [ctx.node() for ctx in repo.set('secret() or extinct()')]
     else:
         # compute common, missing and exclude secret stuff
@@ -192,9 +193,10 @@
 
     # D. Update newmap with outgoing changes.
     # This will possibly add new heads and remove existing ones.
-    newmap = dict((branch, heads[1]) for branch, heads in headssum.iteritems()
-                  if heads[0] is not None)
-    repo._updatebranchcache(newmap, missingctx)
+    newmap = branchmap.branchcache((branch, heads[1])
+                                 for branch, heads in headssum.iteritems()
+                                 if heads[0] is not None)
+    newmap.update(repo, (ctx.rev() for ctx in missingctx))
     for branch, newheads in newmap.iteritems():
         headssum[branch][1][:] = newheads
     return headssum
@@ -205,7 +207,7 @@
     cl = repo.changelog
     # 1-4b. old servers: Check for new topological heads.
     # Construct {old,new}map with branch = None (topological branch).
-    # (code based on _updatebranchcache)
+    # (code based on update)
     oldheads = set(h for h in remoteheads if h in cl.nodemap)
     # all nodes in outgoing.missing are children of either:
     # - an element of oldheads
@@ -266,7 +268,7 @@
     allmissing = set(outgoing.missing)
     allfuturecommon = set(c.node() for c in repo.set('%ld', outgoing.common))
     allfuturecommon.update(allmissing)
-    for branch, heads in headssum.iteritems():
+    for branch, heads in sorted(headssum.iteritems()):
         if heads[0] is None:
             # Maybe we should abort if we push more that one head
             # for new branches ?
@@ -310,7 +312,7 @@
             unsynced = True
         if len(newhs) > len(oldhs):
             # strip updates to existing remote heads from the new heads list
-            dhs = list(newhs - bookmarkedheads - oldhs)
+            dhs = sorted(newhs - bookmarkedheads - oldhs)
         if dhs:
             if error is None:
                 if branch not in ('default', None):
@@ -335,43 +337,3 @@
     # 6. Check for unsynced changes on involved branches.
     if unsynced:
         repo.ui.warn(_("note: unsynced remote changes!\n"))
-
-def visibleheads(repo):
-    """return the set of visible head of this repo"""
-    # XXX we want a cache on this
-    sroots = repo._phasecache.phaseroots[phases.secret]
-    if sroots or repo.obsstore:
-        # XXX very slow revset. storing heads or secret "boundary"
-        # would help.
-        revset = repo.set('heads(not (%ln:: + extinct()))', sroots)
-
-        vheads = [ctx.node() for ctx in revset]
-        if not vheads:
-            vheads.append(nullid)
-    else:
-        vheads = repo.heads()
-    return vheads
-
-
-def visiblebranchmap(repo):
-    """return a branchmap for the visible set"""
-    # XXX Recomputing this data on the fly is very slow.  We should build a
-    # XXX cached version while computing the standard branchmap version.
-    sroots = repo._phasecache.phaseroots[phases.secret]
-    if sroots or repo.obsstore:
-        vbranchmap = {}
-        for branch, nodes in  repo.branchmap().iteritems():
-            # search for secret heads.
-            for n in nodes:
-                if repo[n].phase() >= phases.secret:
-                    nodes = None
-                    break
-            # if secret heads were found we must compute them again
-            if nodes is None:
-                s = repo.set('heads(branch(%s) - secret() - extinct())',
-                             branch)
-                nodes = [c.node() for c in s]
-            vbranchmap[branch] = nodes
-    else:
-        vbranchmap = repo.branchmap()
-    return vbranchmap
--- a/mercurial/dispatch.py	Mon Jan 14 23:14:45 2013 +0900
+++ b/mercurial/dispatch.py	Sat Jan 19 17:24:33 2013 -0600
@@ -183,8 +183,8 @@
         else:
             raise
     except OSError, inst:
-        if getattr(inst, "filename", None):
-            ui.warn(_("abort: %s: %s\n") % (inst.strerror, inst.filename))
+        if getattr(inst, "filename", None) is not None:
+            ui.warn(_("abort: %s: '%s'\n") % (inst.strerror, inst.filename))
         else:
             ui.warn(_("abort: %s\n") % inst.strerror)
     except KeyboardInterrupt:
@@ -710,6 +710,8 @@
                 repo = hg.repository(ui, path=path)
                 if not repo.local():
                     raise util.Abort(_("repository '%s' is not local") % path)
+                if options['hidden']:
+                    repo = repo.unfiltered()
                 repo.ui.setconfig("bundle", "mainreporoot", repo.root)
             except error.RequirementError:
                 raise
--- a/mercurial/encoding.py	Mon Jan 14 23:14:45 2013 +0900
+++ b/mercurial/encoding.py	Sat Jan 19 17:24:33 2013 -0600
@@ -80,8 +80,8 @@
     'foo: \\xc3\\xa4'
     >>> u2 = 'foo: \\xc3\\xa1'
     >>> d = { l: 1, tolocal(u2): 2 }
-    >>> d # no collision
-    {'foo: ?': 1, 'foo: ?': 2}
+    >>> len(d) # no collision
+    2
     >>> 'foo: ?' in d
     False
     >>> l1 = 'foo: \\xe4' # historical latin1 fallback
--- a/mercurial/filemerge.py	Mon Jan 14 23:14:45 2013 +0900
+++ b/mercurial/filemerge.py	Sat Jan 19 17:24:33 2013 -0600
@@ -171,13 +171,15 @@
 
 def _premerge(repo, toolconf, files):
     tool, toolpath, binary, symlink = toolconf
+    if symlink:
+        return 1
     a, b, c, back = files
 
     ui = repo.ui
 
     # do we attempt to simplemerge first?
     try:
-        premerge = _toolbool(ui, tool, "premerge", not (binary or symlink))
+        premerge = _toolbool(ui, tool, "premerge", not binary)
     except error.ConfigError:
         premerge = _toolstr(ui, tool, "premerge").lower()
         valid = 'keep'.split()
@@ -204,6 +206,12 @@
     Uses the internal non-interactive simple merge algorithm for merging
     files. It will fail if there are any conflicts and leave markers in
     the partially merged file."""
+    tool, toolpath, binary, symlink = toolconf
+    if symlink:
+        repo.ui.warn(_('warning: internal:merge cannot merge symlinks '
+                       'for %s\n') % fcd.path())
+        return False, 1
+
     r = _premerge(repo, toolconf, files)
     if r:
         a, b, c, back = files
--- a/mercurial/fileset.py	Mon Jan 14 23:14:45 2013 +0900
+++ b/mercurial/fileset.py	Sat Jan 19 17:24:33 2013 -0600
@@ -373,7 +373,7 @@
     # i18n: "subrepo" is a keyword
     getargs(x, 0, 1, _("subrepo takes at most one argument"))
     ctx = mctx.ctx
-    sstate = ctx.substate
+    sstate = sorted(ctx.substate)
     if x:
         pat = getstring(x, _("subrepo requires a pattern or no arguments"))
 
--- a/mercurial/formatter.py	Mon Jan 14 23:14:45 2013 +0900
+++ b/mercurial/formatter.py	Sat Jan 19 17:24:33 2013 -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/hbisect.py	Mon Jan 14 23:14:45 2013 +0900
+++ b/mercurial/hbisect.py	Sat Jan 19 17:24:33 2013 -0600
@@ -147,7 +147,7 @@
     f = repo.opener("bisect.state", "w", atomictemp=True)
     wlock = repo.wlock()
     try:
-        for kind in state:
+        for kind in sorted(state):
             for node in state[kind]:
                 f.write("%s %s\n" % (kind, hex(node)))
         f.close()
--- a/mercurial/help/config.txt	Mon Jan 14 23:14:45 2013 +0900
+++ b/mercurial/help/config.txt	Sat Jan 19 17:24:33 2013 -0600
@@ -850,14 +850,6 @@
   ``prompt``
     Always prompt for merge success, regardless of success reported by tool.
 
-``checkchanged``
-  True is equivalent to ``check = changed``.
-  Default: False
-
-``checkconflicts``
-  True is equivalent to ``check = conflicts``.
-  Default: False
-
 ``fixeol``
   Attempt to fix up EOL changes caused by the merge tool.
   Default: False
@@ -1295,6 +1287,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	Mon Jan 14 23:14:45 2013 +0900
+++ b/mercurial/hg.py	Sat Jan 19 17:24:33 2013 -0600
@@ -113,7 +113,7 @@
     if not repo:
         raise util.Abort(_("repository '%s' is not local") %
                          (path or peer.url()))
-    return repo
+    return repo.filtered('visible')
 
 def peer(uiorrepo, opts, path, create=False):
     '''return a repository peer for the specified path'''
@@ -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"))
@@ -288,17 +291,7 @@
         elif os.listdir(dest):
             raise util.Abort(_("destination '%s' is not empty") % dest)
 
-    class DirCleanup(object):
-        def __init__(self, dir_):
-            self.rmtree = shutil.rmtree
-            self.dir_ = dir_
-        def close(self):
-            self.dir_ = None
-        def cleanup(self):
-            if self.dir_:
-                self.rmtree(self.dir_, True)
-
-    srclock = destlock = dircleanup = None
+    srclock = destlock = cleandir = None
     srcrepo = srcpeer.local()
     try:
         abspath = origsource
@@ -306,7 +299,7 @@
             abspath = os.path.abspath(util.urllocalpath(origsource))
 
         if islocal(dest):
-            dircleanup = DirCleanup(dest)
+            cleandir = dest
 
         copy = False
         if (srcrepo and srcrepo.cancopy() and islocal(dest)
@@ -330,13 +323,13 @@
                 os.mkdir(dest)
             else:
                 # only clean up directories we create ourselves
-                dircleanup.dir_ = hgdir
+                cleandir = hgdir
             try:
                 destpath = hgdir
                 util.makedir(destpath, notindexed=True)
             except OSError, inst:
                 if inst.errno == errno.EEXIST:
-                    dircleanup.close()
+                    cleandir = None
                     raise util.Abort(_("destination '%s' already exists")
                                      % dest)
                 raise
@@ -364,7 +357,7 @@
                                 # only pass ui when no srcrepo
             except OSError, inst:
                 if inst.errno == errno.EEXIST:
-                    dircleanup.close()
+                    cleandir = None
                     raise util.Abort(_("destination '%s' already exists")
                                      % dest)
                 raise
@@ -384,21 +377,21 @@
             else:
                 raise util.Abort(_("clone from remote to remote not supported"))
 
-        if dircleanup:
-            dircleanup.close()
+        cleandir = None
 
         # clone all bookmarks except divergent ones
         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))
@@ -450,8 +443,8 @@
         return srcpeer, destpeer
     finally:
         release(srclock, destlock)
-        if dircleanup is not None:
-            dircleanup.cleanup()
+        if cleandir is not None:
+            shutil.rmtree(cleandir, True)
         if srcpeer is not None:
             srcpeer.close()
 
--- a/mercurial/hgweb/common.py	Mon Jan 14 23:14:45 2013 +0900
+++ b/mercurial/hgweb/common.py	Sat Jan 19 17:24:33 2013 -0600
@@ -140,11 +140,11 @@
     try:
         os.stat(path)
         ct = mimetypes.guess_type(path)[0] or "text/plain"
-        req.respond(HTTP_OK, ct, length = os.path.getsize(path))
         fp = open(path, 'rb')
         data = fp.read()
         fp.close()
-        return data
+        req.respond(HTTP_OK, ct, body=data)
+        return ""
     except TypeError:
         raise ErrorResponse(HTTP_SERVER_ERROR, 'illegal filename')
     except OSError, err:
--- a/mercurial/hgweb/hgweb_mod.py	Mon Jan 14 23:14:45 2013 +0900
+++ b/mercurial/hgweb/hgweb_mod.py	Sat Jan 19 17:24:33 2013 -0600
@@ -24,6 +24,30 @@
     'pushkey': 'push',
 }
 
+def makebreadcrumb(url):
+    '''Return a 'URL breadcrumb' list
+
+    A 'URL breadcrumb' is a list of URL-name pairs,
+    corresponding to each of the path items on a URL.
+    This can be used to create path navigation entries.
+    '''
+    if url.endswith('/'):
+        url = url[:-1]
+    relpath = url
+    if relpath.startswith('/'):
+        relpath = relpath[1:]
+
+    breadcrumb = []
+    urlel = url
+    pathitems = [''] + relpath.split('/')
+    for pathel in reversed(pathitems):
+        if not pathel or not urlel:
+            break
+        breadcrumb.append({'url': urlel, 'name': pathel})
+        urlel = os.path.dirname(urlel)
+    return reversed(breadcrumb)
+
+
 class hgweb(object):
     def __init__(self, repo, name=None, baseui=None):
         if isinstance(repo, str):
@@ -35,6 +59,7 @@
         else:
             self.repo = repo
 
+        self.repo =  self.repo.filtered('served')
         self.repo.ui.setconfig('ui', 'report_untrusted', 'off')
         self.repo.ui.setconfig('ui', 'nontty', 'true')
         hook.redirect(True)
@@ -71,6 +96,7 @@
             self.mtime = st.st_mtime
             self.size = st.st_size
             self.repo = hg.repository(self.repo.ui, self.repo.root)
+            self.repo =  self.repo.filtered('served')
             self.maxchanges = int(self.config("web", "maxchanges", 10))
             self.stripecount = int(self.config("web", "stripes", 1))
             self.maxshortchanges = int(self.config("web", "maxshortchanges",
@@ -134,8 +160,9 @@
                                  '').lower() != '100-continue') or
                     req.env.get('X-HgHttp2', '')):
                     req.drain()
-                req.respond(inst, protocol.HGTYPE)
-                return '0\n%s\n' % inst.message
+                req.respond(inst, protocol.HGTYPE,
+                            body='0\n%s\n' % inst.message)
+                return ''
 
         # translate user-visible url structure to internal structure
 
@@ -285,7 +312,8 @@
                                              "header": header,
                                              "footer": footer,
                                              "motd": motd,
-                                             "sessionvars": sessionvars
+                                             "sessionvars": sessionvars,
+                                             "pathdef": makebreadcrumb(req.url),
                                             })
         return tmpl
 
--- a/mercurial/hgweb/hgwebdir_mod.py	Mon Jan 14 23:14:45 2013 +0900
+++ b/mercurial/hgweb/hgwebdir_mod.py	Sat Jan 19 17:24:33 2013 -0600
@@ -12,7 +12,7 @@
 from mercurial import error, encoding
 from common import ErrorResponse, get_mtime, staticfile, paritygen, \
                    get_contact, HTTP_OK, HTTP_NOT_FOUND, HTTP_SERVER_ERROR
-from hgweb_mod import hgweb
+from hgweb_mod import hgweb, makebreadcrumb
 from request import wsgirequest
 import webutil
 
@@ -310,7 +310,8 @@
                                description_sort="",
                                lastchange=d,
                                lastchange_sort=d[1]-d[0],
-                               archives=[])
+                               archives=[],
+                               isdirectory=True)
 
                     seendirs.add(name)
                     yield row
@@ -394,6 +395,7 @@
         self.updatereqenv(req.env)
 
         return tmpl("index", entries=entries, subdir=subdir,
+                    pathdef=makebreadcrumb('/' + subdir),
                     sortcolumn=sortcolumn, descending=descending,
                     **dict(sort))
 
--- a/mercurial/hgweb/protocol.py	Mon Jan 14 23:14:45 2013 +0900
+++ b/mercurial/hgweb/protocol.py	Sat Jan 19 17:24:33 2013 -0600
@@ -75,23 +75,24 @@
     p = webproto(req, repo.ui)
     rsp = wireproto.dispatch(repo, p, cmd)
     if isinstance(rsp, str):
-        req.respond(HTTP_OK, HGTYPE, length=len(rsp))
-        return [rsp]
+        req.respond(HTTP_OK, HGTYPE, body=rsp)
+        return []
     elif isinstance(rsp, wireproto.streamres):
         req.respond(HTTP_OK, HGTYPE)
         return rsp.gen
     elif isinstance(rsp, wireproto.pushres):
         val = p.restore()
-        req.respond(HTTP_OK, HGTYPE)
-        return ['%d\n%s' % (rsp.res, val)]
+        rsp = '%d\n%s' % (rsp.res, val)
+        req.respond(HTTP_OK, HGTYPE, body=rsp)
+        return []
     elif isinstance(rsp, wireproto.pusherr):
         # drain the incoming bundle
         req.drain()
         p.restore()
         rsp = '0\n%s\n' % rsp.res
-        req.respond(HTTP_OK, HGTYPE, length=len(rsp))
-        return [rsp]
+        req.respond(HTTP_OK, HGTYPE, body=rsp)
+        return []
     elif isinstance(rsp, wireproto.ooberror):
         rsp = rsp.message
-        req.respond(HTTP_OK, HGERRTYPE, length=len(rsp))
-        return [rsp]
+        req.respond(HTTP_OK, HGERRTYPE, body=rsp)
+        return []
--- a/mercurial/hgweb/request.py	Mon Jan 14 23:14:45 2013 +0900
+++ b/mercurial/hgweb/request.py	Sat Jan 19 17:24:33 2013 -0600
@@ -70,19 +70,23 @@
         for s in util.filechunkiter(self.inp, limit=length):
             pass
 
-    def respond(self, status, type=None, filename=None, length=0):
+    def respond(self, status, type, filename=None, body=None):
         if self._start_response is not None:
-
-            self.httphdr(type, filename, length)
-            if not self.headers:
-                raise RuntimeError("request.write called before headers sent")
+            self.headers.append(('Content-Type', type))
+            if filename:
+                filename = (filename.split('/')[-1]
+                            .replace('\\', '\\\\').replace('"', '\\"'))
+                self.headers.append(('Content-Disposition',
+                                     'inline; filename="%s"' % filename))
+            if body is not None:
+                self.headers.append(('Content-Length', str(len(body))))
 
             for k, v in self.headers:
                 if not isinstance(v, str):
-                    raise TypeError('header value must be string: %r' % v)
+                    raise TypeError('header value must be string: %r' % (v,))
 
             if isinstance(status, ErrorResponse):
-                self.header(status.headers)
+                self.headers.extend(status.headers)
                 if status.code == HTTP_NOT_MODIFIED:
                     # RFC 2616 Section 10.3.5: 304 Not Modified has cases where
                     # it MUST NOT include any headers other than these and no
@@ -99,13 +103,12 @@
             self.server_write = self._start_response(status, self.headers)
             self._start_response = None
             self.headers = []
+        if body is not None:
+            self.write(body)
+            self.server_write = None
 
     def write(self, thing):
-        if util.safehasattr(thing, "__iter__"):
-            for part in thing:
-                self.write(part)
-        else:
-            thing = str(thing)
+        if thing:
             try:
                 self.server_write(thing)
             except socket.error, inst:
@@ -122,22 +125,6 @@
     def close(self):
         return None
 
-    def header(self, headers=[('Content-Type','text/html')]):
-        self.headers.extend(headers)
-
-    def httphdr(self, type=None, filename=None, length=0, headers={}):
-        headers = headers.items()
-        if type is not None:
-            headers.append(('Content-Type', type))
-        if filename:
-            filename = (filename.split('/')[-1]
-                        .replace('\\', '\\\\').replace('"', '\\"'))
-            headers.append(('Content-Disposition',
-                            'inline; filename="%s"' % filename))
-        if length:
-            headers.append(('Content-Length', str(length)))
-        self.header(headers)
-
 def wsgiapplication(app_maker):
     '''For compatibility with old CGI scripts. A plain hgweb() or hgwebdir()
     can and should now be used as a WSGI application.'''
--- a/mercurial/hgweb/server.py	Mon Jan 14 23:14:45 2013 +0900
+++ b/mercurial/hgweb/server.py	Sat Jan 19 17:24:33 2013 -0600
@@ -129,13 +129,16 @@
                                               SocketServer.ForkingMixIn)
         env['wsgi.run_once'] = 0
 
-        self.close_connection = True
         self.saved_status = None
         self.saved_headers = []
         self.sent_headers = False
         self.length = None
+        self._chunked = None
         for chunk in self.server.application(env, self._start_response):
             self._write(chunk)
+        if not self.sent_headers:
+            self.send_headers()
+        self._done()
 
     def send_headers(self):
         if not self.saved_status:
@@ -144,20 +147,20 @@
         saved_status = self.saved_status.split(None, 1)
         saved_status[0] = int(saved_status[0])
         self.send_response(*saved_status)
-        should_close = True
+        self.length = None
+        self._chunked = False
         for h in self.saved_headers:
             self.send_header(*h)
             if h[0].lower() == 'content-length':
-                should_close = False
                 self.length = int(h[1])
-        # The value of the Connection header is a list of case-insensitive
-        # tokens separated by commas and optional whitespace.
-        if 'close' in [token.strip().lower() for token in
-                       self.headers.get('connection', '').split(',')]:
-            should_close = True
-        if should_close:
-            self.send_header('Connection', 'close')
-        self.close_connection = should_close
+        if (self.length is None and
+            saved_status[0] != common.HTTP_NOT_MODIFIED):
+            self._chunked = (not self.close_connection and
+                             self.request_version == "HTTP/1.1")
+            if self._chunked:
+                self.send_header('Transfer-Encoding', 'chunked')
+            else:
+                self.send_header('Connection', 'close')
         self.end_headers()
         self.sent_headers = True
 
@@ -180,9 +183,16 @@
                 raise AssertionError("Content-length header sent, but more "
                                      "bytes than specified are being written.")
             self.length = self.length - len(data)
+        elif self._chunked and data:
+            data = '%x\r\n%s\r\n' % (len(data), data)
         self.wfile.write(data)
         self.wfile.flush()
 
+    def _done(self):
+        if self._chunked:
+            self.wfile.write('0\r\n\r\n')
+            self.wfile.flush()
+
 class _httprequesthandleropenssl(_httprequesthandler):
     """HTTPS handler based on pyOpenSSL"""
 
--- a/mercurial/hgweb/webcommands.py	Mon Jan 14 23:14:45 2013 +0900
+++ b/mercurial/hgweb/webcommands.py	Sat Jan 19 17:24:33 2013 -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
@@ -60,8 +61,8 @@
     if mt.startswith('text/'):
         mt += '; charset="%s"' % encoding.encoding
 
-    req.respond(HTTP_OK, mt, path, len(text))
-    return [text]
+    req.respond(HTTP_OK, mt, path, body=text)
+    return []
 
 def _filerevision(web, tmpl, fctx):
     f = fctx.path()
@@ -193,34 +194,37 @@
         except error.RepoError:
             return _search(web, req, tmpl) # XXX redirect to 404 page?
 
-    def changelist(limit=0, **map):
+    def changelist(latestonly, **map):
         l = [] # build a list in forward order for efficiency
-        for i in xrange(start, end):
+        revs = []
+        if start < end:
+            revs = web.repo.changelog.revs(start, end - 1)
+        if latestonly:
+            for r in revs:
+                pass
+            revs = (r,)
+        for i in revs:
             ctx = web.repo[i]
             n = ctx.node()
             showtags = webutil.showtag(web.repo, tmpl, 'changelogtag', n)
             files = webutil.listfilediffs(tmpl, ctx.files(), n, web.maxfiles)
 
-            l.insert(0, {"parity": parity.next(),
-                         "author": ctx.user(),
-                         "parent": webutil.parents(ctx, i - 1),
-                         "child": webutil.children(ctx, i + 1),
-                         "changelogtag": showtags,
-                         "desc": ctx.description(),
-                         "date": ctx.date(),
-                         "files": files,
-                         "rev": i,
-                         "node": hex(n),
-                         "tags": webutil.nodetagsdict(web.repo, n),
-                         "bookmarks": webutil.nodebookmarksdict(web.repo, n),
-                         "inbranch": webutil.nodeinbranch(web.repo, ctx),
-                         "branches": webutil.nodebranchdict(web.repo, ctx)
-                        })
-
-        if limit > 0:
-            l = l[:limit]
-
-        for e in l:
+            l.append({"parity": parity.next(),
+                      "author": ctx.user(),
+                      "parent": webutil.parents(ctx, i - 1),
+                      "child": webutil.children(ctx, i + 1),
+                      "changelogtag": showtags,
+                      "desc": ctx.description(),
+                      "date": ctx.date(),
+                      "files": files,
+                      "rev": i,
+                      "node": hex(n),
+                      "tags": webutil.nodetagsdict(web.repo, n),
+                      "bookmarks": webutil.nodebookmarksdict(web.repo, n),
+                      "inbranch": webutil.nodeinbranch(web.repo, ctx),
+                      "branches": webutil.nodebranchdict(web.repo, ctx)
+                     })
+        for e in reversed(l):
             yield e
 
     revcount = shortlog and web.maxshortchanges or web.maxchanges
@@ -241,12 +245,12 @@
     pos = end - 1
     parity = paritygen(web.stripecount, offset=start - end)
 
-    changenav = webutil.revnavgen(pos, revcount, count, web.repo.changectx)
+    changenav = webutil.revnav(web.repo).gen(pos, revcount, count)
 
     return tmpl(shortlog and 'shortlog' or 'changelog', changenav=changenav,
                 node=ctx.hex(), rev=pos, changesets=count,
-                entries=lambda **x: changelist(limit=0,**x),
-                latestentry=lambda **x: changelist(limit=1,**x),
+                entries=lambda **x: changelist(latestonly=False, **x),
+                latestentry=lambda **x: changelist(latestonly=True, **x),
                 archives=web.archivelist("tip"), revcount=revcount,
                 morevars=morevars, lessvars=lessvars)
 
@@ -255,6 +259,9 @@
 
 def changeset(web, req, tmpl):
     ctx = webutil.changectx(web.repo, req)
+    basectx = webutil.basechangectx(web.repo, req)
+    if basectx is None:
+        basectx = ctx.p1()
     showtags = webutil.showtag(web.repo, tmpl, 'changesettag', ctx.node())
     showbookmarks = webutil.showbookmark(web.repo, tmpl, 'changesetbookmark',
                                          ctx.node())
@@ -273,10 +280,10 @@
         style = req.form['style'][0]
 
     parity = paritygen(web.stripecount)
-    diffs = webutil.diffs(web.repo, tmpl, ctx, None, parity, style)
+    diffs = webutil.diffs(web.repo, tmpl, ctx, basectx, None, parity, style)
 
     parity = paritygen(web.stripecount)
-    diffstatgen = webutil.diffstatgen(ctx)
+    diffstatgen = webutil.diffstatgen(ctx, basectx)
     diffstat = webutil.diffstat(tmpl, ctx, diffstatgen, parity)
 
     return tmpl('changeset',
@@ -285,6 +292,7 @@
                 node=ctx.hex(),
                 parent=webutil.parents(ctx),
                 child=webutil.children(ctx),
+                currentbaseline=basectx.hex(),
                 changesettag=showtags,
                 changesetbookmark=showbookmarks,
                 changesetbranch=showbranch,
@@ -397,14 +405,13 @@
     i = list(reversed(web.repo.tagslist()))
     parity = paritygen(web.stripecount)
 
-    def entries(notip=False, limit=0, **map):
-        count = 0
-        for k, n in i:
-            if notip and k == "tip":
-                continue
-            if limit > 0 and count >= limit:
-                continue
-            count = count + 1
+    def entries(notip, latestonly, **map):
+        t = i
+        if notip:
+            t = [(k, n) for k, n in i if k != "tip"]
+        if latestonly:
+            t = t[:1]
+        for k, n in t:
             yield {"parity": parity.next(),
                    "tag": k,
                    "date": web.repo[n].date(),
@@ -412,20 +419,20 @@
 
     return tmpl("tags",
                 node=hex(web.repo.changelog.tip()),
-                entries=lambda **x: entries(False, 0, **x),
-                entriesnotip=lambda **x: entries(True, 0, **x),
-                latestentry=lambda **x: entries(True, 1, **x))
+                entries=lambda **x: entries(False, False, **x),
+                entriesnotip=lambda **x: entries(True, False, **x),
+                latestentry=lambda **x: entries(True, True, **x))
 
 def bookmarks(web, req, tmpl):
     i = web.repo._bookmarks.items()
     parity = paritygen(web.stripecount)
 
-    def entries(limit=0, **map):
-        count = 0
-        for k, n in sorted(i):
-            if limit > 0 and count >= limit:
-                continue
-            count = count + 1
+    def entries(latestonly, **map):
+        if latestonly:
+            t = [min(i)]
+        else:
+            t = sorted(i)
+        for k, n in t:
             yield {"parity": parity.next(),
                    "bookmark": k,
                    "date": web.repo[n].date(),
@@ -433,8 +440,8 @@
 
     return tmpl("bookmarks",
                 node=hex(web.repo.changelog.tip()),
-                entries=lambda **x: entries(0, **x),
-                latestentry=lambda **x: entries(1, **x))
+                entries=lambda **x: entries(latestonly=False, **x),
+                latestentry=lambda **x: entries(latestonly=True, **x))
 
 def branches(web, req, tmpl):
     tips = []
@@ -515,7 +522,7 @@
             n = ctx.node()
             hn = hex(n)
 
-            l.insert(0, tmpl(
+            l.append(tmpl(
                'shortlogentry',
                 parity=parity.next(),
                 author=ctx.user(),
@@ -528,6 +535,7 @@
                 inbranch=webutil.nodeinbranch(web.repo, ctx),
                 branches=webutil.nodebranchdict(web.repo, ctx)))
 
+        l.reverse()
         yield l
 
     tip = web.repo['tip']
@@ -569,7 +577,7 @@
     if 'style' in req.form:
         style = req.form['style'][0]
 
-    diffs = webutil.diffs(web.repo, tmpl, ctx, [path], parity, style)
+    diffs = webutil.diffs(web.repo, tmpl, ctx, None, [path], parity, style)
     rename = fctx and webutil.renamelink(fctx) or []
     ctx = fctx and fctx or ctx
     return tmpl("filediff",
@@ -736,41 +744,42 @@
     end = min(count, start + revcount) # last rev on this page
     parity = paritygen(web.stripecount, offset=start - end)
 
-    def entries(limit=0, **map):
+    def entries(latestonly, **map):
         l = []
 
         repo = web.repo
-        for i in xrange(start, end):
+        revs = repo.changelog.revs(start, end - 1)
+        if latestonly:
+            for r in revs:
+                pass
+            revs = (r,)
+        for i in revs:
             iterfctx = fctx.filectx(i)
 
-            l.insert(0, {"parity": parity.next(),
-                         "filerev": i,
-                         "file": f,
-                         "node": iterfctx.hex(),
-                         "author": iterfctx.user(),
-                         "date": iterfctx.date(),
-                         "rename": webutil.renamelink(iterfctx),
-                         "parent": webutil.parents(iterfctx),
-                         "child": webutil.children(iterfctx),
-                         "desc": iterfctx.description(),
-                         "tags": webutil.nodetagsdict(repo, iterfctx.node()),
-                         "bookmarks": webutil.nodebookmarksdict(
-                             repo, iterfctx.node()),
-                         "branch": webutil.nodebranchnodefault(iterfctx),
-                         "inbranch": webutil.nodeinbranch(repo, iterfctx),
-                         "branches": webutil.nodebranchdict(repo, iterfctx)})
-
-        if limit > 0:
-            l = l[:limit]
-
-        for e in l:
+            l.append({"parity": parity.next(),
+                      "filerev": i,
+                      "file": f,
+                      "node": iterfctx.hex(),
+                      "author": iterfctx.user(),
+                      "date": iterfctx.date(),
+                      "rename": webutil.renamelink(iterfctx),
+                      "parent": webutil.parents(iterfctx),
+                      "child": webutil.children(iterfctx),
+                      "desc": iterfctx.description(),
+                      "tags": webutil.nodetagsdict(repo, iterfctx.node()),
+                      "bookmarks": webutil.nodebookmarksdict(
+                          repo, iterfctx.node()),
+                      "branch": webutil.nodebranchnodefault(iterfctx),
+                      "inbranch": webutil.nodeinbranch(repo, iterfctx),
+                      "branches": webutil.nodebranchdict(repo, iterfctx)})
+        for e in reversed(l):
             yield e
 
-    nodefunc = lambda x: fctx.filectx(fileid=x)
-    nav = webutil.revnavgen(end - 1, revcount, count, nodefunc)
+    revnav = webutil.filerevnav(web.repo, fctx.path())
+    nav = revnav.gen(end - 1, revcount, count)
     return tmpl("filelog", file=f, node=fctx.hex(), nav=nav,
-                entries=lambda **x: entries(limit=0, **x),
-                latestentry=lambda **x: entries(limit=1, **x),
+                entries=lambda **x: entries(latestonly=False, **x),
+                latestentry=lambda **x: entries(latestonly=True, **x),
                 revcount=revcount, morevars=morevars, lessvars=lessvars)
 
 def archive(web, req, tmpl):
@@ -795,14 +804,17 @@
     name = "%s-%s" % (reponame, arch_version)
     mimetype, artype, extension, encoding = web.archive_specs[type_]
     headers = [
-        ('Content-Type', mimetype),
         ('Content-Disposition', 'attachment; filename=%s%s' % (name, extension))
-    ]
+        ]
     if encoding:
         headers.append(('Content-Encoding', encoding))
-    req.header(headers)
-    req.respond(HTTP_OK)
-    archival.archive(web.repo, req, cnode, artype, prefix=name)
+    req.headers.extend(headers)
+    req.respond(HTTP_OK, mimetype)
+
+    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 []
 
 
@@ -843,10 +855,13 @@
 
     uprev = min(max(0, count - 1), rev + revcount)
     downrev = max(0, rev - revcount)
-    changenav = webutil.revnavgen(pos, revcount, count, web.repo.changectx)
+    changenav = webutil.revnav(web.repo).gen(pos, revcount, count)
 
-    dag = graphmod.dagwalker(web.repo, range(start, end)[::-1])
-    tree = list(graphmod.colored(dag, web.repo))
+    tree = []
+    if start < end:
+        revs = list(web.repo.changelog.revs(end - 1, start))
+        dag = graphmod.dagwalker(web.repo, revs)
+        tree = list(graphmod.colored(dag, web.repo))
 
     def getcolumns(tree):
         cols = 0
--- a/mercurial/hgweb/webutil.py	Mon Jan 14 23:14:45 2013 +0900
+++ b/mercurial/hgweb/webutil.py	Sat Jan 19 17:24:33 2013 -0600
@@ -24,46 +24,100 @@
         return "/"
     return up + "/"
 
-def revnavgen(pos, pagelen, limit, nodefunc):
-    def seq(factor, limit=None):
-        if limit:
-            yield limit
-            if limit >= 20 and limit <= 40:
-                yield 50
-        else:
-            yield 1 * factor
-            yield 3 * factor
-        for f in seq(factor * 10):
-            yield f
+def _navseq(step, firststep=None):
+    if firststep:
+        yield firststep
+        if firststep >= 20 and firststep <= 40:
+            firststep = 50
+            yield firststep
+        assert step > 0
+        assert firststep > 0
+        while step <= firststep:
+            step *= 10
+    while True:
+        yield 1 * step
+        yield 3 * step
+        step *= 10
+
+class revnav(object):
+
+    def __init__(self, repo):
+        """Navigation generation object
 
-    navbefore = []
-    navafter = []
+        :repo: repo object we generate nav for
+        """
+        # used for hex generation
+        self._revlog = repo.changelog
+
+    def __nonzero__(self):
+        """return True if any revision to navigate over"""
+        try:
+            self._revlog.node(0)
+            return True
+        except error.RepoError:
+            return False
+
+    def hex(self, rev):
+        return hex(self._revlog.node(rev))
+
+    def gen(self, pos, pagelen, limit):
+        """computes label and revision id for navigation link
+
+        :pos: is the revision relative to which we generate navigation.
+        :pagelen: the size of each navigation page
+        :limit: how far shall we link
 
-    last = 0
-    for f in seq(1, pagelen):
-        if f < pagelen or f <= last:
-            continue
-        if f > limit:
-            break
-        last = f
-        if pos + f < limit:
-            navafter.append(("+%d" % f, hex(nodefunc(pos + f).node())))
-        if pos - f >= 0:
-            navbefore.insert(0, ("-%d" % f, hex(nodefunc(pos - f).node())))
+        The return is:
+            - a single element tuple
+            - containing a dictionary with a `before` and `after` key
+            - values are generator functions taking arbitrary number of kwargs
+            - yield items are dictionaries with `label` and `node` keys
+        """
+        if not self:
+            # empty repo
+            return ({'before': (), 'after': ()},)
+
+        targets = []
+        for f in _navseq(1, pagelen):
+            if f > limit:
+                break
+            targets.append(pos + f)
+            targets.append(pos - f)
+        targets.sort()
 
-    navafter.append(("tip", "tip"))
-    try:
-        navbefore.insert(0, ("(0)", hex(nodefunc('0').node())))
-    except error.RepoError:
-        pass
+        navbefore = [("(0)", self.hex(0))]
+        navafter = []
+        for rev in targets:
+            if rev not in self._revlog:
+                continue
+            if pos < rev < limit:
+                navafter.append(("+%d" % f, self.hex(rev)))
+            if 0 < rev < pos:
+                navbefore.append(("-%d" % f, self.hex(rev)))
+
+
+        navafter.append(("tip", "tip"))
+
+        data = lambda i: {"label": i[0], "node": i[1]}
+        return ({'before': lambda **map: (data(i) for i in navbefore),
+                 'after':  lambda **map: (data(i) for i in navafter)},)
 
-    def gen(l):
-        def f(**map):
-            for label, node in l:
-                yield {"label": label, "node": node}
-        return f
+class filerevnav(revnav):
+
+    def __init__(self, repo, path):
+        """Navigation generation object
 
-    return (dict(before=gen(navbefore), after=gen(navafter)),)
+        :repo: repo object we generate nav for
+        :path: path of the file we generate nav for
+        """
+        # used for iteration
+        self._changelog = repo.unfiltered().changelog
+        # used for hex generation
+        self._revlog = repo.file(path)
+
+    def hex(self, rev):
+        return hex(self._changelog.node(self._revlog.linkrev(rev)))
+
 
 def _siblings(siblings=[], hiderev=None):
     siblings = [s for s in siblings if s.node() != nullid]
@@ -140,13 +194,7 @@
     path = path.lstrip('/')
     return scmutil.canonpath(repo.root, '', path)
 
-def changectx(repo, req):
-    changeid = "tip"
-    if 'node' in req.form:
-        changeid = req.form['node'][0]
-    elif 'manifest' in req.form:
-        changeid = req.form['manifest'][0]
-
+def changeidctx (repo, changeid):
     try:
         ctx = repo[changeid]
     except error.RepoError:
@@ -155,6 +203,28 @@
 
     return ctx
 
+def changectx (repo, req):
+    changeid = "tip"
+    if 'node' in req.form:
+        changeid = req.form['node'][0]
+        ipos=changeid.find(':')
+        if ipos != -1:
+            changeid = changeid[(ipos + 1):]
+    elif 'manifest' in req.form:
+        changeid = req.form['manifest'][0]
+
+    return changeidctx(repo, changeid)
+
+def basechangectx(repo, req):
+    if 'node' in req.form:
+        changeid = req.form['node'][0]
+        ipos=changeid.find(':')
+        if ipos != -1:
+            changeid = changeid[:ipos]
+            return changeidctx(repo, changeid)
+
+    return None
+
 def filectx(repo, req):
     if 'file' not in req.form:
         raise ErrorResponse(HTTP_NOT_FOUND, 'file not given')
@@ -178,7 +248,7 @@
     if len(files) > max:
         yield tmpl('fileellipses')
 
-def diffs(repo, tmpl, ctx, files, parity, style):
+def diffs(repo, tmpl, ctx, basectx, files, parity, style):
 
     def countgen():
         start = 1
@@ -209,8 +279,11 @@
         m = match.always(repo.root, repo.getcwd())
 
     diffopts = patch.diffopts(repo.ui, untrusted=True)
-    parents = ctx.parents()
-    node1 = parents and parents[0].node() or nullid
+    if basectx is None:
+        parents = ctx.parents()
+        node1 = parents and parents[0].node() or nullid
+    else:
+        node1 = basectx.node()
     node2 = ctx.node()
 
     block = []
@@ -274,10 +347,10 @@
         for oc in s.get_grouped_opcodes(n=context):
             yield tmpl('comparisonblock', lines=getblock(oc))
 
-def diffstatgen(ctx):
+def diffstatgen(ctx, basectx):
     '''Generator function that provides the diffstat data.'''
 
-    stats = patch.diffstatdata(util.iterlines(ctx.diff()))
+    stats = patch.diffstatdata(util.iterlines(ctx.diff(basectx)))
     maxname, maxtotal, addtotal, removetotal, binary = patch.diffstatsum(stats)
     while True:
         yield stats, maxname, maxtotal, addtotal, removetotal, binary
@@ -321,7 +394,7 @@
         return sessionvars(copy.copy(self.vars), self.start)
     def __iter__(self):
         separator = self.start
-        for key, value in self.vars.iteritems():
+        for key, value in sorted(self.vars.iteritems()):
             yield {'name': key, 'value': str(value), 'separator': separator}
             separator = '&'
 
--- a/mercurial/hook.py	Mon Jan 14 23:14:45 2013 +0900
+++ b/mercurial/hook.py	Sat Jan 19 17:24:33 2013 -0600
@@ -7,7 +7,7 @@
 
 from i18n import _
 import os, sys
-import extensions, util
+import extensions, util, demandimport
 
 def _pythonhook(ui, repo, name, hname, funcname, args, throw):
     '''call python hook. hook is callable object, looked up as
@@ -35,13 +35,17 @@
                 sys.path = sys.path[:] + [modpath]
                 modname = modfile
         try:
+            demandimport.disable()
             obj = __import__(modname)
+            demandimport.enable()
         except ImportError:
             e1 = sys.exc_type, sys.exc_value, sys.exc_traceback
             try:
                 # extensions are loaded with hgext_ prefix
                 obj = __import__("hgext_%s" % modname)
+                demandimport.enable()
             except ImportError:
+                demandimport.enable()
                 e2 = sys.exc_type, sys.exc_value, sys.exc_traceback
                 if ui.tracebackflag:
                     ui.warn(_('exception from first failed import attempt:\n'))
--- a/mercurial/httpclient/socketutil.py	Mon Jan 14 23:14:45 2013 +0900
+++ b/mercurial/httpclient/socketutil.py	Sat Jan 19 17:24:33 2013 -0600
@@ -70,7 +70,7 @@
                 continue
             break
         if not sock:
-            raise socket.error, msg
+            raise socket.error(msg)
         return sock
 
 if ssl:
--- a/mercurial/ignore.py	Mon Jan 14 23:14:45 2013 +0900
+++ b/mercurial/ignore.py	Sat Jan 19 17:24:33 2013 -0600
@@ -46,12 +46,32 @@
                 pat = line
                 break
             elif line.startswith(s+':'):
-                pat = rels + line[len(s)+1:]
+                pat = rels + line[len(s) + 1:]
                 break
         patterns.append(pat)
 
     return patterns, warnings
 
+def readpats(root, files, warn):
+    '''return a dict mapping ignore-file-name to list-of-patterns'''
+
+    pats = {}
+    for f in files:
+        if f in pats:
+            continue
+        try:
+            pats[f] = []
+            fp = open(f)
+            pats[f], warnings = ignorepats(fp)
+            fp.close()
+            for warning in warnings:
+                warn("%s: %s\n" % (f, warning))
+        except IOError, inst:
+            if f != files[0]:
+                warn(_("skipping unreadable ignore file '%s': %s\n") %
+                     (f, inst.strerror))
+    return [(f, pats[f]) for f in files if f in pats]
+
 def ignore(root, files, warn):
     '''return matcher covering patterns in 'files'.
 
@@ -72,22 +92,10 @@
     glob:pattern   # non-rooted glob
     pattern        # pattern of the current default type'''
 
-    pats = {}
-    for f in files:
-        try:
-            pats[f] = []
-            fp = open(f)
-            pats[f], warnings = ignorepats(fp)
-            fp.close()
-            for warning in warnings:
-                warn("%s: %s\n" % (f, warning))
-        except IOError, inst:
-            if f != files[0]:
-                warn(_("skipping unreadable ignore file '%s': %s\n") %
-                     (f, inst.strerror))
+    pats = readpats(root, files, warn)
 
     allpats = []
-    for patlist in pats.values():
+    for f, patlist in pats:
         allpats.extend(patlist)
     if not allpats:
         return util.never
@@ -96,7 +104,7 @@
         ignorefunc = match.match(root, '', [], allpats)
     except util.Abort:
         # Re-raise an exception where the src is the right file
-        for f, patlist in pats.iteritems():
+        for f, patlist in pats:
             try:
                 match.match(root, '', [], patlist)
             except util.Abort, inst:
--- a/mercurial/localrepo.py	Mon Jan 14 23:14:45 2013 +0900
+++ b/mercurial/localrepo.py	Sat Jan 19 17:24:33 2013 -0600
@@ -4,9 +4,9 @@
 #
 # This software may be used and distributed according to the terms of the
 # GNU General Public License version 2 or any later version.
-from node import bin, hex, nullid, nullrev, short
+from node import hex, nullid, short
 from i18n import _
-import peer, changegroup, subrepo, discovery, pushkey, obsolete
+import peer, changegroup, subrepo, discovery, pushkey, obsolete, repoview
 import changelog, dirstate, filelog, manifest, context, bookmarks, phases
 import lock, transaction, store, encoding, base85
 import scmutil, util, extensions, hook, error, revset
@@ -15,14 +15,49 @@
 import tags as tagsmod
 from lock import release
 import weakref, errno, os, time, inspect
+import branchmap
 propertycache = util.propertycache
 filecache = scmutil.filecache
 
-class storecache(filecache):
+class repofilecache(filecache):
+    """All filecache usage on repo are done for logic that should be unfiltered
+    """
+
+    def __get__(self, repo, type=None):
+        return super(repofilecache, self).__get__(repo.unfiltered(), type)
+    def __set__(self, repo, value):
+        return super(repofilecache, self).__set__(repo.unfiltered(), value)
+    def __delete__(self, repo):
+        return super(repofilecache, self).__delete__(repo.unfiltered())
+
+class storecache(repofilecache):
     """filecache for files in the store"""
     def join(self, obj, fname):
         return obj.sjoin(fname)
 
+class unfilteredpropertycache(propertycache):
+    """propertycache that apply to unfiltered repo only"""
+
+    def __get__(self, repo, type=None):
+        return super(unfilteredpropertycache, self).__get__(repo.unfiltered())
+
+class filteredpropertycache(propertycache):
+    """propertycache that must take filtering in account"""
+
+    def cachevalue(self, obj, value):
+        object.__setattr__(obj, self.name, value)
+
+
+def hasunfilteredcache(repo, name):
+    """check if an repo and a unfilteredproperty cached value for <name>"""
+    return name in vars(repo.unfiltered())
+
+def unfilteredmethod(orig):
+    """decorate method that always need to be run on unfiltered version"""
+    def wrapper(repo, *args, **kwargs):
+        return orig(repo.unfiltered(), *args, **kwargs)
+    return wrapper
+
 MODERNCAPS = set(('lookup', 'branchmap', 'pushkey', 'known', 'getbundle'))
 LEGACYCAPS = MODERNCAPS.union(set(['changegroupsubset']))
 
@@ -31,7 +66,7 @@
 
     def __init__(self, repo, caps=MODERNCAPS):
         peer.peerrepository.__init__(self)
-        self._repo = repo
+        self._repo = repo.filtered('served')
         self.ui = repo.ui
         self._caps = repo._restrictcapabilities(caps)
         self.requirements = repo.requirements
@@ -56,10 +91,10 @@
         return self._repo.lookup(key)
 
     def branchmap(self):
-        return discovery.visiblebranchmap(self._repo)
+        return self._repo.branchmap()
 
     def heads(self):
-        return discovery.visibleheads(self._repo)
+        return self._repo.heads()
 
     def known(self, nodes):
         return self._repo.known(nodes)
@@ -112,6 +147,7 @@
                                         'dotencode'))
     openerreqs = set(('revlogv1', 'generaldelta'))
     requirements = ['revlogv1']
+    filtername = None
 
     def _baserequirements(self, create):
         return self.requirements[:]
@@ -193,8 +229,7 @@
             self._writerequirements()
 
 
-        self._branchcache = None
-        self._branchcachetip = None
+        self._branchcaches = {}
         self.filterpats = {}
         self._datafilters = {}
         self._transref = self._lockref = self._wlockref = None
@@ -205,6 +240,15 @@
         # Maps a property name to its util.filecacheentry
         self._filecache = {}
 
+        # hold sets of revision to be filtered
+        # should be cleared when something might have changed the filter value:
+        # - new changesets,
+        # - phase change,
+        # - new obsolescence marker,
+        # - working directory parent change,
+        # - bookmark changes
+        self.filteredrevcache = {}
+
     def close(self):
         pass
 
@@ -218,7 +262,7 @@
 
     def _writerequirements(self):
         reqfile = self.opener("requires", "w")
-        for r in self.requirements:
+        for r in sorted(self.requirements):
             reqfile.write("%s\n" % r)
         reqfile.close()
 
@@ -263,17 +307,28 @@
     def peer(self):
         return localpeer(self) # not cached to avoid reference cycle
 
-    @filecache('bookmarks')
+    def unfiltered(self):
+        """Return unfiltered version of the repository
+
+        Intended to be ovewritten by filtered repo."""
+        return self
+
+    def filtered(self, name):
+        """Return a filtered version of a repository"""
+        # build a new class with the mixin and the current class
+        # (possibily subclass of the repo)
+        class proxycls(repoview.repoview, self.unfiltered().__class__):
+            pass
+        return proxycls(self, name)
+
+    @repofilecache('bookmarks')
     def _bookmarks(self):
-        return bookmarks.read(self)
+        return bookmarks.bmstore(self)
 
-    @filecache('bookmarks.current')
+    @repofilecache('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 = []
@@ -295,27 +350,6 @@
             self.ui.warn(msg % len(list(store)))
         return store
 
-    @propertycache
-    def hiddenrevs(self):
-        """hiddenrevs: revs that should be hidden by command and tools
-
-        This set is carried on the repo to ease initialization and lazy
-        loading; it'll probably move back to changelog for efficiency and
-        consistency reasons.
-
-        Note that the hiddenrevs will needs invalidations when
-        - a new changesets is added (possible unstable above extinct)
-        - a new obsolete marker is added (possible new extinct changeset)
-
-        hidden changesets cannot have non-hidden descendants
-        """
-        hidden = set()
-        if self.obsstore:
-            ### hide extinct changeset that are not accessible by any mean
-            hiddenquery = 'extinct() - ::(. + bookmark())'
-            hidden.update(self.revs(hiddenquery))
-        return hidden
-
     @storecache('00changelog.i')
     def changelog(self):
         c = changelog.changelog(self.sopener)
@@ -329,7 +363,7 @@
     def manifest(self):
         return manifest.manifest(self.sopener)
 
-    @filecache('dirstate')
+    @repofilecache('dirstate')
     def dirstate(self):
         warned = [0]
         def validate(node):
@@ -385,6 +419,7 @@
     def hook(self, name, throw=False, **args):
         return hook.hook(self.ui, self, name, throw, **args)
 
+    @unfilteredmethod
     def _tag(self, names, node, message, local, user, date, extra={}):
         if isinstance(names, str):
             names = (names,)
@@ -482,7 +517,7 @@
         self.tags() # instantiate the cache
         self._tag(names, node, message, local, user, date)
 
-    @propertycache
+    @filteredpropertycache
     def _tagscache(self):
         '''Returns a tagscache object that contains various tags related
         caches.'''
@@ -594,43 +629,10 @@
                 marks.append(bookmark)
         return sorted(marks)
 
-    def _branchtags(self, partial, lrev):
-        # TODO: rename this function?
-        tiprev = len(self) - 1
-        if lrev != tiprev:
-            ctxgen = (self[r] for r in self.changelog.revs(lrev + 1, tiprev))
-            self._updatebranchcache(partial, ctxgen)
-            self._writebranchcache(partial, self.changelog.tip(), tiprev)
-
-        return partial
-
-    def updatebranchcache(self):
-        tip = self.changelog.tip()
-        if self._branchcache is not None and self._branchcachetip == tip:
-            return
-
-        oldtip = self._branchcachetip
-        self._branchcachetip = tip
-        if oldtip is None or oldtip not in self.changelog.nodemap:
-            partial, last, lrev = self._readbranchcache()
-        else:
-            lrev = self.changelog.rev(oldtip)
-            partial = self._branchcache
-
-        self._branchtags(partial, lrev)
-        # this private cache holds all heads (not just the branch tips)
-        self._branchcache = partial
-
     def branchmap(self):
         '''returns a dictionary {branch: [branchheads]}'''
-        if self.changelog.filteredrevs:
-            # some changeset are excluded we can't use the cache
-            branchmap = {}
-            self._updatebranchcache(branchmap, (self[r] for r in self))
-            return branchmap
-        else:
-            self.updatebranchcache()
-            return self._branchcache
+        branchmap.updatecache(self)
+        return self._branchcaches[self.filtername]
 
 
     def _branchtip(self, heads):
@@ -656,109 +658,6 @@
             bt[bn] = self._branchtip(heads)
         return bt
 
-    def _readbranchcache(self):
-        partial = {}
-        try:
-            f = self.opener("cache/branchheads")
-            lines = f.read().split('\n')
-            f.close()
-        except (IOError, OSError):
-            return {}, nullid, nullrev
-
-        try:
-            last, lrev = lines.pop(0).split(" ", 1)
-            last, lrev = bin(last), int(lrev)
-            if lrev >= len(self) or self[lrev].node() != last:
-                # invalidate the cache
-                raise ValueError('invalidating branch cache (tip differs)')
-            for l in lines:
-                if not l:
-                    continue
-                node, label = l.split(" ", 1)
-                label = encoding.tolocal(label.strip())
-                if not node in self:
-                    raise ValueError('invalidating branch cache because node '+
-                                     '%s does not exist' % node)
-                partial.setdefault(label, []).append(bin(node))
-        except KeyboardInterrupt:
-            raise
-        except Exception, inst:
-            if self.ui.debugflag:
-                self.ui.warn(str(inst), '\n')
-            partial, last, lrev = {}, nullid, nullrev
-        return partial, last, lrev
-
-    def _writebranchcache(self, branches, tip, tiprev):
-        try:
-            f = self.opener("cache/branchheads", "w", atomictemp=True)
-            f.write("%s %s\n" % (hex(tip), tiprev))
-            for label, nodes in branches.iteritems():
-                for node in nodes:
-                    f.write("%s %s\n" % (hex(node), encoding.fromlocal(label)))
-            f.close()
-        except (IOError, OSError):
-            pass
-
-    def _updatebranchcache(self, partial, ctxgen):
-        """Given a branchhead cache, partial, that may have extra nodes or be
-        missing heads, and a generator of nodes that are at least a superset of
-        heads missing, this function updates partial to be correct.
-        """
-        # collect new branch entries
-        newbranches = {}
-        for c in ctxgen:
-            newbranches.setdefault(c.branch(), []).append(c.node())
-        # if older branchheads are reachable from new ones, they aren't
-        # really branchheads. Note checking parents is insufficient:
-        # 1 (branch a) -> 2 (branch b) -> 3 (branch a)
-        for branch, newnodes in newbranches.iteritems():
-            bheads = partial.setdefault(branch, [])
-            # Remove candidate heads that no longer are in the repo (e.g., as
-            # the result of a strip that just happened).  Avoid using 'node in
-            # self' here because that dives down into branchcache code somewhat
-            # recursively.
-            bheadrevs = [self.changelog.rev(node) for node in bheads
-                         if self.changelog.hasnode(node)]
-            newheadrevs = [self.changelog.rev(node) for node in newnodes
-                           if self.changelog.hasnode(node)]
-            ctxisnew = bheadrevs and min(newheadrevs) > max(bheadrevs)
-            # Remove duplicates - nodes that are in newheadrevs and are already
-            # in bheadrevs.  This can happen if you strip a node whose parent
-            # was already a head (because they're on different branches).
-            bheadrevs = sorted(set(bheadrevs).union(newheadrevs))
-
-            # Starting from tip means fewer passes over reachable.  If we know
-            # the new candidates are not ancestors of existing heads, we don't
-            # have to examine ancestors of existing heads
-            if ctxisnew:
-                iterrevs = sorted(newheadrevs)
-            else:
-                iterrevs = list(bheadrevs)
-
-            # This loop prunes out two kinds of heads - heads that are
-            # superseded by a head in newheadrevs, and newheadrevs that are not
-            # heads because an existing head is their descendant.
-            while iterrevs:
-                latest = iterrevs.pop()
-                if latest not in bheadrevs:
-                    continue
-                ancestors = set(self.changelog.ancestors([latest],
-                                                         bheadrevs[0]))
-                if ancestors:
-                    bheadrevs = [b for b in bheadrevs if b not in ancestors]
-            partial[branch] = [self.changelog.node(rev) for rev in bheadrevs]
-
-        # There may be branches that cease to exist when the last commit in the
-        # branch was stripped.  This code filters them out.  Note that the
-        # branch that ceased to exist may not be in newbranches because
-        # newbranches is the set of candidate heads, which when you strip the
-        # last commit in a branch will be the parent branch.
-        for branch in partial.keys():
-            nodes = [head for head in partial[branch]
-                     if self.changelog.hasnode(head)]
-            if not nodes:
-                del partial[branch]
-
     def lookup(self, key):
         return self[key].node()
 
@@ -865,11 +764,11 @@
 
         return data
 
-    @propertycache
+    @unfilteredpropertycache
     def _encodefilterpats(self):
         return self._loadfilter('encode')
 
-    @propertycache
+    @unfilteredpropertycache
     def _decodefilterpats(self):
         return self._loadfilter('decode')
 
@@ -964,6 +863,7 @@
         finally:
             release(lock, wlock)
 
+    @unfilteredmethod # Until we get smarter cache management
     def _rollback(self, dryrun, force):
         ui = self.ui
         try:
@@ -995,6 +895,7 @@
             return 0
 
         parents = self.dirstate.parents()
+        self.destroying()
         transaction.rollback(self.sopener, self.sjoin('undo'), ui.warn)
         if os.path.exists(self.join('undo.bookmarks')):
             util.rename(self.join('undo.bookmarks'),
@@ -1004,9 +905,6 @@
                         self.sjoin('phaseroots'))
         self.invalidate()
 
-        # Discard all cache entries to force reloading everything.
-        self._filecache.clear()
-
         parentgone = (parents[0] not in self.changelog.nodemap or
                       parents[1] not in self.changelog.nodemap)
         if parentgone:
@@ -1034,16 +932,16 @@
         return 0
 
     def invalidatecaches(self):
-        def delcache(name):
-            try:
-                delattr(self, name)
-            except AttributeError:
-                pass
+
+        if '_tagscache' in vars(self):
+            # can't use delattr on proxy
+            del self.__dict__['_tagscache']
 
-        delcache('_tagscache')
+        self.unfiltered()._branchcaches.clear()
+        self.invalidatevolatilesets()
 
-        self._branchcache = None # in UTF-8
-        self._branchcachetip = None
+    def invalidatevolatilesets(self):
+        self.filteredrevcache.clear()
         obsolete.clearobscaches(self)
 
     def invalidatedirstate(self):
@@ -1055,22 +953,23 @@
         rereads the dirstate. Use dirstate.invalidate() if you want to
         explicitly read the dirstate again (i.e. restoring it to a previous
         known good state).'''
-        if 'dirstate' in self.__dict__:
+        if hasunfilteredcache(self, 'dirstate'):
             for k in self.dirstate._filecache:
                 try:
                     delattr(self.dirstate, k)
                 except AttributeError:
                     pass
-            delattr(self, 'dirstate')
+            delattr(self.unfiltered(), 'dirstate')
 
     def invalidate(self):
+        unfiltered = self.unfiltered() # all filecaches are stored on unfiltered
         for k in self._filecache:
             # dirstate is invalidated separately in invalidatedirstate()
             if k == 'dirstate':
                 continue
 
             try:
-                delattr(self, k)
+                delattr(unfiltered, k)
             except AttributeError:
                 pass
         self.invalidatecaches()
@@ -1111,10 +1010,10 @@
 
         def unlock():
             self.store.write()
-            if '_phasecache' in vars(self):
+            if hasunfilteredcache(self, '_phasecache'):
                 self._phasecache.write()
             for k, ce in self._filecache.items():
-                if k == 'dirstate':
+                if k == 'dirstate' or k not in self.__dict__:
                     continue
                 ce.refresh()
 
@@ -1134,9 +1033,7 @@
 
         def unlock():
             self.dirstate.write()
-            ce = self._filecache.get('dirstate')
-            if ce:
-                ce.refresh()
+            self._filecache['dirstate'].refresh()
 
         l = self._lock(self.join("wlock"), wait, unlock,
                        self.invalidatedirstate, _('working directory of %s') %
@@ -1224,6 +1121,7 @@
 
         return fparent1
 
+    @unfilteredmethod
     def commit(self, text="", user=None, date=None, match=None, force=False,
                editor=False, extra={}):
         """Add a new revision to current repository.
@@ -1394,6 +1292,7 @@
         self._afterlock(commithook)
         return ret
 
+    @unfilteredmethod
     def commitctx(self, ctx, error=False):
         """Add a new revision to current repository.
         Revision information is passed via the context argument.
@@ -1468,14 +1367,33 @@
                 # if minimal phase was 0 we don't need to retract anything
                 phases.retractboundary(self, targetphase, [n])
             tr.close()
-            self.updatebranchcache()
+            branchmap.updatecache(self.filtered('served'))
             return n
         finally:
             if tr:
                 tr.release()
             lock.release()
 
-    def destroyed(self, newheadnodes=None):
+    @unfilteredmethod
+    def destroying(self):
+        '''Inform the repository that nodes are about to be destroyed.
+        Intended for use by strip and rollback, so there's a common
+        place for anything that has to be done before destroying history.
+
+        This is mostly useful for saving state that is in memory and waiting
+        to be flushed when the current lock is released. Because a call to
+        destroyed is imminent, the repo will be invalidated causing those
+        changes to stay in memory (waiting for the next unlock), or vanish
+        completely.
+        '''
+        # When using the same lock to commit and strip, the phasecache is left
+        # dirty after committing. Then when we strip, the repo is invalidated,
+        # causing those changes to disappear.
+        if '_phasecache' in vars(self):
+            self._phasecache.write()
+
+    @unfilteredmethod
+    def destroyed(self):
         '''Inform the repository that nodes have been destroyed.
         Intended for use by strip and rollback, so there's a common
         place for anything that has to be done after destroying history.
@@ -1486,16 +1404,22 @@
         code to update the branchheads cache, rather than having future code
         decide it's invalid and regenerating it from scratch.
         '''
-        # If we have info, newheadnodes, on how to update the branch cache, do
-        # it, Otherwise, since nodes were destroyed, the cache is stale and this
-        # will be caught the next time it is read.
-        if newheadnodes:
-            tiprev = len(self) - 1
-            ctxgen = (self[node] for node in newheadnodes
-                      if self.changelog.hasnode(node))
-            self._updatebranchcache(self._branchcache, ctxgen)
-            self._writebranchcache(self._branchcache, self.changelog.tip(),
-                                   tiprev)
+        # When one tries to:
+        # 1) destroy nodes thus calling this method (e.g. strip)
+        # 2) use phasecache somewhere (e.g. commit)
+        #
+        # then 2) will fail because the phasecache contains nodes that were
+        # removed. We can either remove phasecache from the filecache,
+        # causing it to reload next time it is accessed, or simply filter
+        # the removed nodes now and write the updated cache.
+        if '_phasecache' in self._filecache:
+            self._phasecache.filterunknown(self)
+            self._phasecache.write()
+
+        # update the 'served' branch cache to help read only server process
+        # Thanks to branchcach collaboration this is done from the nearest
+        # filtered subset and it is expected to be fast.
+        branchmap.updatecache(self.filtered('served'))
 
         # Ensure the persistent tag cache is updated.  Doing it now
         # means that the tag cache only has to worry about destroyed
@@ -1507,10 +1431,7 @@
         # head, refresh the tag cache, then immediately add a new head.
         # But I think doing it this way is necessary for the "instant
         # tag cache retrieval" case to work.
-        self.invalidatecaches()
-
-        # Discard all cache entries to force reloading everything.
-        self._filecache.clear()
+        self.invalidate()
 
     def walk(self, match, node=None):
         '''
@@ -1568,7 +1489,7 @@
         if working: # we need to scan the working dir
             subrepos = []
             if '.hgsub' in self.dirstate:
-                subrepos = ctx2.substate.keys()
+                subrepos = sorted(ctx2.substate)
             s = self.dirstate.status(match, subrepos, listignored,
                                      listclean, listunknown)
             cmp, modified, added, removed, deleted, unknown, ignored, clean = s
@@ -1806,6 +1727,7 @@
                         if key.startswith('dump'):
                             data = base85.b85decode(remoteobs[key])
                             self.obsstore.mergemarkers(tr, data)
+                    self.invalidatevolatilesets()
             if tr is not None:
                 tr.close()
         finally:
@@ -1841,6 +1763,7 @@
 
         if not remote.canpush():
             raise util.Abort(_("destination does not support push"))
+        unfi = self.unfiltered()
         # get local lock as we might write phase data
         locallock = self.lock()
         try:
@@ -1852,40 +1775,43 @@
             try:
                 # discovery
                 fci = discovery.findcommonincoming
-                commoninc = fci(self, remote, force=force)
+                commoninc = fci(unfi, remote, force=force)
                 common, inc, remoteheads = commoninc
                 fco = discovery.findcommonoutgoing
-                outgoing = fco(self, remote, onlyheads=revs,
+                outgoing = fco(unfi, remote, onlyheads=revs,
                                commoninc=commoninc, force=force)
 
 
                 if not outgoing.missing:
                     # nothing to push
-                    scmutil.nochangesfound(self.ui, self, outgoing.excluded)
+                    scmutil.nochangesfound(unfi.ui, unfi, outgoing.excluded)
                     ret = None
                 else:
                     # something to push
                     if not force:
                         # if self.obsstore == False --> no obsolete
                         # then, save the iteration
-                        if self.obsstore:
+                        if unfi.obsstore:
                             # this message are here for 80 char limit reason
                             mso = _("push includes obsolete changeset: %s!")
-                            msu = _("push includes unstable changeset: %s!")
-                            msb = _("push includes bumped changeset: %s!")
+                            mst = "push includes %s changeset: %s!"
+                            # plain versions for i18n tool to detect them
+                            _("push includes unstable changeset: %s!")
+                            _("push includes bumped changeset: %s!")
+                            _("push includes divergent changeset: %s!")
                             # If we are to push if there is at least one
                             # obsolete or unstable changeset in missing, at
                             # least one of the missinghead will be obsolete or
                             # unstable. So checking heads only is ok
                             for node in outgoing.missingheads:
-                                ctx = self[node]
+                                ctx = unfi[node]
                                 if ctx.obsolete():
                                     raise util.Abort(mso % ctx)
-                                elif ctx.unstable():
-                                    raise util.Abort(msu % ctx)
-                                elif ctx.bumped():
-                                    raise util.Abort(msb % ctx)
-                        discovery.checkheads(self, remote, outgoing,
+                                elif ctx.troubled():
+                                    raise util.Abort(_(mst)
+                                                     % (ctx.troubles()[0],
+                                                        ctx))
+                        discovery.checkheads(unfi, remote, outgoing,
                                              remoteheads, newbranch,
                                              bool(inc))
 
@@ -1938,7 +1864,7 @@
                     cheads = [node for node in revs if node in common]
                     # and
                     # * commonheads parents on missing
-                    revset = self.set('%ln and parents(roots(%ln))',
+                    revset = unfi.set('%ln and parents(roots(%ln))',
                                      outgoing.commonheads,
                                      outgoing.missing)
                     cheads.extend(c.node() for c in revset)
@@ -1961,7 +1887,7 @@
                     # Get the list of all revs draft on remote by public here.
                     # XXX Beware that revset break if droots is not strictly
                     # XXX root we may want to ensure it is but it is costly
-                    outdated =  self.set('heads((%ln::%ln) and public())',
+                    outdated =  unfi.set('heads((%ln::%ln) and public())',
                                          droots, cheads)
                     for newremotehead in outdated:
                         r = remote.pushkey('phases',
@@ -1992,12 +1918,12 @@
         self.ui.debug("checking for updated bookmarks\n")
         rb = remote.listkeys('bookmarks')
         for k in rb.keys():
-            if k in self._bookmarks:
+            if k in unfi._bookmarks:
                 nr, nl = rb[k], hex(self._bookmarks[k])
-                if nr in self:
-                    cr = self[nr]
-                    cl = self[nl]
-                    if bookmarks.validdest(self, cr, cl):
+                if nr in unfi:
+                    cr = unfi[nr]
+                    cl = unfi[nl]
+                    if bookmarks.validdest(unfi, cr, cl):
                         r = remote.pushkey('bookmarks', k, nr, nl)
                         if r:
                             self.ui.status(_("updating bookmark %s\n") % k)
@@ -2033,7 +1959,7 @@
             bases = [nullid]
         csets, bases, heads = cl.nodesbetween(bases, heads)
         # We assume that all ancestors of bases are known
-        common = set(cl.ancestors([cl.rev(n) for n in bases]))
+        common = cl.ancestors([cl.rev(n) for n in bases])
         return self._changegroupsubset(common, csets, heads, source)
 
     def getlocalbundle(self, source, outgoing):
@@ -2059,8 +1985,8 @@
         """
         cl = self.changelog
         if common:
-            nm = cl.nodemap
-            common = [n for n in common if n in nm]
+            hasnode = cl.hasnode
+            common = [n for n in common if hasnode(n)]
         else:
             common = [nullid]
         if not heads:
@@ -2068,6 +1994,7 @@
         return self.getlocalbundle(source,
                                    discovery.outgoing(cl, common, heads))
 
+    @unfilteredmethod
     def _changegroupsubset(self, commonrevs, csets, heads, source):
 
         cl = self.changelog
@@ -2179,6 +2106,7 @@
         # to avoid a race we use changegroupsubset() (issue1320)
         return self.changegroupsubset(basenodes, self.heads(), source)
 
+    @unfilteredmethod
     def _changegroup(self, nodes, source):
         """Compute the changegroup of all nodes that we have that a recipient
         doesn't.  Return a chunkbuffer object whose read() method will return
@@ -2272,6 +2200,7 @@
 
         return changegroup.unbundle10(util.chunkbuffer(gengroup()), 'UN')
 
+    @unfilteredmethod
     def addchangegroup(self, source, srctype, url, emptyok=False):
         """Add the changegroup returned by source.read() to this repo.
         srctype is a string like 'push', 'pull', or 'unbundle'.  url is
@@ -2382,6 +2311,9 @@
                         n = fl.node(new)
                         if n in needs:
                             needs.remove(n)
+                        else:
+                            raise util.Abort(
+                                _("received spurious file revlog entry"))
                     if not needs:
                         del needfiles[f]
             self.ui.progress(_('files'), None)
@@ -2410,7 +2342,7 @@
             self.ui.status(_("added %d changesets"
                              " with %d changes to %d files%s\n")
                              % (changesets, revisions, files, htext))
-            obsolete.clearobscaches(self)
+            self.invalidatevolatilesets()
 
             if changesets > 0:
                 p = lambda: cl.writepending() and self.root or ""
@@ -2444,7 +2376,11 @@
             tr.close()
 
             if changesets > 0:
-                self.updatebranchcache()
+                if srctype != 'strip':
+                    # During strip, branchcache is invalid but coming call to
+                    # `destroyed` will repair it.
+                    # In other case we can safely update cache on disk.
+                    branchmap.updatecache(self.filtered('served'))
                 def runhooks():
                     # forcefully update the on-disk branch cache
                     self.ui.debug("updating the branch cache\n")
@@ -2538,12 +2474,20 @@
                 for bheads in rbranchmap.itervalues():
                     rbheads.extend(bheads)
 
-                self.branchcache = rbranchmap
                 if rbheads:
                     rtiprev = max((int(self.changelog.rev(node))
                             for node in rbheads))
-                    self._writebranchcache(self.branchcache,
-                            self[rtiprev].node(), rtiprev)
+                    cache = branchmap.branchcache(rbranchmap,
+                                                  self[rtiprev].node(),
+                                                  rtiprev)
+                    # Try to stick it as low as possible
+                    # filter above served are unlikely to be fetch from a clone
+                    for candidate in ('base', 'immutable', 'served'):
+                        rview = self.filtered(candidate)
+                        if cache.validfor(rview):
+                            self._branchcaches[candidate] = cache
+                            cache.write(rview)
+                            break
             self.invalidate()
             return len(self.heads()) + 1
         finally:
@@ -2607,7 +2551,7 @@
             fp.write(text)
         finally:
             fp.close()
-        return self.pathto(fp.name[len(self.root)+1:])
+        return self.pathto(fp.name[len(self.root) + 1:])
 
 # used to avoid circular references so destructors work
 def aftertrans(files):
--- a/mercurial/manifest.py	Mon Jan 14 23:14:45 2013 +0900
+++ b/mercurial/manifest.py	Sat Jan 19 17:24:33 2013 -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	Mon Jan 14 23:14:45 2013 +0900
+++ b/mercurial/mdiff.py	Sat Jan 19 17:24:33 2013 -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/merge.py	Mon Jan 14 23:14:45 2013 +0900
+++ b/mercurial/merge.py	Sat Jan 19 17:24:33 2013 -0600
@@ -7,7 +7,7 @@
 
 from node import nullid, nullrev, hex, bin
 from i18n import _
-import error, scmutil, util, filemerge, copies, subrepo
+import error, util, filemerge, copies, subrepo
 import errno, os, shutil
 
 class mergestate(object):
@@ -45,11 +45,11 @@
                 f.write("\0".join([d] + v) + "\n")
             f.close()
             self._dirty = False
-    def add(self, fcl, fco, fca, fd, flags):
+    def add(self, fcl, fco, fca, fd):
         hash = util.sha1(fcl.path()).hexdigest()
         self._repo.opener.write("merge/" + hash, fcl.data())
         self._state[fd] = ['u', hash, fcl.path(), fca.path(),
-                           hex(fca.filenode()), fco.path(), flags]
+                           hex(fca.filenode()), fco.path(), fcl.flags()]
         self._dirty = True
     def __contains__(self, dfile):
         return dfile in self._state
@@ -67,12 +67,22 @@
         if self[dfile] == 'r':
             return 0
         state, hash, lfile, afile, anode, ofile, flags = self._state[dfile]
+        fcd = wctx[dfile]
+        fco = octx[ofile]
+        fca = self._repo.filectx(afile, fileid=anode)
+        # "premerge" x flags
+        flo = fco.flags()
+        fla = fca.flags()
+        if 'x' in flags + flo + fla and 'l' not in flags + flo + fla:
+            if fca.node() == nullid:
+                self._repo.ui.warn(_('warning: cannot merge flags for %s\n') %
+                                   afile)
+            elif flags == fla:
+                flags = flo
+        # restore local
         f = self._repo.opener("merge/" + hash)
         self._repo.wwrite(dfile, f.read(), flags)
         f.close()
-        fcd = wctx[dfile]
-        fco = octx[ofile]
-        fca = self._repo.filectx(afile, fileid=anode)
         r = filemerge.filemerge(self._repo, self._local, lfile, fcd, fco, fca)
         if r is None:
             # no real conflict
@@ -162,18 +172,18 @@
     as removed.
     """
 
-    action = []
+    actions = []
     state = branchmerge and 'r' or 'f'
     for f in wctx.deleted():
         if f not in mctx:
-            action.append((f, state))
+            actions.append((f, state))
 
     if not branchmerge:
         for f in wctx.removed():
             if f not in mctx:
-                action.append((f, "f"))
+                actions.append((f, "f"))
 
-    return action
+    return actions
 
 def manifestmerge(repo, p1, p2, pa, overwrite, partial):
     """
@@ -183,44 +193,19 @@
     partial = function to filter file lists
     """
 
-    def fmerge(f, f2, fa):
-        """merge flags"""
-        a, m, n = ma.flags(fa), m1.flags(f), m2.flags(f2)
-        if m == n: # flags agree
-            return m # unchanged
-        if m and n and not a: # flags set, don't agree, differ from parent
-            r = repo.ui.promptchoice(
-                _(" conflicting flags for %s\n"
-                  "(n)one, e(x)ec or sym(l)ink?") % f,
-                (_("&None"), _("E&xec"), _("Sym&link")), 0)
-            if r == 1:
-                return "x" # Exec
-            if r == 2:
-                return "l" # Symlink
-            return ""
-        if m and m != a: # changed from a to m
-            return m
-        if n and n != a: # changed from a to n
-            if (n == 'l' or a == 'l') and m1.get(f) != ma.get(f):
-                # can't automatically merge symlink flag when there
-                # are file-level conflicts here, let filemerge take
-                # care of it
-                return m
-            return n
-        return '' # flag was cleared
-
     def act(msg, m, f, *args):
         repo.ui.debug(" %s: %s -> %s\n" % (f, msg, m))
-        action.append((f, m) + args)
+        actions.append((f, m) + args)
 
-    action, copy = [], {}
+    actions, copy, movewithdir = [], {}, {}
 
     if overwrite:
         pa = p1
     elif pa == p2: # backwards
         pa = p1.p1()
     elif pa and repo.ui.configbool("merge", "followcopies", True):
-        copy, diverge, renamedelete = copies.mergecopies(repo, p1, p2, pa)
+        ret = copies.mergecopies(repo, p1, p2, pa)
+        copy, movewithdir, diverge, renamedelete = ret
         for of, fl in diverge.iteritems():
             act("divergent renames", "dr", of, fl)
         for of, fl in renamedelete.iteritems():
@@ -233,40 +218,48 @@
 
     m1, m2, ma = p1.manifest(), p2.manifest(), pa.manifest()
     copied = set(copy.values())
+    copied.update(movewithdir.values())
 
     if '.hgsubstate' in m1:
         # check whether sub state is modified
-        for s in p1.substate:
+        for s in sorted(p1.substate):
             if p1.sub(s).dirty():
                 m1['.hgsubstate'] += "+"
                 break
 
     # Compare manifests
-    for f, n in m1.iteritems():
+    for f, n in sorted(m1.iteritems()):
         if partial and not partial(f):
             continue
         if f in m2:
-            rflags = fmerge(f, f, f)
+            n2 = m2[f]
+            fl1, fl2, fla = m1.flags(f), m2.flags(f), ma.flags(f)
+            nol = 'l' not in fl1 + fl2 + fla
             a = ma.get(f, nullid)
-            if n == m2[f] or m2[f] == a: # same or local newer
-                # is file locally modified or flags need changing?
-                # dirstate flags may need to be made current
-                if m1.flags(f) != rflags or n[20:]:
-                    act("update permissions", "e", f, rflags)
-            elif n == a: # remote newer
-                act("remote is newer", "g", f, rflags)
-            else: # both changed
-                act("versions differ", "m", f, f, f, rflags, False)
+            if n == n2 and fl1 == fl2:
+                pass # same - keep local
+            elif n2 == a and fl2 == fla:
+                pass # remote unchanged - keep local
+            elif n == a and fl1 == fla: # local unchanged - use remote
+                if n == n2: # optimization: keep local content
+                    act("update permissions", "e", f, fl2)
+                else:
+                    act("remote is newer", "g", f, fl2)
+            elif nol and n2 == a: # remote only changed 'x'
+                act("update permissions", "e", f, fl2)
+            elif nol and n == a: # local only changed 'x'
+                act("remote is newer", "g", f, fl)
+            else: # both changed something
+                act("versions differ", "m", f, f, f, False)
         elif f in copied: # files we'll deal with on m2 side
             pass
+        elif f in movewithdir: # directory rename
+            f2 = movewithdir[f]
+            act("remote renamed directory to " + f2, "d", f, None, f2,
+                m1.flags(f))
         elif f in copy:
             f2 = copy[f]
-            if f2 not in m2: # directory rename
-                act("remote renamed directory to " + f2, "d",
-                    f, None, f2, m1.flags(f))
-            else: # case 2 A,B/B/B or case 4,21 A/B/B
-                act("local copied/moved to " + f2, "m",
-                    f, f2, f, fmerge(f, f2, f2), False)
+            act("local copied/moved to " + f2, "m", f, f2, f, False)
         elif f in ma: # clean, a different, no remote
             if n != ma[f]:
                 if repo.ui.promptchoice(
@@ -281,28 +274,28 @@
             else:
                 act("other deleted", "r", f)
 
-    for f, n in m2.iteritems():
+    for f, n in sorted(m2.iteritems()):
         if partial and not partial(f):
             continue
         if f in m1 or f in copied: # files already visited
             continue
-        if f in copy:
+        if f in movewithdir:
+            f2 = movewithdir[f]
+            act("local renamed directory to " + f2, "d", None, f, f2,
+                m2.flags(f))
+        elif f in copy:
             f2 = copy[f]
-            if f2 not in m1: # directory rename
-                act("local renamed directory to " + f2, "d",
-                    None, f, f2, m2.flags(f))
-            elif f2 in m2: # rename case 1, A/A,B/A
+            if f2 in m2:
                 act("remote copied to " + f, "m",
-                    f2, f, f, fmerge(f2, f, f2), False)
-            else: # case 3,20 A/B/A
+                    f2, f, f, False)
+            else:
                 act("remote moved to " + f, "m",
-                    f2, f, f, fmerge(f2, f, f2), True)
+                    f2, f, f, True)
         elif f not in ma:
             if (not overwrite
                 and _checkunknownfile(repo, p1, p2, f)):
-                rflags = fmerge(f, f, f)
                 act("remote differs from untracked local",
-                    "m", f, f, f, rflags, False)
+                    "m", f, f, f, False)
             else:
                 act("remote created", "g", f, m2.flags(f))
         elif n != ma[f]:
@@ -312,12 +305,12 @@
                 (_("&Changed"), _("&Deleted")), 0) == 0:
                 act("prompt recreating", "g", f, m2.flags(f))
 
-    return action
+    return actions
 
 def actionkey(a):
-    return a[1] == 'r' and -1 or 0, a
+    return a[1] == "r" and -1 or 0, a
 
-def applyupdates(repo, action, wctx, mctx, actx, overwrite):
+def applyupdates(repo, actions, wctx, mctx, actx, overwrite):
     """apply the merge action list to the working directory
 
     wctx is the working copy context
@@ -332,14 +325,14 @@
     ms = mergestate(repo)
     ms.reset(wctx.p1().node())
     moves = []
-    action.sort(key=actionkey)
+    actions.sort(key=actionkey)
 
     # prescan for merges
-    for a in action:
+    for a in actions:
         f, m = a[:2]
-        if m == 'm': # merge
-            f2, fd, flags, move = a[2:]
-            if f == '.hgsubstate': # merged internally
+        if m == "m": # merge
+            f2, fd, move = a[2:]
+            if fd == '.hgsubstate': # merged internally
                 continue
             repo.ui.debug("preserving %s for resolve of %s\n" % (f, fd))
             fcl = wctx[f]
@@ -353,45 +346,42 @@
                 fca = fcl.ancestor(fco, actx)
             if not fca:
                 fca = repo.filectx(f, fileid=nullrev)
-            ms.add(fcl, fco, fca, fd, flags)
+            ms.add(fcl, fco, fca, fd)
             if f != fd and move:
                 moves.append(f)
 
-    audit = scmutil.pathauditor(repo.root)
+    audit = repo.wopener.audit
 
     # remove renamed files after safely stored
     for f in moves:
         if os.path.lexists(repo.wjoin(f)):
             repo.ui.debug("removing %s\n" % f)
             audit(f)
-            os.unlink(repo.wjoin(f))
+            util.unlinkpath(repo.wjoin(f))
 
-    numupdates = len(action)
-    for i, a in enumerate(action):
+    numupdates = len(actions)
+    for i, a in enumerate(actions):
         f, m = a[:2]
         repo.ui.progress(_('updating'), i + 1, item=f, total=numupdates,
                          unit=_('files'))
-        if f and f[0] == "/":
-            continue
         if m == "r": # remove
             repo.ui.note(_("removing %s\n") % f)
             audit(f)
             if f == '.hgsubstate': # subrepo states need updating
                 subrepo.submerge(repo, wctx, mctx, wctx, overwrite)
             try:
-                util.unlinkpath(repo.wjoin(f))
+                util.unlinkpath(repo.wjoin(f), ignoremissing=True)
             except OSError, inst:
-                if inst.errno != errno.ENOENT:
-                    repo.ui.warn(_("update failed to remove %s: %s!\n") %
-                                 (f, inst.strerror))
+                repo.ui.warn(_("update failed to remove %s: %s!\n") %
+                             (f, inst.strerror))
             removed += 1
         elif m == "m": # merge
-            if f == '.hgsubstate': # subrepo states need updating
+            if fd == '.hgsubstate': # subrepo states need updating
                 subrepo.submerge(repo, wctx, mctx, wctx.ancestor(mctx),
                                  overwrite)
                 continue
-            f2, fd, flags, move = a[2:]
-            repo.wopener.audit(fd)
+            f2, fd, move = a[2:]
+            audit(fd)
             r = ms.resolve(fd, wctx, mctx)
             if r is not None and r > 0:
                 unresolved += 1
@@ -400,17 +390,10 @@
                     updated += 1
                 else:
                     merged += 1
-            if (move and repo.dirstate.normalize(fd) != f
-                and os.path.lexists(repo.wjoin(f))):
-                repo.ui.debug("removing %s\n" % f)
-                audit(f)
-                os.unlink(repo.wjoin(f))
         elif m == "g": # get
             flags = a[2]
             repo.ui.note(_("getting %s\n") % f)
-            t = mctx.filectx(f).data()
-            repo.wwrite(f, t, flags)
-            t = None
+            repo.wwrite(f, mctx.filectx(f).data(), flags)
             updated += 1
             if f == '.hgsubstate': # subrepo states need updating
                 subrepo.submerge(repo, wctx, mctx, wctx, overwrite)
@@ -419,13 +402,11 @@
             if f:
                 repo.ui.note(_("moving %s to %s\n") % (f, fd))
                 audit(f)
-                t = wctx.filectx(f).data()
-                repo.wwrite(fd, t, flags)
+                repo.wwrite(fd, wctx.filectx(f).data(), flags)
                 util.unlinkpath(repo.wjoin(f))
             if f2:
                 repo.ui.note(_("getting %s to %s\n") % (f2, fd))
-                t = mctx.filectx(f2).data()
-                repo.wwrite(fd, t, flags)
+                repo.wwrite(fd, mctx.filectx(f2).data(), flags)
             updated += 1
         elif m == "dr": # divergent renames
             fl = a[2]
@@ -441,17 +422,39 @@
                 repo.ui.warn(" %s\n" % nf)
         elif m == "e": # exec
             flags = a[2]
-            repo.wopener.audit(f)
+            audit(f)
             util.setflags(repo.wjoin(f), 'l' in flags, 'x' in flags)
+            updated += 1
     ms.commit()
     repo.ui.progress(_('updating'), None, total=numupdates, unit=_('files'))
 
     return updated, merged, removed, unresolved
 
-def recordupdates(repo, action, branchmerge):
+def calculateupdates(repo, tctx, mctx, ancestor, branchmerge, force, partial):
+    "Calculate the actions needed to merge mctx into tctx"
+    actions = []
+    folding = not util.checkcase(repo.path)
+    if folding:
+        # collision check is not needed for clean update
+        if (not branchmerge and
+            (force or not tctx.dirty(missing=True, branch=False))):
+            _checkcollision(mctx, None)
+        else:
+            _checkcollision(mctx, (tctx, ancestor))
+    if not force:
+        _checkunknown(repo, tctx, mctx)
+    if tctx.rev() is None:
+        actions += _forgetremoved(tctx, mctx, branchmerge)
+    actions += manifestmerge(repo, tctx, mctx,
+                             ancestor,
+                             force and not branchmerge,
+                             partial)
+    return actions
+
+def recordupdates(repo, actions, branchmerge):
     "record merge actions to the dirstate"
 
-    for a in action:
+    for a in actions:
         f, m = a[:2]
         if m == "r": # remove
             if branchmerge:
@@ -471,7 +474,7 @@
             else:
                 repo.dirstate.normal(f)
         elif m == "m": # merge
-            f2, fd, flag, move = a[2:]
+            f2, fd, move = a[2:]
             if branchmerge:
                 # We've done a branch merge, mark this file as merged
                 # so that we properly record the merger later
@@ -590,7 +593,7 @@
             if not force and (wc.files() or wc.deleted()):
                 raise util.Abort(_("outstanding uncommitted changes"),
                                  hint=_("use 'hg status' to list changes"))
-            for s in wc.substate:
+            for s in sorted(wc.substate):
                 if wc.sub(s).dirty():
                     raise util.Abort(_("outstanding uncommitted changes in "
                                        "subrepository '%s'") % s)
@@ -609,19 +612,8 @@
                 pa = p1
 
         ### calculate phase
-        action = []
-        folding = not util.checkcase(repo.path)
-        if folding:
-            # collision check is not needed for clean update
-            if (not branchmerge and
-                (force or not wc.dirty(missing=True, branch=False))):
-                _checkcollision(p2, None)
-            else:
-                _checkcollision(p2, (wc, pa))
-        if not force:
-            _checkunknown(repo, wc, p2)
-        action += _forgetremoved(wc, p2, branchmerge)
-        action += manifestmerge(repo, wc, p2, pa, overwrite, partial)
+        actions = calculateupdates(repo, wc, p2, pa,
+                                   branchmerge, force, partial)
 
         ### apply phase
         if not branchmerge: # just jump to the new rev
@@ -629,11 +621,11 @@
         if not partial:
             repo.hook('preupdate', throw=True, parent1=xp1, parent2=xp2)
 
-        stats = applyupdates(repo, action, wc, p2, pa, overwrite)
+        stats = applyupdates(repo, actions, wc, p2, pa, overwrite)
 
         if not partial:
             repo.setparents(fp1, fp2)
-            recordupdates(repo, action, branchmerge)
+            recordupdates(repo, actions, branchmerge)
             if not branchmerge:
                 repo.dirstate.setbranch(p2.branch())
     finally:
--- a/mercurial/obsolete.py	Mon Jan 14 23:14:45 2013 +0900
+++ b/mercurial/obsolete.py	Sat Jan 19 17:24:33 2013 -0600
@@ -402,6 +402,200 @@
                     seen.add(suc)
                     remaining.add(suc)
 
+def successorssets(repo, initialnode, cache=None):
+    """Return all set of successors of initial nodes
+
+    Successors set of changeset A are a group of revision that succeed A. It
+    succeed A as a consistent whole, each revision being only partial
+    replacement.  Successors set contains non-obsolete changeset only.
+
+    In most cases a changeset A have zero (changeset pruned) or a single
+    successors set that contains a single successor (changeset A replaced by
+    A')
+
+    When changeset is split, it results successors set containing more than
+    a single element. Divergent rewriting will result in multiple successors
+    sets.
+
+    They are returned as a list of tuples containing all valid successors sets.
+
+    Final successors unknown locally are considered plain prune (obsoleted
+    without successors).
+
+    The optional `cache` parameter is a dictionary that may contains
+    precomputed successors sets. It is meant to reuse the computation of
+    previous call to `successorssets` when multiple calls are made at the same
+    time. The cache dictionary is updated in place. The caller is responsible
+    for its live spawn. Code that makes multiple calls to `successorssets`
+    *must* use this cache mechanism or suffer terrible performances."""
+
+    succmarkers = repo.obsstore.successors
+
+    # Stack of nodes we search successors sets for
+    toproceed = [initialnode]
+    # set version of above list for fast loop detection
+    # element added to "toproceed" must be added here
+    stackedset = set(toproceed)
+    if cache is None:
+        cache = {}
+
+    # This while loop is the flattened version of a recursive search for
+    # successors sets
+    #
+    # def successorssets(x):
+    #    successors = directsuccessors(x)
+    #    ss = [[]]
+    #    for succ in directsuccessors(x):
+    #        # product as in itertools cartesian product
+    #        ss = product(ss, successorssets(succ))
+    #    return ss
+    #
+    # But we can not use plain recursive calls here:
+    # - that would blow the python call stack
+    # - obsolescence markers may have cycles, we need to handle them.
+    #
+    # The `toproceed` list act as our call stack. Every node we search
+    # successors set for are stacked there.
+    #
+    # The `stackedset` is set version of this stack used to check if a node is
+    # already stacked. This check is used to detect cycles and prevent infinite
+    # loop.
+    #
+    # successors set of all nodes are stored in the `cache` dictionary.
+    #
+    # After this while loop ends we use the cache to return the successors sets
+    # for the node requested by the caller.
+    while toproceed:
+        # Every iteration tries to compute the successors sets of the topmost
+        # node of the stack: CURRENT.
+        #
+        # There are four possible outcomes:
+        #
+        # 1) We already know the successors sets of CURRENT:
+        #    -> mission accomplished, pop it from the stack.
+        # 2) Node is not obsolete:
+        #    -> the node is its own successors sets. Add it to the cache.
+        # 3) We do not know successors set of direct successors of CURRENT:
+        #    -> We add those successors to the stack.
+        # 4) We know successors sets of all direct successors of CURRENT:
+        #    -> We can compute CURRENT successors set and add it to the
+        #       cache.
+        #
+        current = toproceed[-1]
+        if current in cache:
+            # case (1): We already know the successors sets
+            stackedset.remove(toproceed.pop())
+        elif current not in succmarkers:
+            # case (2): The node is not obsolete.
+            if current in repo:
+                # We have a valid last successors.
+                cache[current] = [(current,)]
+            else:
+                # Final obsolete version is unknown locally.
+                # Do not count that as a valid successors
+                cache[current] = []
+        else:
+            # cases (3) and (4)
+            #
+            # We proceed in two phases. Phase 1 aims to distinguish case (3)
+            # from case (4):
+            #
+            #     For each direct successors of CURRENT, we check whether its
+            #     successors sets are known. If they are not, we stack the
+            #     unknown node and proceed to the next iteration of the while
+            #     loop. (case 3)
+            #
+            #     During this step, we may detect obsolescence cycles: a node
+            #     with unknown successors sets but already in the call stack.
+            #     In such a situation, we arbitrary set the successors sets of
+            #     the node to nothing (node pruned) to break the cycle.
+            #
+            #     If no break was encountered we proceeed to phase 2.
+            #
+            # Phase 2 computes successors sets of CURRENT (case 4); see details
+            # in phase 2 itself.
+            #
+            # Note the two levels of iteration in each phase.
+            # - The first one handles obsolescence markers using CURRENT as
+            #   precursor (successors markers of CURRENT).
+            #
+            #   Having multiple entry here means divergence.
+            #
+            # - The second one handles successors defined in each marker.
+            #
+            #   Having none means pruned node, multiple successors means split,
+            #   single successors are standard replacement.
+            #
+            for mark in sorted(succmarkers[current]):
+                for suc in mark[1]:
+                    if suc not in cache:
+                        if suc in stackedset:
+                            # cycle breaking
+                            cache[suc] = []
+                        else:
+                            # case (3) If we have not computed successors sets
+                            # of one of those successors we add it to the
+                            # `toproceed` stack and stop all work for this
+                            # iteration.
+                            toproceed.append(suc)
+                            stackedset.add(suc)
+                            break
+                else:
+                    continue
+                break
+            else:
+                # case (4): we know all successors sets of all direct
+                # successors
+                #
+                # Successors set contributed by each marker depends on the
+                # successors sets of all its "successors" node.
+                #
+                # Each different marker is a divergence in the obsolescence
+                # history. It contributes successors sets dictinct from other
+                # markers.
+                #
+                # Within a marker, a successor may have divergent successors
+                # sets. In such a case, the marker will contribute multiple
+                # divergent successors sets. If multiple successors have
+                # divergents successors sets, a cartesian product is used.
+                #
+                # At the end we post-process successors sets to remove
+                # duplicated entry and successors set that are strict subset of
+                # another one.
+                succssets = []
+                for mark in sorted(succmarkers[current]):
+                    # successors sets contributed by this marker
+                    markss = [[]]
+                    for suc in mark[1]:
+                        # cardinal product with previous successors
+                        productresult = []
+                        for prefix in markss:
+                            for suffix in cache[suc]:
+                                newss = list(prefix)
+                                for part in suffix:
+                                    # do not duplicated entry in successors set
+                                    # first entry wins.
+                                    if part not in newss:
+                                        newss.append(part)
+                                productresult.append(newss)
+                        markss = productresult
+                    succssets.extend(markss)
+                # remove duplicated and subset
+                seen = []
+                final = []
+                candidate = sorted(((set(s), s) for s in succssets if s),
+                                   key=lambda x: len(x[1]), reverse=True)
+                for setversion, listversion in candidate:
+                    for seenset in seen:
+                        if setversion.issubset(seenset):
+                            break
+                    else:
+                        final.append(listversion)
+                        seen.append(setversion)
+                final.reverse() # put small successors set first
+                cache[current] = final
+    return cache[initialnode]
+
 def _knownrevs(repo, nodes):
     """yield revision numbers of known nodes passed in parameters
 
@@ -426,6 +620,7 @@
     """Return the set of revision that belong to the <name> set
 
     Such access may compute the set and cache it for future use"""
+    repo = repo.unfiltered()
     if not repo.obsstore:
         return ()
     if name not in repo.obsstore.caches:
@@ -454,27 +649,35 @@
 def _computeobsoleteset(repo):
     """the set of obsolete revisions"""
     obs = set()
-    nm = repo.changelog.nodemap
+    getrev = repo.changelog.nodemap.get
+    getphase = repo._phasecache.phase
     for node in repo.obsstore.successors:
-        rev = nm.get(node)
-        if rev is not None:
+        rev = getrev(node)
+        if rev is not None and getphase(repo, rev):
             obs.add(rev)
-    return set(repo.revs('%ld - public()', obs))
+    return obs
 
 @cachefor('unstable')
 def _computeunstableset(repo):
     """the set of non obsolete revisions with obsolete parents"""
-    return set(repo.revs('(obsolete()::) - obsolete()'))
+    # revset is not efficient enough here
+    # we do (obsolete()::) - obsolete() by hand
+    obs = getrevs(repo, 'obsolete')
+    if not obs:
+        return set()
+    cl = repo.changelog
+    return set(r for r in cl.descendants(obs) if r not in obs)
 
 @cachefor('suspended')
 def _computesuspendedset(repo):
     """the set of obsolete parents with non obsolete descendants"""
-    return set(repo.revs('obsolete() and obsolete()::unstable()'))
+    suspended = repo.changelog.ancestors(getrevs(repo, 'unstable'))
+    return set(r for r in getrevs(repo, 'obsolete') if r in suspended)
 
 @cachefor('extinct')
 def _computeextinctset(repo):
     """the set of obsolete parents without non obsolete descendants"""
-    return set(repo.revs('obsolete() - obsolete()::unstable()'))
+    return getrevs(repo, 'obsolete') - getrevs(repo, 'suspended')
 
 
 @cachefor('bumped')
@@ -489,6 +692,28 @@
     query = '%ld - obsolete() - public()'
     return set(repo.revs(query, _knownrevs(repo, successors)))
 
+@cachefor('divergent')
+def _computedivergentset(repo):
+    """the set of rev that compete to be the final successors of some revision.
+    """
+    divergent = set()
+    obsstore = repo.obsstore
+    newermap = {}
+    for ctx in repo.set('(not public()) - obsolete()'):
+        mark = obsstore.precursors.get(ctx.node(), ())
+        toprocess = set(mark)
+        while toprocess:
+            prec = toprocess.pop()[0]
+            if prec not in newermap:
+                successorssets(repo, prec, newermap)
+            newer = [n for n in newermap[prec] if n]
+            if len(newer) > 1:
+                divergent.add(ctx.rev())
+                break
+            toprocess.update(obsstore.precursors.get(prec, ()))
+    return divergent
+
+
 def createmarkers(repo, relations, flag=0, metadata=None):
     """Add obsolete markers between changesets in a repo
 
@@ -521,6 +746,7 @@
             if nprec in nsucs:
                 raise util.Abort("changeset %s cannot obsolete itself" % prec)
             repo.obsstore.create(tr, nprec, nsucs, flag, metadata)
+            repo.filteredrevcache.clear()
         tr.close()
     finally:
         tr.release()
--- a/mercurial/osutil.c	Mon Jan 14 23:14:45 2013 +0900
+++ b/mercurial/osutil.c	Sat Jan 19 17:24:33 2013 -0600
@@ -276,6 +276,16 @@
 	return -1;
 }
 
+static PyObject *makestat(const struct stat *st)
+{
+	PyObject *stat;
+
+	stat = PyObject_CallObject((PyObject *)&listdir_stat_type, NULL);
+	if (stat)
+		memcpy(&((struct listdir_stat *)stat)->st, st, sizeof(*st));
+	return stat;
+}
+
 static PyObject *_listdir(char *path, int pathlen, int keepstat, char *skip)
 {
 	PyObject *list, *elem, *stat, *ret = NULL;
@@ -351,10 +361,9 @@
 		}
 
 		if (keepstat) {
-			stat = PyObject_CallObject((PyObject *)&listdir_stat_type, NULL);
+			stat = makestat(&st);
 			if (!stat)
 				goto error;
-			memcpy(&((struct listdir_stat *)stat)->st, &st, sizeof(st));
 			elem = Py_BuildValue("siN", ent->d_name, kind, stat);
 		} else
 			elem = Py_BuildValue("si", ent->d_name, kind);
@@ -380,6 +389,55 @@
 	return ret;
 }
 
+static PyObject *statfiles(PyObject *self, PyObject *args)
+{
+	PyObject *names, *stats;
+	Py_ssize_t i, count;
+
+	if (!PyArg_ParseTuple(args, "O:statfiles", &names))
+		return NULL;
+
+	count = PySequence_Length(names);
+	if (count == -1) {
+		PyErr_SetString(PyExc_TypeError, "not a sequence");
+		return NULL;
+	}
+
+	stats = PyList_New(count);
+	if (stats == NULL)
+		return NULL;
+
+	for (i = 0; i < count; i++) {
+		PyObject *stat;
+		struct stat st;
+		int ret, kind;
+		char *path;
+
+		path = PyString_AsString(PySequence_GetItem(names, i));
+		if (path == NULL) {
+			PyErr_SetString(PyExc_TypeError, "not a string");
+			goto bail;
+		}
+		ret = lstat(path, &st);
+		kind = st.st_mode & S_IFMT;
+		if (ret != -1 && (kind == S_IFREG || kind == S_IFLNK)) {
+			stat = makestat(&st);
+			if (stat == NULL)
+				goto bail;
+			PyList_SET_ITEM(stats, i, stat);
+		} else {
+			Py_INCREF(Py_None);
+			PyList_SET_ITEM(stats, i, Py_None);
+		}
+	}
+
+	return stats;
+
+bail:
+	Py_DECREF(stats);
+	return NULL;
+}
+
 #endif /* ndef _WIN32 */
 
 static PyObject *listdir(PyObject *self, PyObject *args, PyObject *kwargs)
@@ -544,6 +602,10 @@
 	{"posixfile", (PyCFunction)posixfile, METH_VARARGS | METH_KEYWORDS,
 	 "Open a file with POSIX-like semantics.\n"
 "On error, this function may raise either a WindowsError or an IOError."},
+#else
+	{"statfiles", (PyCFunction)statfiles, METH_VARARGS | METH_KEYWORDS,
+	 "stat a series of files or symlinks\n"
+"Returns None for non-existent entries and entries of other types.\n"},
 #endif
 #ifdef __APPLE__
 	{
--- a/mercurial/parsers.c	Mon Jan 14 23:14:45 2013 +0900
+++ b/mercurial/parsers.c	Sat Jan 19 17:24:33 2013 -0600
@@ -1508,6 +1508,7 @@
 
 PyObject *encodedir(PyObject *self, PyObject *args);
 PyObject *pathencode(PyObject *self, PyObject *args);
+PyObject *lowerencode(PyObject *self, PyObject *args);
 
 static PyMethodDef methods[] = {
 	{"pack_dirstate", pack_dirstate, METH_VARARGS, "pack a dirstate\n"},
@@ -1516,6 +1517,7 @@
 	{"parse_index2", parse_index2, METH_VARARGS, "parse a revlog index\n"},
 	{"encodedir", encodedir, METH_VARARGS, "encodedir a path\n"},
 	{"pathencode", pathencode, METH_VARARGS, "fncache-encode a path\n"},
+	{"lowerencode", lowerencode, METH_VARARGS, "lower-encode a path\n"},
 	{NULL, NULL}
 };
 
--- a/mercurial/patch.py	Mon Jan 14 23:14:45 2013 +0900
+++ b/mercurial/patch.py	Sat Jan 19 17:24:33 2013 -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 _
@@ -439,11 +439,7 @@
                 util.setflags(self._join(fname), False, True)
 
     def unlink(self, fname):
-        try:
-            util.unlinkpath(self._join(fname))
-        except OSError, inst:
-            if inst.errno != errno.ENOENT:
-                raise
+        util.unlinkpath(self._join(fname), ignoremissing=True)
 
     def writerej(self, fname, failed, total, lines):
         fname = fname + ".rej"
@@ -1007,7 +1003,7 @@
 
             bot = min(fuzz, bot)
             top = min(fuzz, top)
-            return old[top:len(old)-bot], new[top:len(new)-bot], top
+            return old[top:len(old) - bot], new[top:len(new) - bot], top
         return old, new, 0
 
     def fuzzit(self, fuzz, toponly):
@@ -1514,44 +1510,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 +1580,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 +1647,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 +1718,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 +1764,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/pathencode.c	Mon Jan 14 23:14:45 2013 +0900
+++ b/mercurial/pathencode.c	Sat Jan 19 17:24:33 2013 -0600
@@ -15,6 +15,7 @@
  * required.
  */
 
+#define PY_SSIZE_T_CLEAN
 #include <Python.h>
 #include <assert.h>
 #include <ctype.h>
@@ -481,12 +482,244 @@
 
 static const Py_ssize_t maxstorepathlen = 120;
 
+static Py_ssize_t _lowerencode(char *dest, size_t destsize,
+			       const char *src, Py_ssize_t len)
+{
+	static const uint32_t onebyte[8] = {
+		1, 0x2bfffbfb, 0xe8000001, 0x2fffffff
+	};
+
+	static const uint32_t lower[8] = { 0, 0, 0x7fffffe };
+
+	Py_ssize_t i, destlen = 0;
+
+	for (i = 0; i < len; i++) {
+		if (inset(onebyte, src[i]))
+			charcopy(dest, &destlen, destsize, src[i]);
+		else if (inset(lower, src[i]))
+			charcopy(dest, &destlen, destsize, src[i] + 32);
+		else
+			escape3(dest, &destlen, destsize, src[i]);
+	}
+
+	return destlen;
+}
+
+PyObject *lowerencode(PyObject *self, PyObject *args)
+{
+	char *path;
+	Py_ssize_t len, newlen;
+	PyObject *ret;
+
+	if (!PyArg_ParseTuple(args, "s#:lowerencode", &path, &len))
+		return NULL;
+
+	newlen = _lowerencode(NULL, 0, path, len);
+	ret = PyString_FromStringAndSize(NULL, newlen);
+	if (ret)
+		newlen = _lowerencode(PyString_AS_STRING(ret), newlen,
+				      path, len);
+
+	return ret;
+}
+
+/* See store.py:_auxencode for a description. */
+static Py_ssize_t auxencode(char *dest, size_t destsize,
+			    const char *src, Py_ssize_t len)
+{
+	static const uint32_t twobytes[8];
+
+	static const uint32_t onebyte[8] = {
+		~0, 0xffff3ffe, ~0, ~0, ~0, ~0, ~0, ~0,
+	};
+
+	return _encode(twobytes, onebyte, dest, 0, destsize, src, len, 0);
+}
+
+static PyObject *hashmangle(const char *src, Py_ssize_t len, const char sha[20])
+{
+	static const Py_ssize_t dirprefixlen = 8;
+	static const Py_ssize_t maxshortdirslen = 68;
+	char *dest;
+	PyObject *ret;
+
+	Py_ssize_t i, d, p, lastslash = len - 1, lastdot = -1;
+	Py_ssize_t destsize, destlen = 0, slop, used;
+
+	while (lastslash >= 0 && src[lastslash] != '/') {
+		if (src[lastslash] == '.' && lastdot == -1)
+			lastdot = lastslash;
+		lastslash--;
+	}
+
+#if 0
+	/* All paths should end in a suffix of ".i" or ".d".
+           Unfortunately, the file names in test-hybridencode.py
+           violate this rule.  */
+	if (lastdot != len - 3) {
+		PyErr_SetString(PyExc_ValueError,
+				"suffix missing or wrong length");
+		return NULL;
+	}
+#endif
+
+	/* If src contains a suffix, we will append it to the end of
+	   the new string, so make room. */
+	destsize = 120;
+	if (lastdot >= 0)
+		destsize += len - lastdot - 1;
+
+	ret = PyString_FromStringAndSize(NULL, destsize);
+	if (ret == NULL)
+		return NULL;
+
+	dest = PyString_AS_STRING(ret);
+	memcopy(dest, &destlen, destsize, "dh/", 3);
+
+	/* Copy up to dirprefixlen bytes of each path component, up to
+	   a limit of maxshortdirslen bytes. */
+	for (i = d = p = 0; i < lastslash; i++, p++) {
+		if (src[i] == '/') {
+			char d = dest[destlen - 1];
+			/* After truncation, a directory name may end
+			   in a space or dot, which are unportable. */
+			if (d == '.' || d == ' ')
+				dest[destlen - 1] = '_';
+			if (destlen > maxshortdirslen)
+				break;
+			charcopy(dest, &destlen, destsize, src[i]);
+			p = -1;
+		}
+		else if (p < dirprefixlen)
+			charcopy(dest, &destlen, destsize, src[i]);
+	}
+
+	/* Rewind to just before the last slash copied. */
+	if (destlen > maxshortdirslen + 3)
+		do {
+			destlen--;
+		} while (destlen > 0 && dest[destlen] != '/');
+
+	if (destlen > 3) {
+		if (lastslash > 0) {
+			char d = dest[destlen - 1];
+			/* The last directory component may be
+			   truncated, so make it safe. */
+			if (d == '.' || d == ' ')
+				dest[destlen - 1] = '_';
+		}
+
+		charcopy(dest, &destlen, destsize, '/');
+	}
+
+	/* Add a prefix of the original file's name. Its length
+	   depends on the number of bytes left after accounting for
+	   hash and suffix. */
+	used = destlen + 40;
+	if (lastdot >= 0)
+		used += len - lastdot - 1;
+	slop = maxstorepathlen - used;
+	if (slop > 0) {
+		Py_ssize_t basenamelen =
+			lastslash >= 0 ? len - lastslash - 2 : len - 1;
+
+		if (basenamelen > slop)
+			basenamelen = slop;
+		if (basenamelen > 0)
+			memcopy(dest, &destlen, destsize, &src[lastslash + 1],
+				basenamelen);
+	}
+
+	/* Add hash and suffix. */
+	for (i = 0; i < 20; i++)
+		hexencode(dest, &destlen, destsize, sha[i]);
+
+	if (lastdot >= 0)
+		memcopy(dest, &destlen, destsize, &src[lastdot],
+			len - lastdot - 1);
+
+	PyString_GET_SIZE(ret) = destlen;
+
+	return ret;
+}
+
 /*
- * We currently implement only basic encoding.
- *
- * If a name is too long to encode due to Windows path name limits,
- * this function returns None.
+ * Avoiding a trip through Python would improve performance by 50%,
+ * but we don't encounter enough long names to be worth the code.
  */
+static int sha1hash(char hash[20], const char *str, Py_ssize_t len)
+{
+	static PyObject *shafunc;
+	PyObject *shaobj, *hashobj;
+
+	if (shafunc == NULL) {
+		PyObject *util, *name = PyString_FromString("mercurial.util");
+
+		if (name == NULL)
+			return -1;
+
+		util = PyImport_Import(name);
+		Py_DECREF(name);
+
+		if (util == NULL) {
+			PyErr_SetString(PyExc_ImportError, "mercurial.util");
+			return -1;
+		}
+		shafunc = PyObject_GetAttrString(util, "sha1");
+		Py_DECREF(util);
+
+		if (shafunc == NULL) {
+			PyErr_SetString(PyExc_AttributeError,
+					"module 'mercurial.util' has no "
+					"attribute 'sha1'");
+			return -1;
+		}
+	}
+
+	shaobj = PyObject_CallFunction(shafunc, "s#", str, len);
+
+	if (shaobj == NULL)
+		return -1;
+
+	hashobj = PyObject_CallMethod(shaobj, "digest", "");
+	Py_DECREF(shaobj);
+
+	if (!PyString_Check(hashobj) || PyString_GET_SIZE(hashobj) != 20) {
+		PyErr_SetString(PyExc_TypeError,
+				"result of digest is not a 20-byte hash");
+		Py_DECREF(hashobj);
+		return -1;
+	}
+
+	memcpy(hash, PyString_AS_STRING(hashobj), 20);
+	Py_DECREF(hashobj);
+	return 0;
+}
+
+#define MAXENCODE 4096 * 3
+
+static PyObject *hashencode(const char *src, Py_ssize_t len)
+{
+	char dired[MAXENCODE];
+	char lowered[MAXENCODE];
+	char auxed[MAXENCODE];
+	Py_ssize_t dirlen, lowerlen, auxlen, baselen;
+	char sha[20];
+
+	baselen = (len - 5) * 3;
+	if (baselen >= MAXENCODE) {
+		PyErr_SetString(PyExc_ValueError, "string too long");
+		return NULL;
+	}
+
+	dirlen = _encodedir(dired, baselen, src, len);
+	if (sha1hash(sha, dired, dirlen - 1) == -1)
+		return NULL;
+	lowerlen = _lowerencode(lowered, baselen, dired + 5, dirlen - 5);
+	auxlen = auxencode(auxed, baselen, lowered, lowerlen);
+	return hashmangle(auxed, auxlen, sha);
+}
+
 PyObject *pathencode(PyObject *self, PyObject *args)
 {
 	Py_ssize_t len, newlen;
@@ -501,13 +734,10 @@
 		return NULL;
 	}
 
-	if (len > maxstorepathlen) {
-		newobj = Py_None;
-		Py_INCREF(newobj);
-		return newobj;
-	}
-
-	newlen = len ? basicencode(NULL, 0, path, len + 1) : 1;
+	if (len > maxstorepathlen)
+		newlen = maxstorepathlen + 2;
+	else
+		newlen = len ? basicencode(NULL, 0, path, len + 1) : 1;
 
 	if (newlen <= maxstorepathlen + 1) {
 		if (newlen == len + 1) {
@@ -522,10 +752,9 @@
 			basicencode(PyString_AS_STRING(newobj), newlen, path,
 				    len + 1);
 		}
-	} else {
-		newobj = Py_None;
-		Py_INCREF(newobj);
 	}
+	else
+		newobj = hashencode(path, len + 1);
 
 	return newobj;
 }
--- a/mercurial/phases.py	Mon Jan 14 23:14:45 2013 +0900
+++ b/mercurial/phases.py	Sat Jan 19 17:24:33 2013 -0600
@@ -104,30 +104,11 @@
 from node import nullid, nullrev, bin, hex, short
 from i18n import _
 import util, error
-import obsolete
 
 allphases = public, draft, secret = range(3)
 trackedphases = allphases[1:]
 phasenames = ['public', 'draft', 'secret']
 
-def _filterunknown(ui, changelog, phaseroots):
-    """remove unknown nodes from the phase boundary
-
-    Nothing is lost as unknown nodes only hold data for their descendants.
-    """
-    updated = False
-    nodemap = changelog.nodemap # to filter unknown nodes
-    for phase, nodes in enumerate(phaseroots):
-        missing = [node for node in nodes if node not in nodemap]
-        if missing:
-            for mnode in missing:
-                ui.debug(
-                    'removing unknown node %s from %i-phase boundary\n'
-                    % (short(mnode), phase))
-            nodes.symmetric_difference_update(missing)
-            updated = True
-    return updated
-
 def _readroots(repo, phasedefaults=None):
     """Read phase roots from disk
 
@@ -139,6 +120,7 @@
     Return (roots, dirty) where dirty is true if roots differ from
     what is being stored.
     """
+    repo = repo.unfiltered()
     dirty = False
     roots = [set() for i in allphases]
     try:
@@ -156,8 +138,6 @@
             for f in phasedefaults:
                 roots = f(repo, roots)
         dirty = True
-    if _filterunknown(repo.ui, repo.changelog, roots):
-        dirty = True
     return roots, dirty
 
 class phasecache(object):
@@ -165,8 +145,9 @@
         if _load:
             # Cheap trick to allow shallow-copy without copy module
             self.phaseroots, self.dirty = _readroots(repo, phasedefaults)
+            self._phaserevs = None
+            self.filterunknown(repo)
             self.opener = repo.sopener
-            self._phaserevs = None
 
     def copy(self):
         # Shallow copy meant to ensure isolation in
@@ -184,6 +165,7 @@
 
     def getphaserevs(self, repo, rebuild=False):
         if rebuild or self._phaserevs is None:
+            repo = repo.unfiltered()
             revs = [public] * len(repo.changelog)
             for phase in trackedphases:
                 roots = map(repo.changelog.rev, self.phaseroots[phase])
@@ -228,6 +210,7 @@
         # Be careful to preserve shallow-copied values: do not update
         # phaseroots values, replace them.
 
+        repo = repo.unfiltered()
         delroots = [] # set of root deleted by this path
         for phase in xrange(targetphase + 1, len(allphases)):
             # filter nodes that are not in a compatible phase already
@@ -245,12 +228,13 @@
             # declare deleted root in the target phase
             if targetphase != 0:
                 self.retractboundary(repo, targetphase, delroots)
-        obsolete.clearobscaches(repo)
+        repo.invalidatevolatilesets()
 
     def retractboundary(self, repo, targetphase, nodes):
         # Be careful to preserve shallow-copied values: do not update
         # phaseroots values, replace them.
 
+        repo = repo.unfiltered()
         currentroots = self.phaseroots[targetphase]
         newroots = [n for n in nodes
                     if self.phase(repo, repo[n].rev()) < targetphase]
@@ -262,7 +246,27 @@
             ctxs = repo.set('roots(%ln::)', currentroots)
             currentroots.intersection_update(ctx.node() for ctx in ctxs)
             self._updateroots(targetphase, currentroots)
-        obsolete.clearobscaches(repo)
+        repo.invalidatevolatilesets()
+
+    def filterunknown(self, repo):
+        """remove unknown nodes from the phase boundary
+
+        Nothing is lost as unknown nodes only hold data for their descendants.
+        """
+        filtered = False
+        nodemap = repo.changelog.nodemap # to filter unknown nodes
+        for phase, nodes in enumerate(self.phaseroots):
+            missing = [node for node in nodes if node not in nodemap]
+            if missing:
+                for mnode in missing:
+                    repo.ui.debug(
+                        'removing unknown node %s from %i-phase boundary\n'
+                        % (short(mnode), phase))
+                nodes.symmetric_difference_update(missing)
+                filtered = True
+        if filtered:
+            self.dirty = True
+            self._phaserevs = None
 
 def advanceboundary(repo, targetphase, nodes):
     """Add nodes to a phase changing other nodes phases if necessary.
@@ -316,6 +320,7 @@
 
 def pushphase(repo, nhex, oldphasestr, newphasestr):
     """List phases root for serialization over pushkey"""
+    repo = repo.unfiltered()
     lock = repo.lock()
     try:
         currentphase = repo[nhex].phase()
@@ -340,6 +345,7 @@
 
     Accept unknown element input
     """
+    repo = repo.unfiltered()
     # build list from dictionary
     draftroots = []
     nodemap = repo.changelog.nodemap # to filter unknown nodes
@@ -367,6 +373,7 @@
 
     * `heads`: define the first subset
     * `roots`: define the second we subtract from the first"""
+    repo = repo.unfiltered()
     revset = repo.set('heads((%ln + parents(%ln)) - (%ln::%ln))',
                       heads, roots, roots, heads)
     return [c.node() for c in revset]
--- a/mercurial/posix.py	Mon Jan 14 23:14:45 2013 +0900
+++ b/mercurial/posix.py	Sat Jan 19 17:24:33 2013 -0600
@@ -7,7 +7,7 @@
 
 from i18n import _
 import encoding
-import os, sys, errno, stat, getpass, pwd, grp, tempfile, unicodedata
+import os, sys, errno, stat, getpass, pwd, grp, socket, tempfile, unicodedata
 
 posixfile = open
 normpath = os.path.normpath
@@ -21,14 +21,26 @@
 os.umask(umask)
 
 def split(p):
-    '''Same as os.path.split, but faster'''
+    '''Same as posixpath.split, but faster
+
+    >>> import posixpath
+    >>> for f in ['/absolute/path/to/file',
+    ...           'relative/path/to/file',
+    ...           'file_alone',
+    ...           'path/to/directory/',
+    ...           '/multiple/path//separators',
+    ...           '/file_at_root',
+    ...           '///multiple_leading_separators_at_root',
+    ...           '']:
+    ...     assert split(f) == posixpath.split(f), f
+    '''
     ht = p.rsplit('/', 1)
     if len(ht) == 1:
         return '', p
     nh = ht[0].rstrip('/')
     if nh:
         return nh, ht[1]
-    return ht
+    return ht[0] + '/', ht[1]
 
 def openhardlinks():
     '''return true if it is safe to hold open file handles to hardlinks'''
@@ -352,12 +364,18 @@
 def setsignalhandler():
     pass
 
+_wantedkinds = set([stat.S_IFREG, stat.S_IFLNK])
+
 def statfiles(files):
-    'Stat each file in files and yield stat or None if file does not exist.'
+    '''Stat each file in files. Yield each stat, or None if a file does not
+    exist or has a type we don't care about.'''
     lstat = os.lstat
+    getkind = stat.S_IFMT
     for nf in files:
         try:
             st = lstat(nf)
+            if getkind(st.st_mode) not in _wantedkinds:
+                st = None
         except OSError, err:
             if err.errno not in (errno.ENOENT, errno.ENOTDIR):
                 raise
@@ -437,9 +455,13 @@
 def makedir(path, notindexed):
     os.mkdir(path)
 
-def unlinkpath(f):
+def unlinkpath(f, ignoremissing=False):
     """unlink and remove the directory if it is empty"""
-    os.unlink(f)
+    try:
+        os.unlink(f)
+    except OSError, e:
+        if not (ignoremissing and e.errno == errno.ENOENT):
+            raise
     # try removing directories that might now be empty
     try:
         os.removedirs(os.path.dirname(f))
@@ -468,7 +490,20 @@
 
     def __eq__(self, other):
         try:
-            return self.stat == other.stat
+            # Only dev, ino, size, mtime and atime are likely to change. Out
+            # of these, we shouldn't compare atime but should compare the
+            # rest. However, one of the other fields changing indicates
+            # something fishy going on, so return False if anything but atime
+            # changes.
+            return (self.stat.st_mode == other.stat.st_mode and
+                    self.stat.st_ino == other.stat.st_ino and
+                    self.stat.st_dev == other.stat.st_dev and
+                    self.stat.st_nlink == other.stat.st_nlink and
+                    self.stat.st_uid == other.stat.st_uid and
+                    self.stat.st_gid == other.stat.st_gid and
+                    self.stat.st_size == other.stat.st_size and
+                    self.stat.st_mtime == other.stat.st_mtime and
+                    self.stat.st_ctime == other.stat.st_ctime)
         except AttributeError:
             return False
 
@@ -477,3 +512,43 @@
 
 def executablepath():
     return None # available on Windows only
+
+class unixdomainserver(socket.socket):
+    def __init__(self, join, subsystem):
+        '''Create a unix domain socket with the given prefix.'''
+        super(unixdomainserver, self).__init__(socket.AF_UNIX)
+        sockname = subsystem + '.sock'
+        self.realpath = self.path = join(sockname)
+        if os.path.islink(self.path):
+            if os.path.exists(self.path):
+                self.realpath = os.readlink(self.path)
+            else:
+                os.unlink(self.path)
+        try:
+            self.bind(self.realpath)
+        except socket.error, err:
+            if err.args[0] == 'AF_UNIX path too long':
+                tmpdir = tempfile.mkdtemp(prefix='hg-%s-' % subsystem)
+                self.realpath = os.path.join(tmpdir, sockname)
+                try:
+                    self.bind(self.realpath)
+                    os.symlink(self.realpath, self.path)
+                except (OSError, socket.error):
+                    self.cleanup()
+                    raise
+            else:
+                raise
+        self.listen(5)
+
+    def cleanup(self):
+        def okayifmissing(f, path):
+            try:
+                f(path)
+            except OSError, err:
+                if err.errno != errno.ENOENT:
+                    raise
+
+        okayifmissing(os.unlink, self.path)
+        if self.realpath != self.path:
+            okayifmissing(os.unlink, self.realpath)
+            okayifmissing(os.rmdir, os.path.dirname(self.realpath))
--- a/mercurial/repair.py	Mon Jan 14 23:14:45 2013 +0900
+++ b/mercurial/repair.py	Sat Jan 19 17:24:33 2013 -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
@@ -56,10 +56,8 @@
     return s
 
 def strip(ui, repo, nodelist, backup="all", topic='backup'):
-    # It simplifies the logic around updating the branchheads cache if we only
-    # have to consider the effect of the stripped revisions and not revisions
-    # missing because the cache is out-of-date.
-    repo.updatebranchcache()
+    repo = repo.unfiltered()
+    repo.destroying()
 
     cl = repo.changelog
     # TODO handle undo of merge sets
@@ -68,17 +66,6 @@
     striplist = [cl.rev(node) for node in nodelist]
     striprev = min(striplist)
 
-    # Generate set of branches who will have nodes stripped.
-    striprevs = repo.revs("%ld::", striplist)
-    stripbranches = set([repo[rev].branch() for rev in striprevs])
-
-    # Set of potential new heads resulting from the strip.  The parents of any
-    # node removed could be a new head because the node to be removed could have
-    # been the only child of the parent.
-    newheadrevs = repo.revs("parents(%ld::) - %ld::", striprevs, striprevs)
-    newheadnodes = set([cl.node(rev) for rev in newheadrevs])
-    newheadbranches = set([repo[rev].branch() for rev in newheadrevs])
-
     keeppartialbundle = backup == 'strip'
 
     # Some revisions with rev > striprev may not be descendants of striprev.
@@ -111,8 +98,10 @@
         saverevs.difference_update(descendants)
     savebases = [cl.node(r) for r in saverevs]
     stripbases = [cl.node(r) for r in tostrip]
-    newbmtarget = repo.revs('sort(heads((::%ld) - (%ld)), -rev)',
-                            tostrip, tostrip)
+
+    # For a set s, max(parents(s) - s) is the same as max(heads(::s - s)), but
+    # is much faster
+    newbmtarget = repo.revs('max(parents(%ld) - (%ld))', tostrip, tostrip)
     if newbmtarget:
         newbmtarget = repo[newbmtarget[0]].node()
     else:
@@ -181,7 +170,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")
@@ -191,10 +180,4 @@
                     % chgrpfile)
         raise
 
-    if len(stripbranches) == 1 and len(newheadbranches) == 1 \
-            and stripbranches == newheadbranches:
-        repo.destroyed(newheadnodes)
-    else:
-        # Multiple branches involved in strip. Will allow branchcache to become
-        # invalid and later on rebuilt from scratch
-        repo.destroyed()
+    repo.destroyed()
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/mercurial/repoview.py	Sat Jan 19 17:24:33 2013 -0600
@@ -0,0 +1,219 @@
+# repoview.py - Filtered view of a localrepo object
+#
+# Copyright 2012 Pierre-Yves David <pierre-yves.david@ens-lyon.org>
+#                Logilab SA        <contact@logilab.fr>
+#
+# This software may be used and distributed according to the terms of the
+# GNU General Public License version 2 or any later version.
+
+import copy
+import phases
+import util
+import obsolete, bookmarks, revset
+
+
+def hideablerevs(repo):
+    """Revisions candidates to be hidden
+
+    This is a standalone function to help extensions to wrap it."""
+    return obsolete.getrevs(repo, 'obsolete')
+
+def computehidden(repo):
+    """compute the set of hidden revision to filter
+
+    During most operation hidden should be filtered."""
+    assert not repo.changelog.filteredrevs
+    hideable = hideablerevs(repo)
+    if hideable:
+        cl = repo.changelog
+        firsthideable = min(hideable)
+        revs = cl.revs(start=firsthideable)
+        blockers = [r for r in revset._children(repo, revs, hideable)
+                      if r not in hideable]
+        for par in repo[None].parents():
+            blockers.append(par.rev())
+        for bm in bookmarks.listbookmarks(repo).values():
+            blockers.append(repo[bm].rev())
+        blocked = cl.ancestors(blockers, inclusive=True)
+        return frozenset(r for r in hideable if r not in blocked)
+    return frozenset()
+
+def computeunserved(repo):
+    """compute the set of revision that should be filtered when used a server
+
+    Secret and hidden changeset should not pretend to be here."""
+    assert not repo.changelog.filteredrevs
+    # fast path in simple case to avoid impact of non optimised code
+    hiddens = filterrevs(repo, 'visible')
+    if phases.hassecret(repo):
+        cl = repo.changelog
+        secret = phases.secret
+        getphase = repo._phasecache.phase
+        first = min(cl.rev(n) for n in repo._phasecache.phaseroots[secret])
+        revs = cl.revs(start=first)
+        secrets = set(r for r in revs if getphase(repo, r) >= secret)
+        return frozenset(hiddens | secrets)
+    else:
+        return hiddens
+    return frozenset()
+
+def computemutable(repo):
+    """compute the set of revision that should be filtered when used a server
+
+    Secret and hidden changeset should not pretend to be here."""
+    assert not repo.changelog.filteredrevs
+    # fast check to avoid revset call on huge repo
+    if util.any(repo._phasecache.phaseroots[1:]):
+        getphase = repo._phasecache.phase
+        maymutable = filterrevs(repo, 'base')
+        return frozenset(r for r in maymutable if getphase(repo, r))
+    return frozenset()
+
+def computeimpactable(repo):
+    """Everything impactable by mutable revision
+
+    The mutable filter still have some chance to get invalidated. This will
+    happen when:
+
+    - you garbage collect hidden changeset,
+    - public phase is moved backward,
+    - something is changed in the filtering (this could be fixed)
+
+    This filter out any mutable changeset and any public changeset that may be
+    impacted by something happening to a mutable revision.
+
+    This is achieved by filtered everything with a revision number egal or
+    higher than the first mutable changeset is filtered."""
+    assert not repo.changelog.filteredrevs
+    cl = repo.changelog
+    firstmutable = len(cl)
+    for roots in repo._phasecache.phaseroots[1:]:
+        if roots:
+            firstmutable = min(firstmutable, min(cl.rev(r) for r in roots))
+    # protect from nullrev root
+    firstmutable = max(0, firstmutable)
+    return frozenset(xrange(firstmutable, len(cl)))
+
+# function to compute filtered set
+filtertable = {'visible': computehidden,
+               'served': computeunserved,
+               'immutable':  computemutable,
+               'base':  computeimpactable}
+### Nearest subset relation
+# Nearest subset of filter X is a filter Y so that:
+# * Y is included in X,
+# * X - Y is as small as possible.
+# This create and ordering used for branchmap purpose.
+# the ordering may be partial
+subsettable = {None: 'visible',
+               'visible': 'served',
+               'served': 'immutable',
+               'immutable': 'base'}
+
+def filterrevs(repo, filtername):
+    """returns set of filtered revision for this filter name"""
+    if filtername not in repo.filteredrevcache:
+        func = filtertable[filtername]
+        repo.filteredrevcache[filtername] = func(repo.unfiltered())
+    return repo.filteredrevcache[filtername]
+
+class repoview(object):
+    """Provide a read/write view of a repo through a filtered changelog
+
+    This object is used to access a filtered version of a repository without
+    altering the original repository object itself. We can not alter the
+    original object for two main reasons:
+    - It prevents the use of a repo with multiple filters at the same time. In
+      particular when multiple threads are involved.
+    - It makes scope of the filtering harder to control.
+
+    This object behaves very closely to the original repository. All attribute
+    operations are done on the original repository:
+    - An access to `repoview.someattr` actually returns `repo.someattr`,
+    - A write to `repoview.someattr` actually sets value of `repo.someattr`,
+    - A deletion of `repoview.someattr` actually drops `someattr`
+      from `repo.__dict__`.
+
+    The only exception is the `changelog` property. It is overridden to return
+    a (surface) copy of `repo.changelog` with some revisions filtered. The
+    `filtername` attribute of the view control the revisions that need to be
+    filtered.  (the fact the changelog is copied is an implementation detail).
+
+    Unlike attributes, this object intercepts all method calls. This means that
+    all methods are run on the `repoview` object with the filtered `changelog`
+    property. For this purpose the simple `repoview` class must be mixed with
+    the actual class of the repository. This ensures that the resulting
+    `repoview` object have the very same methods than the repo object. This
+    leads to the property below.
+
+        repoview.method() --> repo.__class__.method(repoview)
+
+    The inheritance has to be done dynamically because `repo` can be of any
+    subclasses of `localrepo`. Eg: `bundlerepo` or `httprepo`.
+    """
+
+    def __init__(self, repo, filtername):
+        object.__setattr__(self, '_unfilteredrepo', repo)
+        object.__setattr__(self, 'filtername', filtername)
+        object.__setattr__(self, '_clcachekey', None)
+        object.__setattr__(self, '_clcache', None)
+
+    # not a cacheproperty on purpose we shall implement a proper cache later
+    @property
+    def changelog(self):
+        """return a filtered version of the changeset
+
+        this changelog must not be used for writing"""
+        # some cache may be implemented later
+        unfi = self._unfilteredrepo
+        unfichangelog = unfi.changelog
+        revs = filterrevs(unfi, self.filtername)
+        cl = self._clcache
+        newkey = (len(unfichangelog), unfichangelog.tip(), hash(revs))
+        if cl is not None:
+            # we need to check curkey too for some obscure reason.
+            # MQ test show a corruption of the underlying repo (in _clcache)
+            # without change in the cachekey.
+            oldfilter = cl.filteredrevs
+            try:
+                cl.filterrevs = ()  # disable filtering for tip
+                curkey = (len(cl), cl.tip(), hash(oldfilter))
+            finally:
+                cl.filteredrevs = oldfilter
+            if newkey != self._clcachekey or newkey != curkey:
+                cl = None
+        # could have been made None by the previous if
+        if cl is None:
+            cl = copy.copy(unfichangelog)
+            cl.filteredrevs = revs
+            object.__setattr__(self, '_clcache', cl)
+            object.__setattr__(self, '_clcachekey', newkey)
+        return cl
+
+    def unfiltered(self):
+        """Return an unfiltered version of a repo"""
+        return self._unfilteredrepo
+
+    def filtered(self, name):
+        """Return a filtered version of a repository"""
+        if name == self.filtername:
+            return self
+        return self.unfiltered().filtered(name)
+
+    # everything access are forwarded to the proxied repo
+    def __getattr__(self, attr):
+        return getattr(self._unfilteredrepo, attr)
+
+    def __setattr__(self, attr, value):
+        return setattr(self._unfilteredrepo, attr, value)
+
+    def __delattr__(self, attr):
+        return delattr(self._unfilteredrepo, attr)
+
+    # The `requirement` attribut is initialiazed during __init__. But
+    # __getattr__ won't be called as it also exists on the class. We need
+    # explicit forwarding to main repo here
+    @property
+    def requirements(self):
+        return self._unfilteredrepo.requirements
+
--- a/mercurial/revlog.py	Mon Jan 14 23:14:45 2013 +0900
+++ b/mercurial/revlog.py	Sat Jan 19 17:24:33 2013 -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):
@@ -338,33 +341,14 @@
         return len(t)
     size = rawsize
 
-    def ancestors(self, revs, stoprev=0):
+    def ancestors(self, revs, stoprev=0, inclusive=False):
         """Generate the ancestors of 'revs' in reverse topological order.
         Does not generate revs lower than stoprev.
 
-        Yield a sequence of revision numbers starting with the parents
-        of each revision in revs, i.e., each revision is *not* considered
-        an ancestor of itself.  Results are in breadth-first order:
-        parents of each rev in revs, then parents of those, etc.  Result
-        does not include the null revision."""
-        visit = util.deque(revs)
-        seen = set([nullrev])
-        while visit:
-            for parent in self.parentrevs(visit.popleft()):
-                if parent < stoprev:
-                    continue
-                if parent not in seen:
-                    visit.append(parent)
-                    seen.add(parent)
-                    yield parent
+        See the documentation for ancestor.lazyancestors for more details."""
 
-    def incancestors(self, revs, stoprev=0):
-        """Identical to ancestors() except it also generates the
-        revisions, 'revs'"""
-        for rev in revs:
-            yield rev
-        for rev in self.ancestors(revs, stoprev):
-            yield rev
+        return ancestor.lazyancestors(self, revs, stoprev=stoprev,
+                                      inclusive=inclusive)
 
     def descendants(self, revs):
         """Generate the descendants of 'revs' in revision order.
@@ -429,6 +413,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 +451,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	Mon Jan 14 23:14:45 2013 +0900
+++ b/mercurial/revset.py	Sat Jan 19 17:24:33 2013 -0600
@@ -13,6 +13,7 @@
 from i18n import _
 import encoding
 import obsolete as obsmod
+import repoview
 
 def _revancestors(repo, revs, followfirst):
     """Like revlog.ancestors(), but supports followfirst."""
@@ -442,6 +443,18 @@
     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:
+        bundlerevs = repo.changelog.bundlerevs
+    except AttributeError:
+        raise util.Abort(_("no bundle provided - specify with -R"))
+    return [r for r in subset if r in bundlerevs]
+
 def checkstatus(repo, subset, pat, field):
     m = None
     s = []
@@ -475,8 +488,13 @@
 
 def _children(repo, narrow, parentset):
     cs = set()
+    if not parentset:
+        return cs
     pr = repo.changelog.parentrevs
+    minrev = min(parentset)
     for r in narrow:
+        if r <= minrev:
+            continue
         for p in pr(r):
             if p in parentset:
                 cs.add(r)
@@ -628,6 +646,15 @@
 
     return [r for r in subset if r in dests]
 
+def divergent(repo, subset, x):
+    """``divergent()``
+    Final successors of changesets with an alternative set of final successors.
+    """
+    # i18n: "divergent" is a keyword
+    getargs(x, 0, 0, _("divergent takes no arguments"))
+    divergent = obsmod.getrevs(repo, 'divergent')
+    return [r for r in subset if r in divergent]
+
 def draft(repo, subset, x):
     """``draft()``
     Changeset in draft phase."""
@@ -865,7 +892,8 @@
     """
     # i18n: "hidden" is a keyword
     getargs(x, 0, 0, _("hidden takes no arguments"))
-    return [r for r in subset if r in repo.hiddenrevs]
+    hiddenrevs = repoview.filterrevs(repo, 'visible')
+    return [r for r in subset if r in hiddenrevs]
 
 def keyword(repo, subset, x):
     """``keyword(string)``
@@ -1513,6 +1541,7 @@
     "branch": branch,
     "branchpoint": branchpoint,
     "bumped": bumped,
+    "bundle": bundle,
     "children": children,
     "closed": closed,
     "contains": contains,
@@ -1522,6 +1551,7 @@
     "descendants": descendants,
     "_firstdescendants": _firstdescendants,
     "destination": destination,
+    "divergent": divergent,
     "draft": draft,
     "extinct": extinct,
     "extra": extra,
--- a/mercurial/scmutil.py	Mon Jan 14 23:14:45 2013 +0900
+++ b/mercurial/scmutil.py	Sat Jan 19 17:24:33 2013 -0600
@@ -252,9 +252,9 @@
     def _setmustaudit(self, onoff):
         self._audit = onoff
         if onoff:
-            self.auditor = pathauditor(self.base)
+            self.audit = pathauditor(self.base)
         else:
-            self.auditor = util.always
+            self.audit = util.always
 
     mustaudit = property(_getmustaudit, _setmustaudit)
 
@@ -276,51 +276,52 @@
             r = util.checkosfilename(path)
             if r:
                 raise util.Abort("%s: %r" % (r, path))
-        self.auditor(path)
+        self.audit(path)
         f = self.join(path)
 
         if not text and "b" not in mode:
             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)
         return fp
 
     def symlink(self, src, dst):
-        self.auditor(dst)
+        self.audit(dst)
         linkname = self.join(dst)
         try:
             os.unlink(linkname)
@@ -340,9 +341,6 @@
         else:
             self.write(dst, src)
 
-    def audit(self, path):
-        self.auditor(path)
-
     def join(self, path):
         if path:
             return os.path.join(self.base, path)
@@ -381,6 +379,18 @@
 
 filteropener = filtervfs
 
+class readonlyvfs(abstractvfs, auditvfs):
+    '''Wrapper vfs preventing any writing.'''
+
+    def __init__(self, vfs):
+        auditvfs.__init__(self, vfs)
+
+    def __call__(self, path, mode='r', *args, **kw):
+        if mode not in ('r', 'rb'):
+            raise util.Abort('this vfs is read only')
+        return self.vfs(path, mode, *args, **kw)
+
+
 def canonpath(root, cwd, myname, auditor=None):
     '''return the canonical path of myname, given cwd and root'''
     if util.endswithsep(root):
@@ -425,7 +435,7 @@
                 break
             name = dirname
 
-        raise util.Abort('%s not under root' % myname)
+        raise util.Abort(_("%s not under root '%s'") % (myname, root))
 
 def walkrepos(path, followsym=False, seen_dirs=None, recurse=False):
     '''yield every hg repository under path, always recursively.
@@ -637,13 +647,13 @@
                 start, end = spec.split(_revrangesep, 1)
                 start = revfix(repo, start, 0)
                 end = revfix(repo, end, len(repo) - 1)
-                step = start > end and -1 or 1
+                rangeiter = repo.changelog.revs(start, end)
                 if not seen and not l:
                     # by far the most common case: revs = ["-1:0"]
-                    l = range(start, end + step, step)
+                    l = list(rangeiter)
                     # defer syncing seen until next iteration
                     continue
-                newrevs = set(xrange(start, end + step, step))
+                newrevs = set(rangeiter)
                 if seen:
                     newrevs.difference_update(seen)
                     seen.update(newrevs)
@@ -850,15 +860,19 @@
     return requirements
 
 class filecacheentry(object):
-    def __init__(self, path):
+    def __init__(self, path, stat=True):
         self.path = path
-        self.cachestat = filecacheentry.stat(self.path)
+        self.cachestat = None
+        self._cacheable = None
 
-        if self.cachestat:
-            self._cacheable = self.cachestat.cacheable()
-        else:
-            # None means we don't know yet
-            self._cacheable = None
+        if stat:
+            self.cachestat = filecacheentry.stat(self.path)
+
+            if self.cachestat:
+                self._cacheable = self.cachestat.cacheable()
+            else:
+                # None means we don't know yet
+                self._cacheable = None
 
     def refresh(self):
         if self.cacheable():
@@ -933,6 +947,7 @@
     def __get__(self, obj, type=None):
         # do we need to check if the file changed?
         if self.name in obj.__dict__:
+            assert self.name in obj._filecache, self.name
             return obj.__dict__[self.name]
 
         entry = obj._filecache.get(self.name)
@@ -954,12 +969,19 @@
         return entry.obj
 
     def __set__(self, obj, value):
-        if self.name in obj._filecache:
-            obj._filecache[self.name].obj = value # update cached copy
+        if self.name not in obj._filecache:
+            # we add an entry for the missing value because X in __dict__
+            # implies X in _filecache
+            ce = filecacheentry(self.join(obj, self.path), False)
+            obj._filecache[self.name] = ce
+        else:
+            ce = obj._filecache[self.name]
+
+        ce.obj = value # update cached copy
         obj.__dict__[self.name] = value # update copy returned by obj.x
 
     def __delete__(self, obj):
         try:
             del obj.__dict__[self.name]
         except KeyError:
-            raise AttributeError, self.name
+            raise AttributeError(self.name)
--- a/mercurial/statichttprepo.py	Mon Jan 14 23:14:45 2013 +0900
+++ b/mercurial/statichttprepo.py	Sat Jan 19 17:24:33 2013 -0600
@@ -134,8 +134,7 @@
         self.changelog = changelog.changelog(self.sopener)
         self._tags = None
         self.nodetagscache = None
-        self._branchcache = None
-        self._branchcachetip = None
+        self._branchcaches = {}
         self.encodepats = None
         self.decodepats = None
 
--- a/mercurial/store.py	Mon Jan 14 23:14:45 2013 +0900
+++ b/mercurial/store.py	Sat Jan 19 17:24:33 2013 -0600
@@ -76,7 +76,7 @@
     cmap = dict([(chr(x), chr(x)) for x in xrange(127)])
     for x in (range(32) + range(126, 256) + winreserved):
         cmap[chr(x)] = "~%02x" % x
-    for x in range(ord("A"), ord("Z")+1) + [ord(e)]:
+    for x in range(ord("A"), ord("Z") + 1) + [ord(e)]:
         cmap[chr(x)] = e + chr(x).lower()
     dmap = {}
     for k, v in cmap.iteritems():
@@ -128,11 +128,11 @@
     cmap = dict([(chr(x), chr(x)) for x in xrange(127)])
     for x in (range(32) + range(126, 256) + winreserved):
         cmap[chr(x)] = "~%02x" % x
-    for x in range(ord("A"), ord("Z")+1):
+    for x in range(ord("A"), ord("Z") + 1):
         cmap[chr(x)] = chr(x).lower()
     return lambda s: "".join([cmap[c] for c in s])
 
-lowerencode = _buildlowerencodefun()
+lowerencode = getattr(parsers, 'lowerencode', None) or _buildlowerencodefun()
 
 # Windows reserved names: con, prn, aux, nul, com1..com9, lpt1..lpt9
 _winres3 = ('aux', 'con', 'prn', 'nul') # length 3
@@ -255,22 +255,17 @@
     return res
 
 def _pathencode(path):
+    de = encodedir(path)
     if len(path) > _maxstorepathlen:
-        return None
-    ef = _encodefname(encodedir(path)).split('/')
+        return _hashencode(de, True)
+    ef = _encodefname(de).split('/')
     res = '/'.join(_auxencode(ef, True))
     if len(res) > _maxstorepathlen:
-        return None
+        return _hashencode(de, True)
     return res
 
 _pathencode = getattr(parsers, 'pathencode', _pathencode)
 
-def _dothybridencode(f):
-    ef = _pathencode(f)
-    if ef is None:
-        return _hashencode(encodedir(f), True)
-    return ef
-
 def _plainhybridencode(f):
     return _hybridencode(f, False)
 
@@ -456,7 +451,7 @@
 class fncachestore(basicstore):
     def __init__(self, path, vfstype, dotencode):
         if dotencode:
-            encode = _dothybridencode
+            encode = _pathencode
         else:
             encode = _plainhybridencode
         self.encode = encode
--- a/mercurial/subrepo.py	Mon Jan 14 23:14:45 2013 +0900
+++ b/mercurial/subrepo.py	Sat Jan 19 17:24:33 2013 -0600
@@ -14,6 +14,27 @@
 
 nullstate = ('', '', 'empty')
 
+class SubrepoAbort(error.Abort):
+    """Exception class used to avoid handling a subrepo error more than once"""
+    def __init__(self, *args, **kw):
+        error.Abort.__init__(self, *args, **kw)
+        self.subrepo = kw.get('subrepo')
+
+def annotatesubrepoerror(func):
+    def decoratedmethod(self, *args, **kargs):
+        try:
+            res = func(self, *args, **kargs)
+        except SubrepoAbort, ex:
+            # This exception has already been handled
+            raise ex
+        except error.Abort, ex:
+            subrepo = subrelpath(self)
+            errormsg = str(ex) + ' ' + _('(in subrepo %s)') % subrepo
+            # avoid handling this exception by raising a SubrepoAbort exception
+            raise SubrepoAbort(errormsg, hint=ex.hint, subrepo=subrepo)
+        return res
+    return decoratedmethod
+
 def state(ctx, ui):
     """return a state dict, mapping subrepo paths configured in .hgsub
     to tuple: (source from .hgsub, revision from .hgsubstate, kind
@@ -126,7 +147,7 @@
             r = "%s:%s:%s" % r
         repo.ui.debug("  subrepo %s: %s %s\n" % (s, msg, r))
 
-    for s, l in s1.items():
+    for s, l in sorted(s1.iteritems()):
         a = sa.get(s, nullstate)
         ld = l # local state with possible dirty flag for compares
         if wctx.sub(s).dirty():
@@ -244,8 +265,7 @@
         if repo.ui.config('paths', 'default'):
             return repo.ui.config('paths', 'default')
     if abort:
-        raise util.Abort(_("default path for subrepository %s not found") %
-            reporelpath(repo))
+        raise util.Abort(_("default path for subrepository not found"))
 
 def itersubrepos(ctx1, ctx2):
     """find subrepos in ctx1 or ctx2"""
@@ -402,6 +422,7 @@
                 self._repo.ui.setconfig(s, k, v)
         self._initrepo(r, state[0], create)
 
+    @annotatesubrepoerror
     def _initrepo(self, parentrepo, source, create):
         self._repo._subparent = parentrepo
         self._repo._subsource = source
@@ -422,10 +443,12 @@
                 addpathconfig('default-push', defpushpath)
             fp.close()
 
+    @annotatesubrepoerror
     def add(self, ui, match, dryrun, listsubrepos, prefix, explicitonly):
         return cmdutil.add(ui, self._repo, match, dryrun, listsubrepos,
                            os.path.join(prefix, self._path), explicitonly)
 
+    @annotatesubrepoerror
     def status(self, rev2, **opts):
         try:
             rev1 = self._state[1]
@@ -437,6 +460,7 @@
                                % (inst, subrelpath(self)))
             return [], [], [], [], [], [], []
 
+    @annotatesubrepoerror
     def diff(self, ui, diffopts, node2, match, prefix, **opts):
         try:
             node1 = node.bin(self._state[1])
@@ -446,12 +470,13 @@
                 node2 = node.bin(node2)
             cmdutil.diffordiffstat(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')
                                % (inst, subrelpath(self)))
 
+    @annotatesubrepoerror
     def archive(self, ui, archiver, prefix, match=None):
         self._get(self._state + ('hg',))
         abstractsubrepo.archive(self, ui, archiver, prefix, match)
@@ -463,6 +488,7 @@
             submatch = matchmod.narrowmatcher(subpath, match)
             s.archive(ui, archiver, os.path.join(prefix, self._path), submatch)
 
+    @annotatesubrepoerror
     def dirty(self, ignoreupdate=False):
         r = self._state[1]
         if r == '' and not ignoreupdate: # no state recorded
@@ -479,6 +505,7 @@
     def checknested(self, path):
         return self._repo._checknested(self._repo.wjoin(path))
 
+    @annotatesubrepoerror
     def commit(self, text, user, date):
         # don't bother committing in the subrepo if it's only been
         # updated
@@ -490,6 +517,7 @@
             return self._repo['.'].hex() # different version checked out
         return node.hex(n)
 
+    @annotatesubrepoerror
     def remove(self):
         # we can't fully delete the repository as it may contain
         # local-only history
@@ -519,12 +547,14 @@
                 bookmarks.updatefromremote(self._repo.ui, self._repo, other,
                                            srcurl)
 
+    @annotatesubrepoerror
     def get(self, state, overwrite=False):
         self._get(state)
         source, revision, kind = state
         self._repo.ui.debug("getting subrepo %s\n" % self._path)
         hg.updaterepo(self._repo, revision, overwrite)
 
+    @annotatesubrepoerror
     def merge(self, state):
         self._get(state)
         cur = self._repo['.']
@@ -551,6 +581,7 @@
         else:
             mergefunc()
 
+    @annotatesubrepoerror
     def push(self, opts):
         force = opts.get('force')
         newbranch = opts.get('new_branch')
@@ -569,12 +600,15 @@
         other = hg.peer(self._repo, {'ssh': ssh}, dsturl)
         return self._repo.push(other, force, newbranch=newbranch)
 
+    @annotatesubrepoerror
     def outgoing(self, ui, dest, opts):
         return hg.outgoing(ui, self._repo, _abssource(self._repo, True), opts)
 
+    @annotatesubrepoerror
     def incoming(self, ui, source, opts):
         return hg.incoming(ui, self._repo, _abssource(self._repo, False), opts)
 
+    @annotatesubrepoerror
     def files(self):
         rev = self._state[1]
         ctx = self._repo[rev]
@@ -593,10 +627,12 @@
         ctx = self._repo[None]
         return ctx.walk(match)
 
+    @annotatesubrepoerror
     def forget(self, ui, match, prefix):
         return cmdutil.forget(ui, self._repo, match,
                               os.path.join(prefix, self._path), True)
 
+    @annotatesubrepoerror
     def revert(self, ui, substate, *pats, **opts):
         # reverting a subrepo is a 2 step process:
         # 1. if the no_backup is not set, revert all modified
@@ -751,6 +787,7 @@
                 pass
         return rev
 
+    @annotatesubrepoerror
     def commit(self, text, user, date):
         # user and date are out of our hands since svn is centralized
         changed, extchanged, missing = self._wcchanged()
@@ -778,6 +815,7 @@
         self._ui.status(self._svncommand(['update', '-r', newrev])[0])
         return newrev
 
+    @annotatesubrepoerror
     def remove(self):
         if self.dirty():
             self._ui.warn(_('not removing repo %s because '
@@ -802,6 +840,7 @@
         except OSError:
             pass
 
+    @annotatesubrepoerror
     def get(self, state, overwrite=False):
         if overwrite:
             self._svncommand(['revert', '--recursive'])
@@ -822,6 +861,7 @@
             raise util.Abort((status or err).splitlines()[-1])
         self._ui.status(status)
 
+    @annotatesubrepoerror
     def merge(self, state):
         old = self._state[1]
         new = state[1]
@@ -835,6 +875,7 @@
         # push is a no-op for SVN
         return True
 
+    @annotatesubrepoerror
     def files(self):
         output = self._svncommand(['list', '--recursive', '--xml'])[0]
         doc = xml.dom.minidom.parseString(output)
@@ -1021,6 +1062,7 @@
             raise util.Abort(_("revision %s does not exist in subrepo %s\n") %
                                (revision, self._relpath))
 
+    @annotatesubrepoerror
     def dirty(self, ignoreupdate=False):
         if self._gitmissing():
             return self._state[1] != ''
@@ -1037,6 +1079,7 @@
     def basestate(self):
         return self._gitstate()
 
+    @annotatesubrepoerror
     def get(self, state, overwrite=False):
         source, revision, kind = state
         if not revision:
@@ -1120,6 +1163,7 @@
             # a real merge would be required, just checkout the revision
             rawcheckout()
 
+    @annotatesubrepoerror
     def commit(self, text, user, date):
         if self._gitmissing():
             raise util.Abort(_("subrepo %s is missing") % self._relpath)
@@ -1137,6 +1181,7 @@
         # circumstances
         return self._gitstate()
 
+    @annotatesubrepoerror
     def merge(self, state):
         source, revision, kind = state
         self._fetch(source, revision)
@@ -1159,6 +1204,7 @@
         else:
             mergefunc()
 
+    @annotatesubrepoerror
     def push(self, opts):
         force = opts.get('force')
 
@@ -1198,6 +1244,7 @@
                           (self._relpath, self._state[1]))
             return False
 
+    @annotatesubrepoerror
     def remove(self):
         if self._gitmissing():
             return
@@ -1247,6 +1294,7 @@
         ui.progress(_('archiving (%s)') % relpath, None)
 
 
+    @annotatesubrepoerror
     def status(self, rev2, **opts):
         rev1 = self._state[1]
         if self._gitmissing() or not rev1:
--- a/mercurial/templater.py	Mon Jan 14 23:14:45 2013 +0900
+++ b/mercurial/templater.py	Sat Jan 19 17:24:33 2013 -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):
@@ -179,6 +184,7 @@
     for i in d:
         if isinstance(i, dict):
             lm.update(i)
+            lm['originalnode'] = mapping.get('node')
             yield runtemplate(context, lm, ctmpl)
         else:
             # v is not an iterable of dicts, this happen when 'key'
@@ -259,6 +265,15 @@
         t = stringify(args[3][0](context, mapping, args[3][1]))
         yield runtemplate(context, mapping, compiletemplate(t, context))
 
+def label(context, mapping, args):
+    if len(args) != 2:
+        # i18n: "label" is a keyword
+        raise error.ParseError(_("label expects two arguments"))
+
+    # ignore args[0] (the label string) since this is supposed to be a a no-op
+    t = stringify(args[1][0](context, mapping, args[1][1]))
+    yield runtemplate(context, mapping, compiletemplate(t, context))
+
 methods = {
     "string": lambda e, c: (runstring, e[1]),
     "symbol": lambda e, c: (runsymbol, e[1]),
@@ -274,6 +289,7 @@
     "ifeq": ifeq,
     "join": join,
     "sub": sub,
+    "label": label,
 }
 
 # template engine
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/mercurial/templates/atom/branchentry.tmpl	Sat Jan 19 17:24:33 2013 -0600
@@ -0,0 +1,8 @@
+ <entry>
+  <title>{branch|escape}</title>
+  <link rel="alternate" href="{urlbase}{url}rev/{node|short}"/>
+  <id>{urlbase}{url}#branch-{node}</id>
+  <updated>{date|rfc3339date}</updated>
+  <published>{date|rfc3339date}</published>
+  <content type="text"><![CDATA[{branch|strip|escape|addbreaks}]]></content>
+ </entry>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/mercurial/templates/atom/branches.tmpl	Sat Jan 19 17:24:33 2013 -0600
@@ -0,0 +1,11 @@
+{header}
+ <id>{urlbase}{url}</id>
+ <link rel="self" href="{urlbase}{url}atom-tags"/>
+ <link rel="alternate" href="{urlbase}{url}tags"/>
+ <title>{repo|escape}: branches</title>
+ <summary>{repo|escape} branch history</summary>
+ <author><name>Mercurial SCM</name></author>
+ {latestentry%feedupdated}
+
+ {entries%branchentry}
+</feed>
--- a/mercurial/templates/atom/map	Mon Jan 14 23:14:45 2013 +0900
+++ b/mercurial/templates/atom/map	Sat Jan 19 17:24:33 2013 -0600
@@ -10,4 +10,6 @@
 tagentry = tagentry.tmpl
 bookmarks = bookmarks.tmpl
 bookmarkentry = bookmarkentry.tmpl
+branches = branches.tmpl
+branchentry = branchentry.tmpl
 error = error.tmpl
--- a/mercurial/templates/coal/map	Mon Jan 14 23:14:45 2013 +0900
+++ b/mercurial/templates/coal/map	Sat Jan 19 17:24:33 2013 -0600
@@ -223,3 +223,4 @@
 error = ../paper/error.tmpl
 urlparameter = '{separator}{name}={value|urlescape}'
 hiddenformentry = '<input type="hidden" name="{name}" value="{value|escape}" />'
+breadcrumb = '&gt; <a href="{url}">{name}</a> '
--- a/mercurial/templates/gitweb/bookmarks.tmpl	Mon Jan 14 23:14:45 2013 +0900
+++ b/mercurial/templates/gitweb/bookmarks.tmpl	Sat Jan 19 17:24:33 2013 -0600
@@ -8,7 +8,8 @@
 <body>
 
 <div class="page_header">
-<a href="{logourl}" title="Mercurial" style="float: right;">Mercurial</a><a href="{url}summary{sessionvars%urlparameter}">{repo|escape}</a> / bookmarks
+<a href="{logourl}" title="Mercurial" style="float: right;">Mercurial</a>
+<a href="/">Mercurial</a> {pathdef%breadcrumb} / bookmarks
 </div>
 
 <div class="page_nav">
--- a/mercurial/templates/gitweb/branches.tmpl	Mon Jan 14 23:14:45 2013 +0900
+++ b/mercurial/templates/gitweb/branches.tmpl	Sat Jan 19 17:24:33 2013 -0600
@@ -1,14 +1,15 @@
 {header}
 <title>{repo|escape}: Branches</title>
 <link rel="alternate" type="application/atom+xml"
-   href="{url}atom-tags" title="Atom feed for {repo|escape}"/>
+   href="{url}atom-branches" title="Atom feed for {repo|escape}"/>
 <link rel="alternate" type="application/rss+xml"
-   href="{url}rss-tags" title="RSS feed for {repo|escape}"/>
+   href="{url}rss-branches" title="RSS feed for {repo|escape}"/>
 </head>
 <body>
 
 <div class="page_header">
-<a href="{logourl}" title="Mercurial" style="float: right;">Mercurial</a><a href="{url}summary{sessionvars%urlparameter}">{repo|escape}</a> / branches
+<a href="{logourl}" title="Mercurial" style="float: right;">Mercurial</a>
+<a href="/">Mercurial</a> {pathdef%breadcrumb} / branches
 </div>
 
 <div class="page_nav">
--- a/mercurial/templates/gitweb/changelog.tmpl	Mon Jan 14 23:14:45 2013 +0900
+++ b/mercurial/templates/gitweb/changelog.tmpl	Sat Jan 19 17:24:33 2013 -0600
@@ -8,7 +8,8 @@
 <body>
 
 <div class="page_header">
-<a href="{logourl}" title="Mercurial" style="float: right;">Mercurial</a><a href="{url}summary{sessionvars%urlparameter}">{repo|escape}</a> / changelog
+<a href="{logourl}" title="Mercurial" style="float: right;">Mercurial</a>
+<a href="/">Mercurial</a> {pathdef%breadcrumb} / changelog
 </div>
 
 <form action="{url}log">
--- a/mercurial/templates/gitweb/changeset.tmpl	Mon Jan 14 23:14:45 2013 +0900
+++ b/mercurial/templates/gitweb/changeset.tmpl	Sat Jan 19 17:24:33 2013 -0600
@@ -8,7 +8,8 @@
 <body>
 
 <div class="page_header">
-<a href="{logourl}" title="Mercurial" style="float: right;">Mercurial</a><a href="{url}summary{sessionvars%urlparameter}">{repo|escape}</a> / changeset
+<a href="{logourl}" title="Mercurial" style="float: right;">Mercurial</a>
+<a href="/">Mercurial</a> {pathdef%breadcrumb} / changeset
 </div>
 
 <div class="page_nav">
--- a/mercurial/templates/gitweb/error.tmpl	Mon Jan 14 23:14:45 2013 +0900
+++ b/mercurial/templates/gitweb/error.tmpl	Sat Jan 19 17:24:33 2013 -0600
@@ -8,7 +8,8 @@
 <body>
 
 <div class="page_header">
-<a href="{logourl}" title="Mercurial" style="float: right;">Mercurial</a><a href="{url}summary{sessionvars%urlparameter}">{repo|escape}</a> / error
+<a href="{logourl}" title="Mercurial" style="float: right;">Mercurial</a>
+<a href="/">Mercurial</a> {pathdef%breadcrumb} / error
 </div>
 
 <div class="page_nav">
--- a/mercurial/templates/gitweb/fileannotate.tmpl	Mon Jan 14 23:14:45 2013 +0900
+++ b/mercurial/templates/gitweb/fileannotate.tmpl	Sat Jan 19 17:24:33 2013 -0600
@@ -8,7 +8,8 @@
 <body>
 
 <div class="page_header">
-<a href="{logourl}" title="Mercurial" style="float: right;">Mercurial</a><a href="{url}summary{sessionvars%urlparameter}">{repo|escape}</a> / annotate
+<a href="{logourl}" title="Mercurial" style="float: right;">Mercurial</a>
+<a href="/">Mercurial</a> {pathdef%breadcrumb} / annotate
 </div>
 
 <div class="page_nav">
--- a/mercurial/templates/gitweb/filecomparison.tmpl	Mon Jan 14 23:14:45 2013 +0900
+++ b/mercurial/templates/gitweb/filecomparison.tmpl	Sat Jan 19 17:24:33 2013 -0600
@@ -8,7 +8,8 @@
 <body>
 
 <div class="page_header">
-<a href="{logourl}" title="Mercurial" style="float: right;">Mercurial</a><a href="{url}summary{sessionvars%urlparameter}">{repo|escape}</a> / comparison
+<a href="{logourl}" title="Mercurial" style="float: right;">Mercurial</a>
+<a href="/">Mercurial</a> {pathdef%breadcrumb} / comparison
 </div>
 
 <div class="page_nav">
--- a/mercurial/templates/gitweb/filediff.tmpl	Mon Jan 14 23:14:45 2013 +0900
+++ b/mercurial/templates/gitweb/filediff.tmpl	Sat Jan 19 17:24:33 2013 -0600
@@ -8,7 +8,8 @@
 <body>
 
 <div class="page_header">
-<a href="{logourl}" title="Mercurial" style="float: right;">Mercurial</a><a href="{url}summary{sessionvars%urlparameter}">{repo|escape}</a> / diff
+<a href="{logourl}" title="Mercurial" style="float: right;">Mercurial</a>
+<a href="/">Mercurial</a> {pathdef%breadcrumb} / diff
 </div>
 
 <div class="page_nav">
--- a/mercurial/templates/gitweb/filelog.tmpl	Mon Jan 14 23:14:45 2013 +0900
+++ b/mercurial/templates/gitweb/filelog.tmpl	Sat Jan 19 17:24:33 2013 -0600
@@ -8,7 +8,8 @@
 <body>
 
 <div class="page_header">
-<a href="{logourl}" title="Mercurial" style="float: right;">Mercurial</a><a href="{url}summary{sessionvars%urlparameter}">{repo|escape}</a> / file revisions
+<a href="{logourl}" title="Mercurial" style="float: right;">Mercurial</a>
+<a href="/">Mercurial</a> {pathdef%breadcrumb} / file revisions
 </div>
 
 <div class="page_nav">
--- a/mercurial/templates/gitweb/filerevision.tmpl	Mon Jan 14 23:14:45 2013 +0900
+++ b/mercurial/templates/gitweb/filerevision.tmpl	Sat Jan 19 17:24:33 2013 -0600
@@ -8,7 +8,8 @@
 <body>
 
 <div class="page_header">
-<a href="{logourl}" title="Mercurial" style="float: right;">Mercurial</a><a href="{url}summary{sessionvars%urlparameter}">{repo|escape}</a> / file revision
+<a href="{logourl}" title="Mercurial" style="float: right;">Mercurial</a>
+<a href="/">Mercurial</a> {pathdef%breadcrumb} / file revision
 </div>
 
 <div class="page_nav">
--- a/mercurial/templates/gitweb/graph.tmpl	Mon Jan 14 23:14:45 2013 +0900
+++ b/mercurial/templates/gitweb/graph.tmpl	Sat Jan 19 17:24:33 2013 -0600
@@ -9,7 +9,8 @@
 <body>
 
 <div class="page_header">
-<a href="{logourl}" title="Mercurial" style="float: right;">Mercurial</a><a href="{url}summary{sessionvars%urlparameter}">{repo|escape}</a> / graph
+<a href="{logourl}" title="Mercurial" style="float: right;">Mercurial</a>
+<a href="/">Mercurial</a> {pathdef%breadcrumb} / graph
 </div>
 
 <form action="{url}log">
--- a/mercurial/templates/gitweb/help.tmpl	Mon Jan 14 23:14:45 2013 +0900
+++ b/mercurial/templates/gitweb/help.tmpl	Sat Jan 19 17:24:33 2013 -0600
@@ -8,7 +8,8 @@
 <body>
 
 <div class="page_header">
-<a href="{logourl}" title="Mercurial" style="float: right;">Mercurial</a><a href="{url}summary{sessionvars%urlparameter}">{repo|escape}</a> / help
+<a href="{logourl}" title="Mercurial" style="float: right;">Mercurial</a>
+<a href="/">Mercurial</a> {pathdef%breadcrumb} / help
 </div>
 
 <div class="page_nav">
--- a/mercurial/templates/gitweb/helptopics.tmpl	Mon Jan 14 23:14:45 2013 +0900
+++ b/mercurial/templates/gitweb/helptopics.tmpl	Sat Jan 19 17:24:33 2013 -0600
@@ -8,7 +8,8 @@
 <body>
 
 <div class="page_header">
-<a href="{logourl}" title="Mercurial" style="float: right;">Mercurial</a><a href="{url}summary{sessionvars%urlparameter}">{repo|escape}</a> / help
+<a href="{logourl}" title="Mercurial" style="float: right;">Mercurial</a>
+<a href="/">Mercurial</a> {pathdef%breadcrumb} / help
 </div>
 
 <div class="page_nav">
--- a/mercurial/templates/gitweb/index.tmpl	Mon Jan 14 23:14:45 2013 +0900
+++ b/mercurial/templates/gitweb/index.tmpl	Sat Jan 19 17:24:33 2013 -0600
@@ -5,7 +5,7 @@
 
 <div class="page_header">
     <a href="{logourl}" title="Mercurial" style="float: right;">Mercurial</a>
-    Repositories list
+    <a href="/">Mercurial</a> {pathdef%breadcrumb}
 </div>
 
 <table cellspacing="0">
--- a/mercurial/templates/gitweb/manifest.tmpl	Mon Jan 14 23:14:45 2013 +0900
+++ b/mercurial/templates/gitweb/manifest.tmpl	Sat Jan 19 17:24:33 2013 -0600
@@ -8,7 +8,8 @@
 <body>
 
 <div class="page_header">
-<a href="{logourl}" title="Mercurial" style="float: right;">Mercurial</a><a href="{url}summary{sessionvars%urlparameter}">{repo|escape}</a> / files
+<a href="{logourl}" title="Mercurial" style="float: right;">Mercurial</a>
+<a href="/">Mercurial</a> {pathdef%breadcrumb} / files
 </div>
 
 <div class="page_nav">
--- a/mercurial/templates/gitweb/map	Mon Jan 14 23:14:45 2013 +0900
+++ b/mercurial/templates/gitweb/map	Sat Jan 19 17:24:33 2013 -0600
@@ -294,9 +294,15 @@
     <td>{contact|obfuscate}</td>
     <td class="age">{lastchange|rfc822date}</td>
     <td class="indexlinks">{archives%indexarchiveentry}</td>
-    <td><div class="rss_logo"><a href="{url}rss-log">RSS</a> <a href="{url}atom-log">Atom</a></div></td>
+    <td>{if(isdirectory, '',
+            '<div class="rss_logo">
+                <a href="{url}rss-log">RSS</a> <a href="{url}atom-log">Atom</a>
+            </div>'
+            )}
+    </td>
   </tr>\n'
 indexarchiveentry = ' <a href="{url}archive/{node|short}{extension}">{type|escape}</a> '
 index = index.tmpl
 urlparameter = '{separator}{name}={value|urlescape}'
 hiddenformentry = '<input type="hidden" name="{name}" value="{value|escape}" />'
+breadcrumb = '&gt; <a href="{url}">{name}</a> '
--- a/mercurial/templates/gitweb/search.tmpl	Mon Jan 14 23:14:45 2013 +0900
+++ b/mercurial/templates/gitweb/search.tmpl	Sat Jan 19 17:24:33 2013 -0600
@@ -8,7 +8,8 @@
 <body>
 
 <div class="page_header">
-<a href="{logourl}" title="Mercurial" style="float: right;">Mercurial</a><a href="{url}summary{sessionvars%urlparameter}">{repo|escape}</a> / search
+<a href="{logourl}" title="Mercurial" style="float: right;">Mercurial</a>
+<a href="/">Mercurial</a> {pathdef%breadcrumb} / search
 
 <form action="{url}log">
 {sessionvars%hiddenformentry}
--- a/mercurial/templates/gitweb/shortlog.tmpl	Mon Jan 14 23:14:45 2013 +0900
+++ b/mercurial/templates/gitweb/shortlog.tmpl	Sat Jan 19 17:24:33 2013 -0600
@@ -8,7 +8,8 @@
 <body>
 
 <div class="page_header">
-<a href="{logourl}" title="Mercurial" style="float: right;">Mercurial</a><a href="{url}summary{sessionvars%urlparameter}">{repo|escape}</a> / shortlog
+<a href="{logourl}" title="Mercurial" style="float: right;">Mercurial</a>
+<a href="/">Mercurial</a> {pathdef%breadcrumb} / shortlog
 </div>
 
 <form action="{url}log">
--- a/mercurial/templates/gitweb/summary.tmpl	Mon Jan 14 23:14:45 2013 +0900
+++ b/mercurial/templates/gitweb/summary.tmpl	Sat Jan 19 17:24:33 2013 -0600
@@ -8,8 +8,8 @@
 <body>
 
 <div class="page_header">
-<a href="{logourl}" title="Mercurial" style="float: right;">Mercurial</a><a href="{url}summary{sessionvars%urlparameter}">{repo|escape}</a> / summary
-
+<a href="{logourl}" title="Mercurial" style="float: right;">Mercurial</a>
+<a href="/">Mercurial</a> {pathdef%breadcrumb} / summary
 <form action="{url}log">
 {sessionvars%hiddenformentry}
 <div class="search">
--- a/mercurial/templates/gitweb/tags.tmpl	Mon Jan 14 23:14:45 2013 +0900
+++ b/mercurial/templates/gitweb/tags.tmpl	Sat Jan 19 17:24:33 2013 -0600
@@ -8,7 +8,8 @@
 <body>
 
 <div class="page_header">
-<a href="{logourl}" title="Mercurial" style="float: right;">Mercurial</a><a href="{url}summary{sessionvars%urlparameter}">{repo|escape}</a> / tags
+<a href="{logourl}" title="Mercurial" style="float: right;">Mercurial</a>
+<a href="/">Mercurial</a> {pathdef%breadcrumb} / tags
 </div>
 
 <div class="page_nav">
--- a/mercurial/templates/monoblue/bookmarks.tmpl	Mon Jan 14 23:14:45 2013 +0900
+++ b/mercurial/templates/monoblue/bookmarks.tmpl	Sat Jan 19 17:24:33 2013 -0600
@@ -7,7 +7,7 @@
 <body>
 <div id="container">
     <div class="page-header">
-        <h1><a href="{url}summary{sessionvars%urlparameter}">{repo|escape}</a> / bookmarks</h1>
+        <h1 class="breadcrumb"><a href="/">Mercurial</a> {pathdef%breadcrumb} / bookmarks</h1>
 
         <form action="{url}log">
             {sessionvars%hiddenformentry}
--- a/mercurial/templates/monoblue/branches.tmpl	Mon Jan 14 23:14:45 2013 +0900
+++ b/mercurial/templates/monoblue/branches.tmpl	Sat Jan 19 17:24:33 2013 -0600
@@ -1,13 +1,13 @@
 {header}
     <title>{repo|escape}: Branches</title>
-    <link rel="alternate" type="application/atom+xml" href="{url}atom-log" title="Atom feed for {repo|escape}"/>
-    <link rel="alternate" type="application/rss+xml" href="{url}rss-log" title="RSS feed for {repo|escape}"/>
+    <link rel="alternate" type="application/atom+xml" href="{url}atom-branches" title="Atom feed for {repo|escape}"/>
+    <link rel="alternate" type="application/rss+xml" href="{url}rss-branches" title="RSS feed for {repo|escape}"/>
 </head>
 
 <body>
 <div id="container">
     <div class="page-header">
-        <h1><a href="{url}summary{sessionvars%urlparameter}">{repo|escape}</a> / branches</h1>
+        <h1 class="breadcrumb"><a href="/">Mercurial</a> {pathdef%breadcrumb} / branches</h1>
 
         <form action="{url}log">
             {sessionvars%hiddenformentry}
--- a/mercurial/templates/monoblue/changelog.tmpl	Mon Jan 14 23:14:45 2013 +0900
+++ b/mercurial/templates/monoblue/changelog.tmpl	Sat Jan 19 17:24:33 2013 -0600
@@ -7,7 +7,7 @@
 <body>
 <div id="container">
     <div class="page-header">
-        <h1><a href="{url}summary{sessionvars%urlparameter}">{repo|escape}</a> / changelog</h1>
+        <h1 class="breadcrumb"><a href="/">Mercurial</a> {pathdef%breadcrumb} / changelog</h1>
 
         <form action="{url}log">
             {sessionvars%hiddenformentry}
--- a/mercurial/templates/monoblue/changeset.tmpl	Mon Jan 14 23:14:45 2013 +0900
+++ b/mercurial/templates/monoblue/changeset.tmpl	Sat Jan 19 17:24:33 2013 -0600
@@ -7,7 +7,7 @@
 <body>
 <div id="container">
     <div class="page-header">
-        <h1><a href="{url}summary{sessionvars%urlparameter}">{repo|escape}</a> / changeset</h1>
+        <h1 class="breadcrumb"><a href="/">Mercurial</a> {pathdef%breadcrumb} / changeset</h1>
 
         <form action="{url}log">
             {sessionvars%hiddenformentry}
--- a/mercurial/templates/monoblue/error.tmpl	Mon Jan 14 23:14:45 2013 +0900
+++ b/mercurial/templates/monoblue/error.tmpl	Sat Jan 19 17:24:33 2013 -0600
@@ -7,7 +7,7 @@
 <body>
 <div id="container">
     <div class="page-header">
-        <h1><a href="{url}summary{sessionvars%urlparameter}">{repo|escape}</a> / not found: {repo|escape}</h1>
+        <h1 class="breadcrumb"><a href="/">Mercurial</a> {pathdef%breadcrumb} / not found: {repo|escape}</h1>
 
         <form action="{url}log">
             {sessionvars%hiddenformentry}
--- a/mercurial/templates/monoblue/fileannotate.tmpl	Mon Jan 14 23:14:45 2013 +0900
+++ b/mercurial/templates/monoblue/fileannotate.tmpl	Sat Jan 19 17:24:33 2013 -0600
@@ -7,7 +7,7 @@
 <body>
 <div id="container">
     <div class="page-header">
-        <h1><a href="{url}summary{sessionvars%urlparameter}">{repo|escape}</a> / annotate</h1>
+        <h1 class="breadcrumb"><a href="/">Mercurial</a> {pathdef%breadcrumb} / annotate</h1>
 
         <form action="{url}log">
             {sessionvars%hiddenformentry}
--- a/mercurial/templates/monoblue/filecomparison.tmpl	Mon Jan 14 23:14:45 2013 +0900
+++ b/mercurial/templates/monoblue/filecomparison.tmpl	Sat Jan 19 17:24:33 2013 -0600
@@ -7,7 +7,7 @@
 <body>
 <div id="container">
     <div class="page-header">
-        <h1><a href="{url}summary{sessionvars%urlparameter}">{repo|escape}</a> / file comparison</h1>
+        <h1 class="breadcrumb"><a href="/">Mercurial</a> {pathdef%breadcrumb} / file comparison</h1>
 
         <form action="{url}log">
             {sessionvars%hiddenformentry}
--- a/mercurial/templates/monoblue/filediff.tmpl	Mon Jan 14 23:14:45 2013 +0900
+++ b/mercurial/templates/monoblue/filediff.tmpl	Sat Jan 19 17:24:33 2013 -0600
@@ -7,7 +7,7 @@
 <body>
 <div id="container">
     <div class="page-header">
-        <h1><a href="{url}summary{sessionvars%urlparameter}">{repo|escape}</a> / file diff</h1>
+        <h1 class="breadcrumb"><a href="/">Mercurial</a> {pathdef%breadcrumb} / file diff</h1>
 
         <form action="{url}log">
             {sessionvars%hiddenformentry}
--- a/mercurial/templates/monoblue/filelog.tmpl	Mon Jan 14 23:14:45 2013 +0900
+++ b/mercurial/templates/monoblue/filelog.tmpl	Sat Jan 19 17:24:33 2013 -0600
@@ -7,7 +7,7 @@
 <body>
 <div id="container">
     <div class="page-header">
-        <h1><a href="{url}summary{sessionvars%urlparameter}">{repo|escape}</a> / file revisions</h1>
+        <h1 class="breadcrumb"><a href="/">Mercurial</a> {pathdef%breadcrumb} / file revisions</h1>
 
         <form action="{url}log">
             {sessionvars%hiddenformentry}
--- a/mercurial/templates/monoblue/filerevision.tmpl	Mon Jan 14 23:14:45 2013 +0900
+++ b/mercurial/templates/monoblue/filerevision.tmpl	Sat Jan 19 17:24:33 2013 -0600
@@ -7,7 +7,7 @@
 <body>
 <div id="container">
     <div class="page-header">
-        <h1><a href="{url}summary{sessionvars%urlparameter}">{repo|escape}</a> / file revision</h1>
+        <h1 class="breadcrumb"><a href="/">Mercurial</a> {pathdef%breadcrumb} / file revision</h1>
 
         <form action="{url}log">
             {sessionvars%hiddenformentry}
--- a/mercurial/templates/monoblue/graph.tmpl	Mon Jan 14 23:14:45 2013 +0900
+++ b/mercurial/templates/monoblue/graph.tmpl	Sat Jan 19 17:24:33 2013 -0600
@@ -8,7 +8,7 @@
 <body>
 <div id="container">
     <div class="page-header">
-        <h1><a href="{url}summary{sessionvars%urlparameter}">{repo|escape}</a> / graph</h1>
+        <h1 class="breadcrumb"><a href="/">Mercurial</a> {pathdef%breadcrumb} / graph</h1>
 
         <form action="{url}log">
             {sessionvars%hiddenformentry}
--- a/mercurial/templates/monoblue/help.tmpl	Mon Jan 14 23:14:45 2013 +0900
+++ b/mercurial/templates/monoblue/help.tmpl	Sat Jan 19 17:24:33 2013 -0600
@@ -7,7 +7,7 @@
 <body>
 <div id="container">
     <div class="page-header">
-        <h1><a href="{url}summary{sessionvars%urlparameter}">{repo|escape}</a> / help</h1>
+        <h1 class="breadcrumb"><a href="/">Mercurial</a> {pathdef%breadcrumb} / help</h1>
 
         <form action="{url}log">
             {sessionvars%hiddenformentry}
--- a/mercurial/templates/monoblue/helptopics.tmpl	Mon Jan 14 23:14:45 2013 +0900
+++ b/mercurial/templates/monoblue/helptopics.tmpl	Sat Jan 19 17:24:33 2013 -0600
@@ -7,7 +7,7 @@
 <body>
 <div id="container">
     <div class="page-header">
-        <h1><a href="{url}summary{sessionvars%urlparameter}">{repo|escape}</a> / help</h1>
+        <h1 class="breadcrumb"><a href="/">Mercurial</a> {pathdef%breadcrumb} / help</h1>
 
         <form action="{url}log">
             {sessionvars%hiddenformentry}
--- a/mercurial/templates/monoblue/index.tmpl	Mon Jan 14 23:14:45 2013 +0900
+++ b/mercurial/templates/monoblue/index.tmpl	Sat Jan 19 17:24:33 2013 -0600
@@ -5,7 +5,7 @@
 <body>
 <div id="container">
     <div class="page-header">
-        <h1>Mercurial Repositories</h1>
+        <h1 class="breadcrumb"><a href="/">Mercurial</a> {pathdef%breadcrumb}</h1>
         <ul class="page-nav">
         </ul>
     </div>
--- a/mercurial/templates/monoblue/manifest.tmpl	Mon Jan 14 23:14:45 2013 +0900
+++ b/mercurial/templates/monoblue/manifest.tmpl	Sat Jan 19 17:24:33 2013 -0600
@@ -7,7 +7,7 @@
 <body>
 <div id="container">
     <div class="page-header">
-        <h1><a href="{url}summary{sessionvars%urlparameter}">{repo|escape}</a> / files</h1>
+        <h1 class="breadcrumb"><a href="/">Mercurial</a> {pathdef%breadcrumb} / files</h1>
 
         <form action="{url}log">
             {sessionvars%hiddenformentry}
--- a/mercurial/templates/monoblue/map	Mon Jan 14 23:14:45 2013 +0900
+++ b/mercurial/templates/monoblue/map	Sat Jan 19 17:24:33 2013 -0600
@@ -247,10 +247,11 @@
     <td class="age">{lastchange|rfc822date}</td>
     <td class="indexlinks">{archives%indexarchiveentry}</td>
     <td>
-      <div class="rss_logo">
-        <a href="{url}rss-log">RSS</a>
-        <a href="{url}atom-log">Atom</a>
-      </div>
+        {if(isdirectory, '',
+            '<div class="rss_logo">
+                <a href="{url}rss-log">RSS</a> <a href="{url}atom-log">Atom</a>
+            </div>'
+            )}
     </td>
   </tr>\n'
 indexarchiveentry = '<a href="{url}archive/{node|short}{extension}">{type|escape}</a> '
@@ -258,3 +259,4 @@
 urlparameter = '{separator}{name}={value|urlescape}'
 hiddenformentry = '<input type="hidden" name="{name}" value="{value|escape}" />'
 graph = graph.tmpl
+breadcrumb = '&gt; <a href="{url}">{name}</a> '
--- a/mercurial/templates/monoblue/notfound.tmpl	Mon Jan 14 23:14:45 2013 +0900
+++ b/mercurial/templates/monoblue/notfound.tmpl	Sat Jan 19 17:24:33 2013 -0600
@@ -7,7 +7,7 @@
 <body>
 <div id="container">
     <div class="page-header">
-        <h1><a href="{url}summary{sessionvars%urlparameter}">{repo|escape}</a> / not found: {repo|escape}</h1>
+        <h1 class="breadcrumb"><a href="/">Mercurial</a> {pathdef%breadcrumb} / not found: {repo|escape}</h1>
 
         <form action="{url}log">
             {sessionvars%hiddenformentry}
--- a/mercurial/templates/monoblue/search.tmpl	Mon Jan 14 23:14:45 2013 +0900
+++ b/mercurial/templates/monoblue/search.tmpl	Sat Jan 19 17:24:33 2013 -0600
@@ -7,7 +7,7 @@
 <body>
 <div id="container">
     <div class="page-header">
-        <h1><a href="{url}summary{sessionvars%urlparameter}">{repo|escape}</a> / search</h1>
+        <h1 class="breadcrumb"><a href="/">Mercurial</a> {pathdef%breadcrumb} / search</h1>
 
         <form action="{url}log">
             {sessionvars%hiddenformentry}
--- a/mercurial/templates/monoblue/shortlog.tmpl	Mon Jan 14 23:14:45 2013 +0900
+++ b/mercurial/templates/monoblue/shortlog.tmpl	Sat Jan 19 17:24:33 2013 -0600
@@ -7,7 +7,7 @@
 <body>
 <div id="container">
     <div class="page-header">
-        <h1><a href="{url}summary{sessionvars%urlparameter}">{repo|escape}</a> / shortlog</h1>
+        <h1 class="breadcrumb"><a href="/">Mercurial</a> {pathdef%breadcrumb} / shortlog</h1>
 
         <form action="{url}log">
             {sessionvars%hiddenformentry}
--- a/mercurial/templates/monoblue/summary.tmpl	Mon Jan 14 23:14:45 2013 +0900
+++ b/mercurial/templates/monoblue/summary.tmpl	Sat Jan 19 17:24:33 2013 -0600
@@ -7,7 +7,7 @@
 <body>
 <div id="container">
     <div class="page-header">
-        <h1><a href="{url}summary{sessionvars%urlparameter}">{repo|escape}</a> / summary</h1>
+        <h1 class="breadcrumb"><a href="/">Mercurial</a> {pathdef%breadcrumb} / summary</h1>
 
         <form action="{url}log">
             {sessionvars%hiddenformentry}
--- a/mercurial/templates/monoblue/tags.tmpl	Mon Jan 14 23:14:45 2013 +0900
+++ b/mercurial/templates/monoblue/tags.tmpl	Sat Jan 19 17:24:33 2013 -0600
@@ -7,7 +7,7 @@
 <body>
 <div id="container">
     <div class="page-header">
-        <h1><a href="{url}summary{sessionvars%urlparameter}">{repo|escape}</a> / tags</h1>
+        <h1 class="breadcrumb"><a href="/">Mercurial</a> {pathdef%breadcrumb} / tags</h1>
 
         <form action="{url}log">
             {sessionvars%hiddenformentry}
--- a/mercurial/templates/paper/bookmarks.tmpl	Mon Jan 14 23:14:45 2013 +0900
+++ b/mercurial/templates/paper/bookmarks.tmpl	Sat Jan 19 17:24:33 2013 -0600
@@ -23,10 +23,16 @@
 <ul>
 <li><a href="{url}help{sessionvars%urlparameter}">help</a></li>
 </ul>
+<p>
+<div class="atom-logo">
+<a href="{url}atom-bookmarks" title="subscribe to atom feed">
+<img class="atom-logo" src="{staticurl}feed-icon-14x14.png" alt="atom feed">
+</a>
+</div>
 </div>
 
 <div class="main">
-<h2><a href="{url}{sessionvars%urlparameter}">{repo|escape}</a></h2>
+<h2 class="breadcrumb"><a href="/">Mercurial</a> {pathdef%breadcrumb}</h2>
 <h3>bookmarks</h3>
 
 <form class="search" action="{url}log">
--- a/mercurial/templates/paper/branches.tmpl	Mon Jan 14 23:14:45 2013 +0900
+++ b/mercurial/templates/paper/branches.tmpl	Sat Jan 19 17:24:33 2013 -0600
@@ -1,9 +1,9 @@
 {header}
 <title>{repo|escape}: branches</title>
 <link rel="alternate" type="application/atom+xml"
-   href="{url}atom-tags" title="Atom feed for {repo|escape}: branches" />
+   href="{url}atom-branches" title="Atom feed for {repo|escape}: branches" />
 <link rel="alternate" type="application/rss+xml"
-   href="{url}rss-tags" title="RSS feed for {repo|escape}: branches" />
+   href="{url}rss-branches" title="RSS feed for {repo|escape}: branches" />
 </head>
 <body>
 
@@ -23,10 +23,16 @@
 <ul>
  <li><a href="{url}help{sessionvars%urlparameter}">help</a></li>
 </ul>
+<p>
+<div class="atom-logo">
+<a href="{url}atom-branches" title="subscribe to atom feed">
+<img class="atom-logo" src="{staticurl}feed-icon-14x14.png" alt="atom feed">
+</a>
+</div>
 </div>
 
 <div class="main">
-<h2><a href="{url}{sessionvars%urlparameter}">{repo|escape}</a></h2>
+<h2 class="breadcrumb"><a href="/">Mercurial</a> {pathdef%breadcrumb}</h2>
 <h3>branches</h3>
 
 <form class="search" action="{url}log">
--- a/mercurial/templates/paper/changeset.tmpl	Mon Jan 14 23:14:45 2013 +0900
+++ b/mercurial/templates/paper/changeset.tmpl	Sat Jan 19 17:24:33 2013 -0600
@@ -30,7 +30,7 @@
 
 <div class="main">
 
-<h2><a href="{url}{sessionvars%urlparameter}">{repo|escape}</a></h2>
+<h2 class="breadcrumb"><a href="/">Mercurial</a> {pathdef%breadcrumb}</h2>
 <h3>changeset {rev}:{node|short} {changesetbranch%changelogbranchname} {changesettag} {changesetbookmark}</h3>
 
 <form class="search" action="{url}log">
@@ -74,6 +74,14 @@
     </div>
   </td>
 </tr>
+<tr>
+ <th class="author">change baseline</th>
+ <td class="author">{parent%changesetbaseline}</td>
+</tr>
+<tr>
+ <th class="author">current baseline</th>
+ <td class="author"><a href="{url}rev/{currentbaseline|short}{sessionvars%urlparameter}">{currentbaseline|short}</a></td>
+</tr>
 </table>
 
 <div class="overflow">
--- a/mercurial/templates/paper/error.tmpl	Mon Jan 14 23:14:45 2013 +0900
+++ b/mercurial/templates/paper/error.tmpl	Sat Jan 19 17:24:33 2013 -0600
@@ -23,7 +23,7 @@
 
 <div class="main">
 
-<h2><a href="{url}{sessionvars%urlparameter}">{repo|escape}</a></h2>
+<h2 class="breadcrumb"><a href="/">Mercurial</a> {pathdef%breadcrumb}</h2>
 <h3>error</h3>
 
 <form class="search" action="{url}log">
--- a/mercurial/templates/paper/fileannotate.tmpl	Mon Jan 14 23:14:45 2013 +0900
+++ b/mercurial/templates/paper/fileannotate.tmpl	Sat Jan 19 17:24:33 2013 -0600
@@ -36,7 +36,7 @@
 </div>
 
 <div class="main">
-<h2><a href="{url}{sessionvars%urlparameter}">{repo|escape}</a></h2>
+<h2 class="breadcrumb"><a href="/">Mercurial</a> {pathdef%breadcrumb}</h2>
 <h3>annotate {file|escape} @ {rev}:{node|short}</h3>
 
 <form class="search" action="{url}log">
--- a/mercurial/templates/paper/filecomparison.tmpl	Mon Jan 14 23:14:45 2013 +0900
+++ b/mercurial/templates/paper/filecomparison.tmpl	Sat Jan 19 17:24:33 2013 -0600
@@ -35,7 +35,7 @@
 </div>
 
 <div class="main">
-<h2><a href="{url}{sessionvars%urlparameter}">{repo|escape}</a></h2>
+<h2 class="breadcrumb"><a href="/">Mercurial</a> {pathdef%breadcrumb}</h2>
 <h3>comparison {file|escape} @ {rev}:{node|short}</h3>
 
 <form class="search" action="{url}log">
--- a/mercurial/templates/paper/filediff.tmpl	Mon Jan 14 23:14:45 2013 +0900
+++ b/mercurial/templates/paper/filediff.tmpl	Sat Jan 19 17:24:33 2013 -0600
@@ -35,7 +35,7 @@
 </div>
 
 <div class="main">
-<h2><a href="{url}{sessionvars%urlparameter}">{repo|escape}</a></h2>
+<h2 class="breadcrumb"><a href="/">Mercurial</a> {pathdef%breadcrumb}</h2>
 <h3>diff {file|escape} @ {rev}:{node|short}</h3>
 
 <form class="search" action="{url}log">
--- a/mercurial/templates/paper/filelog.tmpl	Mon Jan 14 23:14:45 2013 +0900
+++ b/mercurial/templates/paper/filelog.tmpl	Sat Jan 19 17:24:33 2013 -0600
@@ -35,10 +35,15 @@
 <ul>
 <li><a href="{url}help{sessionvars%urlparameter}">help</a></li>
 </ul>
+<p>
+<div class="atom-logo">
+<a href="{url}atom-log/{node|short}/{file|urlescape}" title="subscribe to atom feed">
+<img class="atom-logo" src="{staticurl}feed-icon-14x14.png" alt="atom feed"></a>
+</div>
 </div>
 
 <div class="main">
-<h2><a href="{url}{sessionvars%urlparameter}">{repo|escape}</a></h2>
+<h2 class="breadcrumb"><a href="/">Mercurial</a> {pathdef%breadcrumb}</h2>
 <h3>log {file|escape}</h3>
 
 <form class="search" action="{url}log">
--- a/mercurial/templates/paper/filerevision.tmpl	Mon Jan 14 23:14:45 2013 +0900
+++ b/mercurial/templates/paper/filerevision.tmpl	Sat Jan 19 17:24:33 2013 -0600
@@ -34,7 +34,7 @@
 </div>
 
 <div class="main">
-<h2><a href="{url}{sessionvars%urlparameter}">{repo|escape}</a></h2>
+<h2 class="breadcrumb"><a href="/">Mercurial</a> {pathdef%breadcrumb}</h2>
 <h3>view {file|escape} @ {rev}:{node|short}</h3>
 
 <form class="search" action="{url}log">
--- a/mercurial/templates/paper/graph.tmpl	Mon Jan 14 23:14:45 2013 +0900
+++ b/mercurial/templates/paper/graph.tmpl	Sat Jan 19 17:24:33 2013 -0600
@@ -28,10 +28,16 @@
 <ul>
  <li><a href="{url}help{sessionvars%urlparameter}">help</a></li>
 </ul>
+<p>
+<div class="atom-logo">
+<a href="{url}atom-log" title="subscribe to atom feed">
+<img class="atom-logo" src="{staticurl}feed-icon-14x14.png" alt="atom feed">
+</a>
+</div>
 </div>
 
 <div class="main">
-<h2><a href="{url}{sessionvars%urlparameter}">{repo|escape}</a></h2>
+<h2 class="breadcrumb"><a href="/">Mercurial</a> {pathdef%breadcrumb}</h2>
 <h3>graph</h3>
 
 <form class="search" action="{url}log">
--- a/mercurial/templates/paper/help.tmpl	Mon Jan 14 23:14:45 2013 +0900
+++ b/mercurial/templates/paper/help.tmpl	Sat Jan 19 17:24:33 2013 -0600
@@ -22,7 +22,7 @@
 </div>
 
 <div class="main">
-<h2><a href="{url}{sessionvars%urlparameter}">{repo|escape}</a></h2>
+<h2 class="breadcrumb"><a href="/">Mercurial</a> {pathdef%breadcrumb}</h2>
 <h3>Help: {topic}</h3>
 
 <form class="search" action="{url}log">
--- a/mercurial/templates/paper/helptopics.tmpl	Mon Jan 14 23:14:45 2013 +0900
+++ b/mercurial/templates/paper/helptopics.tmpl	Sat Jan 19 17:24:33 2013 -0600
@@ -22,7 +22,7 @@
 </div>
 
 <div class="main">
-<h2><a href="{url}{sessionvars%urlparameter}">{repo|escape}</a></h2>
+<h2 class="breadcrumb"><a href="/">Mercurial</a> {pathdef%breadcrumb}</h2>
 <form class="search" action="{url}log">
 {sessionvars%hiddenformentry}
 <p><input name="rev" id="search1" type="text" size="30" /></p>
--- a/mercurial/templates/paper/index.tmpl	Mon Jan 14 23:14:45 2013 +0900
+++ b/mercurial/templates/paper/index.tmpl	Sat Jan 19 17:24:33 2013 -0600
@@ -9,7 +9,7 @@
 <img src="{staticurl}{logoimg}" width=75 height=90 border=0 alt="mercurial" /></a>
 </div>
 <div class="main">
-<h2>Mercurial Repositories</h2>
+<h2 class="breadcrumb"><a href="/">Mercurial</a> {pathdef%breadcrumb}</h2>
 
 <table class="bigtable">
     <tr>
@@ -18,6 +18,7 @@
         <th><a href="?sort={sort_contact}">Contact</a></th>
         <th><a href="?sort={sort_lastchange}">Last modified</a></th>
         <th>&nbsp;</th>
+        <th>&nbsp;</th>
     </tr>
     {entries%indexentry}
 </table>
--- a/mercurial/templates/paper/manifest.tmpl	Mon Jan 14 23:14:45 2013 +0900
+++ b/mercurial/templates/paper/manifest.tmpl	Sat Jan 19 17:24:33 2013 -0600
@@ -29,7 +29,7 @@
 </div>
 
 <div class="main">
-<h2><a href="{url}{sessionvars%urlparameter}">{repo|escape}</a></h2>
+<h2 class="breadcrumb"><a href="/">Mercurial</a> {pathdef%breadcrumb}</h2>
 <h3>directory {path|escape} @ {rev}:{node|short} {tags%changelogtag}</h3>
 
 <form class="search" action="{url}log">
--- a/mercurial/templates/paper/map	Mon Jan 14 23:14:45 2013 +0900
+++ b/mercurial/templates/paper/map	Sat Jan 19 17:24:33 2013 -0600
@@ -101,6 +101,8 @@
 
 changesetparent = '<a href="{url}rev/{node|short}{sessionvars%urlparameter}">{node|short}</a> '
 
+changesetbaseline = '<a href="{url}rev/{node|short}:{originalnode|short}{sessionvars%urlparameter}">{node|short}</a> '
+
 filerevparent = '<a href="{url}file/{node|short}/{file|urlescape}{sessionvars%urlparameter}">{rename%filerename}{node|short}</a> '
 filerevchild = '<a href="{url}file/{node|short}/{file|urlescape}{sessionvars%urlparameter}">{node|short}</a> '
 
@@ -211,6 +213,13 @@
     <td>{contact|obfuscate}</td>
     <td class="age">{lastchange|rfc822date}</td>
     <td class="indexlinks">{archives%indexarchiveentry}</td>
+    <td>
+        {if(isdirectory, '',
+            '<a href="{url}atom-log" title="subscribe to repository atom feed">
+                <img class="atom-logo" src="{staticurl}feed-icon-14x14.png" alt="subscribe to repository atom feed">
+            </a>'
+            )}
+    </td>
   </tr>\n'
 indexarchiveentry = '<a href="{url}archive/{node|short}{extension|urlescape}">&nbsp;&darr;{type|escape}</a>'
 index = index.tmpl
@@ -222,3 +231,4 @@
 error = error.tmpl
 urlparameter = '{separator}{name}={value|urlescape}'
 hiddenformentry = '<input type="hidden" name="{name}" value="{value|escape}" />'
+breadcrumb = '&gt; <a href="{url}">{name}</a> '
--- a/mercurial/templates/paper/search.tmpl	Mon Jan 14 23:14:45 2013 +0900
+++ b/mercurial/templates/paper/search.tmpl	Sat Jan 19 17:24:33 2013 -0600
@@ -20,7 +20,7 @@
 </div>
 
 <div class="main">
-<h2><a href="{url}{sessionvars%urlparameter}">{repo|escape}</a></h2>
+<h2 class="breadcrumb"><a href="/">Mercurial</a> {pathdef%breadcrumb}</h2>
 <h3>searching for '{query|escape}'</h3>
 
 <form class="search" action="{url}log">
--- a/mercurial/templates/paper/shortlog.tmpl	Mon Jan 14 23:14:45 2013 +0900
+++ b/mercurial/templates/paper/shortlog.tmpl	Sat Jan 19 17:24:33 2013 -0600
@@ -30,10 +30,16 @@
 <ul>
  <li><a href="{url}help{sessionvars%urlparameter}">help</a></li>
 </ul>
+<p>
+<div class="atom-logo">
+<a href="{url}atom-log" title="subscribe to atom feed">
+<img class="atom-logo" src="{staticurl}feed-icon-14x14.png" alt="atom feed">
+</a>
+</div>
 </div>
 
 <div class="main">
-<h2><a href="{url}{sessionvars%urlparameter}">{repo|escape}</a></h2>
+<h2 class="breadcrumb"><a href="/">Mercurial</a> {pathdef%breadcrumb}</h2>
 <h3>log</h3>
 
 <form class="search" action="{url}log">
--- a/mercurial/templates/paper/tags.tmpl	Mon Jan 14 23:14:45 2013 +0900
+++ b/mercurial/templates/paper/tags.tmpl	Sat Jan 19 17:24:33 2013 -0600
@@ -23,10 +23,15 @@
 <ul>
 <li><a href="{url}help{sessionvars%urlparameter}">help</a></li>
 </ul>
+<p>
+<div class="atom-logo">
+<a href="{url}atom-tags" title="subscribe to atom feed">
+<img class="atom-logo" src="{staticurl}feed-icon-14x14.png" alt="atom feed"></a>
+</div>
 </div>
 
 <div class="main">
-<h2><a href="{url}{sessionvars%urlparameter}">{repo|escape}</a></h2>
+<h2 class="breadcrumb"><a href="/">Mercurial</a> {pathdef%breadcrumb}</h2>
 <h3>tags</h3>
 
 <form class="search" action="{url}log">
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/mercurial/templates/rss/branchentry.tmpl	Sat Jan 19 17:24:33 2013 -0600
@@ -0,0 +1,6 @@
+<item>
+    <title>{branch|escape}</title>
+    <link>{urlbase}{url}rev/{node|short}</link>
+    <description><![CDATA[{branch|strip|escape|addbreaks}]]></description>
+    <pubDate>{date|rfc822date}</pubDate>
+</item>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/mercurial/templates/rss/branches.tmpl	Sat Jan 19 17:24:33 2013 -0600
@@ -0,0 +1,6 @@
+{header}
+    <title>{repo|escape}: branches</title>
+    <description>{repo|escape} branch history</description>
+    {entries%branchentry}
+  </channel>
+</rss>
--- a/mercurial/templates/rss/map	Mon Jan 14 23:14:45 2013 +0900
+++ b/mercurial/templates/rss/map	Sat Jan 19 17:24:33 2013 -0600
@@ -9,4 +9,6 @@
 tagentry = tagentry.tmpl
 bookmarks = bookmarks.tmpl
 bookmarkentry = bookmarkentry.tmpl
+branches = branches.tmpl
+branchentry = branchentry.tmpl
 error = error.tmpl
--- a/mercurial/templates/spartan/branches.tmpl	Mon Jan 14 23:14:45 2013 +0900
+++ b/mercurial/templates/spartan/branches.tmpl	Sat Jan 19 17:24:33 2013 -0600
@@ -18,7 +18,7 @@
 <a type="application/atom+xml" href="{url}atom-branches">atom</a>
 </div>
 
-<h2>branches:</h2>
+<h2><a href="/">Mercurial</a> {pathdef%breadcrumb} / branches</h2>
 
 <ul id="tagEntries">
 {entries%branchentry}
--- a/mercurial/templates/spartan/changelog.tmpl	Mon Jan 14 23:14:45 2013 +0900
+++ b/mercurial/templates/spartan/changelog.tmpl	Sat Jan 19 17:24:33 2013 -0600
@@ -19,7 +19,7 @@
 <a type="application/atom+xml" href="{url}atom-log" title="Atom feed for {repo|escape}">atom</a>
 </div>
 
-<h2>changelog for {repo|escape}</h2>
+<h2><a href="/">Mercurial</a> {pathdef%breadcrumb} / changelog</h2>
 
 <form action="{url}log">
 {sessionvars%hiddenformentry}
--- a/mercurial/templates/spartan/changeset.tmpl	Mon Jan 14 23:14:45 2013 +0900
+++ b/mercurial/templates/spartan/changeset.tmpl	Sat Jan 19 17:24:33 2013 -0600
@@ -15,7 +15,7 @@
 <a href="{url}help{sessionvars%urlparameter}">help</a>
 </div>
 
-<h2>changeset: {desc|strip|escape|firstline|nonempty}</h2>
+<h2><a href="/">Mercurial</a> {pathdef%breadcrumb} / changeset: {desc|strip|escape|firstline|nonempty}</h2>
 
 <table id="changesetEntry">
 <tr>
--- a/mercurial/templates/spartan/fileannotate.tmpl	Mon Jan 14 23:14:45 2013 +0900
+++ b/mercurial/templates/spartan/fileannotate.tmpl	Sat Jan 19 17:24:33 2013 -0600
@@ -17,7 +17,7 @@
 <a href="{url}help{sessionvars%urlparameter}">help</a>
 </div>
 
-<h2>Annotate {file|escape}</h2>
+<h2><a href="/">Mercurial</a> {pathdef%breadcrumb} / annotate {file|escape}</h2>
 
 <table>
 <tr>
--- a/mercurial/templates/spartan/filediff.tmpl	Mon Jan 14 23:14:45 2013 +0900
+++ b/mercurial/templates/spartan/filediff.tmpl	Sat Jan 19 17:24:33 2013 -0600
@@ -17,7 +17,7 @@
 <a href="{url}help{sessionvars%urlparameter}">help</a>
 </div>
 
-<h2>{file|escape}</h2>
+<h2><a href="/">Mercurial</a> {pathdef%breadcrumb} / {file|escape}</h2>
 
 <table id="filediffEntry">
 <tr>
--- a/mercurial/templates/spartan/filelog.tmpl	Mon Jan 14 23:14:45 2013 +0900
+++ b/mercurial/templates/spartan/filelog.tmpl	Sat Jan 19 17:24:33 2013 -0600
@@ -20,7 +20,7 @@
 <a type="application/atom+xml" href="{url}atom-log/tip/{file|urlescape}" title="Atom feed for {repo|escape}:{file}">atom</a>
 </div>
 
-<h2>{file|escape} revision history</h2>
+<h2><a href="/">Mercurial</a> {pathdef%breadcrumb} / {file|escape} revision history</h2>
 
 <p>navigate: <small class="navigate">{nav%filenav}</small></p>
 
--- a/mercurial/templates/spartan/filerevision.tmpl	Mon Jan 14 23:14:45 2013 +0900
+++ b/mercurial/templates/spartan/filerevision.tmpl	Sat Jan 19 17:24:33 2013 -0600
@@ -17,7 +17,7 @@
 <a href="{url}help{sessionvars%urlparameter}">help</a>
 </div>
 
-<h2>{file|escape}</h2>
+<h2><a href="/">Mercurial</a> {pathdef%breadcrumb} / {file|escape}</h2>
 
 <table>
 <tr>
--- a/mercurial/templates/spartan/graph.tmpl	Mon Jan 14 23:14:45 2013 +0900
+++ b/mercurial/templates/spartan/graph.tmpl	Sat Jan 19 17:24:33 2013 -0600
@@ -17,7 +17,7 @@
 <a href="{url}help{sessionvars%urlparameter}">help</a>
 </div>
 
-<h2>graph</h2>
+<h2><a href="/">Mercurial</a> {pathdef%breadcrumb} / graph</h2>
 
 <form action="{url}log">
 {sessionvars%hiddenformentry}
--- a/mercurial/templates/spartan/index.tmpl	Mon Jan 14 23:14:45 2013 +0900
+++ b/mercurial/templates/spartan/index.tmpl	Sat Jan 19 17:24:33 2013 -0600
@@ -3,7 +3,7 @@
 </head>
 <body>
 
-<h2>Mercurial Repositories</h2>
+<h2><a href="/">Mercurial</a> {pathdef%breadcrumb}</h2>
 
 <table>
     <tr>
--- a/mercurial/templates/spartan/manifest.tmpl	Mon Jan 14 23:14:45 2013 +0900
+++ b/mercurial/templates/spartan/manifest.tmpl	Sat Jan 19 17:24:33 2013 -0600
@@ -14,7 +14,7 @@
 <a href="{url}help{sessionvars%urlparameter}">help</a>
 </div>
 
-<h2>files for changeset {node|short}: {path|escape}</h2>
+<h2><a href="/">Mercurial</a> {pathdef%breadcrumb} / files for changeset <a href="{url}rev/{node|short}">{node|short}</a>: {path|escape}</h2>
 
 <table cellpadding="0" cellspacing="0">
 <tr class="parity{upparity}">
--- a/mercurial/templates/spartan/map	Mon Jan 14 23:14:45 2013 +0900
+++ b/mercurial/templates/spartan/map	Sat Jan 19 17:24:33 2013 -0600
@@ -181,3 +181,4 @@
 error = error.tmpl
 urlparameter = '{separator}{name}={value|urlescape}'
 hiddenformentry = '<input type="hidden" name="{name}" value="{value|escape}" />'
+breadcrumb = '&gt; <a href="{url}">{name}</a> '
--- a/mercurial/templates/spartan/shortlog.tmpl	Mon Jan 14 23:14:45 2013 +0900
+++ b/mercurial/templates/spartan/shortlog.tmpl	Sat Jan 19 17:24:33 2013 -0600
@@ -19,7 +19,7 @@
 <a type="application/rss+xml" href="{url}atom-log" title="Atom feed for {repo|escape}">atom</a>
 </div>
 
-<h2>shortlog for {repo|escape}</h2>
+<h2><a href="/">Mercurial</a> {pathdef%breadcrumb} / shortlog</h2>
 
 <form action="{url}log">
 {sessionvars%hiddenformentry}
--- a/mercurial/templates/spartan/tags.tmpl	Mon Jan 14 23:14:45 2013 +0900
+++ b/mercurial/templates/spartan/tags.tmpl	Sat Jan 19 17:24:33 2013 -0600
@@ -18,7 +18,7 @@
 <a type="application/atom+xml" href="{url}atom-tags">atom</a>
 </div>
 
-<h2>tags:</h2>
+<h2><a href="/">Mercurial</a> {pathdef%breadcrumb} / tags</h2>
 
 <ul id="tagEntries">
 {entries%tagentry}
Binary file mercurial/templates/static/feed-icon-14x14.png has changed
--- a/mercurial/templates/static/style-coal.css	Mon Jan 14 23:14:45 2013 +0900
+++ b/mercurial/templates/static/style-coal.css	Sat Jan 19 17:24:33 2013 -0600
@@ -323,3 +323,11 @@
 .block {
     border-top: 1px solid #999;
 }
+
+.breadcrumb {
+    color: gray;
+}
+
+.breadcrumb a {
+    color: blue;
+}
--- a/mercurial/templates/static/style-monoblue.css	Mon Jan 14 23:14:45 2013 +0900
+++ b/mercurial/templates/static/style-monoblue.css	Sat Jan 19 17:24:33 2013 -0600
@@ -524,3 +524,7 @@
     border-top: 1px solid #999;
 }
 /** end of comparison **/
+
+.breadcrumb a:hover {
+    text-decoration:underline;
+}
--- a/mercurial/templates/static/style-paper.css	Mon Jan 14 23:14:45 2013 +0900
+++ b/mercurial/templates/static/style-paper.css	Sat Jan 19 17:24:33 2013 -0600
@@ -60,6 +60,12 @@
   border: 0;
 }
 
+.atom-logo img{
+  width: 14px;
+  height: 14px;
+  border: 0;
+}
+
 .menu a { color: black; display: block; }
 
 .search {
@@ -312,3 +318,11 @@
 .block {
     border-top: 1px solid #999;
 }
+
+.breadcrumb {
+    color: gray;
+}
+
+.breadcrumb a {
+    color: blue;
+}
--- a/mercurial/ui.py	Mon Jan 14 23:14:45 2013 +0900
+++ b/mercurial/ui.py	Sat Jan 19 17:24:33 2013 -0600
@@ -613,7 +613,7 @@
         ('&None', 'E&xec', 'Sym&link') Responses are case insensitive.
         If ui is not interactive, the default is returned.
         """
-        resps = [s[s.index('&')+1].lower() for s in choices]
+        resps = [s[s.index('&') + 1].lower() for s in choices]
         while True:
             r = self.prompt(msg, resps[default])
             if r.lower() in resps:
--- a/mercurial/url.py	Mon Jan 14 23:14:45 2013 +0900
+++ b/mercurial/url.py	Sat Jan 19 17:24:33 2013 -0600
@@ -164,7 +164,7 @@
                     if sock is not None:
                         sock.close()
 
-            raise socket.error, msg
+            raise socket.error(msg)
 
 class httpconnection(keepalive.HTTPConnection):
     # must be able to send big bundle as stream.
--- a/mercurial/util.py	Mon Jan 14 23:14:45 2013 +0900
+++ b/mercurial/util.py	Sat Jan 19 17:24:33 2013 -0600
@@ -64,7 +64,7 @@
 spawndetached = platform.spawndetached
 split = platform.split
 sshargs = platform.sshargs
-statfiles = platform.statfiles
+statfiles = getattr(osutil, 'statfiles', platform.statfiles)
 termwidth = platform.termwidth
 testpid = platform.testpid
 umask = platform.umask
@@ -244,9 +244,12 @@
         self.name = func.__name__
     def __get__(self, obj, type=None):
         result = self.func(obj)
-        setattr(obj, self.name, result)
+        self.cachevalue(obj, result)
         return result
 
+    def cachevalue(self, obj, value):
+        setattr(obj, self.name, value)
+
 def pipefilter(s, cmd):
     '''filter string S through command CMD, returning its output'''
     p = subprocess.Popen(cmd, shell=True, close_fds=closefds,
@@ -479,11 +482,9 @@
 
 def copyfile(src, dest):
     "copy a file, preserving mode and atime/mtime"
+    if os.path.lexists(dest):
+        unlink(dest)
     if os.path.islink(src):
-        try:
-            os.unlink(dest)
-        except OSError:
-            pass
         os.symlink(os.readlink(src), dest)
     else:
         try:
--- a/mercurial/verify.py	Mon Jan 14 23:14:45 2013 +0900
+++ b/mercurial/verify.py	Sat Jan 19 17:24:33 2013 -0600
@@ -25,6 +25,7 @@
     return f
 
 def _verify(repo):
+    repo = repo.unfiltered()
     mflinkrevs = {}
     filelinkrevs = {}
     filenodes = {}
--- a/mercurial/win32.py	Mon Jan 14 23:14:45 2013 +0900
+++ b/mercurial/win32.py	Sat Jan 19 17:24:33 2013 -0600
@@ -370,7 +370,7 @@
             if e.errno != errno.EEXIST:
                 raise
     else:
-        raise IOError, (errno.EEXIST, "No usable temporary filename found")
+        raise IOError(errno.EEXIST, "No usable temporary filename found")
 
     try:
         os.unlink(temp)
--- a/mercurial/windows.py	Mon Jan 14 23:14:45 2013 +0900
+++ b/mercurial/windows.py	Sat Jan 19 17:24:33 2013 -0600
@@ -7,7 +7,7 @@
 
 from i18n import _
 import osutil, encoding
-import errno, msvcrt, os, re, sys, _winreg
+import errno, msvcrt, os, re, stat, sys, _winreg
 
 import win32
 executablepath = win32.executablepath
@@ -213,10 +213,15 @@
             return executable
     return findexisting(os.path.expanduser(os.path.expandvars(command)))
 
+_wantedkinds = set([stat.S_IFREG, stat.S_IFLNK])
+
 def statfiles(files):
-    '''Stat each file in files and yield stat or None if file does not exist.
+    '''Stat each file in files. Yield each stat, or None if a file
+    does not exist or has a type we don't care about.
+
     Cluster and cache stat per directory to minimize number of OS stat calls.'''
     dircache = {} # dirname -> filename -> status | None if file does not exist
+    getkind = stat.S_IFMT
     for nf in files:
         nf  = normcase(nf)
         dir, base = os.path.split(nf)
@@ -226,7 +231,8 @@
         if cache is None:
             try:
                 dmap = dict([(normcase(n), s)
-                    for n, k, s in osutil.listdir(dir, True)])
+                             for n, k, s in osutil.listdir(dir, True)
+                             if getkind(s.st_mode) in _wantedkinds])
             except OSError, err:
                 # handle directory not found in Python version prior to 2.5
                 # Python <= 2.4 returns native Windows code 3 in errno
@@ -269,9 +275,13 @@
             break
         head, tail = os.path.split(head)
 
-def unlinkpath(f):
+def unlinkpath(f, ignoremissing=False):
     """unlink and remove the directory if it is empty"""
-    unlink(f)
+    try:
+        unlink(f)
+    except OSError, e:
+        if not (ignoremissing and e.errno == errno.ENOENT):
+            raise
     # try removing directories that might now be empty
     try:
         _removedirs(os.path.dirname(f))
--- a/mercurial/wireproto.py	Mon Jan 14 23:14:45 2013 +0900
+++ b/mercurial/wireproto.py	Sat Jan 19 17:24:33 2013 -0600
@@ -10,7 +10,6 @@
 from node import bin, hex
 import changegroup as changegroupmod
 import peer, error, encoding, util, store
-import discovery, phases
 
 # abstract batching support
 
@@ -346,6 +345,7 @@
         self.message = message
 
 def dispatch(repo, proto, command):
+    repo = repo.filtered("served")
     func, spec = commands[command]
     args = proto.getargs(spec)
     return func(repo, proto, *args)
@@ -362,6 +362,7 @@
     return opts
 
 def batch(repo, proto, cmds, others):
+    repo = repo.filtered("served")
     res = []
     for pair in cmds.split(';'):
         op, args = pair.split(' ', 1)
@@ -399,7 +400,7 @@
     return "".join(r)
 
 def branchmap(repo, proto):
-    branchmap = discovery.visiblebranchmap(repo)
+    branchmap = repo.branchmap()
     heads = []
     for branch, nodes in branchmap.iteritems():
         branchname = urllib.quote(encoding.fromlocal(branch))
@@ -455,7 +456,7 @@
     return streamres(proto.groupchunks(cg))
 
 def heads(repo, proto):
-    h = discovery.visibleheads(repo)
+    h = repo.heads()
     return encodelist(h) + "\n"
 
 def hello(repo, proto):
@@ -478,8 +479,6 @@
     try:
         k = encoding.tolocal(key)
         c = repo[k]
-        if c.phase() == phases.secret:
-            raise error.RepoLookupError(_("unknown revision '%s'") % k)
         r = c.hex()
         success = 1
     except Exception, inst:
@@ -546,8 +545,9 @@
         try:
             repo.ui.debug('scanning\n')
             for name, ename, size in repo.store.walk():
-                entries.append((name, size))
-                total_bytes += size
+                if size:
+                    entries.append((name, size))
+                    total_bytes += size
         finally:
             lock.release()
     except error.LockError:
@@ -593,7 +593,7 @@
     their_heads = decodelist(heads)
 
     def check_heads():
-        heads = discovery.visibleheads(repo)
+        heads = repo.heads()
         heads_hash = util.sha1(''.join(sorted(heads))).digest()
         return (their_heads == ['force'] or their_heads == heads or
                 their_heads == ['hashed', heads_hash])
--- a/setup.py	Mon Jan 14 23:14:45 2013 +0900
+++ b/setup.py	Sat Jan 19 17:24:33 2013 -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	Mon Jan 14 23:14:45 2013 +0900
+++ b/tests/autodiff.py	Sat Jan 19 17:24:33 2013 -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":
Binary file tests/bundles/hgweb+obs.hg has changed
--- a/tests/get-with-headers.py	Mon Jan 14 23:14:45 2013 +0900
+++ b/tests/get-with-headers.py	Sat Jan 19 17:24:33 2013 -0600
@@ -16,6 +16,10 @@
 if '--twice' in sys.argv:
     sys.argv.remove('--twice')
     twice = True
+headeronly = False
+if '--headeronly' in sys.argv:
+    sys.argv.remove('--headeronly')
+    headeronly = True
 
 reasons = {'Not modified': 'Not Modified'} # python 2.4
 
@@ -31,16 +35,19 @@
     conn.request("GET", '/' + path, None, headers)
     response = conn.getresponse()
     print response.status, reasons.get(response.reason, response.reason)
+    if show[:1] == ['-']:
+        show = sorted(h for h, v in response.getheaders()
+                      if h.lower() not in show)
     for h in [h.lower() for h in show]:
         if response.getheader(h, None) is not None:
             print "%s: %s" % (h, response.getheader(h))
+    if not headeronly:
+        print
+        data = response.read()
+        sys.stdout.write(data)
 
-    print
-    data = response.read()
-    sys.stdout.write(data)
-
-    if twice and response.getheader('ETag', None):
-        tag = response.getheader('ETag')
+        if twice and response.getheader('ETag', None):
+            tag = response.getheader('ETag')
 
     return response.status
 
--- a/tests/hghave	Mon Jan 14 23:14:45 2013 +0900
+++ b/tests/hghave	Sat Jan 19 17:24:33 2013 -0600
@@ -59,7 +59,7 @@
 
         if feature not in checks:
             error('skipped: unknown feature: ' + feature)
-            continue
+            sys.exit(2)
 
         check, desc = checks[feature]
         try:
--- a/tests/hghave.py	Mon Jan 14 23:14:45 2013 +0900
+++ b/tests/hghave.py	Sat Jan 19 17:24:33 2013 -0600
@@ -41,6 +41,10 @@
     re = r'Concurrent Versions System.*?server'
     return matchoutput('cvs --version 2>&1', re) and not has_msys()
 
+def has_cvs112():
+    re = r'Concurrent Versions System \(CVS\) 1.12.*?server'
+    return matchoutput('cvs --version 2>&1', re) and not has_msys()
+
 def has_darcs():
     return matchoutput('darcs --version', r'2\.[2-9]', True)
 
@@ -278,6 +282,7 @@
     "bzr114": (has_bzr114, "Canonical's Bazaar client >= 1.14"),
     "cacheable": (has_cacheable_fs, "cacheable filesystem"),
     "cvs": (has_cvs, "cvs client/server"),
+    "cvs112": (has_cvs112, "cvs client/server >= 1.12"),
     "darcs": (has_darcs, "darcs client"),
     "docutils": (has_docutils, "Docutils text processing library"),
     "eol-in-paths": (has_eol_in_paths, "end-of-lines in paths"),
--- a/tests/run-tests.py	Mon Jan 14 23:14:45 2013 +0900
+++ b/tests/run-tests.py	Sat Jan 19 17:24:33 2013 -0600
@@ -55,6 +55,8 @@
 import re
 import threading
 import killdaemons as killmod
+import cPickle as pickle
+import Queue as queue
 
 processlock = threading.Lock()
 
@@ -93,7 +95,8 @@
 if 'java' in sys.platform:
     IMPL_PATH = 'JYTHONPATH'
 
-requiredtools = ["python", "diff", "grep", "unzip", "gunzip", "bunzip2", "sed"]
+requiredtools = [os.path.basename(sys.executable), "diff", "grep", "unzip",
+                 "gunzip", "bunzip2", "sed"]
 
 defaults = {
     'jobs': ('HGTEST_JOBS', 1),
@@ -162,6 +165,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 +180,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 +270,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+')
@@ -317,7 +328,7 @@
     # Before we go any further, check for pre-requisite tools
     # stuff from coreutils (cat, rm, etc) are not tested
     for p in requiredtools:
-        if os.name == 'nt':
+        if os.name == 'nt' and not p.endswith('.exe'):
             p += '.exe'
         found = findprogram(p)
         if found:
@@ -345,25 +356,42 @@
 def usecorrectpython():
     # some tests run python interpreter. they must use same
     # interpreter we use or bad things will happen.
-    exedir, exename = os.path.split(sys.executable)
-    if exename in ('python', 'python.exe'):
-        path = findprogram(exename)
-        if os.path.dirname(path) == exedir:
-            return
+    pyexename = sys.platform == 'win32' and 'python.exe' or 'python'
+    if getattr(os, 'symlink', None):
+        vlog("# Making python executable in test path a symlink to '%s'" %
+             sys.executable)
+        mypython = os.path.join(BINDIR, pyexename)
+        try:
+            if os.readlink(mypython) == sys.executable:
+                return
+            os.unlink(mypython)
+        except OSError, err:
+            if err.errno != errno.ENOENT:
+                raise
+        if findprogram(pyexename) != sys.executable:
+            try:
+                os.symlink(sys.executable, mypython)
+            except OSError, err:
+                # child processes may race, which is harmless
+                if err.errno != errno.EEXIST:
+                    raise
     else:
-        exename = 'python'
-    vlog('# Making python executable in test path use correct Python')
-    mypython = os.path.join(BINDIR, exename)
-    try:
-        os.symlink(sys.executable, mypython)
-    except AttributeError:
-        # windows fallback
-        shutil.copyfile(sys.executable, mypython)
-        shutil.copymode(sys.executable, mypython)
+        exedir, exename = os.path.split(sys.executable)
+        vlog("# Modifying search path to find %s as %s in '%s'" %
+             (exename, pyexename, exedir))
+        path = os.environ['PATH'].split(os.pathsep)
+        while exedir in path:
+            path.remove(exedir)
+        os.environ['PATH'] = os.pathsep.join([exedir] + path)
+        if not findprogram(pyexename):
+            print "WARNING: Cannot find %s in search path" % pyexename
 
 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 +405,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 +477,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')
@@ -566,10 +604,13 @@
         tdir = TESTDIR.replace('\\', '/')
         proc = Popen4('%s -c "%s/hghave %s"' %
                       (options.shell, tdir, ' '.join(reqs)), wd, 0)
-        proc.communicate()
+        stdout, stderr = proc.communicate()
         ret = proc.wait()
         if wifexited(ret):
             ret = os.WEXITSTATUS(ret)
+        if ret == 2:
+            print stdout
+            sys.exit(1)
         return ret == 0
 
     f = open(test)
@@ -833,6 +874,7 @@
     hgrc = open(HGRCPATH, 'w+')
     hgrc.write('[ui]\n')
     hgrc.write('slash = True\n')
+    hgrc.write('interactive = False\n')
     hgrc.write('[defaults]\n')
     hgrc.write('backout = -d "0 0"\n')
     hgrc.write('commit = -d "0 0"\n')
@@ -891,9 +933,16 @@
         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)
 
+    killdaemons()
+
     mark = '.'
 
     skipped = (ret == SKIPPED_STATUS)
@@ -964,8 +1013,6 @@
         sys.stdout.flush()
         iolock.release()
 
-    killdaemons()
-
     if not options.keep_tmpdir:
         shutil.rmtree(testtmp, True)
     if skipped:
@@ -1003,6 +1050,8 @@
     if INST:
         installhg(options)
         _checkhglib("Testing")
+    else:
+        usecorrectpython()
 
     optcopy = dict(options.__dict__)
     optcopy['jobs'] = 1
@@ -1045,7 +1094,13 @@
                 blacklisted.append(test)
             else:
                 job.append(test)
-    fps = {}
+
+    waitq = queue.Queue()
+
+    # windows lacks os.wait, so we must emulate it
+    def waitfor(proc, rfd):
+        fp = os.fdopen(rfd, 'rb')
+        return lambda: waitq.put((proc.pid, proc.wait(), fp))
 
     for j, job in enumerate(jobs):
         if not job:
@@ -1056,29 +1111,32 @@
         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')
+        proc = subprocess.Popen(cmdline, executable=cmdline[0])
+        threading.Thread(target=waitfor(proc, rfd)).start()
         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()
+    for job in jobs:
+        if not job:
+            continue
+        pid, status, fp = waitq.get()
         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, EOFError):
+            sys.exit(255)
+        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 +1151,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:
@@ -1118,6 +1179,8 @@
         if INST:
             installhg(options)
             _checkhglib("Testing")
+        else:
+            usecorrectpython()
 
         if options.restart:
             orig = list(tests)
@@ -1129,7 +1192,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 +1200,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,12 +1214,15 @@
             _checkhglib("Tested")
             print "# Ran %d tests, %d skipped, %d failed." % (
                 tested, skipped + ignored, failed)
+            if options.time:
+                outputtimes(options)
 
         if options.anycoverage:
             outputcoverage(options)
     except KeyboardInterrupt:
         failed = True
-        print "\ninterrupted!"
+        if not options.child:
+            print "\ninterrupted!"
 
     if failed:
         sys.exit(1)
@@ -1170,9 +1234,9 @@
 
         checktools()
 
-    if len(args) == 0:
-        args = os.listdir(".")
-    args.sort()
+        if len(args) == 0:
+            args = os.listdir(".")
+        args.sort()
 
     tests = args
 
@@ -1188,6 +1252,7 @@
     os.environ['no_proxy'] = ''
     os.environ['NO_PROXY'] = ''
     os.environ['TERM'] = 'xterm'
+    os.environ['PYTHONHASHSEED'] = 'random'
 
     # unset env related to hooks
     for k in os.environ.keys():
--- a/tests/test-acl.t	Mon Jan 14 23:14:45 2013 +0900
+++ b/tests/test-acl.t	Sat Jan 19 17:24:33 2013 -0600
@@ -140,7 +140,7 @@
   query 1; heads
   searching for changes
   all remote heads known locally
-  invalidating branch cache (tip differs)
+  invalid branchheads cache (served): tip differs
   listing keys for "bookmarks"
   3 changesets found
   list of changesets:
@@ -202,7 +202,7 @@
   query 1; heads
   searching for changes
   all remote heads known locally
-  invalidating branch cache (tip differs)
+  invalid branchheads cache (served): tip differs
   listing keys for "bookmarks"
   3 changesets found
   list of changesets:
@@ -274,7 +274,7 @@
   query 1; heads
   searching for changes
   all remote heads known locally
-  invalidating branch cache (tip differs)
+  invalid branchheads cache (served): tip differs
   listing keys for "bookmarks"
   3 changesets found
   list of changesets:
@@ -341,6 +341,7 @@
   query 1; heads
   searching for changes
   all remote heads known locally
+  invalid branchheads cache (served): tip differs
   listing keys for "bookmarks"
   3 changesets found
   list of changesets:
@@ -412,6 +413,7 @@
   query 1; heads
   searching for changes
   all remote heads known locally
+  invalid branchheads cache (served): tip differs
   listing keys for "bookmarks"
   3 changesets found
   list of changesets:
@@ -480,6 +482,7 @@
   query 1; heads
   searching for changes
   all remote heads known locally
+  invalid branchheads cache (served): tip differs
   listing keys for "bookmarks"
   3 changesets found
   list of changesets:
@@ -553,6 +556,7 @@
   query 1; heads
   searching for changes
   all remote heads known locally
+  invalid branchheads cache (served): tip differs
   listing keys for "bookmarks"
   3 changesets found
   list of changesets:
@@ -623,6 +627,7 @@
   query 1; heads
   searching for changes
   all remote heads known locally
+  invalid branchheads cache (served): tip differs
   listing keys for "bookmarks"
   3 changesets found
   list of changesets:
@@ -695,6 +700,7 @@
   query 1; heads
   searching for changes
   all remote heads known locally
+  invalid branchheads cache (served): tip differs
   listing keys for "bookmarks"
   3 changesets found
   list of changesets:
@@ -773,7 +779,7 @@
   query 1; heads
   searching for changes
   all remote heads known locally
-  invalidating branch cache (tip differs)
+  invalid branchheads cache (served): tip differs
   listing keys for "bookmarks"
   3 changesets found
   list of changesets:
@@ -853,6 +859,7 @@
   query 1; heads
   searching for changes
   all remote heads known locally
+  invalid branchheads cache (served): tip differs
   listing keys for "bookmarks"
   3 changesets found
   list of changesets:
@@ -927,6 +934,7 @@
   query 1; heads
   searching for changes
   all remote heads known locally
+  invalid branchheads cache (served): tip differs
   listing keys for "bookmarks"
   3 changesets found
   list of changesets:
@@ -1012,6 +1020,7 @@
   query 1; heads
   searching for changes
   all remote heads known locally
+  invalid branchheads cache (served): tip differs
   listing keys for "bookmarks"
   3 changesets found
   list of changesets:
@@ -1091,7 +1100,7 @@
   query 1; heads
   searching for changes
   all remote heads known locally
-  invalidating branch cache (tip differs)
+  invalid branchheads cache (served): tip differs
   listing keys for "bookmarks"
   3 changesets found
   list of changesets:
@@ -1167,7 +1176,7 @@
   query 1; heads
   searching for changes
   all remote heads known locally
-  invalidating branch cache (tip differs)
+  invalid branchheads cache (served): tip differs
   listing keys for "bookmarks"
   3 changesets found
   list of changesets:
@@ -1243,6 +1252,7 @@
   query 1; heads
   searching for changes
   all remote heads known locally
+  invalid branchheads cache (served): tip differs
   listing keys for "bookmarks"
   3 changesets found
   list of changesets:
@@ -1319,7 +1329,7 @@
   query 1; heads
   searching for changes
   all remote heads known locally
-  invalidating branch cache (tip differs)
+  invalid branchheads cache (served): tip differs
   listing keys for "bookmarks"
   3 changesets found
   list of changesets:
@@ -1517,7 +1527,6 @@
   query 1; heads
   searching for changes
   all remote heads known locally
-  invalidating branch cache (tip differs)
   listing keys for "bookmarks"
   4 changesets found
   list of changesets:
@@ -1829,7 +1838,6 @@
   query 1; heads
   searching for changes
   all remote heads known locally
-  invalidating branch cache (tip differs)
   listing keys for "bookmarks"
   4 changesets found
   list of changesets:
@@ -1917,7 +1925,6 @@
   query 1; heads
   searching for changes
   all remote heads known locally
-  invalidating branch cache (tip differs)
   listing keys for "bookmarks"
   4 changesets found
   list of changesets:
@@ -2073,7 +2080,6 @@
   query 1; heads
   searching for changes
   all remote heads known locally
-  invalidating branch cache (tip differs)
   listing keys for "bookmarks"
   4 changesets found
   list of changesets:
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tests/test-ancestor.py	Sat Jan 19 17:24:33 2013 -0600
@@ -0,0 +1,106 @@
+from mercurial import ancestor
+
+# 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
+
+class mockchangelog(object):
+    parentrevs = graph.get
+
+def runmissingancestors(revs, bases):
+    print "%% ancestors of %s and not of %s" % (revs, bases)
+    print ancestor.missingancestors(revs, bases, pfunc)
+
+def test_missingancestors():
+    # Empty revs
+    runmissingancestors([], [1])
+    runmissingancestors([], [])
+
+    # If bases is empty, it's the same as if it were [nullrev]
+    runmissingancestors([12], [])
+
+    # Trivial case: revs == bases
+    runmissingancestors([0], [0])
+    runmissingancestors([4, 5, 6], [6, 5, 4])
+
+    # With nullrev
+    runmissingancestors([-1], [12])
+    runmissingancestors([12], [-1])
+
+    # 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.
+    runmissingancestors([12], [9])
+    runmissingancestors([9], [12])
+    runmissingancestors([12, 9], [7])
+    runmissingancestors([7, 6], [12])
+
+    # More complex cases
+    runmissingancestors([10], [11, 12])
+    runmissingancestors([11], [10])
+    runmissingancestors([11], [10, 12])
+    runmissingancestors([12], [10])
+    runmissingancestors([12], [11])
+    runmissingancestors([10, 11, 12], [13])
+    runmissingancestors([13], [10, 11, 12])
+
+def genlazyancestors(revs, stoprev=0, inclusive=False):
+    print ("%% lazy ancestor set for %s, stoprev = %s, inclusive = %s" %
+           (revs, stoprev, inclusive))
+    return ancestor.lazyancestors(mockchangelog, revs, stoprev=stoprev,
+                                  inclusive=inclusive)
+
+def printlazyancestors(s, l):
+    print [n for n in l if n in s]
+
+def test_lazyancestors():
+    # Empty revs
+    s = genlazyancestors([])
+    printlazyancestors(s, [3, 0, -1])
+
+    # Standard example
+    s = genlazyancestors([11, 13])
+    printlazyancestors(s, [11, 13, 7, 9, 8, 3, 6, 4, 1, -1, 0])
+
+    # Including revs
+    s = genlazyancestors([11, 13], inclusive=True)
+    printlazyancestors(s, [11, 13, 7, 9, 8, 3, 6, 4, 1, -1, 0])
+
+    # Test with stoprev
+    s = genlazyancestors([11, 13], stoprev=6)
+    printlazyancestors(s, [11, 13, 7, 9, 8, 3, 6, 4, 1, -1, 0])
+    s = genlazyancestors([11, 13], stoprev=6, inclusive=True)
+    printlazyancestors(s, [11, 13, 7, 9, 8, 3, 6, 4, 1, -1, 0])
+
+if __name__ == '__main__':
+    test_missingancestors()
+    test_lazyancestors()
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tests/test-ancestor.py.out	Sat Jan 19 17:24:33 2013 -0600
@@ -0,0 +1,46 @@
+% ancestors of [] and not of [1]
+[]
+% ancestors of [] and not of []
+[]
+% ancestors of [12] and not of []
+[0, 1, 2, 4, 6, 7, 9, 12]
+% ancestors of [0] and not of [0]
+[]
+% ancestors of [4, 5, 6] and not of [6, 5, 4]
+[]
+% ancestors of [-1] and not of [12]
+[]
+% ancestors of [12] and not of [-1]
+[0, 1, 2, 4, 6, 7, 9, 12]
+% ancestors of [12] and not of [9]
+[12]
+% ancestors of [9] and not of [12]
+[]
+% ancestors of [12, 9] and not of [7]
+[6, 9, 12]
+% ancestors of [7, 6] and not of [12]
+[]
+% ancestors of [10] and not of [11, 12]
+[5, 10]
+% ancestors of [11] and not of [10]
+[3, 7, 11]
+% ancestors of [11] and not of [10, 12]
+[3, 11]
+% ancestors of [12] and not of [10]
+[6, 7, 9, 12]
+% ancestors of [12] and not of [11]
+[6, 9, 12]
+% ancestors of [10, 11, 12] and not of [13]
+[0, 1, 2, 3, 4, 5, 6, 7, 9, 10, 11, 12]
+% ancestors of [13] and not of [10, 11, 12]
+[8, 13]
+% lazy ancestor set for [], stoprev = 0, inclusive = False
+[]
+% lazy ancestor set for [11, 13], stoprev = 0, inclusive = False
+[7, 8, 3, 4, 1, 0]
+% lazy ancestor set for [11, 13], stoprev = 0, inclusive = True
+[11, 13, 7, 8, 3, 4, 1, 0]
+% lazy ancestor set for [11, 13], stoprev = 6, inclusive = False
+[7, 8]
+% lazy ancestor set for [11, 13], stoprev = 6, inclusive = True
+[11, 13, 7, 8]
--- a/tests/test-audit-path.t	Mon Jan 14 23:14:45 2013 +0900
+++ b/tests/test-audit-path.t	Sat Jan 19 17:24:33 2013 -0600
@@ -86,7 +86,7 @@
   $ hg manifest -r4
   /tmp/test
   $ hg update -Cr4
-  abort: *: $TESTTMP/target//tmp/test (glob)
+  abort: path contains illegal component: /tmp/test
   [255]
 
   $ cd ..
--- a/tests/test-bisect.t	Mon Jan 14 23:14:45 2013 +0900
+++ b/tests/test-bisect.t	Sat Jan 19 17:24:33 2013 -0600
@@ -222,21 +222,21 @@
   Testing changeset 12:1941b52820a5 (23 changesets remaining, ~4 tests)
   1 files updated, 0 files merged, 0 files removed, 0 files unresolved
   $ cat .hg/bisect.state
-  current 1941b52820a544549596820a8ae006842b0e2c64
-  skip 9d7d07bc967ca98ad0600c24953fd289ad5fa991
-  skip ce8f0998e922c179e80819d5066fbe46e2998784
-  skip e7fa0811edb063f6319531f0d0a865882138e180
-  skip a2e6ea4973e9196ddd3386493b0c214b41fd97d3
   bad b99c7b9c8e11558adef3fad9af211c58d46f325b
   bad 5cd978ea51499179507ee7b6f340d2dbaa401185
   bad db07c04beaca44cf24832541e7f4a2346a95275b
   bad b53bea5e2fcb30d3e00bd3409507a5659ce0fd8b
+  current 1941b52820a544549596820a8ae006842b0e2c64
   good 3efc6fd51aeb8594398044c6c846ca59ae021203
   good 288867a866e9adb7a29880b66936c874b80f4651
   good 8e0c2264c8af790daf3585ada0669d93dee09c83
   good b5bd63375ab9a290419f2024b7f4ee9ea7ce90a8
   good ed2d2f24b11c368fa8aa0da9f4e1db580abade59
   good 58c80a7c8a4025a94cedaf7b4a4e3124e8909a96
+  skip 9d7d07bc967ca98ad0600c24953fd289ad5fa991
+  skip ce8f0998e922c179e80819d5066fbe46e2998784
+  skip e7fa0811edb063f6319531f0d0a865882138e180
+  skip a2e6ea4973e9196ddd3386493b0c214b41fd97d3
 
 bisect reverse test
 
--- a/tests/test-bookmarks-pushpull.t	Mon Jan 14 23:14:45 2013 +0900
+++ b/tests/test-bookmarks-pushpull.t	Sat Jan 19 17:24:33 2013 -0600
@@ -41,8 +41,8 @@
   adding manifests
   adding file changes
   added 1 changesets with 1 changes to 1 files
+  adding remote bookmark X
   updating bookmark Y
-  adding remote bookmark X
   adding remote bookmark Z
   (run 'hg update' to get a working copy)
   $ hg bookmarks
@@ -51,12 +51,12 @@
      Z                         0:4e3505fd9583
   $ hg debugpushkey ../a namespaces
   bookmarks	
-  phases	
   namespaces	
   obsolete	
+  phases	
   $ hg debugpushkey ../a bookmarks
+  X	4e3505fd95835d721066b76e75dbb8cc554d7f77
   Y	4e3505fd95835d721066b76e75dbb8cc554d7f77
-  X	4e3505fd95835d721066b76e75dbb8cc554d7f77
   Z	4e3505fd95835d721066b76e75dbb8cc554d7f77
   $ hg pull -B X ../a
   pulling from ../a
@@ -145,9 +145,9 @@
   adding manifests
   adding file changes
   added 1 changesets with 1 changes to 1 files (+1 heads)
+  divergent bookmark @ stored as @foo
   divergent bookmark X stored as X@foo
   updating bookmark Z
-  divergent bookmark @ stored as @foo
   (run 'hg heads' to see heads, 'hg merge' to merge)
   $ hg book
      @                         1:9b140be10808
@@ -292,16 +292,16 @@
 
   $ hg debugpushkey http://localhost:$HGPORT/ namespaces
   bookmarks	
-  phases	
   namespaces	
   obsolete	
+  phases	
   $ hg debugpushkey http://localhost:$HGPORT/ bookmarks
   @	9b140be1080824d768c5a4691a564088eede71f9
+  X	9b140be1080824d768c5a4691a564088eede71f9
+  Y	c922c0139ca03858f655e4a2af4dd02796a63969
+  Z	0d2164f0ce0d8f1d6f94351eba04b794909be66c
   foo	0000000000000000000000000000000000000000
   foobar	9b140be1080824d768c5a4691a564088eede71f9
-  Y	c922c0139ca03858f655e4a2af4dd02796a63969
-  X	9b140be1080824d768c5a4691a564088eede71f9
-  Z	0d2164f0ce0d8f1d6f94351eba04b794909be66c
   $ hg out -B http://localhost:$HGPORT/
   comparing with http://localhost:$HGPORT/
   searching for changed bookmarks
@@ -324,10 +324,10 @@
   pulling from http://localhost:$HGPORT/
   no changes found
   divergent bookmark @ stored as @1
+  divergent bookmark X stored as X@1
+  adding remote bookmark Z
   adding remote bookmark foo
   adding remote bookmark foobar
-  divergent bookmark X stored as X@1
-  adding remote bookmark Z
   importing bookmark Z
   $ hg clone http://localhost:$HGPORT/ cloned-bookmarks
   requesting all changes
--- a/tests/test-bookmarks.t	Mon Jan 14 23:14:45 2013 +0900
+++ b/tests/test-bookmarks.t	Sat Jan 19 17:24:33 2013 -0600
@@ -40,9 +40,9 @@
   summary:     0
   
 
-second bookmark for rev 0
+second bookmark for rev 0, command should work even with ui.strict on
 
-  $ hg bookmark X2
+  $ hg --config ui.strict=1 bookmark X2
 
 bookmark rev -1 again
 
--- a/tests/test-bundle.t	Mon Jan 14 23:14:45 2013 +0900
+++ b/tests/test-bundle.t	Sat Jan 19 17:24:33 2013 -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	Mon Jan 14 23:14:45 2013 +0900
+++ b/tests/test-check-code-hg.t	Sat Jan 19 17:24:33 2013 -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-check-code.t	Mon Jan 14 23:14:45 2013 +0900
+++ b/tests/test-check-code.t	Sat Jan 19 17:24:33 2013 -0600
@@ -110,6 +110,18 @@
    > class empty():
    class foo() not available in Python 2.4, use class foo(object)
   [1]
+  $ cat > python3-compat.py << EOF
+  > foo <> bar
+  > reduce(lambda a, b: a + b, [1, 2, 3, 4])
+  > EOF
+  $ "$check_code" python3-compat.py
+  python3-compat.py:1:
+   > foo <> bar
+   <> operator is not available in Python 3+, use !=
+  python3-compat.py:2:
+   > reduce(lambda a, b: a + b, [1, 2, 3, 4])
+   reduce is not available in Python 3+
+  [1]
 
   $ cat > is-op.py <<EOF
   > # is-operator comparing number or string literal
@@ -159,3 +171,14 @@
    > except:
    warning: naked except clause
   [1]
+
+  $ cat > raise-format.py <<EOF
+  > raise SomeException, message
+  > # this next line is okay
+  > raise SomeException(arg1, arg2)
+  > EOF
+  $ "$check_code" raise-format.py
+  raise-format.py:1:
+   > raise SomeException, message
+   don't use old-style two-argument raise, use Exception(message)
+  [1]
--- a/tests/test-churn.t	Mon Jan 14 23:14:45 2013 +0900
+++ b/tests/test-churn.t	Sat Jan 19 17:24:33 2013 -0600
@@ -37,16 +37,16 @@
 churn all
 
   $ hg churn
+  user1      3 ***************************************************************
   user3      3 ***************************************************************
-  user1      3 ***************************************************************
   user2      2 ******************************************
 
 churn excluding one dir
 
   $ hg churn -X e
   user3      3 ***************************************************************
+  user1      2 ******************************************
   user2      2 ******************************************
-  user1      2 ******************************************
 
 churn up to rev 2
 
@@ -68,16 +68,16 @@
   $ mv ../aliases .hgchurn
   $ hg churn
   skipping malformed alias: not-an-alias
+  alias1      3 **************************************************************
   alias3      3 **************************************************************
-  alias1      3 **************************************************************
   user2       2 *****************************************
   $ rm .hgchurn
 
 churn with column specifier
 
   $ COLUMNS=40 hg churn
+  user1      3 ***********************
   user3      3 ***********************
-  user1      3 ***********************
   user2      2 ***************
 
 churn by hour
@@ -155,8 +155,8 @@
   $ hg churn -c
   user1            4 *********************************************************
   user3            3 *******************************************
+  user2            2 *****************************
   user4@x.com      2 *****************************
-  user2            2 *****************************
   with space       1 **************
 
   $ cd ..
--- a/tests/test-clone.t	Mon Jan 14 23:14:45 2013 +0900
+++ b/tests/test-clone.t	Sat Jan 19 17:24:33 2013 -0600
@@ -558,7 +558,7 @@
   $ hg init b
   $ cd b
   $ hg clone . ../a
-  abort: Permission denied: ../a
+  abort: Permission denied: '../a'
   [255]
   $ cd ..
   $ chmod 700 a
--- a/tests/test-commandserver.py.out	Mon Jan 14 23:14:45 2013 +0900
+++ b/tests/test-commandserver.py.out	Sat Jan 19 17:24:33 2013 -0600
@@ -75,6 +75,7 @@
 defaults.commit=-d "0 0"
 defaults.tag=-d "0 0"
 ui.slash=True
+ui.interactive=False
 ui.foo=bar
  runcommand init foo
  runcommand -R foo showconfig ui defaults
@@ -82,6 +83,7 @@
 defaults.commit=-d "0 0"
 defaults.tag=-d "0 0"
 ui.slash=True
+ui.interactive=False
 
 testing hookoutput:
 
--- a/tests/test-commit-amend.t	Mon Jan 14 23:14:45 2013 +0900
+++ b/tests/test-commit-amend.t	Sat Jan 19 17:24:33 2013 -0600
@@ -488,14 +488,33 @@
   | |
 
 
-Test that amend does not make it easy to create obsoletescence cycle
+Test that amend does not make it easy to create obsolescence cycle
 ---------------------------------------------------------------------
 
-
-  $ hg id -r 14
+  $ hg id -r 14 --hidden
   b650e6ee8614 (a)
-  $ hg revert -ar 14
+  $ hg revert -ar 14 --hidden
   reverting a
   $ hg commit --amend
   $ hg id
   b99e5df575f7 (a) tip
+
+Test that rewriting leaving instability behind is allowed
+---------------------------------------------------------------------
+
+  $ hg up '.^'
+  0 files updated, 0 files merged, 1 files removed, 0 files unresolved
+  $ echo 'b' >> a
+  $ hg log --style compact -r 'children(.)'
+  18[tip]:11   b99e5df575f7   1970-01-01 00:00 +0000   test
+    babar
+  
+  $ hg commit --amend
+  $ hg log -r 'unstable()'
+  changeset:   18:b99e5df575f7
+  branch:      a
+  parent:      11:3334b7925910
+  user:        test
+  date:        Thu Jan 01 00:00:00 1970 +0000
+  summary:     babar
+  
--- a/tests/test-convert-clonebranches.t	Mon Jan 14 23:14:45 2013 +0900
+++ b/tests/test-convert-clonebranches.t	Sat Jan 19 17:24:33 2013 -0600
@@ -82,7 +82,7 @@
   pulling from branch0 into branch2
   4 changesets found
   0 c3
-  pulling from branch2 into branch3
+  pulling from branch1 into branch3
   5 changesets found
-  pulling from branch1 into branch3
+  pulling from branch2 into branch3
   1 changesets found
--- a/tests/test-convert-cvs-detectmerge.t	Mon Jan 14 23:14:45 2013 +0900
+++ b/tests/test-convert-cvs-detectmerge.t	Sat Jan 19 17:24:33 2013 -0600
@@ -183,8 +183,8 @@
   sorting...
   converting...
   9 add file1 on trunk
-  8 add text
-  7 unrelated change
+  8 unrelated change
+  7 add text
   6 add text [MERGE from v1_0]
   5 add text [MERGE from v1_1]
   4 add file2 on trunk
@@ -204,8 +204,8 @@
   5: '' add file2 on trunk
   4: '' add text [MERGE from v1_1]
   3: 'v1_1' add text [MERGE from v1_0]
-  2: 'v1_1' unrelated change
-  1: 'v1_0' add text
+  2: 'v1_0' add text
+  1: 'v1_1' unrelated change
   0: '' add file1 on trunk
 
 graphical log
@@ -225,9 +225,9 @@
   |\|
   | o    3: 'v1_1' add text [MERGE from v1_0]
   | |\
-  +---o  2: 'v1_1' unrelated change
+  +---o  2: 'v1_0' add text
   | |
-  | o  1: 'v1_0' add text
+  | o  1: 'v1_1' unrelated change
   |/
   o  0: '' add file1 on trunk
   
--- a/tests/test-convert-cvs-synthetic.t	Mon Jan 14 23:14:45 2013 +0900
+++ b/tests/test-convert-cvs-synthetic.t	Sat Jan 19 17:24:33 2013 -0600
@@ -140,32 +140,35 @@
   collecting CVS rlog
   15 log entries
   creating changesets
-  8 changeset entries
+  9 changeset entries
   sorting...
   converting...
-  7 add file1 on trunk
-  6 add file2
-  5 add file3, file4 on branch v1_1
-  4 MERGE from v1_0: add file2
+  8 add file1 on trunk
+  7 add file2
+  6 MERGE from v1_0: add file2
+  5 file file3 was initially added on branch v1_1.
+  4 add file3, file4 on branch v1_1
   3 add file5 on v1_2
   2 add file6 on trunk post-v1_2
-  1 MERGE from v1_2: add file5
-  0 MERGE from HEAD: add file6
+  1 MERGE from HEAD: add file6
+  0 MERGE from v1_2: add file5
 
 hg glog output (#1)
 
   $ hg -R proj.hg glog --template "{rev} {desc}\n"
-  o  7 MERGE from HEAD: add file6
+  o  8 MERGE from v1_2: add file5
   |
-  | o  6 MERGE from v1_2: add file5
+  | o  7 MERGE from HEAD: add file6
   | |
-  | o  5 add file6 on trunk post-v1_2
+  o |  6 add file6 on trunk post-v1_2
   | |
-  o |  4 add file5 on v1_2
-  |/
-  | o  3 MERGE from v1_0: add file2
+  | o  5 add file5 on v1_2
   | |
-  | o  2 add file3, file4 on branch v1_1
+  | | o  4 add file3, file4 on branch v1_1
+  | | |
+  o | |  3 file file3 was initially added on branch v1_1.
+  |/ /
+  | o  2 MERGE from v1_0: add file2
   |/
   | o  1 add file2
   |/
@@ -184,32 +187,35 @@
   collecting CVS rlog
   15 log entries
   creating changesets
-  8 changeset entries
+  9 changeset entries
   sorting...
   converting...
-  7 add file1 on trunk
-  6 add file2
-  5 add file3, file4 on branch v1_1
-  4 MERGE from v1_0: add file2
+  8 add file1 on trunk
+  7 add file2
+  6 MERGE from v1_0: add file2
+  5 file file3 was initially added on branch v1_1.
+  4 add file3, file4 on branch v1_1
   3 add file5 on v1_2
   2 add file6 on trunk post-v1_2
-  1 MERGE from v1_2: add file5
-  0 MERGE from HEAD: add file6
+  1 MERGE from HEAD: add file6
+  0 MERGE from v1_2: add file5
 
 hg glog output (#2)
 
   $ hg -R proj.hg2 glog --template "{rev} {desc}\n"
-  o  7 MERGE from HEAD: add file6
+  o  8 MERGE from v1_2: add file5
   |
-  | o  6 MERGE from v1_2: add file5
+  | o  7 MERGE from HEAD: add file6
   | |
-  | o  5 add file6 on trunk post-v1_2
+  o |  6 add file6 on trunk post-v1_2
   | |
-  o |  4 add file5 on v1_2
-  |/
-  | o  3 MERGE from v1_0: add file2
+  | o  5 add file5 on v1_2
   | |
-  | o  2 add file3, file4 on branch v1_1
+  | | o  4 add file3, file4 on branch v1_1
+  | | |
+  o | |  3 file file3 was initially added on branch v1_1.
+  |/ /
+  | o  2 MERGE from v1_0: add file2
   |/
   | o  1 add file2
   |/
--- a/tests/test-convert-cvs.t	Mon Jan 14 23:14:45 2013 +0900
+++ b/tests/test-convert-cvs.t	Sat Jan 19 17:24:33 2013 -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...
@@ -84,8 +91,8 @@
   sorting...
   converting...
   2 Initial revision
-  1 import
-  0 ci0
+  1 ci0
+  0 import
   updating tags
   $ hgcat a
   a
@@ -109,10 +116,10 @@
   sorting...
   converting...
   2 Initial revision
-  1 import
+  1 ci0
+  0 import
   filtering out empty revision
-  repository tip rolled back to revision 0 (undo commit)
-  0 ci0
+  repository tip rolled back to revision 1 (undo commit)
   updating tags
   $ hgcat b/c
   c
@@ -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  2 () ci0 files: b/c
+  o  3 () update tags date: * +0000 files: .hgtags (glob)
   |
-  | o  1 (INITIAL) import files:
+  | o  2 (INITIAL) import date: * -1000 files: (glob)
+  | |
+  o |  1 () ci0 date: * -1000 files: b/c (glob)
   |/
-  o  0 () Initial revision files: a b/c
+  o  0 () Initial revision date: * -1000 files: a b/c (glob)
   
 
 testing debugcvsps
@@ -388,12 +395,11 @@
   Author: * (glob)
   Branch: HEAD
   Tag: (none) 
-  Branchpoints: branch 
   Log:
   ci1
   
   Members: 
-  	a:1.1->1.2 
+  	b/c:1.2->1.3 
   
   ---------------------
   PatchSet 6 
@@ -401,11 +407,12 @@
   Author: * (glob)
   Branch: HEAD
   Tag: (none) 
+  Branchpoints: branch 
   Log:
   ci1
   
   Members: 
-  	b/c:1.2->1.3 
+  	a:1.1->1.2 
   
   ---------------------
   PatchSet 7 
--- a/tests/test-convert-cvsnt-mergepoints.t	Mon Jan 14 23:14:45 2013 +0900
+++ b/tests/test-convert-cvsnt-mergepoints.t	Sat Jan 19 17:24:33 2013 -0600
@@ -116,7 +116,7 @@
   Author: user
   Branch: HEAD
   Tag: (none) 
-  Branchpoints: MYBRANCH1_1, MYBRANCH1 
+  Branchpoints: MYBRANCH1, MYBRANCH1_1 
   Log:
   foo.txt
   
--- a/tests/test-convert-git.t	Mon Jan 14 23:14:45 2013 +0900
+++ b/tests/test-convert-git.t	Sat Jan 19 17:24:33 2013 -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-move.t	Mon Jan 14 23:14:45 2013 +0900
+++ b/tests/test-convert-svn-move.t	Sat Jan 19 17:24:33 2013 -0600
@@ -100,8 +100,8 @@
   4 clobber1
   3 clobber2
   2 adddb
-  1 branch
-  0 clobberdir
+  1 clobberdir
+  0 branch
 
   $ cd hg-repo
 
@@ -210,9 +210,7 @@
                                                               \r (no-eol) (esc)
   \r (no-eol) (esc)
   converting [=============================>            ] 5/7\r (no-eol) (esc)
-  scanning paths [                                      ] 0/3\r (no-eol) (esc)
-  scanning paths [===========>                          ] 1/3\r (no-eol) (esc)
-  scanning paths [========================>             ] 2/3\r (no-eol) (esc)
+  scanning paths [                                      ] 0/1\r (no-eol) (esc)
   getting files [===>                                   ] 1/8\r (no-eol) (esc)
   getting files [========>                              ] 2/8\r (no-eol) (esc)
   getting files [=============>                         ] 3/8\r (no-eol) (esc)
@@ -224,7 +222,9 @@
                                                               \r (no-eol) (esc)
   \r (no-eol) (esc)
   converting [===================================>      ] 6/7\r (no-eol) (esc)
-  scanning paths [                                      ] 0/1\r (no-eol) (esc)
+  scanning paths [                                      ] 0/3\r (no-eol) (esc)
+  scanning paths [===========>                          ] 1/3\r (no-eol) (esc)
+  scanning paths [========================>             ] 2/3\r (no-eol) (esc)
   getting files [===>                                   ] 1/8\r (no-eol) (esc)
   getting files [========>                              ] 2/8\r (no-eol) (esc)
   getting files [=============>                         ] 3/8\r (no-eol) (esc)
@@ -243,7 +243,7 @@
   4 clobber1
   3 clobber2
   2 adddb
-  1 branch
-  0 clobberdir
+  1 clobberdir
+  0 branch
 
   $ cd ..
--- a/tests/test-convert-svn-source.t	Mon Jan 14 23:14:45 2013 +0900
+++ b/tests/test-convert-svn-source.t	Sat Jan 19 17:24:33 2013 -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	Mon Jan 14 23:14:45 2013 +0900
+++ b/tests/test-convert.t	Sat Jan 19 17:24:33 2013 -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).
       hooks.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
@@ -308,7 +316,7 @@
   $ chmod 000 bogusdir
 
   $ hg convert a bogusdir
-  abort: Permission denied: bogusdir
+  abort: Permission denied: 'bogusdir'
   [255]
 
 user permissions should succeed
--- a/tests/test-copy-move-merge.t	Mon Jan 14 23:14:45 2013 +0900
+++ b/tests/test-copy-move-merge.t	Sat Jan 19 17:24:33 2013 -0600
@@ -25,14 +25,14 @@
      b
      c
     all copies found (* = to merge, ! = divergent, % = renamed and deleted):
-     c -> a *
-     b -> a *
+     src: 'a' -> dst: 'b' *
+     src: 'a' -> dst: 'c' *
     checking for directory renames
   resolving manifests
    overwrite: False, partial: False
    ancestor: b8bf91eeebbc, local: add3f11052fa+, remote: 17c05bb7fcb6
+   a: remote moved to b -> m
    a: remote moved to c -> m
-   a: remote moved to b -> m
   preserving a for resolve of b
   preserving a for resolve of c
   removing a
--- a/tests/test-debugcomplete.t	Mon Jan 14 23:14:45 2013 +0900
+++ b/tests/test-debugcomplete.t	Sat Jan 19 17:24:33 2013 -0600
@@ -96,6 +96,7 @@
   debugsetparents
   debugstate
   debugsub
+  debugsuccessorssets
   debugwalk
   debugwireargs
 
@@ -122,6 +123,7 @@
   --encoding
   --encodingmode
   --help
+  --hidden
   --noninteractive
   --profile
   --quiet
@@ -152,6 +154,7 @@
   --encodingmode
   --errorlog
   --help
+  --hidden
   --ipv6
   --name
   --noninteractive
@@ -199,7 +202,7 @@
   export: output, switch-parent, rev, text, git, nodates
   forget: include, exclude
   init: ssh, remotecmd, insecure
-  log: follow, follow-first, date, copies, keyword, rev, removed, only-merges, user, only-branch, branch, prune, hidden, patch, git, limit, no-merges, stat, graph, style, template, include, exclude
+  log: follow, follow-first, date, copies, keyword, rev, removed, only-merges, user, only-branch, branch, prune, patch, git, limit, no-merges, stat, graph, style, template, include, exclude
   merge: force, rev, preview, tool
   pull: update, force, rev, bookmark, branch, ssh, remotecmd, insecure
   push: force, rev, bookmark, branch, new-branch, ssh, remotecmd, insecure
@@ -246,6 +249,7 @@
   debugsetparents: 
   debugstate: nodates, datesort
   debugsub: rev
+  debugsuccessorssets: 
   debugwalk: include, exclude
   debugwireargs: three, four, five, ssh, remotecmd, insecure
   graft: rev, continue, edit, log, currentdate, currentuser, date, user, tool, dry-run
--- a/tests/test-dispatch.t	Mon Jan 14 23:14:45 2013 +0900
+++ b/tests/test-dispatch.t	Sat Jan 19 17:24:33 2013 -0600
@@ -46,11 +46,16 @@
 
   $ cd "$TESTTMP"
 
+OSError ... and with filename even when it is empty
+
+  $ hg -R a archive ''
+  abort: No such file or directory: ''
+  [255]
+
 #if no-outer-repo
 
 No repo:
 
-  $ cd $dir
   $ hg cat
   abort: no repository found in '$TESTTMP' (.hg not found)!
   [255]
--- a/tests/test-doctest.py	Mon Jan 14 23:14:45 2013 +0900
+++ b/tests/test-doctest.py	Sat Jan 19 17:24:33 2013 -0600
@@ -6,6 +6,8 @@
 
 import mercurial.util
 doctest.testmod(mercurial.util)
+# Only run doctests for the current platform
+doctest.testmod(mercurial.util.platform)
 
 import mercurial.changelog
 doctest.testmod(mercurial.changelog)
--- a/tests/test-double-merge.t	Mon Jan 14 23:14:45 2013 +0900
+++ b/tests/test-double-merge.t	Sat Jan 19 17:24:33 2013 -0600
@@ -30,7 +30,7 @@
     unmatched files in other:
      bar
     all copies found (* = to merge, ! = divergent, % = renamed and deleted):
-     bar -> foo *
+     src: 'foo' -> dst: 'bar' *
     checking for directory renames
   resolving manifests
    overwrite: False, partial: False
--- a/tests/test-eolfilename.t	Mon Jan 14 23:14:45 2013 +0900
+++ b/tests/test-eolfilename.t	Sat Jan 19 17:24:33 2013 -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-execute-bit.t	Mon Jan 14 23:14:45 2013 +0900
+++ b/tests/test-execute-bit.t	Sat Jan 19 17:24:33 2013 -0600
@@ -20,7 +20,7 @@
   M a
 
   $ hg up 0
-  0 files updated, 0 files merged, 0 files removed, 0 files unresolved
+  1 files updated, 0 files merged, 0 files removed, 0 files unresolved
   $ hg id
   d69afc33ff8a
   $ test -x a && echo executable -- bad || echo not executable -- good
--- a/tests/test-extension.t	Mon Jan 14 23:14:45 2013 +0900
+++ b/tests/test-extension.t	Sat Jan 19 17:24:33 2013 -0600
@@ -200,6 +200,7 @@
       --profile           print command execution profile
       --version           output version information and exit
    -h --help              display help and exit
+      --hidden            consider hidden changesets
   
   [+] marked option can be specified multiple times
 
@@ -230,6 +231,7 @@
       --profile           print command execution profile
       --version           output version information and exit
    -h --help              display help and exit
+      --hidden            consider hidden changesets
   
   [+] marked option can be specified multiple times
   $ echo 'debugextension = !' >> $HGRCPATH
--- a/tests/test-filecache.py	Mon Jan 14 23:14:45 2013 +0900
+++ b/tests/test-filecache.py	Sat Jan 19 17:24:33 2013 -0600
@@ -4,7 +4,7 @@
                     'cacheable']):
     sys.exit(80)
 
-from mercurial import util, scmutil, extensions
+from mercurial import util, scmutil, extensions, hg, ui
 
 filecache = scmutil.filecache
 
@@ -86,6 +86,32 @@
     util.cachestat.cacheable = origcacheable
     util.cachestat.__init__ = originit
 
+def test_filecache_synced():
+    # test old behaviour that caused filecached properties to go out of sync
+    os.system('hg init && echo a >> a && hg ci -qAm.')
+    repo = hg.repository(ui.ui())
+    # first rollback clears the filecache, but changelog to stays in __dict__
+    repo.rollback()
+    repo.commit('.')
+    # second rollback comes along and touches the changelog externally
+    # (file is moved)
+    repo.rollback()
+    # but since changelog isn't under the filecache control anymore, we don't
+    # see that it changed, and return the old changelog without reconstructing
+    # it
+    repo.commit('.')
+
+def setbeforeget(repo):
+    os.remove('x')
+    repo.cached = 0
+    repo.invalidate()
+    print repo.cached
+    repo.invalidate()
+    f = open('x', 'w')
+    f.write('a')
+    f.close()
+    print repo.cached
+
 print 'basic:'
 print
 basic(fakerepo())
@@ -93,3 +119,8 @@
 print 'fakeuncacheable:'
 print
 fakeuncacheable()
+test_filecache_synced()
+print
+print 'setbeforeget:'
+print
+setbeforeget(fakerepo())
--- a/tests/test-filecache.py.out	Mon Jan 14 23:14:45 2013 +0900
+++ b/tests/test-filecache.py.out	Sat Jan 19 17:24:33 2013 -0600
@@ -13,3 +13,13 @@
 creating
 creating
 creating
+repository tip rolled back to revision -1 (undo commit)
+working directory now based on revision -1
+repository tip rolled back to revision -1 (undo commit)
+working directory now based on revision -1
+
+setbeforeget:
+
+0
+creating
+None
--- a/tests/test-flags.t	Mon Jan 14 23:14:45 2013 +0900
+++ b/tests/test-flags.t	Sat Jan 19 17:24:33 2013 -0600
@@ -79,8 +79,11 @@
 
   $ hg -v merge
   resolving manifests
-  0 files updated, 0 files merged, 0 files removed, 0 files unresolved
+  1 files updated, 0 files merged, 0 files removed, 0 files unresolved
   (branch merge, don't forget to commit)
+  $ cat a
+  123
+  $ [ -x a ]
 
   $ cd ../test3
   $ echo 123 >>b
@@ -128,7 +131,7 @@
 
   $ hg -v merge
   resolving manifests
-  0 files updated, 0 files merged, 0 files removed, 0 files unresolved
+  1 files updated, 0 files merged, 0 files removed, 0 files unresolved
   (branch merge, don't forget to commit)
 
   $ ls -l ../test[123]/a > foo
--- a/tests/test-fncache.t	Mon Jan 14 23:14:45 2013 +0900
+++ b/tests/test-fncache.t	Sat Jan 19 17:24:33 2013 -0600
@@ -70,7 +70,7 @@
   .hg/00changelog.i
   .hg/00manifest.i
   .hg/cache
-  .hg/cache/branchheads
+  .hg/cache/branchheads-served
   .hg/data
   .hg/data/tst.d.hg
   .hg/data/tst.d.hg/foo.i
@@ -98,7 +98,7 @@
   .hg
   .hg/00changelog.i
   .hg/cache
-  .hg/cache/branchheads
+  .hg/cache/branchheads-served
   .hg/dirstate
   .hg/last-message.txt
   .hg/requires
--- a/tests/test-git-export.t	Mon Jan 14 23:14:45 2013 +0900
+++ b/tests/test-git-export.t	Sat Jan 19 17:24:33 2013 -0600
@@ -337,12 +337,12 @@
 Reversed:
 
   $ hg diff --git -r -1 -r -2
-  diff --git a/brand-new3 b/brand-new2
-  rename from brand-new3
+  diff --git a/brand-new3-2 b/brand-new2
+  rename from brand-new3-2
   rename to brand-new2
-  diff --git a/brand-new3-2 b/brand-new3-2
+  diff --git a/brand-new3 b/brand-new3
   deleted file mode 100644
-  --- a/brand-new3-2
+  --- a/brand-new3
   +++ /dev/null
   @@ -1,1 +0,0 @@
   -
--- a/tests/test-globalopts.t	Mon Jan 14 23:14:45 2013 +0900
+++ b/tests/test-globalopts.t	Sat Jan 19 17:24:33 2013 -0600
@@ -87,7 +87,7 @@
   abort: no repository found in '$TESTTMP' (.hg not found)!
   [255]
   $ hg -R b ann a/a
-  abort: a/a not under root
+  abort: a/a not under root '$TESTTMP/b'
   [255]
   $ hg log
   abort: no repository found in '$TESTTMP' (.hg not found)!
--- a/tests/test-glog.t	Mon Jan 14 23:14:45 2013 +0900
+++ b/tests/test-glog.t	Sat Jan 19 17:24:33 2013 -0600
@@ -2042,19 +2042,16 @@
   $ cd ..
 
 Test --hidden
+ (enable obsolete)
 
-  $ cat > $HGTMP/testhidden.py << EOF
-  > from mercurial import util
-  > def reposetup(ui, repo):
-  >     for line in repo.opener('hidden'):
-  >         ctx = repo[line.strip()]
-  >         repo.hiddenrevs.add(ctx.rev())
-  >     if repo.revs('children(%ld) - %ld',  repo.hiddenrevs,  repo.hiddenrevs):
-  >       raise util.Abort('hidden revision with children!')
+  $ cat > ${TESTTMP}/obs.py << EOF
+  > import mercurial.obsolete
+  > mercurial.obsolete._enabled = True
   > EOF
-  $ echo '[extensions]' >> .hg/hgrc
-  $ echo "hidden=$HGTMP/testhidden.py" >> .hg/hgrc
-  $ hg id --debug -i -r 8 > .hg/hidden
+  $ echo '[extensions]' >> $HGRCPATH
+  $ echo "obs=${TESTTMP}/obs.py" >> $HGRCPATH
+
+  $ hg debugobsolete `hg id --debug -i -r 8`
   $ testlog
   []
   []
--- a/tests/test-graft.t	Mon Jan 14 23:14:45 2013 +0900
+++ b/tests/test-graft.t	Sat Jan 19 17:24:33 2013 -0600
@@ -131,7 +131,7 @@
     unmatched files in local:
      b
     all copies found (* = to merge, ! = divergent, % = renamed and deleted):
-     b -> a *
+     src: 'a' -> dst: 'b' *
     checking for directory renames
   resolving manifests
    overwrite: False, partial: False
@@ -158,8 +158,8 @@
   resolving manifests
    overwrite: False, partial: False
    ancestor: 4c60f11aa304, local: 1905859650ec+, remote: 9c233e8e184d
+   d: remote is newer -> g
    e: versions differ -> m
-   d: remote is newer -> g
   preserving e for resolve of e
   updating: d 1/2 files (50.00%)
   getting d
--- a/tests/test-hardlinks.t	Mon Jan 14 23:14:45 2013 +0900
+++ b/tests/test-hardlinks.t	Sat Jan 19 17:24:33 2013 -0600
@@ -196,7 +196,7 @@
   $ nlinksdir r4
   2 r4/.hg/00changelog.i
   2 r4/.hg/branch
-  2 r4/.hg/cache/branchheads
+  2 r4/.hg/cache/branchheads-served
   2 r4/.hg/dirstate
   2 r4/.hg/hgrc
   2 r4/.hg/last-message.txt
@@ -226,7 +226,7 @@
   $ nlinksdir r4
   2 r4/.hg/00changelog.i
   1 r4/.hg/branch
-  2 r4/.hg/cache/branchheads
+  2 r4/.hg/cache/branchheads-served
   1 r4/.hg/dirstate
   2 r4/.hg/hgrc
   2 r4/.hg/last-message.txt
--- a/tests/test-help.t	Mon Jan 14 23:14:45 2013 +0900
+++ b/tests/test-help.t	Sat Jan 19 17:24:33 2013 -0600
@@ -248,6 +248,7 @@
       --profile           print command execution profile
       --version           output version information and exit
    -h --help              display help and exit
+      --hidden            consider hidden changesets
   
   [+] marked option can be specified multiple times
   
@@ -334,6 +335,7 @@
       --profile           print command execution profile
       --version           output version information and exit
    -h --help              display help and exit
+      --hidden            consider hidden changesets
   
   [+] marked option can be specified multiple times
 
--- a/tests/test-hgk.t	Mon Jan 14 23:14:45 2013 +0900
+++ b/tests/test-hgk.t	Sat Jan 19 17:24:33 2013 -0600
@@ -11,7 +11,6 @@
   tree a0c8bcbbb45c
   parent 000000000000
   author test 0 0
-  committer test 0 0
   revision 0
   branch default
   
--- a/tests/test-hgweb-commands.t	Mon Jan 14 23:14:45 2013 +0900
+++ b/tests/test-hgweb-commands.t	Sat Jan 19 17:24:33 2013 -0600
@@ -275,10 +275,16 @@
   <ul>
    <li><a href="/help">help</a></li>
   </ul>
+  <p>
+  <div class="atom-logo">
+  <a href="/atom-log" title="subscribe to atom feed">
+  <img class="atom-logo" src="/static/feed-icon-14x14.png" alt="atom feed">
+  </a>
+  </div>
   </div>
   
   <div class="main">
-  <h2><a href="/">test</a></h2>
+  <h2 class="breadcrumb"><a href="/">Mercurial</a> </h2>
   <h3>log</h3>
   
   <form class="search" action="/log">
@@ -380,7 +386,7 @@
   
   <div class="main">
   
-  <h2><a href="/">test</a></h2>
+  <h2 class="breadcrumb"><a href="/">Mercurial</a> </h2>
   <h3>changeset 0:2ef0ac749a14  <span class="tag">1.0</span>  <span class="tag">anotherthing</span> </h3>
   
   <form class="search" action="/log">
@@ -441,6 +447,14 @@
       </div>
     </td>
   </tr>
+  <tr>
+   <th class="author">change baseline</th>
+   <td class="author"></td>
+  </tr>
+  <tr>
+   <th class="author">current baseline</th>
+   <td class="author"><a href="/rev/000000000000">000000000000</a></td>
+  </tr>
   </table>
   
   <div class="overflow">
@@ -514,7 +528,7 @@
   </div>
   
   <div class="main">
-  <h2><a href="/">test</a></h2>
+  <h2 class="breadcrumb"><a href="/">Mercurial</a> </h2>
   <h3>searching for 'base'</h3>
   
   <form class="search" action="/log">
@@ -628,7 +642,7 @@
   </div>
   
   <div class="main">
-  <h2><a href="/">test</a></h2>
+  <h2 class="breadcrumb"><a href="/">Mercurial</a> </h2>
   <h3>view foo @ 1:a4f92ed23982</h3>
   
   <form class="search" action="/log">
@@ -738,8 +752,8 @@
   <body>
   
   <div class="page_header">
-  <a href="http://mercurial.selenic.com/" title="Mercurial" style="float: right;">Mercurial</a><a href="/summary?style=gitweb">test</a> / summary
-  
+  <a href="http://mercurial.selenic.com/" title="Mercurial" style="float: right;">Mercurial</a>
+  <a href="/">Mercurial</a>  / summary
   <form action="/log">
   <input type="hidden" name="style" value="gitweb" />
   <div class="search">
@@ -941,7 +955,8 @@
   <body>
   
   <div class="page_header">
-  <a href="http://mercurial.selenic.com/" title="Mercurial" style="float: right;">Mercurial</a><a href="/summary?style=gitweb">test</a> / graph
+  <a href="http://mercurial.selenic.com/" title="Mercurial" style="float: right;">Mercurial</a>
+  <a href="/">Mercurial</a>  / graph
   </div>
   
   <form action="/log">
@@ -961,8 +976,8 @@
   <a href="/file/cad8025a2e87?style=gitweb">files</a> |
   <a href="/help?style=gitweb">help</a>
   <br/>
-  <a href="/graph/3?style=gitweb&revcount=30">less</a>
-  <a href="/graph/3?style=gitweb&revcount=120">more</a>
+  <a href="/graph/3?revcount=30&style=gitweb">less</a>
+  <a href="/graph/3?revcount=120&style=gitweb">more</a>
   | <a href="/graph/2ef0ac749a14?style=gitweb">(0)</a> <a href="/graph/tip?style=gitweb">tip</a> <br/>
   </div>
   
@@ -1034,8 +1049,8 @@
   </script>
   
   <div class="page_nav">
-  <a href="/graph/3?style=gitweb&revcount=30">less</a>
-  <a href="/graph/3?style=gitweb&revcount=120">more</a>
+  <a href="/graph/3?revcount=30&style=gitweb">less</a>
+  <a href="/graph/3?revcount=120&style=gitweb">more</a>
   | <a href="/graph/2ef0ac749a14?style=gitweb">(0)</a> <a href="/graph/tip?style=gitweb">tip</a> 
   </div>
   
--- a/tests/test-hgweb-descend-empties.t	Mon Jan 14 23:14:45 2013 +0900
+++ b/tests/test-hgweb-descend-empties.t	Sat Jan 19 17:24:33 2013 -0600
@@ -70,7 +70,7 @@
   </div>
   
   <div class="main">
-  <h2><a href="/">test</a></h2>
+  <h2 class="breadcrumb"><a href="/">Mercurial</a> </h2>
   <h3>directory / @ 0:9087c84a0f5d <span class="tag">tip</span> </h3>
   
   <form class="search" action="/log">
--- a/tests/test-hgweb-diffs.t	Mon Jan 14 23:14:45 2013 +0900
+++ b/tests/test-hgweb-diffs.t	Sat Jan 19 17:24:33 2013 -0600
@@ -78,7 +78,7 @@
   
   <div class="main">
   
-  <h2><a href="/">test</a></h2>
+  <h2 class="breadcrumb"><a href="/">Mercurial</a> </h2>
   <h3>changeset 0:0cd96de13884   </h3>
   
   <form class="search" action="/log">
@@ -139,6 +139,14 @@
       </div>
     </td>
   </tr>
+  <tr>
+   <th class="author">change baseline</th>
+   <td class="author"></td>
+  </tr>
+  <tr>
+   <th class="author">current baseline</th>
+   <td class="author"><a href="/rev/000000000000">000000000000</a></td>
+  </tr>
   </table>
   
   <div class="overflow">
@@ -238,7 +246,7 @@
   </div>
   
   <div class="main">
-  <h2><a href="/">test</a></h2>
+  <h2 class="breadcrumb"><a href="/">Mercurial</a> </h2>
   <h3>diff b @ 1:559edbd9ed20</h3>
   
   <form class="search" action="/log">
@@ -339,7 +347,7 @@
   
   <div class="main">
   
-  <h2><a href="/">test</a></h2>
+  <h2 class="breadcrumb"><a href="/">Mercurial</a> </h2>
   <h3>changeset 0:0cd96de13884   </h3>
   
   <form class="search" action="/log">
@@ -400,6 +408,14 @@
       </div>
     </td>
   </tr>
+  <tr>
+   <th class="author">change baseline</th>
+   <td class="author"></td>
+  </tr>
+  <tr>
+   <th class="author">current baseline</th>
+   <td class="author"><a href="/rev/000000000000">000000000000</a></td>
+  </tr>
   </table>
   
   <div class="overflow">
@@ -503,7 +519,7 @@
   </div>
   
   <div class="main">
-  <h2><a href="/">test</a></h2>
+  <h2 class="breadcrumb"><a href="/">Mercurial</a> </h2>
   <h3>diff a @ 1:559edbd9ed20</h3>
   
   <form class="search" action="/log">
@@ -601,7 +617,7 @@
   </div>
   
   <div class="main">
-  <h2><a href="/">test</a></h2>
+  <h2 class="breadcrumb"><a href="/">Mercurial</a> </h2>
   <h3>comparison a @ 0:0cd96de13884</h3>
   
   <form class="search" action="/log">
@@ -673,7 +689,7 @@
 comparison existing file
 
   $ hg up
-  0 files updated, 0 files merged, 1 files removed, 0 files unresolved
+  1 files updated, 0 files merged, 1 files removed, 0 files unresolved
   $ echo a >> a
   $ hg ci -mc
   $ "$TESTDIR/get-with-headers.py" localhost:$HGPORT 'comparison/tip/a'
@@ -723,7 +739,7 @@
   </div>
   
   <div class="main">
-  <h2><a href="/">test</a></h2>
+  <h2 class="breadcrumb"><a href="/">Mercurial</a> </h2>
   <h3>comparison a @ 2:d73db4d812ff</h3>
   
   <form class="search" action="/log">
@@ -847,7 +863,7 @@
   </div>
   
   <div class="main">
-  <h2><a href="/">test</a></h2>
+  <h2 class="breadcrumb"><a href="/">Mercurial</a> </h2>
   <h3>comparison a @ 3:20e80271eb7a</h3>
   
   <form class="search" action="/log">
--- a/tests/test-hgweb-empty.t	Mon Jan 14 23:14:45 2013 +0900
+++ b/tests/test-hgweb-empty.t	Sat Jan 19 17:24:33 2013 -0600
@@ -48,10 +48,16 @@
   <ul>
    <li><a href="/help">help</a></li>
   </ul>
+  <p>
+  <div class="atom-logo">
+  <a href="/atom-log" title="subscribe to atom feed">
+  <img class="atom-logo" src="/static/feed-icon-14x14.png" alt="atom feed">
+  </a>
+  </div>
   </div>
   
   <div class="main">
-  <h2><a href="/">test</a></h2>
+  <h2 class="breadcrumb"><a href="/">Mercurial</a> </h2>
   <h3>log</h3>
   
   <form class="search" action="/log">
@@ -133,10 +139,16 @@
   <ul>
    <li><a href="/help">help</a></li>
   </ul>
+  <p>
+  <div class="atom-logo">
+  <a href="/atom-log" title="subscribe to atom feed">
+  <img class="atom-logo" src="/static/feed-icon-14x14.png" alt="atom feed">
+  </a>
+  </div>
   </div>
   
   <div class="main">
-  <h2><a href="/">test</a></h2>
+  <h2 class="breadcrumb"><a href="/">Mercurial</a> </h2>
   <h3>log</h3>
   
   <form class="search" action="/log">
@@ -216,10 +228,16 @@
   <ul>
    <li><a href="/help">help</a></li>
   </ul>
+  <p>
+  <div class="atom-logo">
+  <a href="/atom-log" title="subscribe to atom feed">
+  <img class="atom-logo" src="/static/feed-icon-14x14.png" alt="atom feed">
+  </a>
+  </div>
   </div>
   
   <div class="main">
-  <h2><a href="/">test</a></h2>
+  <h2 class="breadcrumb"><a href="/">Mercurial</a> </h2>
   <h3>graph</h3>
   
   <form class="search" action="/log">
@@ -355,7 +373,7 @@
   </div>
   
   <div class="main">
-  <h2><a href="/">test</a></h2>
+  <h2 class="breadcrumb"><a href="/">Mercurial</a> </h2>
   <h3>directory / @ -1:000000000000 <span class="tag">tip</span> </h3>
   
   <form class="search" action="/log">
--- a/tests/test-hgweb-filelog.t	Mon Jan 14 23:14:45 2013 +0900
+++ b/tests/test-hgweb-filelog.t	Sat Jan 19 17:24:33 2013 -0600
@@ -156,10 +156,15 @@
   <ul>
   <li><a href="/help">help</a></li>
   </ul>
+  <p>
+  <div class="atom-logo">
+  <a href="/atom-log/01de2d66a28d/a" title="subscribe to atom feed">
+  <img class="atom-logo" src="/static/feed-icon-14x14.png" alt="atom feed"></a>
+  </div>
   </div>
   
   <div class="main">
-  <h2><a href="/">test</a></h2>
+  <h2 class="breadcrumb"><a href="/">Mercurial</a> </h2>
   <h3>log a</h3>
   
   <form class="search" action="/log">
@@ -258,10 +263,15 @@
   <ul>
   <li><a href="/help">help</a></li>
   </ul>
+  <p>
+  <div class="atom-logo">
+  <a href="/atom-log/01de2d66a28d/a" title="subscribe to atom feed">
+  <img class="atom-logo" src="/static/feed-icon-14x14.png" alt="atom feed"></a>
+  </div>
   </div>
   
   <div class="main">
-  <h2><a href="/">test</a></h2>
+  <h2 class="breadcrumb"><a href="/">Mercurial</a> </h2>
   <h3>log a</h3>
   
   <form class="search" action="/log">
@@ -360,10 +370,15 @@
   <ul>
   <li><a href="/help">help</a></li>
   </ul>
+  <p>
+  <div class="atom-logo">
+  <a href="/atom-log/5ed941583260/a" title="subscribe to atom feed">
+  <img class="atom-logo" src="/static/feed-icon-14x14.png" alt="atom feed"></a>
+  </div>
   </div>
   
   <div class="main">
-  <h2><a href="/">test</a></h2>
+  <h2 class="breadcrumb"><a href="/">Mercurial</a> </h2>
   <h3>log a</h3>
   
   <form class="search" action="/log">
@@ -457,10 +472,15 @@
   <ul>
   <li><a href="/help">help</a></li>
   </ul>
+  <p>
+  <div class="atom-logo">
+  <a href="/atom-log/5ed941583260/a" title="subscribe to atom feed">
+  <img class="atom-logo" src="/static/feed-icon-14x14.png" alt="atom feed"></a>
+  </div>
   </div>
   
   <div class="main">
-  <h2><a href="/">test</a></h2>
+  <h2 class="breadcrumb"><a href="/">Mercurial</a> </h2>
   <h3>log a</h3>
   
   <form class="search" action="/log">
@@ -542,7 +562,7 @@
   
   <div class="main">
   
-  <h2><a href="/">test</a></h2>
+  <h2 class="breadcrumb"><a href="/">Mercurial</a> </h2>
   <h3>error</h3>
   
   <form class="search" action="/log">
@@ -605,7 +625,7 @@
   <a type="application/atom+xml" href="/atom-log/tip/c" title="Atom feed for test:c">atom</a>
   </div>
   
-  <h2>c revision history</h2>
+  <h2><a href="/">Mercurial</a>  / c revision history</h2>
   
   <p>navigate: <small class="navigate"><a href="/log/1a6696706df2/c?style=spartan">(0)</a> <a href="/log/tip/c?style=spartan">tip</a> </small></p>
   
--- a/tests/test-hgweb-removed.t	Mon Jan 14 23:14:45 2013 +0900
+++ b/tests/test-hgweb-removed.t	Sat Jan 19 17:24:33 2013 -0600
@@ -59,7 +59,7 @@
   
   <div class="main">
   
-  <h2><a href="/">test</a></h2>
+  <h2 class="breadcrumb"><a href="/">Mercurial</a> </h2>
   <h3>changeset 1:c78f6c5cbea9  <span class="tag">tip</span>  </h3>
   
   <form class="search" action="/log">
@@ -112,6 +112,14 @@
       </div>
     </td>
   </tr>
+  <tr>
+   <th class="author">change baseline</th>
+   <td class="author"><a href="/rev/cb9a9f314b8b:c78f6c5cbea9">cb9a9f314b8b</a> </td>
+  </tr>
+  <tr>
+   <th class="author">current baseline</th>
+   <td class="author"><a href="/rev/cb9a9f314b8b">cb9a9f314b8b</a></td>
+  </tr>
   </table>
   
   <div class="overflow">
@@ -182,7 +190,7 @@
   </div>
   
   <div class="main">
-  <h2><a href="/">test</a></h2>
+  <h2 class="breadcrumb"><a href="/">Mercurial</a> </h2>
   <h3>diff a @ 1:c78f6c5cbea9</h3>
   
   <form class="search" action="/log">
--- a/tests/test-hgweb.t	Mon Jan 14 23:14:45 2013 +0900
+++ b/tests/test-hgweb.t	Sat Jan 19 17:24:33 2013 -0600
@@ -75,7 +75,7 @@
   
   <div class="main">
   
-  <h2><a href="/">test</a></h2>
+  <h2 class="breadcrumb"><a href="/">Mercurial</a> </h2>
   <h3>error</h3>
   
   <form class="search" action="/log">
@@ -165,7 +165,7 @@
   
   <div class="main">
   
-  <h2><a href="/">test</a></h2>
+  <h2 class="breadcrumb"><a href="/">Mercurial</a> </h2>
   <h3>error</h3>
   
   <form class="search" action="/log">
@@ -243,7 +243,7 @@
   </div>
   
   <div class="main">
-  <h2><a href="/">test</a></h2>
+  <h2 class="breadcrumb"><a href="/">Mercurial</a> </h2>
   <h3>directory / @ 0:2ef0ac749a14 <span class="tag">tip</span> </h3>
   
   <form class="search" action="/log">
@@ -310,8 +310,10 @@
 
 static file
 
-  $ "$TESTDIR/get-with-headers.py" --twice localhost:$HGPORT 'static/style-gitweb.css'
+  $ "$TESTDIR/get-with-headers.py" --twice localhost:$HGPORT 'static/style-gitweb.css' - date etag server
   200 Script output follows
+  content-length: 4619
+  content-type: text/css
   
   body { font-family: sans-serif; font-size: 12px; margin:0px; border:solid #d9d8d1; border-width:1px; margin:10px; }
   a { color:#0000cc; }
--- a/tests/test-hgwebdir.t	Mon Jan 14 23:14:45 2013 +0900
+++ b/tests/test-hgwebdir.t	Sat Jan 19 17:24:33 2013 -0600
@@ -198,7 +198,7 @@
   <img src="/static/hglogo.png" width=75 height=90 border=0 alt="mercurial" /></a>
   </div>
   <div class="main">
-  <h2>Mercurial Repositories</h2>
+  <h2 class="breadcrumb"><a href="/">Mercurial</a> </h2>
   
   <table class="bigtable">
       <tr>
@@ -207,6 +207,7 @@
           <th><a href="?sort=contact">Contact</a></th>
           <th><a href="?sort=lastchange">Last modified</a></th>
           <th>&nbsp;</th>
+          <th>&nbsp;</th>
       </tr>
       
   <tr class="parity0">
@@ -215,6 +216,11 @@
   <td>&#70;&#111;&#111;&#32;&#66;&#97;&#114;&#32;&#60;&#102;&#111;&#111;&#46;&#98;&#97;&#114;&#64;&#101;&#120;&#97;&#109;&#112;&#108;&#101;&#46;&#99;&#111;&#109;&#62;</td>
   <td class="age">*</td> (glob)
   <td class="indexlinks"></td>
+  <td>
+  <a href="/t/a/atom-log" title="subscribe to repository atom feed">
+  <img class="atom-logo" src="/static/feed-icon-14x14.png" alt="subscribe to repository atom feed">
+  </a>
+  </td>
   </tr>
   
   <tr class="parity1">
@@ -223,6 +229,11 @@
   <td>&#70;&#111;&#111;&#32;&#66;&#97;&#114;&#32;&#60;&#102;&#111;&#111;&#46;&#98;&#97;&#114;&#64;&#101;&#120;&#97;&#109;&#112;&#108;&#101;&#46;&#99;&#111;&#109;&#62;</td>
   <td class="age">*</td> (glob)
   <td class="indexlinks"></td>
+  <td>
+  <a href="/b/atom-log" title="subscribe to repository atom feed">
+  <img class="atom-logo" src="/static/feed-icon-14x14.png" alt="subscribe to repository atom feed">
+  </a>
+  </td>
   </tr>
   
   <tr class="parity0">
@@ -231,6 +242,11 @@
   <td>&#70;&#111;&#111;&#32;&#66;&#97;&#114;&#32;&#60;&#102;&#111;&#111;&#46;&#98;&#97;&#114;&#64;&#101;&#120;&#97;&#109;&#112;&#108;&#101;&#46;&#99;&#111;&#109;&#62;</td>
   <td class="age">*</td> (glob)
   <td class="indexlinks"></td>
+  <td>
+  <a href="/coll/a/atom-log" title="subscribe to repository atom feed">
+  <img class="atom-logo" src="/static/feed-icon-14x14.png" alt="subscribe to repository atom feed">
+  </a>
+  </td>
   </tr>
   
   <tr class="parity1">
@@ -239,6 +255,11 @@
   <td>&#70;&#111;&#111;&#32;&#66;&#97;&#114;&#32;&#60;&#102;&#111;&#111;&#46;&#98;&#97;&#114;&#64;&#101;&#120;&#97;&#109;&#112;&#108;&#101;&#46;&#99;&#111;&#109;&#62;</td>
   <td class="age">*</td> (glob)
   <td class="indexlinks"></td>
+  <td>
+  <a href="/coll/a/.hg/patches/atom-log" title="subscribe to repository atom feed">
+  <img class="atom-logo" src="/static/feed-icon-14x14.png" alt="subscribe to repository atom feed">
+  </a>
+  </td>
   </tr>
   
   <tr class="parity0">
@@ -247,6 +268,11 @@
   <td>&#70;&#111;&#111;&#32;&#66;&#97;&#114;&#32;&#60;&#102;&#111;&#111;&#46;&#98;&#97;&#114;&#64;&#101;&#120;&#97;&#109;&#112;&#108;&#101;&#46;&#99;&#111;&#109;&#62;</td>
   <td class="age">*</td> (glob)
   <td class="indexlinks"></td>
+  <td>
+  <a href="/coll/b/atom-log" title="subscribe to repository atom feed">
+  <img class="atom-logo" src="/static/feed-icon-14x14.png" alt="subscribe to repository atom feed">
+  </a>
+  </td>
   </tr>
   
   <tr class="parity1">
@@ -255,6 +281,11 @@
   <td>&#70;&#111;&#111;&#32;&#66;&#97;&#114;&#32;&#60;&#102;&#111;&#111;&#46;&#98;&#97;&#114;&#64;&#101;&#120;&#97;&#109;&#112;&#108;&#101;&#46;&#99;&#111;&#109;&#62;</td>
   <td class="age">*</td> (glob)
   <td class="indexlinks"></td>
+  <td>
+  <a href="/coll/c/atom-log" title="subscribe to repository atom feed">
+  <img class="atom-logo" src="/static/feed-icon-14x14.png" alt="subscribe to repository atom feed">
+  </a>
+  </td>
   </tr>
   
   <tr class="parity0">
@@ -263,6 +294,11 @@
   <td>&#70;&#111;&#111;&#32;&#66;&#97;&#114;&#32;&#60;&#102;&#111;&#111;&#46;&#98;&#97;&#114;&#64;&#101;&#120;&#97;&#109;&#112;&#108;&#101;&#46;&#99;&#111;&#109;&#62;</td>
   <td class="age">*</td> (glob)
   <td class="indexlinks"></td>
+  <td>
+  <a href="/coll/notrepo/e/atom-log" title="subscribe to repository atom feed">
+  <img class="atom-logo" src="/static/feed-icon-14x14.png" alt="subscribe to repository atom feed">
+  </a>
+  </td>
   </tr>
   
   <tr class="parity1">
@@ -271,6 +307,11 @@
   <td>&#70;&#111;&#111;&#32;&#66;&#97;&#114;&#32;&#60;&#102;&#111;&#111;&#46;&#98;&#97;&#114;&#64;&#101;&#120;&#97;&#109;&#112;&#108;&#101;&#46;&#99;&#111;&#109;&#62;</td>
   <td class="age">*</td> (glob)
   <td class="indexlinks"></td>
+  <td>
+  <a href="/coll/notrepo/f/atom-log" title="subscribe to repository atom feed">
+  <img class="atom-logo" src="/static/feed-icon-14x14.png" alt="subscribe to repository atom feed">
+  </a>
+  </td>
   </tr>
   
   <tr class="parity0">
@@ -279,6 +320,11 @@
   <td>&#70;&#111;&#111;&#32;&#66;&#97;&#114;&#32;&#60;&#102;&#111;&#111;&#46;&#98;&#97;&#114;&#64;&#101;&#120;&#97;&#109;&#112;&#108;&#101;&#46;&#99;&#111;&#109;&#62;</td>
   <td class="age">*</td> (glob)
   <td class="indexlinks"></td>
+  <td>
+  <a href="/rcoll/a/atom-log" title="subscribe to repository atom feed">
+  <img class="atom-logo" src="/static/feed-icon-14x14.png" alt="subscribe to repository atom feed">
+  </a>
+  </td>
   </tr>
   
   <tr class="parity1">
@@ -287,6 +333,11 @@
   <td>&#70;&#111;&#111;&#32;&#66;&#97;&#114;&#32;&#60;&#102;&#111;&#111;&#46;&#98;&#97;&#114;&#64;&#101;&#120;&#97;&#109;&#112;&#108;&#101;&#46;&#99;&#111;&#109;&#62;</td>
   <td class="age">*</td> (glob)
   <td class="indexlinks"></td>
+  <td>
+  <a href="/rcoll/a/.hg/patches/atom-log" title="subscribe to repository atom feed">
+  <img class="atom-logo" src="/static/feed-icon-14x14.png" alt="subscribe to repository atom feed">
+  </a>
+  </td>
   </tr>
   
   <tr class="parity0">
@@ -295,6 +346,11 @@
   <td>&#70;&#111;&#111;&#32;&#66;&#97;&#114;&#32;&#60;&#102;&#111;&#111;&#46;&#98;&#97;&#114;&#64;&#101;&#120;&#97;&#109;&#112;&#108;&#101;&#46;&#99;&#111;&#109;&#62;</td>
   <td class="age">*</td> (glob)
   <td class="indexlinks"></td>
+  <td>
+  <a href="/rcoll/b/atom-log" title="subscribe to repository atom feed">
+  <img class="atom-logo" src="/static/feed-icon-14x14.png" alt="subscribe to repository atom feed">
+  </a>
+  </td>
   </tr>
   
   <tr class="parity1">
@@ -303,6 +359,11 @@
   <td>&#70;&#111;&#111;&#32;&#66;&#97;&#114;&#32;&#60;&#102;&#111;&#111;&#46;&#98;&#97;&#114;&#64;&#101;&#120;&#97;&#109;&#112;&#108;&#101;&#46;&#99;&#111;&#109;&#62;</td>
   <td class="age">*</td> (glob)
   <td class="indexlinks"></td>
+  <td>
+  <a href="/rcoll/b/d/atom-log" title="subscribe to repository atom feed">
+  <img class="atom-logo" src="/static/feed-icon-14x14.png" alt="subscribe to repository atom feed">
+  </a>
+  </td>
   </tr>
   
   <tr class="parity0">
@@ -311,6 +372,11 @@
   <td>&#70;&#111;&#111;&#32;&#66;&#97;&#114;&#32;&#60;&#102;&#111;&#111;&#46;&#98;&#97;&#114;&#64;&#101;&#120;&#97;&#109;&#112;&#108;&#101;&#46;&#99;&#111;&#109;&#62;</td>
   <td class="age">*</td> (glob)
   <td class="indexlinks"></td>
+  <td>
+  <a href="/rcoll/c/atom-log" title="subscribe to repository atom feed">
+  <img class="atom-logo" src="/static/feed-icon-14x14.png" alt="subscribe to repository atom feed">
+  </a>
+  </td>
   </tr>
   
   <tr class="parity1">
@@ -319,6 +385,11 @@
   <td>&#70;&#111;&#111;&#32;&#66;&#97;&#114;&#32;&#60;&#102;&#111;&#111;&#46;&#98;&#97;&#114;&#64;&#101;&#120;&#97;&#109;&#112;&#108;&#101;&#46;&#99;&#111;&#109;&#62;</td>
   <td class="age">*</td> (glob)
   <td class="indexlinks"></td>
+  <td>
+  <a href="/rcoll/notrepo/e/atom-log" title="subscribe to repository atom feed">
+  <img class="atom-logo" src="/static/feed-icon-14x14.png" alt="subscribe to repository atom feed">
+  </a>
+  </td>
   </tr>
   
   <tr class="parity0">
@@ -327,6 +398,11 @@
   <td>&#70;&#111;&#111;&#32;&#66;&#97;&#114;&#32;&#60;&#102;&#111;&#111;&#46;&#98;&#97;&#114;&#64;&#101;&#120;&#97;&#109;&#112;&#108;&#101;&#46;&#99;&#111;&#109;&#62;</td>
   <td class="age">*</td> (glob)
   <td class="indexlinks"></td>
+  <td>
+  <a href="/rcoll/notrepo/e/e2/atom-log" title="subscribe to repository atom feed">
+  <img class="atom-logo" src="/static/feed-icon-14x14.png" alt="subscribe to repository atom feed">
+  </a>
+  </td>
   </tr>
   
   <tr class="parity1">
@@ -335,6 +411,11 @@
   <td>&#70;&#111;&#111;&#32;&#66;&#97;&#114;&#32;&#60;&#102;&#111;&#111;&#46;&#98;&#97;&#114;&#64;&#101;&#120;&#97;&#109;&#112;&#108;&#101;&#46;&#99;&#111;&#109;&#62;</td>
   <td class="age">*</td> (glob)
   <td class="indexlinks"></td>
+  <td>
+  <a href="/rcoll/notrepo/f/atom-log" title="subscribe to repository atom feed">
+  <img class="atom-logo" src="/static/feed-icon-14x14.png" alt="subscribe to repository atom feed">
+  </a>
+  </td>
   </tr>
   
   <tr class="parity0">
@@ -343,6 +424,11 @@
   <td>&#70;&#111;&#111;&#32;&#66;&#97;&#114;&#32;&#60;&#102;&#111;&#111;&#46;&#98;&#97;&#114;&#64;&#101;&#120;&#97;&#109;&#112;&#108;&#101;&#46;&#99;&#111;&#109;&#62;</td>
   <td class="age">*</td> (glob)
   <td class="indexlinks"></td>
+  <td>
+  <a href="/rcoll/notrepo/f/f2/atom-log" title="subscribe to repository atom feed">
+  <img class="atom-logo" src="/static/feed-icon-14x14.png" alt="subscribe to repository atom feed">
+  </a>
+  </td>
   </tr>
   
   <tr class="parity1">
@@ -351,6 +437,11 @@
   <td>&#70;&#111;&#111;&#32;&#66;&#97;&#114;&#32;&#60;&#102;&#111;&#111;&#46;&#98;&#97;&#114;&#64;&#101;&#120;&#97;&#109;&#112;&#108;&#101;&#46;&#99;&#111;&#109;&#62;</td>
   <td class="age">*</td> (glob)
   <td class="indexlinks"></td>
+  <td>
+  <a href="/star/webdir/a/atom-log" title="subscribe to repository atom feed">
+  <img class="atom-logo" src="/static/feed-icon-14x14.png" alt="subscribe to repository atom feed">
+  </a>
+  </td>
   </tr>
   
   <tr class="parity0">
@@ -359,6 +450,11 @@
   <td>&#70;&#111;&#111;&#32;&#66;&#97;&#114;&#32;&#60;&#102;&#111;&#111;&#46;&#98;&#97;&#114;&#64;&#101;&#120;&#97;&#109;&#112;&#108;&#101;&#46;&#99;&#111;&#109;&#62;</td>
   <td class="age">*</td> (glob)
   <td class="indexlinks"></td>
+  <td>
+  <a href="/star/webdir/a/.hg/patches/atom-log" title="subscribe to repository atom feed">
+  <img class="atom-logo" src="/static/feed-icon-14x14.png" alt="subscribe to repository atom feed">
+  </a>
+  </td>
   </tr>
   
   <tr class="parity1">
@@ -367,6 +463,11 @@
   <td>&#70;&#111;&#111;&#32;&#66;&#97;&#114;&#32;&#60;&#102;&#111;&#111;&#46;&#98;&#97;&#114;&#64;&#101;&#120;&#97;&#109;&#112;&#108;&#101;&#46;&#99;&#111;&#109;&#62;</td>
   <td class="age">*</td> (glob)
   <td class="indexlinks"></td>
+  <td>
+  <a href="/star/webdir/b/atom-log" title="subscribe to repository atom feed">
+  <img class="atom-logo" src="/static/feed-icon-14x14.png" alt="subscribe to repository atom feed">
+  </a>
+  </td>
   </tr>
   
   <tr class="parity0">
@@ -375,6 +476,11 @@
   <td>&#70;&#111;&#111;&#32;&#66;&#97;&#114;&#32;&#60;&#102;&#111;&#111;&#46;&#98;&#97;&#114;&#64;&#101;&#120;&#97;&#109;&#112;&#108;&#101;&#46;&#99;&#111;&#109;&#62;</td>
   <td class="age">*</td> (glob)
   <td class="indexlinks"></td>
+  <td>
+  <a href="/star/webdir/c/atom-log" title="subscribe to repository atom feed">
+  <img class="atom-logo" src="/static/feed-icon-14x14.png" alt="subscribe to repository atom feed">
+  </a>
+  </td>
   </tr>
   
   <tr class="parity1">
@@ -383,6 +489,11 @@
   <td>&#70;&#111;&#111;&#32;&#66;&#97;&#114;&#32;&#60;&#102;&#111;&#111;&#46;&#98;&#97;&#114;&#64;&#101;&#120;&#97;&#109;&#112;&#108;&#101;&#46;&#99;&#111;&#109;&#62;</td>
   <td class="age">*</td> (glob)
   <td class="indexlinks"></td>
+  <td>
+  <a href="/star/webdir/notrepo/e/atom-log" title="subscribe to repository atom feed">
+  <img class="atom-logo" src="/static/feed-icon-14x14.png" alt="subscribe to repository atom feed">
+  </a>
+  </td>
   </tr>
   
   <tr class="parity0">
@@ -391,6 +502,11 @@
   <td>&#70;&#111;&#111;&#32;&#66;&#97;&#114;&#32;&#60;&#102;&#111;&#111;&#46;&#98;&#97;&#114;&#64;&#101;&#120;&#97;&#109;&#112;&#108;&#101;&#46;&#99;&#111;&#109;&#62;</td>
   <td class="age">*</td> (glob)
   <td class="indexlinks"></td>
+  <td>
+  <a href="/star/webdir/notrepo/f/atom-log" title="subscribe to repository atom feed">
+  <img class="atom-logo" src="/static/feed-icon-14x14.png" alt="subscribe to repository atom feed">
+  </a>
+  </td>
   </tr>
   
   <tr class="parity1">
@@ -399,6 +515,11 @@
   <td>&#70;&#111;&#111;&#32;&#66;&#97;&#114;&#32;&#60;&#102;&#111;&#111;&#46;&#98;&#97;&#114;&#64;&#101;&#120;&#97;&#109;&#112;&#108;&#101;&#46;&#99;&#111;&#109;&#62;</td>
   <td class="age">*</td> (glob)
   <td class="indexlinks"></td>
+  <td>
+  <a href="/starstar/webdir/a/atom-log" title="subscribe to repository atom feed">
+  <img class="atom-logo" src="/static/feed-icon-14x14.png" alt="subscribe to repository atom feed">
+  </a>
+  </td>
   </tr>
   
   <tr class="parity0">
@@ -407,6 +528,11 @@
   <td>&#70;&#111;&#111;&#32;&#66;&#97;&#114;&#32;&#60;&#102;&#111;&#111;&#46;&#98;&#97;&#114;&#64;&#101;&#120;&#97;&#109;&#112;&#108;&#101;&#46;&#99;&#111;&#109;&#62;</td>
   <td class="age">*</td> (glob)
   <td class="indexlinks"></td>
+  <td>
+  <a href="/starstar/webdir/a/.hg/patches/atom-log" title="subscribe to repository atom feed">
+  <img class="atom-logo" src="/static/feed-icon-14x14.png" alt="subscribe to repository atom feed">
+  </a>
+  </td>
   </tr>
   
   <tr class="parity1">
@@ -415,6 +541,11 @@
   <td>&#70;&#111;&#111;&#32;&#66;&#97;&#114;&#32;&#60;&#102;&#111;&#111;&#46;&#98;&#97;&#114;&#64;&#101;&#120;&#97;&#109;&#112;&#108;&#101;&#46;&#99;&#111;&#109;&#62;</td>
   <td class="age">*</td> (glob)
   <td class="indexlinks"></td>
+  <td>
+  <a href="/starstar/webdir/b/atom-log" title="subscribe to repository atom feed">
+  <img class="atom-logo" src="/static/feed-icon-14x14.png" alt="subscribe to repository atom feed">
+  </a>
+  </td>
   </tr>
   
   <tr class="parity0">
@@ -423,6 +554,11 @@
   <td>&#70;&#111;&#111;&#32;&#66;&#97;&#114;&#32;&#60;&#102;&#111;&#111;&#46;&#98;&#97;&#114;&#64;&#101;&#120;&#97;&#109;&#112;&#108;&#101;&#46;&#99;&#111;&#109;&#62;</td>
   <td class="age">*</td> (glob)
   <td class="indexlinks"></td>
+  <td>
+  <a href="/starstar/webdir/b/d/atom-log" title="subscribe to repository atom feed">
+  <img class="atom-logo" src="/static/feed-icon-14x14.png" alt="subscribe to repository atom feed">
+  </a>
+  </td>
   </tr>
   
   <tr class="parity1">
@@ -431,6 +567,11 @@
   <td>&#70;&#111;&#111;&#32;&#66;&#97;&#114;&#32;&#60;&#102;&#111;&#111;&#46;&#98;&#97;&#114;&#64;&#101;&#120;&#97;&#109;&#112;&#108;&#101;&#46;&#99;&#111;&#109;&#62;</td>
   <td class="age">*</td> (glob)
   <td class="indexlinks"></td>
+  <td>
+  <a href="/starstar/webdir/c/atom-log" title="subscribe to repository atom feed">
+  <img class="atom-logo" src="/static/feed-icon-14x14.png" alt="subscribe to repository atom feed">
+  </a>
+  </td>
   </tr>
   
   <tr class="parity0">
@@ -439,6 +580,11 @@
   <td>&#70;&#111;&#111;&#32;&#66;&#97;&#114;&#32;&#60;&#102;&#111;&#111;&#46;&#98;&#97;&#114;&#64;&#101;&#120;&#97;&#109;&#112;&#108;&#101;&#46;&#99;&#111;&#109;&#62;</td>
   <td class="age">*</td> (glob)
   <td class="indexlinks"></td>
+  <td>
+  <a href="/starstar/webdir/notrepo/e/atom-log" title="subscribe to repository atom feed">
+  <img class="atom-logo" src="/static/feed-icon-14x14.png" alt="subscribe to repository atom feed">
+  </a>
+  </td>
   </tr>
   
   <tr class="parity1">
@@ -447,6 +593,11 @@
   <td>&#70;&#111;&#111;&#32;&#66;&#97;&#114;&#32;&#60;&#102;&#111;&#111;&#46;&#98;&#97;&#114;&#64;&#101;&#120;&#97;&#109;&#112;&#108;&#101;&#46;&#99;&#111;&#109;&#62;</td>
   <td class="age">*</td> (glob)
   <td class="indexlinks"></td>
+  <td>
+  <a href="/starstar/webdir/notrepo/e/e2/atom-log" title="subscribe to repository atom feed">
+  <img class="atom-logo" src="/static/feed-icon-14x14.png" alt="subscribe to repository atom feed">
+  </a>
+  </td>
   </tr>
   
   <tr class="parity0">
@@ -455,6 +606,11 @@
   <td>&#70;&#111;&#111;&#32;&#66;&#97;&#114;&#32;&#60;&#102;&#111;&#111;&#46;&#98;&#97;&#114;&#64;&#101;&#120;&#97;&#109;&#112;&#108;&#101;&#46;&#99;&#111;&#109;&#62;</td>
   <td class="age">*</td> (glob)
   <td class="indexlinks"></td>
+  <td>
+  <a href="/starstar/webdir/notrepo/f/atom-log" title="subscribe to repository atom feed">
+  <img class="atom-logo" src="/static/feed-icon-14x14.png" alt="subscribe to repository atom feed">
+  </a>
+  </td>
   </tr>
   
   <tr class="parity1">
@@ -463,6 +619,11 @@
   <td>&#70;&#111;&#111;&#32;&#66;&#97;&#114;&#32;&#60;&#102;&#111;&#111;&#46;&#98;&#97;&#114;&#64;&#101;&#120;&#97;&#109;&#112;&#108;&#101;&#46;&#99;&#111;&#109;&#62;</td>
   <td class="age">*</td> (glob)
   <td class="indexlinks"></td>
+  <td>
+  <a href="/starstar/webdir/notrepo/f/f2/atom-log" title="subscribe to repository atom feed">
+  <img class="atom-logo" src="/static/feed-icon-14x14.png" alt="subscribe to repository atom feed">
+  </a>
+  </td>
   </tr>
   
   <tr class="parity0">
@@ -471,6 +632,11 @@
   <td>&#70;&#111;&#111;&#32;&#66;&#97;&#114;&#32;&#60;&#102;&#111;&#111;&#46;&#98;&#97;&#114;&#64;&#101;&#120;&#97;&#109;&#112;&#108;&#101;&#46;&#99;&#111;&#109;&#62;</td>
   <td class="age">*</td> (glob)
   <td class="indexlinks"></td>
+  <td>
+  <a href="/astar/atom-log" title="subscribe to repository atom feed">
+  <img class="atom-logo" src="/static/feed-icon-14x14.png" alt="subscribe to repository atom feed">
+  </a>
+  </td>
   </tr>
   
   <tr class="parity1">
@@ -479,6 +645,11 @@
   <td>&#70;&#111;&#111;&#32;&#66;&#97;&#114;&#32;&#60;&#102;&#111;&#111;&#46;&#98;&#97;&#114;&#64;&#101;&#120;&#97;&#109;&#112;&#108;&#101;&#46;&#99;&#111;&#109;&#62;</td>
   <td class="age">*</td> (glob)
   <td class="indexlinks"></td>
+  <td>
+  <a href="/astar/.hg/patches/atom-log" title="subscribe to repository atom feed">
+  <img class="atom-logo" src="/static/feed-icon-14x14.png" alt="subscribe to repository atom feed">
+  </a>
+  </td>
   </tr>
   
   </table>
@@ -523,7 +694,7 @@
   <img src="/static/hglogo.png" width=75 height=90 border=0 alt="mercurial" /></a>
   </div>
   <div class="main">
-  <h2>Mercurial Repositories</h2>
+  <h2 class="breadcrumb"><a href="/">Mercurial</a> &gt; <a href="/t">t</a> </h2>
   
   <table class="bigtable">
       <tr>
@@ -532,6 +703,7 @@
           <th><a href="?sort=contact">Contact</a></th>
           <th><a href="?sort=lastchange">Last modified</a></th>
           <th>&nbsp;</th>
+          <th>&nbsp;</th>
       </tr>
       
   <tr class="parity0">
@@ -540,6 +712,11 @@
   <td>&#70;&#111;&#111;&#32;&#66;&#97;&#114;&#32;&#60;&#102;&#111;&#111;&#46;&#98;&#97;&#114;&#64;&#101;&#120;&#97;&#109;&#112;&#108;&#101;&#46;&#99;&#111;&#109;&#62;</td>
   <td class="age">*</td> (glob)
   <td class="indexlinks"></td>
+  <td>
+  <a href="/t/a/atom-log" title="subscribe to repository atom feed">
+  <img class="atom-logo" src="/static/feed-icon-14x14.png" alt="subscribe to repository atom feed">
+  </a>
+  </td>
   </tr>
   
   </table>
@@ -890,7 +1067,7 @@
   <img src="/static/hglogo.png" width=75 height=90 border=0 alt="mercurial" /></a>
   </div>
   <div class="main">
-  <h2>Mercurial Repositories</h2>
+  <h2 class="breadcrumb"><a href="/">Mercurial</a> </h2>
   
   <table class="bigtable">
       <tr>
@@ -899,6 +1076,7 @@
           <th><a href="?sort=contact">Contact</a></th>
           <th><a href="?sort=lastchange">Last modified</a></th>
           <th>&nbsp;</th>
+          <th>&nbsp;</th>
       </tr>
       
   </table>
--- a/tests/test-highlight.t	Mon Jan 14 23:14:45 2013 +0900
+++ b/tests/test-highlight.t	Sat Jan 19 17:24:33 2013 -0600
@@ -103,7 +103,7 @@
   </div>
   
   <div class="main">
-  <h2><a href="/">test</a></h2>
+  <h2 class="breadcrumb"><a href="/">Mercurial</a> </h2>
   <h3>view primes.py @ 0:853dcd4de2a6</h3>
   
   <form class="search" action="/log">
@@ -234,7 +234,7 @@
   </div>
   
   <div class="main">
-  <h2><a href="/">test</a></h2>
+  <h2 class="breadcrumb"><a href="/">Mercurial</a> </h2>
   <h3>annotate primes.py @ 0:853dcd4de2a6</h3>
   
   <form class="search" action="/log">
--- a/tests/test-histedit-bookmark-motion.t	Mon Jan 14 23:14:45 2013 +0900
+++ b/tests/test-histedit-bookmark-motion.t	Sat Jan 19 17:24:33 2013 -0600
@@ -84,30 +84,30 @@
   > pick 652413bf663e 5 f
   > EOF
   $ hg histedit 1 --commands commands.txt --verbose | grep histedit
-  histedit: moving bookmarks two from 177f92b77385 to d36c0562f908
-  histedit: moving bookmarks three from 055a42cdd887 to ae467701c500
-  histedit: moving bookmarks four from e860deea161a to ae467701c500
-  histedit: moving bookmarks also-two from 177f92b77385 to d36c0562f908
+  histedit: moving bookmarks also-two from 177f92b77385 to b346ab9a313d
+  histedit: moving bookmarks five from 652413bf663e to cacdfd884a93
+  histedit: moving bookmarks four from e860deea161a to 59d9f330561f
+  histedit: moving bookmarks three from 055a42cdd887 to 59d9f330561f
+  histedit: moving bookmarks two from 177f92b77385 to b346ab9a313d
   histedit: moving bookmarks will-move-backwards from d2ae7f538514 to cb9a9f314b8b
-  histedit: moving bookmarks five from 652413bf663e to 0efacef7cb48
   saved backup bundle to $TESTTMP/r/.hg/strip-backup/d2ae7f538514-backup.hg (glob)
-  saved backup bundle to $TESTTMP/r/.hg/strip-backup/34a9919932c1-backup.hg (glob)
+  saved backup bundle to $TESTTMP/r/.hg/strip-backup/96e494a2d553-backup.hg (glob)
   $ hg log --graph
-  @  changeset:   3:0efacef7cb48
+  @  changeset:   3:cacdfd884a93
   |  bookmark:    five
   |  tag:         tip
   |  user:        test
   |  date:        Thu Jan 01 00:00:00 1970 +0000
   |  summary:     f
   |
-  o  changeset:   2:ae467701c500
+  o  changeset:   2:59d9f330561f
   |  bookmark:    four
   |  bookmark:    three
   |  user:        test
   |  date:        Thu Jan 01 00:00:00 1970 +0000
   |  summary:     d
   |
-  o  changeset:   1:d36c0562f908
+  o  changeset:   1:b346ab9a313d
   |  bookmark:    also-two
   |  bookmark:    two
   |  user:        test
@@ -121,11 +121,11 @@
      summary:     a
   
   $ HGEDITOR=cat hg histedit 1
-  pick d36c0562f908 1 c
-  pick ae467701c500 2 d
-  pick 0efacef7cb48 3 f
+  pick b346ab9a313d 1 c
+  pick 59d9f330561f 2 d
+  pick cacdfd884a93 3 f
   
-  # Edit history between d36c0562f908 and 0efacef7cb48
+  # Edit history between b346ab9a313d and cacdfd884a93
   #
   # Commands:
   #  p, pick = use commit
@@ -136,21 +136,21 @@
   #
   0 files updated, 0 files merged, 0 files removed, 0 files unresolved
   $ cat > commands.txt << EOF
-  > pick d36c0562f908 1 c
-  > pick 0efacef7cb48 3 f
-  > pick ae467701c500 2 d
+  > pick b346ab9a313d 1 c
+  > pick cacdfd884a93 3 f
+  > pick 59d9f330561f 2 d
   > EOF
   $ hg histedit 1 --commands commands.txt --verbose | grep histedit
-  histedit: moving bookmarks three from ae467701c500 to 1be9c35b4cb2
-  histedit: moving bookmarks four from ae467701c500 to 1be9c35b4cb2
-  histedit: moving bookmarks five from 0efacef7cb48 to 1be9c35b4cb2
-  saved backup bundle to $TESTTMP/r/.hg/strip-backup/ae467701c500-backup.hg (glob)
+  histedit: moving bookmarks five from cacdfd884a93 to c04e50810e4b
+  histedit: moving bookmarks four from 59d9f330561f to c04e50810e4b
+  histedit: moving bookmarks three from 59d9f330561f to c04e50810e4b
+  saved backup bundle to $TESTTMP/r/.hg/strip-backup/59d9f330561f-backup.hg (glob)
 
 We expect 'five' to stay at tip, since the tipmost bookmark is most
 likely the useful signal.
 
   $ hg log --graph
-  @  changeset:   3:1be9c35b4cb2
+  @  changeset:   3:c04e50810e4b
   |  bookmark:    five
   |  bookmark:    four
   |  bookmark:    three
@@ -159,12 +159,12 @@
   |  date:        Thu Jan 01 00:00:00 1970 +0000
   |  summary:     d
   |
-  o  changeset:   2:7c044e3e33a9
+  o  changeset:   2:c13eb81022ca
   |  user:        test
   |  date:        Thu Jan 01 00:00:00 1970 +0000
   |  summary:     f
   |
-  o  changeset:   1:d36c0562f908
+  o  changeset:   1:b346ab9a313d
   |  bookmark:    also-two
   |  bookmark:    two
   |  user:        test
--- a/tests/test-histedit-commute.t	Mon Jan 14 23:14:45 2013 +0900
+++ b/tests/test-histedit-commute.t	Sat Jan 19 17:24:33 2013 -0600
@@ -94,18 +94,18 @@
 
 log after edit
   $ hg log --graph
-  @  changeset:   5:853c68da763f
+  @  changeset:   5:07114f51870f
   |  tag:         tip
   |  user:        test
   |  date:        Thu Jan 01 00:00:00 1970 +0000
   |  summary:     d
   |
-  o  changeset:   4:26f6a030ae82
+  o  changeset:   4:8ade9693061e
   |  user:        test
   |  date:        Thu Jan 01 00:00:00 1970 +0000
   |  summary:     f
   |
-  o  changeset:   3:b069cc29fb22
+  o  changeset:   3:d8249471110a
   |  user:        test
   |  date:        Thu Jan 01 00:00:00 1970 +0000
   |  summary:     e
@@ -130,9 +130,9 @@
 
   $ cat > $EDITED <<EOF
   > pick 177f92b77385 c
-  > pick 853c68da763f d
-  > pick b069cc29fb22 e
-  > pick 26f6a030ae82 f
+  > pick 07114f51870f d
+  > pick d8249471110a e
+  > pick 8ade9693061e f
   > EOF
   $ HGEDITOR="cat \"$EDITED\" > " hg histedit 177f92b77385 2>&1 | fixbundle
   0 files updated, 0 files merged, 3 files removed, 0 files unresolved
@@ -141,18 +141,18 @@
   0 files updated, 0 files merged, 0 files removed, 0 files unresolved
 
   $ hg log --graph
-  @  changeset:   5:652413bf663e
+  @  changeset:   5:7eca9b5b1148
   |  tag:         tip
   |  user:        test
   |  date:        Thu Jan 01 00:00:00 1970 +0000
   |  summary:     f
   |
-  o  changeset:   4:e860deea161a
+  o  changeset:   4:915da888f2de
   |  user:        test
   |  date:        Thu Jan 01 00:00:00 1970 +0000
   |  summary:     e
   |
-  o  changeset:   3:055a42cdd887
+  o  changeset:   3:10517e47bbbb
   |  user:        test
   |  date:        Thu Jan 01 00:00:00 1970 +0000
   |  summary:     d
@@ -176,9 +176,9 @@
 slightly different this time
 
   $ cat > $EDITED <<EOF
-  > pick 055a42cdd887 d
-  > pick 652413bf663e f
-  > pick e860deea161a e
+  > pick 10517e47bbbb d
+  > pick 7eca9b5b1148 f
+  > pick 915da888f2de e
   > pick 177f92b77385 c
   > EOF
   $ HGEDITOR="cat \"$EDITED\" > " hg histedit 177f92b77385 2>&1 | fixbundle
@@ -188,23 +188,23 @@
   0 files updated, 0 files merged, 0 files removed, 0 files unresolved
   0 files updated, 0 files merged, 0 files removed, 0 files unresolved
   $ hg log --graph
-  @  changeset:   5:99a62755c625
+  @  changeset:   5:38b92f448761
   |  tag:         tip
   |  user:        test
   |  date:        Thu Jan 01 00:00:00 1970 +0000
   |  summary:     c
   |
-  o  changeset:   4:7c6fdd608667
+  o  changeset:   4:de71b079d9ce
   |  user:        test
   |  date:        Thu Jan 01 00:00:00 1970 +0000
   |  summary:     e
   |
-  o  changeset:   3:c4f52e213402
+  o  changeset:   3:be9ae3a309c6
   |  user:        test
   |  date:        Thu Jan 01 00:00:00 1970 +0000
   |  summary:     f
   |
-  o  changeset:   2:bfe4a5a76b37
+  o  changeset:   2:799205341b6b
   |  user:        test
   |  date:        Thu Jan 01 00:00:00 1970 +0000
   |  summary:     d
@@ -222,48 +222,48 @@
 
 keep prevents stripping dead revs
   $ cat > $EDITED <<EOF
-  > pick bfe4a5a76b37 d
-  > pick c4f52e213402 f
-  > pick 99a62755c625 c
-  > pick 7c6fdd608667 e
+  > pick 799205341b6b d
+  > pick be9ae3a309c6 f
+  > pick 38b92f448761 c
+  > pick de71b079d9ce e
   > EOF
-  $ HGEDITOR="cat \"$EDITED\" > " hg histedit bfe4a5a76b37 --keep 2>&1 | fixbundle
+  $ HGEDITOR="cat \"$EDITED\" > " hg histedit 799205341b6b --keep 2>&1 | fixbundle
   0 files updated, 0 files merged, 2 files removed, 0 files unresolved
   0 files updated, 0 files merged, 0 files removed, 0 files unresolved
   0 files updated, 0 files merged, 0 files removed, 0 files unresolved
   $ hg log --graph
   > cat > $EDITED <<EOF
-  > pick 7c6fdd608667 e
-  > pick 99a62755c625 c
+  > pick de71b079d9ce e
+  > pick 38b92f448761 c
   > EOF
-  @  changeset:   7:99e266581538
+  @  changeset:   7:803ef1c6fcfd
   |  tag:         tip
   |  user:        test
   |  date:        Thu Jan 01 00:00:00 1970 +0000
   |  summary:     e
   |
-  o  changeset:   6:5ad36efb0653
-  |  parent:      3:c4f52e213402
+  o  changeset:   6:ece0b8d93dda
+  |  parent:      3:be9ae3a309c6
   |  user:        test
   |  date:        Thu Jan 01 00:00:00 1970 +0000
   |  summary:     c
   |
-  | o  changeset:   5:99a62755c625
+  | o  changeset:   5:38b92f448761
   | |  user:        test
   | |  date:        Thu Jan 01 00:00:00 1970 +0000
   | |  summary:     c
   | |
-  | o  changeset:   4:7c6fdd608667
+  | o  changeset:   4:de71b079d9ce
   |/   user:        test
   |    date:        Thu Jan 01 00:00:00 1970 +0000
   |    summary:     e
   |
-  o  changeset:   3:c4f52e213402
+  o  changeset:   3:be9ae3a309c6
   |  user:        test
   |  date:        Thu Jan 01 00:00:00 1970 +0000
   |  summary:     f
   |
-  o  changeset:   2:bfe4a5a76b37
+  o  changeset:   2:799205341b6b
   |  user:        test
   |  date:        Thu Jan 01 00:00:00 1970 +0000
   |  summary:     d
@@ -283,34 +283,34 @@
   $ hg histedit --commands "$EDITED" --rev -2 2>&1 | fixbundle
   abort: may not use changesets other than the ones listed
   $ hg log --graph
-  @  changeset:   7:99e266581538
+  @  changeset:   7:803ef1c6fcfd
   |  tag:         tip
   |  user:        test
   |  date:        Thu Jan 01 00:00:00 1970 +0000
   |  summary:     e
   |
-  o  changeset:   6:5ad36efb0653
-  |  parent:      3:c4f52e213402
+  o  changeset:   6:ece0b8d93dda
+  |  parent:      3:be9ae3a309c6
   |  user:        test
   |  date:        Thu Jan 01 00:00:00 1970 +0000
   |  summary:     c
   |
-  | o  changeset:   5:99a62755c625
+  | o  changeset:   5:38b92f448761
   | |  user:        test
   | |  date:        Thu Jan 01 00:00:00 1970 +0000
   | |  summary:     c
   | |
-  | o  changeset:   4:7c6fdd608667
+  | o  changeset:   4:de71b079d9ce
   |/   user:        test
   |    date:        Thu Jan 01 00:00:00 1970 +0000
   |    summary:     e
   |
-  o  changeset:   3:c4f52e213402
+  o  changeset:   3:be9ae3a309c6
   |  user:        test
   |  date:        Thu Jan 01 00:00:00 1970 +0000
   |  summary:     f
   |
-  o  changeset:   2:bfe4a5a76b37
+  o  changeset:   2:799205341b6b
   |  user:        test
   |  date:        Thu Jan 01 00:00:00 1970 +0000
   |  summary:     d
--- a/tests/test-histedit-drop.t	Mon Jan 14 23:14:45 2013 +0900
+++ b/tests/test-histedit-drop.t	Sat Jan 19 17:24:33 2013 -0600
@@ -69,18 +69,18 @@
 
 log after edit
   $ hg log --graph
-  @  changeset:   4:708943196e52
+  @  changeset:   4:f518305ce889
   |  tag:         tip
   |  user:        test
   |  date:        Thu Jan 01 00:00:00 1970 +0000
   |  summary:     d
   |
-  o  changeset:   3:75cbdffecadb
+  o  changeset:   3:a4f7421b80f7
   |  user:        test
   |  date:        Thu Jan 01 00:00:00 1970 +0000
   |  summary:     f
   |
-  o  changeset:   2:493dc0964412
+  o  changeset:   2:ee283cb5f2d5
   |  user:        test
   |  date:        Thu Jan 01 00:00:00 1970 +0000
   |  summary:     e
@@ -96,6 +96,25 @@
      summary:     a
   
 
+Check histedit_source
+
+  $ hg log --debug --rev f518305ce889
+  changeset:   4:f518305ce889c07cb5bd05522176d75590ef3324
+  tag:         tip
+  phase:       draft
+  parent:      3:a4f7421b80f79fcc59fff01bcbf4a53d127dd6d3
+  parent:      -1:0000000000000000000000000000000000000000
+  manifest:    4:d3d4f51c157ff242c32ff745d4799aaa26ccda44
+  user:        test
+  date:        Thu Jan 01 00:00:00 1970 +0000
+  files+:      d
+  extra:       branch=default
+  extra:       histedit_source=055a42cdd88768532f9cf79daa407fc8d138de9b
+  description:
+  d
+  
+  
+
 manifest after edit
   $ hg manifest
   a
--- a/tests/test-histedit-edit.t	Mon Jan 14 23:14:45 2013 +0900
+++ b/tests/test-histedit-edit.t	Sat Jan 19 17:24:33 2013 -0600
@@ -88,13 +88,13 @@
   0 files updated, 0 files merged, 0 files removed, 0 files unresolved
 
   $ hg log --graph
-  @  changeset:   6:bf757c081cd0
+  @  changeset:   6:b5f70786f9b0
   |  tag:         tip
   |  user:        test
   |  date:        Thu Jan 01 00:00:00 1970 +0000
   |  summary:     f
   |
-  o  changeset:   5:d6b15fed32d4
+  o  changeset:   5:a5e1ba2f7afb
   |  user:        test
   |  date:        Thu Jan 01 00:00:00 1970 +0000
   |  summary:     foobaz
@@ -128,8 +128,26 @@
   $ hg cat e
   a
 
+check histedit_source
+
+  $ hg log --debug --rev 5
+  changeset:   5:a5e1ba2f7afb899ef1581cea528fd885d2fca70d
+  phase:       draft
+  parent:      4:1a60820cd1f6004a362aa622ebc47d59bc48eb34
+  parent:      -1:0000000000000000000000000000000000000000
+  manifest:    5:5ad3be8791f39117565557781f5464363b918a45
+  user:        test
+  date:        Thu Jan 01 00:00:00 1970 +0000
+  files:       e
+  extra:       branch=default
+  extra:       histedit_source=e860deea161a2f77de56603b340ebbb4536308ae
+  description:
+  foobaz
+  
+  
+
   $ cat > $EDITED <<EOF
-  > edit bf757c081cd0 f
+  > edit b5f70786f9b0 f
   > EOF
   $ HGEDITOR="cat \"$EDITED\" > " hg histedit tip 2>&1 | fixbundle
   0 files updated, 0 files merged, 1 files removed, 0 files unresolved
@@ -139,11 +157,13 @@
   A f
   $ HGEDITOR='true' hg histedit --continue
   0 files updated, 0 files merged, 0 files removed, 0 files unresolved
+  saved backup bundle to $TESTTMP/r/.hg/strip-backup/b5f70786f9b0-backup.hg (glob)
+
   $ hg status
 
 log after edit
   $ hg log --limit 1
-  changeset:   6:bf757c081cd0
+  changeset:   6:a107ee126658
   tag:         tip
   user:        test
   date:        Thu Jan 01 00:00:00 1970 +0000
@@ -160,7 +180,7 @@
   0 files updated, 0 files merged, 0 files removed, 0 files unresolved
   $ hg status
   $ hg log --limit 1
-  changeset:   6:bf757c081cd0
+  changeset:   6:1fd3b2fe7754
   tag:         tip
   user:        test
   date:        Thu Jan 01 00:00:00 1970 +0000
@@ -169,18 +189,18 @@
 
 modify the message
   $ cat > $EDITED <<EOF
-  > mess bf757c081cd0 f
+  > mess 1fd3b2fe7754 f
   > EOF
   $ HGEDITOR="cat \"$EDITED\" > " hg histedit tip 2>&1 | fixbundle
   0 files updated, 0 files merged, 1 files removed, 0 files unresolved
   0 files updated, 0 files merged, 0 files removed, 0 files unresolved
   $ hg status
   $ hg log --limit 1
-  changeset:   6:0b16746f8e89
+  changeset:   6:5585e802ef99
   tag:         tip
   user:        test
   date:        Thu Jan 01 00:00:00 1970 +0000
-  summary:     mess bf757c081cd0 f
+  summary:     mess 1fd3b2fe7754 f
   
 
 rollback should not work after a histedit
--- a/tests/test-histedit-fold-non-commute.t	Mon Jan 14 23:14:45 2013 +0900
+++ b/tests/test-histedit-fold-non-commute.t	Sat Jan 19 17:24:33 2013 -0600
@@ -136,13 +136,13 @@
 
 log after edit
   $ hg log --graph
-  @  changeset:   5:2696a654c663
+  @  changeset:   5:d9cf42e54966
   |  tag:         tip
   |  user:        test
   |  date:        Thu Jan 01 00:00:00 1970 +0000
   |  summary:     f
   |
-  o  changeset:   4:ec2c1cf833a8
+  o  changeset:   4:10486af2e984
   |  user:        test
   |  date:        Thu Jan 01 00:00:00 1970 +0000
   |  summary:     d
--- a/tests/test-histedit-fold.t	Mon Jan 14 23:14:45 2013 +0900
+++ b/tests/test-histedit-fold.t	Sat Jan 19 17:24:33 2013 -0600
@@ -72,18 +72,18 @@
 
 log after edit
   $ hg log --graph
-  @  changeset:   4:82b0c1ff1777
+  @  changeset:   4:7e0a290363ed
   |  tag:         tip
   |  user:        test
   |  date:        Thu Jan 01 00:00:00 1970 +0000
   |  summary:     d
   |
-  o  changeset:   3:150aafb44a91
+  o  changeset:   3:5e24935bad3d
   |  user:        test
   |  date:        Thu Jan 01 00:00:00 1970 +0000
   |  summary:     pick e860deea161a e
   |
-  o  changeset:   2:493dc0964412
+  o  changeset:   2:ee283cb5f2d5
   |  user:        test
   |  date:        Thu Jan 01 00:00:00 1970 +0000
   |  summary:     e
@@ -108,6 +108,28 @@
   e
   f
 
+
+check histedit_source
+
+  $ hg log --debug --rev 3
+  changeset:   3:5e24935bad3d5a4486de3b90f233e991465ced72
+  phase:       draft
+  parent:      2:ee283cb5f2d5955443f23a27b697a04339e9a39a
+  parent:      -1:0000000000000000000000000000000000000000
+  manifest:    3:81eede616954057198ead0b2c73b41d1f392829a
+  user:        test
+  date:        Thu Jan 01 00:00:00 1970 +0000
+  files+:      c f
+  extra:       branch=default
+  extra:       histedit_source=a4f7421b80f79fcc59fff01bcbf4a53d127dd6d3,177f92b773850b59254aa5e923436f921b55483b
+  description:
+  pick e860deea161a e
+  pick 652413bf663e f
+  fold 177f92b77385 c
+  pick 055a42cdd887 d
+  
+  
+
   $ cd ..
 
 folding and creating no new change doesn't break:
@@ -258,7 +280,7 @@
   0 files updated, 0 files merged, 0 files removed, 0 files unresolved
   saved backup bundle to $TESTTMP/fold-with-dropped/.hg/strip-backup/617f94f13c0f-backup.hg (glob)
   $ hg log -G
-  @  changeset:   1:e29e02896e6c
+  @  changeset:   1:10c647b2cdd5
   |  tag:         tip
   |  user:        test
   |  date:        Thu Jan 01 00:00:00 1970 +0000
@@ -273,7 +295,7 @@
   # HG changeset patch
   # User test
   # Date 0 0
-  # Node ID e29e02896e6c2b149d2228a0a64b4f3a9a4237f3
+  # Node ID 10c647b2cdd54db0603ecb99b2ff5ce66d5a5323
   # Parent  0189ba417d34df9dda55f88b637dcae9917b5964
   +4
   ***
@@ -281,7 +303,7 @@
   ***
   +6
   
-  diff -r 0189ba417d34 -r e29e02896e6c file
+  diff -r 0189ba417d34 -r 10c647b2cdd5 file
   --- a/file	Thu Jan 01 00:00:00 1970 +0000
   +++ b/file	Thu Jan 01 00:00:00 1970 +0000
   @@ -1,3 +1,6 @@
--- a/tests/test-histedit-no-change.t	Mon Jan 14 23:14:45 2013 +0900
+++ b/tests/test-histedit-no-change.t	Sat Jan 19 17:24:33 2013 -0600
@@ -96,17 +96,19 @@
   When you are finished, run hg histedit --continue to resume.
   $ continueediting true "(leaving commit message unaltered)"
   % finalize changeset editing (leaving commit message unaltered)
-  1 files updated, 0 files merged, 0 files removed, 0 files unresolved
+  0 files updated, 0 files merged, 0 files removed, 0 files unresolved
+  0 files updated, 0 files merged, 0 files removed, 0 files unresolved
+
 
 check state of working copy
   $ hg id
-  652413bf663e tip
+  794fe033d0a0 tip
 
   $ graphlog "log after history editing"
   % log after history editing
-  @  5 652413bf663ef2a641cab26574e46d5f5a64a55a "f"
+  @  5 794fe033d0a030f8df77c5de945fca35c9181c30 "f"
   |
-  o  4 e860deea161a2f77de56603b340ebbb4536308ae "e"
+  o  4 04d2fab980779f332dec458cc944f28de8b43435 "e"
   |
   o  3 055a42cdd88768532f9cf79daa407fc8d138de9b "d"
   |
@@ -152,12 +154,14 @@
   When you are finished, run hg histedit --continue to resume.
   $ graphlog "log after first edit"
   % log after first edit
-  o  5 652413bf663ef2a641cab26574e46d5f5a64a55a "f"
+  @  6 e5ae3ca2f1ffdbd89ec41ebc273a231f7c3022f2 "d"
   |
-  o  4 e860deea161a2f77de56603b340ebbb4536308ae "e"
-  |
-  @  3 055a42cdd88768532f9cf79daa407fc8d138de9b "d"
-  |
+  | o  5 652413bf663ef2a641cab26574e46d5f5a64a55a "f"
+  | |
+  | o  4 e860deea161a2f77de56603b340ebbb4536308ae "e"
+  | |
+  | o  3 055a42cdd88768532f9cf79daa407fc8d138de9b "d"
+  |/
   o  2 177f92b773850b59254aa5e923436f921b55483b "c"
   |
   o  1 d2ae7f538514cd87c17547b0de4cea71fe1af9fb "b"
--- a/tests/test-histedit-non-commute.t	Mon Jan 14 23:14:45 2013 +0900
+++ b/tests/test-histedit-non-commute.t	Sat Jan 19 17:24:33 2013 -0600
@@ -174,13 +174,13 @@
 
 log after edit
   $ hg log --graph
-  @  changeset:   6:8e082d1a72ea
+  @  changeset:   6:7efe1373e4bc
   |  tag:         tip
   |  user:        test
   |  date:        Thu Jan 01 00:00:00 1970 +0000
   |  summary:     f
   |
-  o  changeset:   5:13b04d775b81
+  o  changeset:   5:e334d87a1e55
   |  user:        test
   |  date:        Thu Jan 01 00:00:00 1970 +0000
   |  summary:     does not commute with e
@@ -255,13 +255,13 @@
 
 post message fix
   $ hg log --graph
-  @  changeset:   6:f14da722aa4b
+  @  changeset:   6:521c4c32c5e2
   |  tag:         tip
   |  user:        test
   |  date:        Thu Jan 01 00:00:00 1970 +0000
   |  summary:     f
   |
-  o  changeset:   5:382ff1adf0ed
+  o  changeset:   5:f4f088e8adf6
   |  user:        test
   |  date:        Thu Jan 01 00:00:00 1970 +0000
   |  summary:     pick 65a9a84f33fd 3 c
--- a/tests/test-histedit-obsolete.t	Mon Jan 14 23:14:45 2013 +0900
+++ b/tests/test-histedit-obsolete.t	Sat Jan 19 17:24:33 2013 -0600
@@ -14,6 +14,7 @@
   > publish=False
   > [extensions]'
   > histedit=
+  > rebase=
   > 
   > obs=${TESTTMP}/obs.py
   > EOF
@@ -66,13 +67,13 @@
   > pick 652413bf663e 5 f
   > EOF
   $ hg histedit 1 --commands commands.txt --verbose | grep histedit
-  saved backup bundle to $TESTTMP/base/.hg/strip-backup/34a9919932c1-backup.hg (glob)
+  saved backup bundle to $TESTTMP/base/.hg/strip-backup/96e494a2d553-backup.hg (glob)
   $ hg log --graph --hidden
-  @  8:0efacef7cb48 f
+  @  8:cacdfd884a93 f
   |
-  o  7:ae467701c500 d
+  o  7:59d9f330561f d
   |
-  o  6:d36c0562f908 c
+  o  6:b346ab9a313d c
   |
   | x  5:652413bf663e f
   | |
@@ -88,10 +89,10 @@
   
   $ hg debugobsolete
   d2ae7f538514cd87c17547b0de4cea71fe1af9fb 0 {'date': '* *', 'user': 'test'} (glob)
-  177f92b773850b59254aa5e923436f921b55483b d36c0562f908c692f5204d606d4ff3537d41f1bf 0 {'date': '* *', 'user': 'test'} (glob)
-  055a42cdd88768532f9cf79daa407fc8d138de9b ae467701c5006bf21ffcfdb555b3d6b63280b6b7 0 {'date': '* *', 'user': 'test'} (glob)
-  e860deea161a2f77de56603b340ebbb4536308ae ae467701c5006bf21ffcfdb555b3d6b63280b6b7 0 {'date': '* *', 'user': 'test'} (glob)
-  652413bf663ef2a641cab26574e46d5f5a64a55a 0efacef7cb481bf574f69075b82d044fdbe5c20f 0 {'date': '* *', 'user': 'test'} (glob)
+  177f92b773850b59254aa5e923436f921b55483b b346ab9a313db8537ecf96fca3ca3ca984ef3bd7 0 {'date': '* *', 'user': 'test'} (glob)
+  055a42cdd88768532f9cf79daa407fc8d138de9b 59d9f330561fd6c88b1a6b32f0e45034d88db784 0 {'date': '* *', 'user': 'test'} (glob)
+  e860deea161a2f77de56603b340ebbb4536308ae 59d9f330561fd6c88b1a6b32f0e45034d88db784 0 {'date': '* *', 'user': 'test'} (glob)
+  652413bf663ef2a641cab26574e46d5f5a64a55a cacdfd884a9321ec4e1de275ef3949fa953a1f83 0 {'date': '* *', 'user': 'test'} (glob)
 
 
 Ensure hidden revision does not prevent histedit
@@ -100,44 +101,304 @@
 create an hidden revision
 
   $ cat > commands.txt <<EOF
-  > pick d36c0562f908 6 c
-  > drop ae467701c500 7 d
-  > pick 0efacef7cb48 8 f
+  > pick b346ab9a313d 6 c
+  > drop 59d9f330561f 7 d
+  > pick cacdfd884a93 8 f
   > EOF
   $ hg histedit 6 --commands commands.txt
   0 files updated, 0 files merged, 3 files removed, 0 files unresolved
   0 files updated, 0 files merged, 0 files removed, 0 files unresolved
   $ hg log --graph
-  @  9:7c044e3e33a9 f
+  @  9:c13eb81022ca f
   |
-  o  6:d36c0562f908 c
+  o  6:b346ab9a313d c
   |
   o  0:cb9a9f314b8b a
   
 check hidden revision are ignored (6 have hidden children 7 and 8)
 
   $ cat > commands.txt <<EOF
-  > pick d36c0562f908 6 c
-  > pick 7c044e3e33a9 8 f
+  > pick b346ab9a313d 6 c
+  > pick c13eb81022ca 8 f
   > EOF
   $ hg histedit 6 --commands commands.txt
   0 files updated, 0 files merged, 0 files removed, 0 files unresolved
 
 
-Check that histedit respect phases
-=========================================
 
-(not directly related to the test file but doesn't deserve it's own test case)
+Test that rewriting leaving instability behind is allowed
+---------------------------------------------------------------------
 
-  $ hg log -G
-  @  9:7c044e3e33a9 f
-  |
-  o  6:d36c0562f908 c
-  |
-  o  0:cb9a9f314b8b a
-  
+  $ hg up '.^'
+  0 files updated, 0 files merged, 1 files removed, 0 files unresolved
+  $ hg log -r 'children(.)'
+  9:c13eb81022ca f (no-eol)
+  $ cat > commands.txt <<EOF
+  > edit b346ab9a313d 6 c
+  > EOF
+  $ hg histedit -r '.' --commands commands.txt
+  0 files updated, 0 files merged, 1 files removed, 0 files unresolved
+  adding c
+  abort: Make changes as needed, you may commit or record as needed now.
+  When you are finished, run hg histedit --continue to resume.
+  [255]
+  $ echo c >> c
+  $ hg histedit --continue
+  0 files updated, 0 files merged, 0 files removed, 0 files unresolved
+
+  $ hg log -r 'unstable()'
+  9:c13eb81022ca f (no-eol)
+
+stabilise
+
+  $ hg rebase  -r 'unstable()' -d .
+
+
+Test phases support
+===========================================
+
+Check that histedit respect immutability
+-------------------------------------------
+
+  $ cat >> $HGRCPATH << EOF
+  > [ui]
+  > logtemplate= {rev}:{node|short} ({phase}) {desc|firstline}\n
+  > EOF
+
   $ hg ph -pv '.^'
   phase changed for 2 changesets
+  $ hg log -G
+  @  11:b449568bf7fc (draft) f
+  |
+  o  10:40db8afa467b (public) c
+  |
+  o  0:cb9a9f314b8b (public) a
+  
   $ hg histedit -r '.~2'
   abort: cannot edit immutable changeset: cb9a9f314b8b
   [255]
+
+
+Prepare further testing
+-------------------------------------------
+
+  $ for x in g h i j k ; do
+  >     echo $x > $x
+  >     hg add $x
+  >     hg ci -m $x
+  > done
+  $ hg phase --force --secret .~2
+  $ hg log -G
+  @  16:ee118ab9fa44 (secret) k
+  |
+  o  15:3a6c53ee7f3d (secret) j
+  |
+  o  14:b605fb7503f2 (secret) i
+  |
+  o  13:7395e1ff83bd (draft) h
+  |
+  o  12:6b70183d2492 (draft) g
+  |
+  o  11:b449568bf7fc (draft) f
+  |
+  o  10:40db8afa467b (public) c
+  |
+  o  0:cb9a9f314b8b (public) a
+  
+  $ cd ..
+
+simple phase conservation
+-------------------------------------------
+
+Resulting changeset should conserve the phase of the original one whatever the
+phases.new-commit option is.
+
+New-commit as draft (default)
+
+  $ cp -r base simple-draft
+  $ cd simple-draft
+  $ cat > commands.txt <<EOF
+  > edit b449568bf7fc 11 f
+  > pick 6b70183d2492 12 g
+  > pick 7395e1ff83bd 13 h
+  > pick b605fb7503f2 14 i
+  > pick 3a6c53ee7f3d 15 j
+  > pick ee118ab9fa44 16 k
+  > EOF
+  $ hg histedit -r 'b449568bf7fc' --commands commands.txt
+  0 files updated, 0 files merged, 6 files removed, 0 files unresolved
+  adding f
+  abort: Make changes as needed, you may commit or record as needed now.
+  When you are finished, run hg histedit --continue to resume.
+  [255]
+  $ echo f >> f
+  $ hg histedit --continue
+  0 files updated, 0 files merged, 0 files removed, 0 files unresolved
+  0 files updated, 0 files merged, 0 files removed, 0 files unresolved
+  0 files updated, 0 files merged, 0 files removed, 0 files unresolved
+  0 files updated, 0 files merged, 0 files removed, 0 files unresolved
+  0 files updated, 0 files merged, 0 files removed, 0 files unresolved
+  0 files updated, 0 files merged, 0 files removed, 0 files unresolved
+  $ hg log -G
+  @  22:12e89af74238 (secret) k
+  |
+  o  21:636a8687b22e (secret) j
+  |
+  o  20:ccaf0a38653f (secret) i
+  |
+  o  19:11a89d1c2613 (draft) h
+  |
+  o  18:c1dec7ca82ea (draft) g
+  |
+  o  17:087281e68428 (draft) f
+  |
+  o  10:40db8afa467b (public) c
+  |
+  o  0:cb9a9f314b8b (public) a
+  
+  $ cd ..
+
+
+New-commit as draft (default)
+
+  $ cp -r base simple-secret
+  $ cd simple-secret
+  $ cat >> .hg/hgrc << EOF
+  > [phases]
+  > new-commit=secret
+  > EOF
+  $ cat > commands.txt <<EOF
+  > edit b449568bf7fc 11 f
+  > pick 6b70183d2492 12 g
+  > pick 7395e1ff83bd 13 h
+  > pick b605fb7503f2 14 i
+  > pick 3a6c53ee7f3d 15 j
+  > pick ee118ab9fa44 16 k
+  > EOF
+  $ hg histedit -r 'b449568bf7fc' --commands commands.txt
+  0 files updated, 0 files merged, 6 files removed, 0 files unresolved
+  adding f
+  abort: Make changes as needed, you may commit or record as needed now.
+  When you are finished, run hg histedit --continue to resume.
+  [255]
+  $ echo f >> f
+  $ hg histedit --continue
+  0 files updated, 0 files merged, 0 files removed, 0 files unresolved
+  0 files updated, 0 files merged, 0 files removed, 0 files unresolved
+  0 files updated, 0 files merged, 0 files removed, 0 files unresolved
+  0 files updated, 0 files merged, 0 files removed, 0 files unresolved
+  0 files updated, 0 files merged, 0 files removed, 0 files unresolved
+  0 files updated, 0 files merged, 0 files removed, 0 files unresolved
+  $ hg log -G
+  @  22:12e89af74238 (secret) k
+  |
+  o  21:636a8687b22e (secret) j
+  |
+  o  20:ccaf0a38653f (secret) i
+  |
+  o  19:11a89d1c2613 (draft) h
+  |
+  o  18:c1dec7ca82ea (draft) g
+  |
+  o  17:087281e68428 (draft) f
+  |
+  o  10:40db8afa467b (public) c
+  |
+  o  0:cb9a9f314b8b (public) a
+  
+  $ cd ..
+
+
+Changeset reordering
+-------------------------------------------
+
+If a secret changeset is put before a draft one, all descendant should be secret.
+It seems more important to present the secret phase.
+
+  $ cp -r base reorder
+  $ cd reorder
+  $ cat > commands.txt <<EOF
+  > pick b449568bf7fc 11 f
+  > pick 3a6c53ee7f3d 15 j
+  > pick 6b70183d2492 12 g
+  > pick b605fb7503f2 14 i
+  > pick 7395e1ff83bd 13 h
+  > pick ee118ab9fa44 16 k
+  > EOF
+  $ hg histedit -r 'b449568bf7fc' --commands commands.txt
+  0 files updated, 0 files merged, 5 files removed, 0 files unresolved
+  0 files updated, 0 files merged, 0 files removed, 0 files unresolved
+  0 files updated, 0 files merged, 0 files removed, 0 files unresolved
+  0 files updated, 0 files merged, 0 files removed, 0 files unresolved
+  0 files updated, 0 files merged, 0 files removed, 0 files unresolved
+  0 files updated, 0 files merged, 0 files removed, 0 files unresolved
+  $ hg log -G
+  @  21:558246857888 (secret) k
+  |
+  o  20:28bd44768535 (secret) h
+  |
+  o  19:d5395202aeb9 (secret) i
+  |
+  o  18:21edda8e341b (secret) g
+  |
+  o  17:5ab64f3a4832 (secret) j
+  |
+  o  11:b449568bf7fc (draft) f
+  |
+  o  10:40db8afa467b (public) c
+  |
+  o  0:cb9a9f314b8b (public) a
+  
+  $ cd ..
+
+Changeset folding
+-------------------------------------------
+
+Folding a secret changeset with a draft one turn the result secret (again,
+better safe than sorry). Folding between same phase changeset still works
+
+Note that there is a few reordering in this series for more extensive test
+
+  $ cp -r base folding
+  $ cd folding
+  $ cat >> .hg/hgrc << EOF
+  > [phases]
+  > new-commit=secret
+  > EOF
+  $ cat > commands.txt <<EOF
+  > pick 7395e1ff83bd 13 h
+  > fold b449568bf7fc 11 f
+  > pick 6b70183d2492 12 g
+  > fold 3a6c53ee7f3d 15 j
+  > pick b605fb7503f2 14 i
+  > fold ee118ab9fa44 16 k
+  > EOF
+  $ hg histedit -r 'b449568bf7fc' --commands commands.txt
+  0 files updated, 0 files merged, 6 files removed, 0 files unresolved
+  0 files updated, 0 files merged, 0 files removed, 0 files unresolved
+  0 files updated, 0 files merged, 2 files removed, 0 files unresolved
+  2 files updated, 0 files merged, 0 files removed, 0 files unresolved
+  0 files updated, 0 files merged, 0 files removed, 0 files unresolved
+  0 files updated, 0 files merged, 0 files removed, 0 files unresolved
+  0 files updated, 0 files merged, 2 files removed, 0 files unresolved
+  2 files updated, 0 files merged, 0 files removed, 0 files unresolved
+  0 files updated, 0 files merged, 0 files removed, 0 files unresolved
+  0 files updated, 0 files merged, 0 files removed, 0 files unresolved
+  0 files updated, 0 files merged, 2 files removed, 0 files unresolved
+  2 files updated, 0 files merged, 0 files removed, 0 files unresolved
+  0 files updated, 0 files merged, 0 files removed, 0 files unresolved
+  saved backup bundle to $TESTTMP/folding/.hg/strip-backup/58019c66f35f-backup.hg (glob)
+  saved backup bundle to $TESTTMP/folding/.hg/strip-backup/83d1858e070b-backup.hg (glob)
+  saved backup bundle to $TESTTMP/folding/.hg/strip-backup/859969f5ed7e-backup.hg (glob)
+  $ hg log -G
+  @  19:f9daec13fb98 (secret) i
+  |
+  o  18:49807617f46a (secret) g
+  |
+  o  17:050280826e04 (draft) h
+  |
+  o  10:40db8afa467b (public) c
+  |
+  o  0:cb9a9f314b8b (public) a
+  
+  $ cd ..
--- a/tests/test-hook.t	Mon Jan 14 23:14:45 2013 +0900
+++ b/tests/test-hook.t	Sat Jan 19 17:24:33 2013 -0600
@@ -369,7 +369,7 @@
   >     ui.note('verbose output from hook\n')
   > 
   > def printtags(ui, repo, **args):
-  >     print repo.tags().keys()
+  >     print sorted(repo.tags())
   > 
   > class container:
   >     unreachable = 1
--- a/tests/test-https.t	Mon Jan 14 23:14:45 2013 +0900
+++ b/tests/test-https.t	Sat Jan 19 17:24:33 2013 -0600
@@ -124,7 +124,6 @@
   adding manifests
   adding file changes
   added 1 changesets with 4 changes to 4 files
-  warning: localhost certificate with fingerprint 91:4f:1a:ff:87:24:9c:09:b6:85:9b:88:b1:90:6d:30:75:64:91:ca not verified (check hostfingerprints or web.cacerts config setting)
   updating to branch default
   4 files updated, 0 files merged, 0 files removed, 0 files unresolved
   $ hg verify -R copy-pull
@@ -152,7 +151,6 @@
   adding manifests
   adding file changes
   added 1 changesets with 1 changes to 1 files
-  warning: localhost certificate with fingerprint 91:4f:1a:ff:87:24:9c:09:b6:85:9b:88:b1:90:6d:30:75:64:91:ca not verified (check hostfingerprints or web.cacerts config setting)
   changegroup hook: HG_NODE=5fed3813f7f5e1824344fdc9cf8f63bb662c292d HG_SOURCE=pull HG_URL=https://localhost:$HGPORT/
   (run 'hg update' to get a working copy)
   $ cd ..
--- a/tests/test-hybridencode.py	Mon Jan 14 23:14:45 2013 +0900
+++ b/tests/test-hybridencode.py	Sat Jan 19 17:24:33 2013 -0600
@@ -5,7 +5,7 @@
     print "A = '%s'" % s.encode("string_escape")
 
     # show the result of the C implementation, if available
-    h = store._dothybridencode(s)
+    h = store._pathencode(s)
     print "B = '%s'" % h.encode("string_escape")
 
     # compare it with reference implementation in Python
--- a/tests/test-inherit-mode.t	Mon Jan 14 23:14:45 2013 +0900
+++ b/tests/test-inherit-mode.t	Sat Jan 19 17:24:33 2013 -0600
@@ -66,7 +66,7 @@
   00700 ./.hg/
   00600 ./.hg/00changelog.i
   00770 ./.hg/cache/
-  00660 ./.hg/cache/branchheads
+  00660 ./.hg/cache/branchheads-served
   00660 ./.hg/dirstate
   00660 ./.hg/last-message.txt
   00600 ./.hg/requires
@@ -111,7 +111,7 @@
   00770 ../push/.hg/
   00660 ../push/.hg/00changelog.i
   00770 ../push/.hg/cache/
-  00660 ../push/.hg/cache/branchheads
+  00660 ../push/.hg/cache/branchheads-base
   00660 ../push/.hg/requires
   00770 ../push/.hg/store/
   00660 ../push/.hg/store/00changelog.i
--- a/tests/test-init.t	Mon Jan 14 23:14:45 2013 +0900
+++ b/tests/test-init.t	Sat Jan 19 17:24:33 2013 -0600
@@ -18,10 +18,10 @@
   $ checknewrepo local
   store created
   00changelog.i created
-  revlogv1
+  dotencode
   fncache
+  revlogv1
   store
-  dotencode
   $ echo this > local/foo
   $ hg ci --cwd local -A -m "init"
   adding foo
@@ -47,8 +47,8 @@
   $ checknewrepo old3
   store created
   00changelog.i created
+  fncache
   revlogv1
-  fncache
   store
 
 test failure
@@ -149,10 +149,10 @@
   $ checknewrepo local/sub/repo
   store created
   00changelog.i created
-  revlogv1
+  dotencode
   fncache
+  revlogv1
   store
-  dotencode
 
 prepare test of init of url configured from paths
 
@@ -166,10 +166,10 @@
   $ checknewrepo "url from paths"
   store created
   00changelog.i created
-  revlogv1
+  dotencode
   fncache
+  revlogv1
   store
-  dotencode
 
 verify that clone also expand urls
 
@@ -179,10 +179,10 @@
   $ checknewrepo "another paths url"
   store created
   00changelog.i created
-  revlogv1
+  dotencode
   fncache
+  revlogv1
   store
-  dotencode
 
 clone bookmarks
 
--- a/tests/test-inotify-issue1208.t	Mon Jan 14 23:14:45 2013 +0900
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,38 +0,0 @@
-
-  $ "$TESTDIR/hghave" inotify || exit 80
-  $ echo "[extensions]" >> $HGRCPATH
-  $ echo "inotify=" >> $HGRCPATH
-  $ p="xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
-  $ hg init $p
-  $ cd $p
-
-fail
-
-  $ ln -sf doesnotexist .hg/inotify.sock
-  $ hg st
-  abort: inotify-server: cannot start: .hg/inotify.sock is a broken symlink
-  inotify-client: could not start inotify server: child process failed to start
-  $ hg inserve
-  abort: inotify-server: cannot start: .hg/inotify.sock is a broken symlink
-  [255]
-  $ rm .hg/inotify.sock
-
-inserve
-
-  $ hg inserve -d --pid-file=hg.pid
-  $ cat hg.pid >> "$DAEMON_PIDS"
-
-status
-
-  $ hg status
-  ? hg.pid
-
-if we try to start twice the server, make sure we get a correct error
-
-  $ hg inserve -d --pid-file=hg2.pid
-  abort: inotify-server: cannot start: socket is already bound
-  abort: child process failed to start
-  [255]
-  $ kill `cat hg.pid`
-
-  $ cd ..
--- a/tests/test-inotify.t	Mon Jan 14 23:14:45 2013 +0900
+++ b/tests/test-inotify.t	Sat Jan 19 17:24:33 2013 -0600
@@ -160,3 +160,23 @@
   $ kill `cat hg.pid`
 
   $ cd ..
+
+Ensure that if the repo is in a directory whose name is too long, the
+unix domain socket is reached through a symlink (issue1208).
+
+  $ mkdir 0_3456789_10_456789_20_456789_30_456789_40_456789_50_45678_
+  $ cd 0_3456789_10_456789_20_456789_30_456789_40_456789_50_45678_
+  $ mkdir 60_456789_70_456789_80_456789_90_456789_100_56789_
+  $ cd 60_456789_70_456789_80_456789_90_456789_100_56789_
+
+  $ hg --config inotify.pidfile=hg3.pid clone -q ../../repo1
+  $ readlink repo1/.hg/inotify.sock
+  */inotify.sock (glob)
+
+Trying to start the server a second time should fail as usual.
+
+  $ hg --cwd repo1 inserve
+  abort: inotify-server: cannot start: socket is already bound
+  [255]
+
+  $ kill `cat hg3.pid`
--- a/tests/test-issue1802.t	Mon Jan 14 23:14:45 2013 +0900
+++ b/tests/test-issue1802.t	Sat Jan 19 17:24:33 2013 -0600
@@ -59,7 +59,7 @@
    ancestor: a03b0deabf2b, local: d6fa54f68ae1+, remote: 2d8bcf2dda39
    a: update permissions -> e
   updating: a 1/1 files (100.00%)
-  0 files updated, 0 files merged, 0 files removed, 0 files unresolved
+  1 files updated, 0 files merged, 0 files removed, 0 files unresolved
   (branch merge, don't forget to commit)
 
 Simulate a Windows commit:
--- a/tests/test-issue672.t	Mon Jan 14 23:14:45 2013 +0900
+++ b/tests/test-issue672.t	Sat Jan 19 17:24:33 2013 -0600
@@ -29,7 +29,7 @@
     unmatched files in other:
      1a
     all copies found (* = to merge, ! = divergent, % = renamed and deleted):
-     1a -> 1 
+     src: '1' -> dst: '1a' 
     checking for directory renames
   resolving manifests
    overwrite: False, partial: False
@@ -60,7 +60,7 @@
     unmatched files in local:
      1a
     all copies found (* = to merge, ! = divergent, % = renamed and deleted):
-     1a -> 1 *
+     src: '1' -> dst: '1a' *
     checking for directory renames
   resolving manifests
    overwrite: False, partial: False
@@ -83,7 +83,7 @@
     unmatched files in other:
      1a
     all copies found (* = to merge, ! = divergent, % = renamed and deleted):
-     1a -> 1 *
+     src: '1' -> dst: '1a' *
     checking for directory renames
   resolving manifests
    overwrite: False, partial: False
--- a/tests/test-keyword.t	Mon Jan 14 23:14:45 2013 +0900
+++ b/tests/test-keyword.t	Sat Jan 19 17:24:33 2013 -0600
@@ -507,6 +507,7 @@
   $ hg -q commit -d '14 1' -m 'prepare amend'
 
   $ hg --debug commit --amend -d '15 1' -m 'amend without changes' | grep keywords
+  invalid branchheads cache (served): tip differs
   overwriting a expanding keywords
   $ hg -q id
   67d8c481a6be
@@ -576,9 +577,10 @@
 Commit and show expansion in original and copy
 
   $ hg --debug commit -ma2c -d '1 0' -u 'User Name <user@example.com>'
+  invalid branchheads cache (served): tip differs
   c
    c: copy a:0045e12f6c5791aac80ca6cbfd97709a88307292
-  removing unknown node 40a904bbbe4c from 1-phase boundary
+  invalid branchheads cache (served): tip differs
   overwriting c expanding keywords
   committed changeset 2:25736cf2f5cbe41f6be4e6784ef6ecf9f3bbcc7d
   $ cat a c
@@ -747,9 +749,22 @@
 
 Commit with multi-line message and custom expansion
 
+|Note:
+|
+| After the last rollback, the "unserved" branchheads cache became invalid, but
+| all changesets in the repo were public. For filtering this means:
+|   "mutable" == "unserved" == ø.
+|
+| As the "unserved" cache is invalid, we fall back to the "mutable" cache. But
+| no update is needed between "mutable" and "unserved" and the "unserved" cache
+| is not updated on disk. The on-disk version therefore stays invalid for some
+| time. This explains why the "unserved" branchheads cache is detected as
+| invalid here.
+
   $ hg --debug commit -l log -d '2 0' -u 'User Name <user@example.com>'
+  invalid branchheads cache (served): tip differs
   a
-  removing unknown node 40a904bbbe4c from 1-phase boundary
+  invalid branchheads cache (served): tip differs
   overwriting a expanding keywords
   committed changeset 2:bb948857c743469b22bbf51f7ec8112279ca5d83
   $ rm log
--- a/tests/test-largefiles-cache.t	Mon Jan 14 23:14:45 2013 +0900
+++ b/tests/test-largefiles-cache.t	Sat Jan 19 17:24:33 2013 -0600
@@ -47,8 +47,7 @@
   $ hg update
   1 files updated, 0 files merged, 0 files removed, 0 files unresolved
   getting changed largefiles
-  large: can't get file locally
-  (no default or default-push path set in hgrc)
+  error getting 7f7097b041ccf68cc5561e9600da4655d21c6d18 from file:$TESTTMP/mirror for large: can't get file locally (glob)
   0 largefiles updated, 0 removed
   $ hg status
   ! large
@@ -65,8 +64,7 @@
   $ hg update
   1 files updated, 0 files merged, 0 files removed, 0 files unresolved
   getting changed largefiles
-  large: can't get file locally
-  (no default or default-push path set in hgrc)
+  error getting 7f7097b041ccf68cc5561e9600da4655d21c6d18 from file:$TESTTMP/mirror for large: can't get file locally (glob)
   0 largefiles updated, 0 removed
   $ hg status
   ! large
--- a/tests/test-largefiles.t	Mon Jan 14 23:14:45 2013 +0900
+++ b/tests/test-largefiles.t	Sat Jan 19 17:24:33 2013 -0600
@@ -17,8 +17,8 @@
   > EOF
 
 Create the repo with a couple of revisions of both large and normal
-files, testing that status correctly shows largefiles and that summary output
-is correct.
+files.
+Test status and dirstate of largefiles and that summary output is correct.
 
   $ hg init a
   $ cd a
@@ -35,6 +35,17 @@
   A normal1
   A sub/large2
   A sub/normal2
+  $ touch large1 sub/large2
+  $ sleep 1
+  $ hg st
+  $ hg debugstate --nodates
+  n 644         41 .hglf/large1
+  n 644         41 .hglf/sub/large2
+  n 644          8 normal1
+  n 644          8 sub/normal2
+  $ hg debugstate --large
+  n 644          7 large1
+  n 644          7 sub/large2
   $ echo normal11 > normal1
   $ echo normal22 > sub/normal2
   $ echo large11 > large1
@@ -79,15 +90,25 @@
   C sub/normal2
   $ rm sub/unknown
 
-Test exit codes for remove warning cases (modified and still exiting)
+Test messages and exit codes for remove warning cases
 
   $ hg remove -A large1
-  not removing large1: file still exists (use forget to undo)
+  not removing large1: file still exists
   [1]
   $ echo 'modified' > large1
   $ hg remove large1
-  not removing large1: file is modified (use forget to undo)
+  not removing large1: file is modified (use -f to force removal)
   [1]
+  $ echo 'new' > normalnew
+  $ hg add normalnew
+  $ echo 'new' > largenew
+  $ hg add --large normalnew
+  normalnew already tracked!
+  $ hg remove normalnew largenew
+  not removing largenew: file is untracked
+  not removing normalnew: file has been marked for add (use forget to undo)
+  [1]
+  $ rm normalnew largenew
   $ hg up -Cq
 
 Remove both largefiles and normal files.
@@ -196,28 +217,28 @@
   ./foo
   $ cd ../../a
 
-#if hgweb
+#if serve
 Test display of largefiles in hgweb
 
   $ hg serve -d -p $HGPORT --pid-file ../hg.pid
   $ cat ../hg.pid >> $DAEMON_PIDS
   $ "$TESTDIR/get-with-headers.py" 127.0.0.1:$HGPORT 'file/tip/?style=raw'
   200 Script output follows
-
-
+  
+  
   drwxr-xr-x sub
   -rw-r--r-- 41 large3
   -rw-r--r-- 9 normal3
-
-
+  
+  
   $ "$TESTDIR/get-with-headers.py" 127.0.0.1:$HGPORT 'file/tip/sub/?style=raw'
   200 Script output follows
-
-
+  
+  
   -rw-r--r-- 41 large4
   -rw-r--r-- 9 normal4
-
-
+  
+  
   $ "$TESTDIR/killdaemons.py" $DAEMON_PIDS
 #endif
 
@@ -684,9 +705,9 @@
   
   searching for changes
   largefiles to upload:
-  large8
+  foo
   large
-  foo
+  large8
   
   $ cd ../a
 
@@ -895,24 +916,15 @@
   M sub/normal4
   M sub2/large6
   saved backup bundle to $TESTTMP/d/.hg/strip-backup/f574fb32bb45-backup.hg (glob)
-  large3: can't get file locally
-  (no default or default-push path set in hgrc)
-  sub/large4: can't get file locally
-  (no default or default-push path set in hgrc)
-  large1: can't get file locally
-  (no default or default-push path set in hgrc)
-  sub/large2: can't get file locally
-  (no default or default-push path set in hgrc)
-  sub/large2: can't get file locally
-  (no default or default-push path set in hgrc)
-  large1: can't get file locally
-  (no default or default-push path set in hgrc)
-  sub/large2: can't get file locally
-  (no default or default-push path set in hgrc)
-  large1: can't get file locally
-  (no default or default-push path set in hgrc)
-  sub/large2: can't get file locally
-  (no default or default-push path set in hgrc)
+  error getting eb7338044dc27f9bc59b8dd5a246b065ead7a9c4 from file:$TESTTMP/b for large3: can't get file locally (glob)
+  error getting eb7338044dc27f9bc59b8dd5a246b065ead7a9c4 from file:$TESTTMP/b for sub/large4: can't get file locally (glob)
+  error getting eb7338044dc27f9bc59b8dd5a246b065ead7a9c4 from file:$TESTTMP/b for large1: can't get file locally (glob)
+  error getting eb7338044dc27f9bc59b8dd5a246b065ead7a9c4 from file:$TESTTMP/b for sub/large2: can't get file locally (glob)
+  error getting eb7338044dc27f9bc59b8dd5a246b065ead7a9c4 from file:$TESTTMP/b for sub/large2: can't get file locally (glob)
+  error getting 5f78770c0e77ba4287ad6ef3071c9bf9c379742f from file:$TESTTMP/b for large1: can't get file locally (glob)
+  error getting eb7338044dc27f9bc59b8dd5a246b065ead7a9c4 from file:$TESTTMP/b for sub/large2: can't get file locally (glob)
+  error getting 4669e532d5b2c093a78eca010077e708a071bb64 from file:$TESTTMP/b for large1: can't get file locally (glob)
+  error getting 1deebade43c8c498a3c8daddac0244dc55d1331d from file:$TESTTMP/b for sub/large2: can't get file locally (glob)
   0 additional largefiles cached
   9 largefiles failed to download
   nothing to rebase
@@ -975,6 +987,47 @@
   $ cat sub2/large7
   large7
 
+Log on largefiles
+
+- same output
+  $ hg log --template '{rev}:{node|short}  {desc|firstline}\n' .hglf/sub/large4
+  8:a381d2c8c80e  modify normal file and largefile in repo b
+  6:4355d653f84f  edit files yet again
+  5:9d5af5072dbd  edit files again
+  4:74c02385b94c  move files
+  $ hg log --template '{rev}:{node|short}  {desc|firstline}\n' sub/large4
+  8:a381d2c8c80e  modify normal file and largefile in repo b
+  6:4355d653f84f  edit files yet again
+  5:9d5af5072dbd  edit files again
+  4:74c02385b94c  move files
+
+- .hglf only matches largefiles, without .hglf it matches 9 bco sub/normal
+  $ hg log --template '{rev}:{node|short}  {desc|firstline}\n' .hglf/sub
+  8:a381d2c8c80e  modify normal file and largefile in repo b
+  6:4355d653f84f  edit files yet again
+  5:9d5af5072dbd  edit files again
+  4:74c02385b94c  move files
+  1:ce8896473775  edit files
+  0:30d30fe6a5be  add files
+  $ hg log --template '{rev}:{node|short}  {desc|firstline}\n' sub
+  9:598410d3eb9a  modify normal file largefile in repo d
+  8:a381d2c8c80e  modify normal file and largefile in repo b
+  6:4355d653f84f  edit files yet again
+  5:9d5af5072dbd  edit files again
+  4:74c02385b94c  move files
+  1:ce8896473775  edit files
+  0:30d30fe6a5be  add files
+
+- globbing gives same result
+  $ hg log --template '{rev}:{node|short}  {desc|firstline}\n' 'glob:sub/*'
+  9:598410d3eb9a  modify normal file largefile in repo d
+  8:a381d2c8c80e  modify normal file and largefile in repo b
+  6:4355d653f84f  edit files yet again
+  5:9d5af5072dbd  edit files again
+  4:74c02385b94c  move files
+  1:ce8896473775  edit files
+  0:30d30fe6a5be  add files
+
 Rollback on largefiles.
 
   $ echo large4-modified-again > sub/large4
@@ -1208,6 +1261,17 @@
   $ hg status
   M large
 
+- make sure update of merge with removed largefiles fails as expected
+  $ hg rm sub2/large6
+  $ hg up -r.
+  abort: outstanding uncommitted merges
+  [255]
+
+- revert should be able to revert files introduced in a pending merge
+  $ hg revert --all -r .
+  removing .hglf/large
+  undeleting .hglf/sub2/large6
+
 Test that a normal file and a largefile with the same name and path cannot
 coexist.
 
@@ -1475,7 +1539,33 @@
   remote: adding manifests
   remote: adding file changes
   remote: added 1 changesets with 1 changes to 1 files
-  $ rm -rf empty
+
+Clone over http, with largefiles being pulled on update, not on clone.
+
+  $ hg clone -q http://localhost:$HGPORT2/ http-clone -U
+
+  $ hg -R http-clone --debug up --config largefiles.usercache=http-clone-usercache
+  resolving manifests
+   overwrite: False, partial: False
+   ancestor: 000000000000, local: 000000000000+, remote: cf03e5bb9936
+   .hglf/f1: remote created -> g
+  updating: .hglf/f1 1/1 files (100.00%)
+  getting .hglf/f1
+  1 files updated, 0 files merged, 0 files removed, 0 files unresolved
+  getting changed largefiles
+  using http://localhost:$HGPORT2/
+  sending capabilities command
+  getting largefiles: 0/1 lfile (0.00%)
+  getting f1:02a439e5c31c526465ab1a0ca1f431f76b827b90
+  sending batch command
+  sending getlfile command
+  found 02a439e5c31c526465ab1a0ca1f431f76b827b90 in store
+  1 largefiles updated, 0 removed
+
+  $ ls http-clone-usercache/*
+  http-clone-usercache/02a439e5c31c526465ab1a0ca1f431f76b827b90
+
+  $ rm -rf empty http-clone http-clone-usercache
 
 used all HGPORTs, kill all daemons
   $ "$TESTDIR/killdaemons.py" $DAEMON_PIDS
--- a/tests/test-lfconvert.t	Mon Jan 14 23:14:45 2013 +0900
+++ b/tests/test-lfconvert.t	Sat Jan 19 17:24:33 2013 -0600
@@ -96,11 +96,11 @@
 
 "lfconvert" adds 'largefiles' to .hg/requires.
   $ cat .hg/requires
+  dotencode
+  fncache
   largefiles
   revlogv1
-  fncache
   store
-  dotencode
 
 "lfconvert" includes a newline at the end of the standin files.
   $ cat .hglf/large .hglf/sub/maybelarge.dat
@@ -349,8 +349,7 @@
   $ rm largefiles-repo/.hg/largefiles/*
   $ hg lfconvert --to-normal issue3519 normalized3519
   initializing destination normalized3519
-  large: can't get file locally
-  (no default or default-push path set in hgrc)
+  error getting 2e000fa7e85759c7f4c254d4d9c33ef481e459a7 from file:$TESTTMP/largefiles-repo for large: can't get file locally (glob)
   abort: missing largefile 'large' from revision d4892ec57ce212905215fad1d9018f56b99202ad
   [255]
 
--- a/tests/test-log.t	Mon Jan 14 23:14:45 2013 +0900
+++ b/tests/test-log.t	Sat Jan 19 17:24:33 2013 -0600
@@ -34,6 +34,32 @@
   date:        Thu Jan 01 00:00:01 1970 +0000
   summary:     a
   
+log on directory
+
+  $ hg log dir
+  changeset:   4:7e4639b4691b
+  tag:         tip
+  user:        test
+  date:        Thu Jan 01 00:00:05 1970 +0000
+  summary:     e
+  
+  changeset:   2:f8954cd4dc1f
+  user:        test
+  date:        Thu Jan 01 00:00:03 1970 +0000
+  summary:     c
+  
+  $ hg log somethingthatdoesntexist dir
+  changeset:   4:7e4639b4691b
+  tag:         tip
+  user:        test
+  date:        Thu Jan 01 00:00:05 1970 +0000
+  summary:     e
+  
+  changeset:   2:f8954cd4dc1f
+  user:        test
+  date:        Thu Jan 01 00:00:03 1970 +0000
+  summary:     c
+  
 
 -f, directory
 
@@ -1142,28 +1168,43 @@
   date:        Thu Jan 01 00:00:00 1970 +0000
   summary:     a
   
-  $ cat > $HGTMP/testhidden.py << EOF
-  > def reposetup(ui, repo):
-  >     for line in repo.opener('hidden'):
-  >         ctx = repo[line.strip()]
-  >         repo.hiddenrevs.add(ctx.rev())
+enable obsolete to test hidden feature
+
+  $ cat > ${TESTTMP}/obs.py << EOF
+  > import mercurial.obsolete
+  > mercurial.obsolete._enabled = True
   > EOF
   $ echo '[extensions]' >> $HGRCPATH
-  $ echo "hidden=$HGTMP/testhidden.py" >> $HGRCPATH
-  $ touch .hg/hidden
+  $ echo "obs=${TESTTMP}/obs.py" >> $HGRCPATH
+
   $ hg log --template='{rev}:{node}\n'
   1:a765632148dc55d38c35c4f247c618701886cb2f
   0:9f758d63dcde62d547ebfb08e1e7ee96535f2b05
-  $ echo a765632148dc55d38c35c4f247c618701886cb2f > .hg/hidden
+  $ hg debugobsolete a765632148dc55d38c35c4f247c618701886cb2f
+  $ hg up null -q
   $ hg log --template='{rev}:{node}\n'
   0:9f758d63dcde62d547ebfb08e1e7ee96535f2b05
   $ hg log --template='{rev}:{node}\n' --hidden
   1:a765632148dc55d38c35c4f247c618701886cb2f
   0:9f758d63dcde62d547ebfb08e1e7ee96535f2b05
 
+test that parent prevent a changeset to be hidden
+
+  $ hg up 1 -q --hidden
+  $ hg log --template='{rev}:{node}\n'
+  1:a765632148dc55d38c35c4f247c618701886cb2f
+  0:9f758d63dcde62d547ebfb08e1e7ee96535f2b05
+
+test that second parent prevent a changeset to be hidden too
+
+  $ hg debugsetparents 0 1 # nothing suitable to merge here
+  $ hg log --template='{rev}:{node}\n'
+  1:a765632148dc55d38c35c4f247c618701886cb2f
+  0:9f758d63dcde62d547ebfb08e1e7ee96535f2b05
+
 clear extensions configuration
   $ echo '[extensions]' >> $HGRCPATH
-  $ echo "hidden=!" >> $HGRCPATH
+  $ echo "obs=!" >> $HGRCPATH
   $ cd ..
 
 test -u/-k for problematic encoding
--- a/tests/test-merge-tools.t	Mon Jan 14 23:14:45 2013 +0900
+++ b/tests/test-merge-tools.t	Sat Jan 19 17:24:33 2013 -0600
@@ -832,3 +832,21 @@
   # hg stat
   M f
   ? f.orig
+
+#if symlink
+
+internal merge cannot handle symlinks and shouldn't try:
+
+  $ hg update -q -C 1
+  $ rm f
+  $ ln -s symlink f
+  $ hg commit -qm 'f is symlink'
+  $ hg merge -r 2 --tool internal:merge
+  merging f
+  warning: internal:merge cannot merge symlinks for f
+  merging f incomplete! (edit conflicts, then use 'hg resolve --mark')
+  0 files updated, 0 files merged, 0 files removed, 1 files unresolved
+  use 'hg resolve' to retry unresolved file merges or 'hg update -C .' to abandon
+  [1]
+
+#endif
--- a/tests/test-merge-types.t	Mon Jan 14 23:14:45 2013 +0900
+++ b/tests/test-merge-types.t	Sat Jan 19 17:24:33 2013 -0600
@@ -1,45 +1,63 @@
   $ "$TESTDIR/hghave" symlink execbit || exit 80
 
-  $ hg init
+  $ tellmeabout() {
+  > if [ -h $1 ]; then
+  >     echo $1 is a symlink:
+  >     $TESTDIR/readlink.py $1
+  > elif [ -x $1 ]; then
+  >     echo $1 is an executable file with content:
+  >     cat $1
+  > else
+  >     echo $1 is a plain file with content:
+  >     cat $1
+  > fi
+  > }
+
+  $ hg init test1
+  $ cd test1
 
   $ echo a > a
-  $ hg ci -Amadd
-  adding a
-
+  $ hg ci -Aqmadd
   $ chmod +x a
   $ hg ci -mexecutable
 
-  $ hg up 0
-  0 files updated, 0 files merged, 0 files removed, 0 files unresolved
+  $ hg up -q 0
   $ rm a
   $ ln -s symlink a
   $ hg ci -msymlink
   created new head
 
+Symlink is local parent, executable is other:
+
   $ hg merge --debug
     searching for copies back to rev 1
   resolving manifests
    overwrite: False, partial: False
    ancestor: c334dc3be0da, local: 521a1e40188f+, remote: 3574f3e69b1c
-   conflicting flags for a
-  (n)one, e(x)ec or sym(l)ink? n
-   a: update permissions -> e
+   a: versions differ -> m
+  preserving a for resolve of a
   updating: a 1/1 files (100.00%)
-  0 files updated, 0 files merged, 0 files removed, 0 files unresolved
-  (branch merge, don't forget to commit)
-
-
-Symlink is local parent, executable is other:
+  picked tool 'internal:merge' for a (binary False symlink True)
+  merging a
+  my a@521a1e40188f+ other a@3574f3e69b1c ancestor a@c334dc3be0da
+  warning: internal:merge cannot merge symlinks for a
+  merging a incomplete! (edit conflicts, then use 'hg resolve --mark')
+  0 files updated, 0 files merged, 0 files removed, 1 files unresolved
+  use 'hg resolve' to retry unresolved file merges or 'hg update -C .' to abandon
+  [1]
 
-  $ if [ -h a ]; then
-  >     echo a is a symlink
-  >     $TESTDIR/readlink.py a
-  > elif [ -x a ]; then
-  >     echo a is executable
-  > else
-  >     echo "a has no flags (default for conflicts)"
-  > fi
-  a has no flags (default for conflicts)
+  $ tellmeabout a
+  a is a symlink:
+  a -> symlink
+  $ hg resolve a --tool internal:other
+  $ tellmeabout a
+  a is an executable file with content:
+  a
+  $ hg st
+  M a
+  ? a.orig
+
+Symlink is other parent, executable is local:
 
   $ hg update -C 1
   1 files updated, 0 files merged, 0 files removed, 0 files unresolved
@@ -49,26 +67,21 @@
   resolving manifests
    overwrite: False, partial: False
    ancestor: c334dc3be0da, local: 3574f3e69b1c+, remote: 521a1e40188f
-   conflicting flags for a
-  (n)one, e(x)ec or sym(l)ink? n
-   a: remote is newer -> g
+   a: versions differ -> m
+  preserving a for resolve of a
   updating: a 1/1 files (100.00%)
-  getting a
-  1 files updated, 0 files merged, 0 files removed, 0 files unresolved
-  (branch merge, don't forget to commit)
-
+  picked tool 'internal:merge' for a (binary False symlink True)
+  merging a
+  my a@3574f3e69b1c+ other a@521a1e40188f ancestor a@c334dc3be0da
+  warning: internal:merge cannot merge symlinks for a
+  merging a incomplete! (edit conflicts, then use 'hg resolve --mark')
+  0 files updated, 0 files merged, 0 files removed, 1 files unresolved
+  use 'hg resolve' to retry unresolved file merges or 'hg update -C .' to abandon
+  [1]
 
-Symlink is other parent, executable is local:
-
-  $ if [ -h a ]; then
-  >    echo a is a symlink
-  >    $TESTDIR/readlink.py a
-  > elif [ -x a ]; then
-  >     echo a is executable
-  > else
-  >     echo "a has no flags (default for conflicts)"
-  > fi
-  a has no flags (default for conflicts)
+  $ tellmeabout a
+  a is an executable file with content:
+  a
 
 Update to link without local change should get us a symlink (issue3316):
 
@@ -77,11 +90,11 @@
   $ hg up
   1 files updated, 0 files merged, 0 files removed, 0 files unresolved
   $ hg st
+  ? a.orig
 
 Update to link with local change should cause a merge prompt (issue3200):
 
-  $ hg up -C 0
-  1 files updated, 0 files merged, 0 files removed, 0 files unresolved
+  $ hg up -Cq 0
   $ echo data > a
   $ HGMERGE= hg up -y --debug
     searching for copies back to rev 2
@@ -108,3 +121,236 @@
   +data
 
 
+Test only 'l' change - happens rarely, except when recovering from situations
+where that was what happened.
+
+  $ hg init test2
+  $ cd test2
+  $ printf base > f
+  $ hg ci -Aqm0
+  $ echo file > f
+  $ echo content >> f
+  $ hg ci -qm1
+  $ hg up -qr0
+  $ rm f
+  $ ln -s base f
+  $ hg ci -qm2
+  $ hg merge
+  merging f
+  warning: internal:merge cannot merge symlinks for f
+  merging f incomplete! (edit conflicts, then use 'hg resolve --mark')
+  0 files updated, 0 files merged, 0 files removed, 1 files unresolved
+  use 'hg resolve' to retry unresolved file merges or 'hg update -C .' to abandon
+  [1]
+  $ tellmeabout f
+  f is a symlink:
+  f -> base
+
+  $ hg up -Cqr1
+  $ hg merge
+  merging f
+  warning: internal:merge cannot merge symlinks for f
+  merging f incomplete! (edit conflicts, then use 'hg resolve --mark')
+  0 files updated, 0 files merged, 0 files removed, 1 files unresolved
+  use 'hg resolve' to retry unresolved file merges or 'hg update -C .' to abandon
+  [1]
+  $ tellmeabout f
+  f is a plain file with content:
+  file
+  content
+
+  $ cd ..
+
+Test removed 'x' flag merged with change to symlink
+
+  $ hg init test3
+  $ cd test3
+  $ echo f > f
+  $ chmod +x f
+  $ hg ci -Aqm0
+  $ chmod -x f
+  $ hg ci -qm1
+  $ hg up -qr0
+  $ rm f
+  $ ln -s dangling f
+  $ hg ci -qm2
+  $ hg merge
+  merging f
+  warning: internal:merge cannot merge symlinks for f
+  merging f incomplete! (edit conflicts, then use 'hg resolve --mark')
+  0 files updated, 0 files merged, 0 files removed, 1 files unresolved
+  use 'hg resolve' to retry unresolved file merges or 'hg update -C .' to abandon
+  [1]
+  $ tellmeabout f
+  f is a symlink:
+  f -> dangling
+
+  $ hg up -Cqr1
+  $ hg merge
+  merging f
+  warning: internal:merge cannot merge symlinks for f
+  merging f incomplete! (edit conflicts, then use 'hg resolve --mark')
+  0 files updated, 0 files merged, 0 files removed, 1 files unresolved
+  use 'hg resolve' to retry unresolved file merges or 'hg update -C .' to abandon
+  [1]
+  $ tellmeabout f
+  f is a plain file with content:
+  f
+
+  $ cd ..
+
+Test merge with no common ancestor:
+a: just different
+b: x vs -, different (cannot calculate x, cannot ask merge tool)
+c: x vs -, same (cannot calculate x, merge tool is no good)
+d: x vs l, different
+e: x vs l, same
+f: - vs l, different
+g: - vs l, same
+h: l vs l, different
+(where same means the filelog entry is shared and there thus is an ancestor!)
+
+  $ hg init test4
+  $ cd test4
+  $ echo 0 > 0
+  $ hg ci -Aqm0
+
+  $ echo 1 > a
+  $ echo 1 > b
+  $ chmod +x b
+  $ echo x > c
+  $ chmod +x c
+  $ echo 1 > d
+  $ chmod +x d
+  $ printf x > e
+  $ chmod +x e
+  $ echo 1 > f
+  $ printf x > g
+  $ ln -s 1 h
+  $ hg ci -qAm1
+
+  $ hg up -qr0
+  $ echo 2 > a
+  $ echo 2 > b
+  $ echo x > c
+  $ ln -s 2 d
+  $ ln -s x e
+  $ ln -s 2 f
+  $ ln -s x g
+  $ ln -s 2 h
+  $ hg ci -Aqm2
+
+  $ hg merge
+  merging a
+  warning: conflicts during merge.
+  merging a incomplete! (edit conflicts, then use 'hg resolve --mark')
+  warning: cannot merge flags for b
+  merging b
+  warning: conflicts during merge.
+  merging b incomplete! (edit conflicts, then use 'hg resolve --mark')
+  merging d
+  warning: internal:merge cannot merge symlinks for d
+  merging d incomplete! (edit conflicts, then use 'hg resolve --mark')
+  merging f
+  warning: internal:merge cannot merge symlinks for f
+  merging f incomplete! (edit conflicts, then use 'hg resolve --mark')
+  merging h
+  warning: internal:merge cannot merge symlinks for h
+  merging h incomplete! (edit conflicts, then use 'hg resolve --mark')
+  3 files updated, 0 files merged, 0 files removed, 5 files unresolved
+  use 'hg resolve' to retry unresolved file merges or 'hg update -C .' to abandon
+  [1]
+  $ hg resolve -l
+  U a
+  U b
+  U d
+  U f
+  U h
+  $ tellmeabout a
+  a is a plain file with content:
+  <<<<<<< local
+  2
+  =======
+  1
+  >>>>>>> other
+  $ tellmeabout b
+  b is a plain file with content:
+  <<<<<<< local
+  2
+  =======
+  1
+  >>>>>>> other
+  $ tellmeabout c
+  c is a plain file with content:
+  x
+  $ tellmeabout d
+  d is a symlink:
+  d -> 2
+  $ tellmeabout e
+  e is a symlink:
+  e -> x
+  $ tellmeabout f
+  f is a symlink:
+  f -> 2
+  $ tellmeabout g
+  g is a symlink:
+  g -> x
+  $ tellmeabout h
+  h is a symlink:
+  h -> 2
+
+  $ hg up -Cqr1
+  $ hg merge
+  merging a
+  warning: conflicts during merge.
+  merging a incomplete! (edit conflicts, then use 'hg resolve --mark')
+  warning: cannot merge flags for b
+  merging b
+  warning: conflicts during merge.
+  merging b incomplete! (edit conflicts, then use 'hg resolve --mark')
+  merging d
+  warning: internal:merge cannot merge symlinks for d
+  merging d incomplete! (edit conflicts, then use 'hg resolve --mark')
+  merging f
+  warning: internal:merge cannot merge symlinks for f
+  merging f incomplete! (edit conflicts, then use 'hg resolve --mark')
+  merging h
+  warning: internal:merge cannot merge symlinks for h
+  merging h incomplete! (edit conflicts, then use 'hg resolve --mark')
+  3 files updated, 0 files merged, 0 files removed, 5 files unresolved
+  use 'hg resolve' to retry unresolved file merges or 'hg update -C .' to abandon
+  [1]
+  $ tellmeabout a
+  a is a plain file with content:
+  <<<<<<< local
+  1
+  =======
+  2
+  >>>>>>> other
+  $ tellmeabout b
+  b is an executable file with content:
+  <<<<<<< local
+  1
+  =======
+  2
+  >>>>>>> other
+  $ tellmeabout c
+  c is a plain file with content:
+  x
+  $ tellmeabout d
+  d is an executable file with content:
+  1
+  $ tellmeabout e
+  e is an executable file with content:
+  x (no-eol)
+  $ tellmeabout f
+  f is a plain file with content:
+  1
+  $ tellmeabout g
+  g is a plain file with content:
+  x (no-eol)
+  $ tellmeabout h
+  h is a symlink:
+  h -> 1
+
+  $ cd ..
--- a/tests/test-mq-caches.t	Mon Jan 14 23:14:45 2013 +0900
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,126 +0,0 @@
-  $ branches=.hg/cache/branchheads
-  $ echo '[extensions]' >> $HGRCPATH
-  $ echo 'mq =' >> $HGRCPATH
-
-  $ show_branch_cache()
-  > {
-  >     # force cache (re)generation
-  >     hg log -r does-not-exist 2> /dev/null
-  >     hg log -r tip --template 'tip: {rev}\n'
-  >     if [ -f $branches ]; then
-  >       sort $branches
-  >     else
-  >       echo No branch cache
-  >     fi
-  >     if [ "$1" = 1 ]; then
-  >       for b in foo bar; do
-  >         hg log -r $b --template "branch $b: "'{rev}\n'
-  >       done
-  >     fi
-  > }
-
-  $ hg init a
-  $ cd a
-  $ hg qinit -c
-
-
-mq patch on an empty repo
-
-  $ hg qnew -d '0 0' p1
-  $ show_branch_cache
-  tip: 0
-  No branch cache
-
-  $ echo > pfile
-  $ hg add pfile
-  $ hg qrefresh -m 'patch 1'
-  $ show_branch_cache
-  tip: 0
-  d986d5caac23a7d44a46efc0ddaf5eb9665844cf 0
-  d986d5caac23a7d44a46efc0ddaf5eb9665844cf default
-
-some regular revisions
-
-  $ hg qpop
-  popping p1
-  patch queue now empty
-  $ echo foo > foo
-  $ hg add foo
-  $ echo foo > .hg/branch
-  $ hg ci -m 'branch foo'
-
-  $ echo bar > bar
-  $ hg add bar
-  $ echo bar > .hg/branch
-  $ hg ci -m 'branch bar'
-  $ show_branch_cache
-  tip: 1
-  c229711f16da3d7591f89b1b8d963b79bda22714 1
-  c229711f16da3d7591f89b1b8d963b79bda22714 bar
-  dc25e3827021582e979f600811852e36cbe57341 foo
-
-add some mq patches
-
-  $ hg qpush
-  applying p1
-  now at: p1
-  $ show_branch_cache
-  tip: 2
-  c229711f16da3d7591f89b1b8d963b79bda22714 1
-  c229711f16da3d7591f89b1b8d963b79bda22714 bar
-  dc25e3827021582e979f600811852e36cbe57341 foo
-
-  $ hg qnew -d '0 0' p2
-  $ echo foo > .hg/branch
-  $ echo foo2 >> foo
-  $ hg qrefresh -m 'patch 2'
-  $ show_branch_cache 1
-  tip: 3
-  982611f6955f9c48d3365decea203217c945ef0d 2
-  982611f6955f9c48d3365decea203217c945ef0d bar
-  dc25e3827021582e979f600811852e36cbe57341 foo
-  branch foo: 3
-  branch bar: 2
-
-removing the cache
-
-  $ rm $branches
-  $ show_branch_cache 1
-  tip: 3
-  c229711f16da3d7591f89b1b8d963b79bda22714 1
-  c229711f16da3d7591f89b1b8d963b79bda22714 bar
-  dc25e3827021582e979f600811852e36cbe57341 foo
-  branch foo: 3
-  branch bar: 2
-
-importing rev 1 (the cache now ends in one of the patches)
-
-  $ hg qimport -r 1 -n p0
-  $ show_branch_cache 1
-  tip: 3
-  c229711f16da3d7591f89b1b8d963b79bda22714 1
-  c229711f16da3d7591f89b1b8d963b79bda22714 bar
-  dc25e3827021582e979f600811852e36cbe57341 foo
-  branch foo: 3
-  branch bar: 2
-  $ hg log -r qbase --template 'qbase: {rev}\n'
-  qbase: 1
-
-detect an invalid cache
-
-  $ hg qpop -a
-  popping p2
-  popping p1
-  popping p0
-  patch queue now empty
-  $ hg qpush -a
-  applying p0
-  applying p1
-  applying p2
-  now at: p2
-  $ show_branch_cache
-  tip: 3
-  dc25e3827021582e979f600811852e36cbe57341 0
-  dc25e3827021582e979f600811852e36cbe57341 foo
-
-  $ cd ..
--- a/tests/test-mq-qgoto.t	Mon Jan 14 23:14:45 2013 +0900
+++ b/tests/test-mq-qgoto.t	Sat Jan 19 17:24:33 2013 -0600
@@ -46,20 +46,35 @@
   applying c.patch
   now at: c.patch
 
-No warnings when using index:
+No warnings when using index ... and update from non-qtip and with pending
+changes in unrelated files:
 
   $ hg qnew bug314159
   $ echo d >> c
   $ hg qrefresh
   $ hg qnew bug141421
-  $ echo e >> c
+  $ echo e >> b
   $ hg qrefresh
 
+  $ hg up -r bug314159
+  1 files updated, 0 files merged, 0 files removed, 0 files unresolved
+  $ echo f >> a
+  $ echo f >> b
+  $ echo f >> c
+
   $ hg qgoto 1
+  abort: local changes found, refresh first
+  [255]
+  $ hg qgoto 1 -f
   popping bug141421
   popping bug314159
   popping c.patch
   now at: b.patch
+  $ hg st
+  M a
+  M b
+  ? c.orig
+  $ hg up -qCr.
 
   $ hg qgoto 3
   applying c.patch
--- a/tests/test-mq-qpush-fail.t	Mon Jan 14 23:14:45 2013 +0900
+++ b/tests/test-mq-qpush-fail.t	Sat Jan 19 17:24:33 2013 -0600
@@ -61,7 +61,6 @@
   patch queue now empty
   $ cp .hg/patches/status.orig .hg/patches/status
   $ hg qpush
-  mq status file refers to unknown node * (glob)
   abort: working directory revision is not qtip
   [255]
   $ rm .hg/patches/status .hg/patches/status.orig
--- a/tests/test-mq-qrefresh.t	Mon Jan 14 23:14:45 2013 +0900
+++ b/tests/test-mq-qrefresh.t	Sat Jan 19 17:24:33 2013 -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-strip.t	Mon Jan 14 23:14:45 2013 +0900
+++ b/tests/test-mq-strip.t	Sat Jan 19 17:24:33 2013 -0600
@@ -309,16 +309,16 @@
 2 different branches: 2 strips
 
   $ hg strip 2 4
-  0 files updated, 0 files merged, 0 files removed, 0 files unresolved
+  1 files updated, 0 files merged, 0 files removed, 0 files unresolved
   saved backup bundle to $TESTTMP/test/.hg/strip-backup/*-backup.hg (glob)
   $ hg glog
-  @  changeset:   2:65bd5f99a4a3
+  o  changeset:   2:65bd5f99a4a3
   |  tag:         tip
   |  user:        test
   |  date:        Thu Jan 01 00:00:00 1970 +0000
   |  summary:     d
   |
-  o  changeset:   1:ef3a871183d7
+  @  changeset:   1:ef3a871183d7
   |  user:        test
   |  date:        Thu Jan 01 00:00:00 1970 +0000
   |  summary:     b
--- a/tests/test-mq.t	Mon Jan 14 23:14:45 2013 +0900
+++ b/tests/test-mq.t	Sat Jan 19 17:24:33 2013 -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
 
@@ -1110,8 +1110,14 @@
   $ hg qpop
   popping baz
   now at: bar
+
+test qdel/qrm
+
   $ hg qdel baz
-
+  $ echo p >> .hg/patches/series
+  $ hg qrm p
+  $ hg qser
+  bar
 
 create a git patch
 
--- a/tests/test-mv-cp-st-diff.t	Mon Jan 14 23:14:45 2013 +0900
+++ b/tests/test-mv-cp-st-diff.t	Sat Jan 19 17:24:33 2013 -0600
@@ -21,11 +21,15 @@
   $ hg add x/y
   $ hg ci -m2
   $ cd ..
+
   $ show()
   > {
-  >     echo "- $2: $1"
+  >     echo "# $2:"
+  >     echo
+  >     echo "% hg st -C $1"
   >     hg st -C $1
   >     echo
+  >     echo "% hg diff --git $1"
   >     hg diff --git $1
   >     echo
   > }
@@ -35,24 +39,28 @@
 $1 - first commit
 $2 - second commit
 $3 - working dir action
-$4 - test description
 
   $ tb()
   > {
-  >     hg clone t t2 ; cd t2
+  >     hg clone -q t t2 ; cd t2
   >     hg co -q -C 0
   > 
+  >     echo % add a $count
   >     add a $count
   >     count=`expr $count + 1`
+  >     echo % hg ci -m "t0"
   >     hg ci -m "t0"
+  >     echo % $1
   >     $1
+  >     echo % hg ci -m "t1"
   >     hg ci -m "t1"
+  >     echo % $2
   >     $2
+  >     echo % hg ci -m "t2"
   >     hg ci -m "t2"
+  >     echo % $3
   >     $3
-  > 
-  >     echo "** $4 **"
-  >     echo "** $1 / $2 / $3"
+  >     echo
   >     show "" "working to parent"
   >     show "--rev 0" "working to root"
   >     show "--rev 2" "working to branch"
@@ -64,26 +72,39 @@
   >     cd ..
   >     rm -rf t2
   > }
-  $ tb "add a a1" "add a a2" "hg mv a b" "rename in working dir"
-  updating to branch default
-  3 files updated, 0 files merged, 0 files removed, 0 files unresolved
+
+rename in working dir
+
+  $ tb "add a a1" "add a a2" "hg mv a b"
+  % add a 0
+  % hg ci -m t0
   created new head
-  ** rename in working dir **
-  ** add a a1 / add a a2 / hg mv a b
-  - working to parent: 
+  % add a a1
+  % hg ci -m t1
+  % add a a2
+  % hg ci -m t2
+  % hg mv a b
+  
+  # working to parent:
+  
+  % hg st -C 
   A b
     a
   R a
   
+  % hg diff --git 
   diff --git a/a b/b
   rename from a
   rename to b
   
-  - working to root: --rev 0
+  # working to root:
+  
+  % hg st -C --rev 0
   A b
     a
   R a
   
+  % hg diff --git --rev 0
   diff --git a/a b/b
   rename from a
   rename to b
@@ -95,12 +116,15 @@
   +a1
   +a2
   
-  - working to branch: --rev 2
+  # working to branch:
+  
+  % hg st -C --rev 2
   A b
     a
   R a
   R x/y
   
+  % hg diff --git --rev 2
   diff --git a/a b/b
   rename from a
   rename to b
@@ -120,9 +144,12 @@
   @@ -1,1 +0,0 @@
   -y1
   
-  - root to parent: --rev 0 --rev .
+  # root to parent:
+  
+  % hg st -C --rev 0 --rev .
   M a
   
+  % hg diff --git --rev 0 --rev .
   diff --git a/a b/a
   --- a/a
   +++ b/a
@@ -132,9 +159,12 @@
   +a1
   +a2
   
-  - parent to root: --rev . --rev 0
+  # parent to root:
+  
+  % hg st -C --rev . --rev 0
   M a
   
+  % hg diff --git --rev . --rev 0
   diff --git a/a b/a
   --- a/a
   +++ b/a
@@ -144,10 +174,13 @@
   -a1
   -a2
   
-  - branch to parent: --rev 2 --rev .
+  # branch to parent:
+  
+  % hg st -C --rev 2 --rev .
   M a
   R x/y
   
+  % hg diff --git --rev 2 --rev .
   diff --git a/a b/a
   --- a/a
   +++ b/a
@@ -165,10 +198,13 @@
   @@ -1,1 +0,0 @@
   -y1
   
-  - parent to branch: --rev . --rev 2
+  # parent to branch:
+  
+  % hg st -C --rev . --rev 2
   M a
   A x/y
   
+  % hg diff --git --rev . --rev 2
   diff --git a/a b/a
   --- a/a
   +++ b/a
@@ -187,25 +223,37 @@
   +y1
   
   
-  $ tb "add a a1" "add a a2" "hg cp a b" "copy in working dir"
-  updating to branch default
-  3 files updated, 0 files merged, 0 files removed, 0 files unresolved
+copy in working dir
+
+  $ tb "add a a1" "add a a2" "hg cp a b"
+  % add a 1
+  % hg ci -m t0
   created new head
-  ** copy in working dir **
-  ** add a a1 / add a a2 / hg cp a b
-  - working to parent: 
+  % add a a1
+  % hg ci -m t1
+  % add a a2
+  % hg ci -m t2
+  % hg cp a b
+  
+  # working to parent:
+  
+  % hg st -C 
   A b
     a
   
+  % hg diff --git 
   diff --git a/a b/b
   copy from a
   copy to b
   
-  - working to root: --rev 0
+  # working to root:
+  
+  % hg st -C --rev 0
   M a
   A b
     a
   
+  % hg diff --git --rev 0
   diff --git a/a b/a
   --- a/a
   +++ b/a
@@ -225,12 +273,15 @@
   +a1
   +a2
   
-  - working to branch: --rev 2
+  # working to branch:
+  
+  % hg st -C --rev 2
   M a
   A b
     a
   R x/y
   
+  % hg diff --git --rev 2
   diff --git a/a b/a
   --- a/a
   +++ b/a
@@ -260,9 +311,12 @@
   @@ -1,1 +0,0 @@
   -y1
   
-  - root to parent: --rev 0 --rev .
+  # root to parent:
+  
+  % hg st -C --rev 0 --rev .
   M a
   
+  % hg diff --git --rev 0 --rev .
   diff --git a/a b/a
   --- a/a
   +++ b/a
@@ -272,9 +326,12 @@
   +a1
   +a2
   
-  - parent to root: --rev . --rev 0
+  # parent to root:
+  
+  % hg st -C --rev . --rev 0
   M a
   
+  % hg diff --git --rev . --rev 0
   diff --git a/a b/a
   --- a/a
   +++ b/a
@@ -284,10 +341,13 @@
   -a1
   -a2
   
-  - branch to parent: --rev 2 --rev .
+  # branch to parent:
+  
+  % hg st -C --rev 2 --rev .
   M a
   R x/y
   
+  % hg diff --git --rev 2 --rev .
   diff --git a/a b/a
   --- a/a
   +++ b/a
@@ -305,10 +365,13 @@
   @@ -1,1 +0,0 @@
   -y1
   
-  - parent to branch: --rev . --rev 2
+  # parent to branch:
+  
+  % hg st -C --rev . --rev 2
   M a
   A x/y
   
+  % hg diff --git --rev . --rev 2
   diff --git a/a b/a
   --- a/a
   +++ b/a
@@ -327,15 +390,24 @@
   +y1
   
   
-  $ tb "hg mv a b" "add b b1" "add b w" "single rename"
-  updating to branch default
-  3 files updated, 0 files merged, 0 files removed, 0 files unresolved
+single rename
+
+  $ tb "hg mv a b" "add b b1" "add b w"
+  % add a 2
+  % hg ci -m t0
   created new head
-  ** single rename **
-  ** hg mv a b / add b b1 / add b w
-  - working to parent: 
+  % hg mv a b
+  % hg ci -m t1
+  % add b b1
+  % hg ci -m t2
+  % add b w
+  
+  # working to parent:
+  
+  % hg st -C 
   M b
   
+  % hg diff --git 
   diff --git a/b b/b
   --- a/b
   +++ b/b
@@ -345,11 +417,14 @@
    b1
   +w
   
-  - working to root: --rev 0
+  # working to root:
+  
+  % hg st -C --rev 0
   A b
     a
   R a
   
+  % hg diff --git --rev 0
   diff --git a/a b/b
   rename from a
   rename to b
@@ -361,12 +436,15 @@
   +b1
   +w
   
-  - working to branch: --rev 2
+  # working to branch:
+  
+  % hg st -C --rev 2
   A b
     a
   R a
   R x/y
   
+  % hg diff --git --rev 2
   diff --git a/a b/b
   rename from a
   rename to b
@@ -386,11 +464,14 @@
   @@ -1,1 +0,0 @@
   -y1
   
-  - root to parent: --rev 0 --rev .
+  # root to parent:
+  
+  % hg st -C --rev 0 --rev .
   A b
     a
   R a
   
+  % hg diff --git --rev 0 --rev .
   diff --git a/a b/b
   rename from a
   rename to b
@@ -401,11 +482,14 @@
   +2
   +b1
   
-  - parent to root: --rev . --rev 0
+  # parent to root:
+  
+  % hg st -C --rev . --rev 0
   A a
     b
   R b
   
+  % hg diff --git --rev . --rev 0
   diff --git a/b b/a
   rename from b
   rename to a
@@ -416,12 +500,15 @@
   -2
   -b1
   
-  - branch to parent: --rev 2 --rev .
+  # branch to parent:
+  
+  % hg st -C --rev 2 --rev .
   A b
     a
   R a
   R x/y
   
+  % hg diff --git --rev 2 --rev .
   diff --git a/a b/b
   rename from a
   rename to b
@@ -440,12 +527,15 @@
   @@ -1,1 +0,0 @@
   -y1
   
-  - parent to branch: --rev . --rev 2
+  # parent to branch:
+  
+  % hg st -C --rev . --rev 2
   A a
     b
   A x/y
   R b
   
+  % hg diff --git --rev . --rev 2
   diff --git a/b b/a
   rename from b
   rename to a
@@ -465,15 +555,24 @@
   +y1
   
   
-  $ tb "hg cp a b" "add b b1" "add a w" "single copy"
-  updating to branch default
-  3 files updated, 0 files merged, 0 files removed, 0 files unresolved
+single copy
+
+  $ tb "hg cp a b" "add b b1" "add a w"
+  % add a 3
+  % hg ci -m t0
   created new head
-  ** single copy **
-  ** hg cp a b / add b b1 / add a w
-  - working to parent: 
+  % hg cp a b
+  % hg ci -m t1
+  % add b b1
+  % hg ci -m t2
+  % add a w
+  
+  # working to parent:
+  
+  % hg st -C 
   M a
   
+  % hg diff --git 
   diff --git a/a b/a
   --- a/a
   +++ b/a
@@ -482,11 +581,14 @@
    3
   +w
   
-  - working to root: --rev 0
+  # working to root:
+  
+  % hg st -C --rev 0
   M a
   A b
     a
   
+  % hg diff --git --rev 0
   diff --git a/a b/a
   --- a/a
   +++ b/a
@@ -504,12 +606,15 @@
   +3
   +b1
   
-  - working to branch: --rev 2
+  # working to branch:
+  
+  % hg st -C --rev 2
   M a
   A b
     a
   R x/y
   
+  % hg diff --git --rev 2
   diff --git a/a b/a
   --- a/a
   +++ b/a
@@ -537,11 +642,14 @@
   @@ -1,1 +0,0 @@
   -y1
   
-  - root to parent: --rev 0 --rev .
+  # root to parent:
+  
+  % hg st -C --rev 0 --rev .
   M a
   A b
     a
   
+  % hg diff --git --rev 0 --rev .
   diff --git a/a b/a
   --- a/a
   +++ b/a
@@ -558,11 +666,13 @@
   +3
   +b1
   
-  - parent to root: --rev . --rev 0
+  # parent to root:
+  
+  % hg st -C --rev . --rev 0
   M a
-    b
   R b
   
+  % hg diff --git --rev . --rev 0
   diff --git a/a b/a
   --- a/a
   +++ b/a
@@ -578,12 +688,15 @@
   -3
   -b1
   
-  - branch to parent: --rev 2 --rev .
+  # branch to parent:
+  
+  % hg st -C --rev 2 --rev .
   M a
   A b
     a
   R x/y
   
+  % hg diff --git --rev 2 --rev .
   diff --git a/a b/a
   --- a/a
   +++ b/a
@@ -610,12 +723,14 @@
   @@ -1,1 +0,0 @@
   -y1
   
-  - parent to branch: --rev . --rev 2
+  # parent to branch:
+  
+  % hg st -C --rev . --rev 2
   M a
-    b
   A x/y
   R b
   
+  % hg diff --git --rev . --rev 2
   diff --git a/a b/a
   --- a/a
   +++ b/a
@@ -640,26 +755,38 @@
   +y1
   
   
-  $ tb "hg mv a b" "hg mv b c" "hg mv c d" "rename chain"
-  updating to branch default
-  3 files updated, 0 files merged, 0 files removed, 0 files unresolved
+rename chain
+
+  $ tb "hg mv a b" "hg mv b c" "hg mv c d"
+  % add a 4
+  % hg ci -m t0
   created new head
-  ** rename chain **
-  ** hg mv a b / hg mv b c / hg mv c d
-  - working to parent: 
+  % hg mv a b
+  % hg ci -m t1
+  % hg mv b c
+  % hg ci -m t2
+  % hg mv c d
+  
+  # working to parent:
+  
+  % hg st -C 
   A d
     c
   R c
   
+  % hg diff --git 
   diff --git a/c b/d
   rename from c
   rename to d
   
-  - working to root: --rev 0
+  # working to root:
+  
+  % hg st -C --rev 0
   A d
     a
   R a
   
+  % hg diff --git --rev 0
   diff --git a/a b/d
   rename from a
   rename to d
@@ -669,12 +796,15 @@
    a
   +4
   
-  - working to branch: --rev 2
+  # working to branch:
+  
+  % hg st -C --rev 2
   A d
     a
   R a
   R x/y
   
+  % hg diff --git --rev 2
   diff --git a/a b/d
   rename from a
   rename to d
@@ -692,11 +822,14 @@
   @@ -1,1 +0,0 @@
   -y1
   
-  - root to parent: --rev 0 --rev .
+  # root to parent:
+  
+  % hg st -C --rev 0 --rev .
   A c
     a
   R a
   
+  % hg diff --git --rev 0 --rev .
   diff --git a/a b/c
   rename from a
   rename to c
@@ -706,11 +839,14 @@
    a
   +4
   
-  - parent to root: --rev . --rev 0
+  # parent to root:
+  
+  % hg st -C --rev . --rev 0
   A a
     c
   R c
   
+  % hg diff --git --rev . --rev 0
   diff --git a/c b/a
   rename from c
   rename to a
@@ -720,12 +856,15 @@
    a
   -4
   
-  - branch to parent: --rev 2 --rev .
+  # branch to parent:
+  
+  % hg st -C --rev 2 --rev .
   A c
     a
   R a
   R x/y
   
+  % hg diff --git --rev 2 --rev .
   diff --git a/a b/c
   rename from a
   rename to c
@@ -743,12 +882,15 @@
   @@ -1,1 +0,0 @@
   -y1
   
-  - parent to branch: --rev . --rev 2
+  # parent to branch:
+  
+  % hg st -C --rev . --rev 2
   A a
     c
   A x/y
   R c
   
+  % hg diff --git --rev . --rev 2
   diff --git a/c b/a
   rename from c
   rename to a
@@ -767,21 +909,32 @@
   +y1
   
   
-  $ tb "hg cp a b" "hg cp b c" "hg cp c d" "copy chain"
-  updating to branch default
-  3 files updated, 0 files merged, 0 files removed, 0 files unresolved
+copy chain
+
+  $ tb "hg cp a b" "hg cp b c" "hg cp c d"
+  % add a 5
+  % hg ci -m t0
   created new head
-  ** copy chain **
-  ** hg cp a b / hg cp b c / hg cp c d
-  - working to parent: 
+  % hg cp a b
+  % hg ci -m t1
+  % hg cp b c
+  % hg ci -m t2
+  % hg cp c d
+  
+  # working to parent:
+  
+  % hg st -C 
   A d
     c
   
+  % hg diff --git 
   diff --git a/c b/d
   copy from c
   copy to d
   
-  - working to root: --rev 0
+  # working to root:
+  
+  % hg st -C --rev 0
   M a
   A b
     a
@@ -790,6 +943,7 @@
   A d
     a
   
+  % hg diff --git --rev 0
   diff --git a/a b/a
   --- a/a
   +++ b/a
@@ -821,7 +975,9 @@
    a
   +5
   
-  - working to branch: --rev 2
+  # working to branch:
+  
+  % hg st -C --rev 2
   M a
   A b
     a
@@ -831,6 +987,7 @@
     a
   R x/y
   
+  % hg diff --git --rev 2
   diff --git a/a b/a
   --- a/a
   +++ b/a
@@ -876,13 +1033,16 @@
   @@ -1,1 +0,0 @@
   -y1
   
-  - root to parent: --rev 0 --rev .
+  # root to parent:
+  
+  % hg st -C --rev 0 --rev .
   M a
   A b
     a
   A c
     a
   
+  % hg diff --git --rev 0 --rev .
   diff --git a/a b/a
   --- a/a
   +++ b/a
@@ -906,12 +1066,14 @@
    a
   +5
   
-  - parent to root: --rev . --rev 0
+  # parent to root:
+  
+  % hg st -C --rev . --rev 0
   M a
-    b
   R b
   R c
   
+  % hg diff --git --rev . --rev 0
   diff --git a/a b/a
   --- a/a
   +++ b/a
@@ -933,7 +1095,9 @@
   -a
   -5
   
-  - branch to parent: --rev 2 --rev .
+  # branch to parent:
+  
+  % hg st -C --rev 2 --rev .
   M a
   A b
     a
@@ -941,6 +1105,7 @@
     a
   R x/y
   
+  % hg diff --git --rev 2 --rev .
   diff --git a/a b/a
   --- a/a
   +++ b/a
@@ -976,13 +1141,15 @@
   @@ -1,1 +0,0 @@
   -y1
   
-  - parent to branch: --rev . --rev 2
+  # parent to branch:
+  
+  % hg st -C --rev . --rev 2
   M a
-    b
   A x/y
   R b
   R c
   
+  % hg diff --git --rev . --rev 2
   diff --git a/a b/a
   --- a/a
   +++ b/a
@@ -1013,24 +1180,36 @@
   +y1
   
   
-  $ tb "add a a1" "hg mv a b" "hg mv b a" "circular rename"
-  updating to branch default
-  3 files updated, 0 files merged, 0 files removed, 0 files unresolved
+circular rename
+
+  $ tb "add a a1" "hg mv a b" "hg mv b a"
+  % add a 6
+  % hg ci -m t0
   created new head
-  ** circular rename **
-  ** add a a1 / hg mv a b / hg mv b a
-  - working to parent: 
+  % add a a1
+  % hg ci -m t1
+  % hg mv a b
+  % hg ci -m t2
+  % hg mv b a
+  
+  # working to parent:
+  
+  % hg st -C 
   A a
     b
   R b
   
+  % hg diff --git 
   diff --git a/b b/a
   rename from b
   rename to a
   
-  - working to root: --rev 0
+  # working to root:
+  
+  % hg st -C --rev 0
   M a
   
+  % hg diff --git --rev 0
   diff --git a/a b/a
   --- a/a
   +++ b/a
@@ -1039,10 +1218,13 @@
   +6
   +a1
   
-  - working to branch: --rev 2
+  # working to branch:
+  
+  % hg st -C --rev 2
   M a
   R x/y
   
+  % hg diff --git --rev 2
   diff --git a/a b/a
   --- a/a
   +++ b/a
@@ -1059,11 +1241,14 @@
   @@ -1,1 +0,0 @@
   -y1
   
-  - root to parent: --rev 0 --rev .
+  # root to parent:
+  
+  % hg st -C --rev 0 --rev .
   A b
     a
   R a
   
+  % hg diff --git --rev 0 --rev .
   diff --git a/a b/b
   rename from a
   rename to b
@@ -1074,11 +1259,14 @@
   +6
   +a1
   
-  - parent to root: --rev . --rev 0
+  # parent to root:
+  
+  % hg st -C --rev . --rev 0
   A a
     b
   R b
   
+  % hg diff --git --rev . --rev 0
   diff --git a/b b/a
   rename from b
   rename to a
@@ -1089,12 +1277,15 @@
   -6
   -a1
   
-  - branch to parent: --rev 2 --rev .
+  # branch to parent:
+  
+  % hg st -C --rev 2 --rev .
   A b
     a
   R a
   R x/y
   
+  % hg diff --git --rev 2 --rev .
   diff --git a/a b/b
   rename from a
   rename to b
@@ -1113,12 +1304,15 @@
   @@ -1,1 +0,0 @@
   -y1
   
-  - parent to branch: --rev . --rev 2
+  # parent to branch:
+  
+  % hg st -C --rev . --rev 2
   A a
     b
   A x/y
   R b
   
+  % hg diff --git --rev . --rev 2
   diff --git a/b b/a
   rename from b
   rename to a
@@ -1138,16 +1332,25 @@
   +y1
   
   
-  $ tb "hg mv x y" "add y/x x1" "add y/x x2" "directory move"
-  updating to branch default
-  3 files updated, 0 files merged, 0 files removed, 0 files unresolved
+directory move
+
+  $ tb "hg mv x y" "add y/x x1" "add y/x x2"
+  % add a 7
+  % hg ci -m t0
   created new head
+  % hg mv x y
   moving x/x to y/x (glob)
-  ** directory move **
-  ** hg mv x y / add y/x x1 / add y/x x2
-  - working to parent: 
+  % hg ci -m t1
+  % add y/x x1
+  % hg ci -m t2
+  % add y/x x2
+  
+  # working to parent:
+  
+  % hg st -C 
   M y/x
   
+  % hg diff --git 
   diff --git a/y/x b/y/x
   --- a/y/x
   +++ b/y/x
@@ -1156,12 +1359,15 @@
    x1
   +x2
   
-  - working to root: --rev 0
+  # working to root:
+  
+  % hg st -C --rev 0
   M a
   A y/x
     x/x
   R x/x
   
+  % hg diff --git --rev 0
   diff --git a/a b/a
   --- a/a
   +++ b/a
@@ -1178,13 +1384,16 @@
   +x1
   +x2
   
-  - working to branch: --rev 2
+  # working to branch:
+  
+  % hg st -C --rev 2
   M a
   A y/x
     x/x
   R x/x
   R x/y
   
+  % hg diff --git --rev 2
   diff --git a/a b/a
   --- a/a
   +++ b/a
@@ -1209,12 +1418,15 @@
   +x1
   +x2
   
-  - root to parent: --rev 0 --rev .
+  # root to parent:
+  
+  % hg st -C --rev 0 --rev .
   M a
   A y/x
     x/x
   R x/x
   
+  % hg diff --git --rev 0 --rev .
   diff --git a/a b/a
   --- a/a
   +++ b/a
@@ -1230,12 +1442,15 @@
    x
   +x1
   
-  - parent to root: --rev . --rev 0
+  # parent to root:
+  
+  % hg st -C --rev . --rev 0
   M a
   A x/x
     y/x
   R y/x
   
+  % hg diff --git --rev . --rev 0
   diff --git a/a b/a
   --- a/a
   +++ b/a
@@ -1251,13 +1466,16 @@
    x
   -x1
   
-  - branch to parent: --rev 2 --rev .
+  # branch to parent:
+  
+  % hg st -C --rev 2 --rev .
   M a
   A y/x
     x/x
   R x/x
   R x/y
   
+  % hg diff --git --rev 2 --rev .
   diff --git a/a b/a
   --- a/a
   +++ b/a
@@ -1281,13 +1499,16 @@
    x
   +x1
   
-  - parent to branch: --rev . --rev 2
+  # parent to branch:
+  
+  % hg st -C --rev . --rev 2
   M a
   A x/x
     y/x
   A x/y
   R y/x
   
+  % hg diff --git --rev . --rev 2
   diff --git a/a b/a
   --- a/a
   +++ b/a
@@ -1318,14 +1539,14 @@
 
   $ hg init unrelated
   $ cd unrelated
-  $ add a a
+  $ echo a >> a
   $ hg ci -Am adda
   adding a
   $ hg mv a b
   $ hg ci -m movea
   $ hg up -C null
   0 files updated, 0 files merged, 1 files removed, 0 files unresolved
-  $ add a a
+  $ echo a >> a
   $ hg ci -Am addunrelateda
   adding a
   created new head
--- a/tests/test-newbranch.t	Mon Jan 14 23:14:45 2013 +0900
+++ b/tests/test-newbranch.t	Sat Jan 19 17:24:33 2013 -0600
@@ -1,5 +1,15 @@
   $ branchcache=.hg/cache/branchheads
 
+  $ listbranchcaches() {
+  >    for f in .hg/cache/branchheads*;
+  >       do echo === $f ===;
+  >       cat $f;
+  >     done;
+  > }
+  $ purgebranchcaches() {
+  >     rm .hg/cache/branchheads*
+  > }
+
   $ hg init t
   $ cd t
 
@@ -112,7 +122,7 @@
   repository tip rolled back to revision 4 (undo commit)
   working directory now based on revisions 4 and 3
 
-  $ cp $branchcache .hg/bc-invalid
+  $ cp ${branchcache}-served .hg/bc-invalid
 
   $ hg log -r foo
   changeset:   4:adf1a74a7f7b
@@ -126,7 +136,6 @@
   $ cp .hg/bc-invalid $branchcache
 
   $ hg --debug log -r foo
-  invalidating branch cache (tip differs)
   changeset:   4:adf1a74a7f7b4cd193d12992f5d0d6a004ed21d6
   branch:      foo
   tag:         tip
@@ -142,17 +151,20 @@
   modify a branch
   
   
-  $ rm $branchcache
+  $ purgebranchcaches
   $ echo corrupted > $branchcache
 
   $ hg log -qr foo
   4:adf1a74a7f7b
 
-  $ cat $branchcache
+  $ listbranchcaches
+  === .hg/cache/branchheads ===
+  corrupted
+  === .hg/cache/branchheads-served ===
   adf1a74a7f7b4cd193d12992f5d0d6a004ed21d6 4
+  c21617b13b220988e7a2e26290fbe4325ffa7139 bar
   1c28f494dae69a2f8fc815059d257eccf3fcfe75 default
   adf1a74a7f7b4cd193d12992f5d0d6a004ed21d6 foo
-  c21617b13b220988e7a2e26290fbe4325ffa7139 bar
 
 Push should update the branch cache:
 
@@ -162,7 +174,8 @@
 
   $ hg push -qr 0 ../target
 
-  $ cat ../target/$branchcache
+  $ (cd ../target/; listbranchcaches)
+  === .hg/cache/branchheads-base ===
   db01e8ea3388fd3c7c94e1436ea2bd6a53d581c5 0
   db01e8ea3388fd3c7c94e1436ea2bd6a53d581c5 default
 
@@ -170,11 +183,12 @@
 
   $ hg push -qf ../target
 
-  $ cat ../target/$branchcache
+  $ (cd ../target/; listbranchcaches)
+  === .hg/cache/branchheads-base ===
   adf1a74a7f7b4cd193d12992f5d0d6a004ed21d6 4
+  c21617b13b220988e7a2e26290fbe4325ffa7139 bar
   1c28f494dae69a2f8fc815059d257eccf3fcfe75 default
   adf1a74a7f7b4cd193d12992f5d0d6a004ed21d6 foo
-  c21617b13b220988e7a2e26290fbe4325ffa7139 bar
 
 Update with no arguments: tipmost revision of the current branch:
 
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tests/test-obsolete-divergent.t	Sat Jan 19 17:24:33 2013 -0600
@@ -0,0 +1,447 @@
+Test file dedicated to testing the divergent troubles from obsolete changeset.
+
+This is the most complexe troubles from far so we isolate it in a dedicated
+file.
+
+Enable obsolete
+
+  $ cat > obs.py << EOF
+  > import mercurial.obsolete
+  > mercurial.obsolete._enabled = True
+  > EOF
+  $ cat >> $HGRCPATH << EOF
+  > [ui]
+  > logtemplate = {rev}:{node|short} {desc}\n
+  > [extensions]
+  > obs=${TESTTMP}/obs.py
+  > [alias]
+  > debugobsolete = debugobsolete -d '0 0'
+  > [phases]
+  > publish=False
+  > EOF
+
+
+  $ mkcommit() {
+  >    echo "$1" > "$1"
+  >    hg add "$1"
+  >    hg ci -m "$1"
+  > }
+  $ getid() {
+  >    hg id --debug --hidden -ir "desc('$1')"
+  > }
+
+setup repo
+
+  $ hg init reference
+  $ cd reference
+  $ mkcommit base
+  $ mkcommit A_0
+  $ hg up 0
+  0 files updated, 0 files merged, 1 files removed, 0 files unresolved
+  $ mkcommit A_1
+  created new head
+  $ hg up 0
+  0 files updated, 0 files merged, 1 files removed, 0 files unresolved
+  $ mkcommit A_2
+  created new head
+  $ hg up 0
+  0 files updated, 0 files merged, 1 files removed, 0 files unresolved
+  $ cd ..
+
+
+  $ newcase() {
+  >    hg clone -u 0 -q reference $1
+  >    cd $1
+  > }
+
+direct divergence
+-----------------
+
+A_1 have two direct and divergent successors A_1 and A_1
+
+  $ newcase direct
+  $ hg debugobsolete `getid A_0` `getid A_1`
+  $ hg debugobsolete `getid A_0` `getid A_2`
+  invalid branchheads cache (served): tip differs
+  $ hg log -G --hidden
+  o  3:392fd25390da A_2
+  |
+  | o  2:82623d38b9ba A_1
+  |/
+  | x  1:007dc284c1f8 A_0
+  |/
+  @  0:d20a80d4def3 base
+  
+  $ hg debugsuccessorssets --hidden 'all()'
+  d20a80d4def3
+      d20a80d4def3
+  007dc284c1f8
+      82623d38b9ba
+      392fd25390da
+  82623d38b9ba
+      82623d38b9ba
+  392fd25390da
+      392fd25390da
+  $ hg log -r 'divergent()'
+  2:82623d38b9ba A_1
+  3:392fd25390da A_2
+
+check that mercurial refuse to push
+
+  $ hg init ../other
+  $ hg push ../other
+  pushing to ../other
+  searching for changes
+  abort: push includes divergent changeset: 392fd25390da!
+  [255]
+
+  $ cd ..
+
+
+indirect divergence with known changeset
+-------------------------------------------
+
+  $ newcase indirect_known
+  $ hg debugobsolete `getid A_0` `getid A_1`
+  $ hg debugobsolete `getid A_0` `getid A_2`
+  invalid branchheads cache (served): tip differs
+  $ mkcommit A_3
+  created new head
+  $ hg debugobsolete `getid A_2` `getid A_3`
+  $ hg log -G --hidden
+  @  4:01f36c5a8fda A_3
+  |
+  | x  3:392fd25390da A_2
+  |/
+  | o  2:82623d38b9ba A_1
+  |/
+  | x  1:007dc284c1f8 A_0
+  |/
+  o  0:d20a80d4def3 base
+  
+  $ hg debugsuccessorssets --hidden 'all()'
+  d20a80d4def3
+      d20a80d4def3
+  007dc284c1f8
+      82623d38b9ba
+      01f36c5a8fda
+  82623d38b9ba
+      82623d38b9ba
+  392fd25390da
+      01f36c5a8fda
+  01f36c5a8fda
+      01f36c5a8fda
+  $ hg log -r 'divergent()'
+  2:82623d38b9ba A_1
+  4:01f36c5a8fda A_3
+  $ cd ..
+
+
+indirect divergence with known changeset
+-------------------------------------------
+
+  $ newcase indirect_unknown
+  $ hg debugobsolete `getid A_0` aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
+  $ hg debugobsolete aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa `getid A_1`
+  invalid branchheads cache (served): tip differs
+  $ hg debugobsolete `getid A_0` `getid A_2`
+  $ hg log -G --hidden
+  o  3:392fd25390da A_2
+  |
+  | o  2:82623d38b9ba A_1
+  |/
+  | x  1:007dc284c1f8 A_0
+  |/
+  @  0:d20a80d4def3 base
+  
+  $ hg debugsuccessorssets --hidden 'all()'
+  d20a80d4def3
+      d20a80d4def3
+  007dc284c1f8
+      82623d38b9ba
+      392fd25390da
+  82623d38b9ba
+      82623d38b9ba
+  392fd25390da
+      392fd25390da
+  $ hg log -r 'divergent()'
+  2:82623d38b9ba A_1
+  3:392fd25390da A_2
+  $ cd ..
+
+do not take unknown node in account if they are final
+-----------------------------------------------------
+
+  $ newcase final-unknown
+  $ hg debugobsolete `getid A_0` `getid A_1`
+  $ hg debugobsolete `getid A_1` `getid A_2`
+  invalid branchheads cache (served): tip differs
+  $ hg debugobsolete `getid A_0` bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb
+  $ hg debugobsolete bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb cccccccccccccccccccccccccccccccccccccccc
+  $ hg debugobsolete `getid A_1` dddddddddddddddddddddddddddddddddddddddd
+
+  $ hg debugsuccessorssets --hidden 'desc('A_0')'
+  007dc284c1f8
+      392fd25390da
+
+  $ cd ..
+
+divergence that converge again is not divergence anymore
+-----------------------------------------------------
+
+  $ newcase converged_divergence
+  $ hg debugobsolete `getid A_0` `getid A_1`
+  $ hg debugobsolete `getid A_0` `getid A_2`
+  invalid branchheads cache (served): tip differs
+  $ mkcommit A_3
+  created new head
+  $ hg debugobsolete `getid A_1` `getid A_3`
+  $ hg debugobsolete `getid A_2` `getid A_3`
+  $ hg log -G --hidden
+  @  4:01f36c5a8fda A_3
+  |
+  | x  3:392fd25390da A_2
+  |/
+  | x  2:82623d38b9ba A_1
+  |/
+  | x  1:007dc284c1f8 A_0
+  |/
+  o  0:d20a80d4def3 base
+  
+  $ hg debugsuccessorssets --hidden 'all()'
+  d20a80d4def3
+      d20a80d4def3
+  007dc284c1f8
+      01f36c5a8fda
+  82623d38b9ba
+      01f36c5a8fda
+  392fd25390da
+      01f36c5a8fda
+  01f36c5a8fda
+      01f36c5a8fda
+  $ hg log -r 'divergent()'
+  $ cd ..
+
+split is not divergences
+-----------------------------
+
+  $ newcase split
+  $ hg debugobsolete `getid A_0` `getid A_1` `getid A_2`
+  $ hg log -G --hidden
+  o  3:392fd25390da A_2
+  |
+  | o  2:82623d38b9ba A_1
+  |/
+  | x  1:007dc284c1f8 A_0
+  |/
+  @  0:d20a80d4def3 base
+  
+  $ hg debugsuccessorssets --hidden 'all()'
+  d20a80d4def3
+      d20a80d4def3
+  007dc284c1f8
+      82623d38b9ba 392fd25390da
+  82623d38b9ba
+      82623d38b9ba
+  392fd25390da
+      392fd25390da
+  $ hg log -r 'divergent()'
+
+Even when subsequente rewriting happen
+
+  $ mkcommit A_3
+  created new head
+  $ hg debugobsolete `getid A_1` `getid A_3`
+  $ hg up 0
+  0 files updated, 0 files merged, 1 files removed, 0 files unresolved
+  $ mkcommit A_4
+  created new head
+  $ hg debugobsolete `getid A_2` `getid A_4`
+  $ hg up 0
+  0 files updated, 0 files merged, 1 files removed, 0 files unresolved
+  $ mkcommit A_5
+  created new head
+  $ hg debugobsolete `getid A_4` `getid A_5`
+  $ hg log -G --hidden
+  @  6:e442cfc57690 A_5
+  |
+  | x  5:6a411f0d7a0a A_4
+  |/
+  | o  4:01f36c5a8fda A_3
+  |/
+  | x  3:392fd25390da A_2
+  |/
+  | x  2:82623d38b9ba A_1
+  |/
+  | x  1:007dc284c1f8 A_0
+  |/
+  o  0:d20a80d4def3 base
+  
+  $ hg debugsuccessorssets --hidden 'all()'
+  d20a80d4def3
+      d20a80d4def3
+  007dc284c1f8
+      01f36c5a8fda e442cfc57690
+  82623d38b9ba
+      01f36c5a8fda
+  392fd25390da
+      e442cfc57690
+  01f36c5a8fda
+      01f36c5a8fda
+  6a411f0d7a0a
+      e442cfc57690
+  e442cfc57690
+      e442cfc57690
+  $ hg log -r 'divergent()'
+
+Check more complexe obsolescence graft (with divergence)
+
+  $ mkcommit B_0; hg up 0
+  0 files updated, 0 files merged, 2 files removed, 0 files unresolved
+  $ hg debugobsolete `getid B_0` `getid A_2`
+  $ mkcommit A_7; hg up 0
+  created new head
+  0 files updated, 0 files merged, 1 files removed, 0 files unresolved
+  $ mkcommit A_8; hg up 0
+  created new head
+  0 files updated, 0 files merged, 1 files removed, 0 files unresolved
+  $ hg debugobsolete `getid A_5` `getid A_7` `getid A_8`
+  $ mkcommit A_9; hg up 0
+  created new head
+  0 files updated, 0 files merged, 1 files removed, 0 files unresolved
+  $ hg debugobsolete `getid A_5` `getid A_9`
+  $ hg log -G --hidden
+  o  10:bed64f5d2f5a A_9
+  |
+  | o  9:14608b260df8 A_8
+  |/
+  | o  8:7ae126973a96 A_7
+  |/
+  | x  7:3750ebee865d B_0
+  | |
+  | x  6:e442cfc57690 A_5
+  |/
+  | x  5:6a411f0d7a0a A_4
+  |/
+  | o  4:01f36c5a8fda A_3
+  |/
+  | x  3:392fd25390da A_2
+  |/
+  | x  2:82623d38b9ba A_1
+  |/
+  | x  1:007dc284c1f8 A_0
+  |/
+  @  0:d20a80d4def3 base
+  
+  $ hg debugsuccessorssets --hidden 'all()'
+  d20a80d4def3
+      d20a80d4def3
+  007dc284c1f8
+      01f36c5a8fda bed64f5d2f5a
+      01f36c5a8fda 7ae126973a96 14608b260df8
+  82623d38b9ba
+      01f36c5a8fda
+  392fd25390da
+      bed64f5d2f5a
+      7ae126973a96 14608b260df8
+  01f36c5a8fda
+      01f36c5a8fda
+  6a411f0d7a0a
+      bed64f5d2f5a
+      7ae126973a96 14608b260df8
+  e442cfc57690
+      bed64f5d2f5a
+      7ae126973a96 14608b260df8
+  3750ebee865d
+      bed64f5d2f5a
+      7ae126973a96 14608b260df8
+  7ae126973a96
+      7ae126973a96
+  14608b260df8
+      14608b260df8
+  bed64f5d2f5a
+      bed64f5d2f5a
+  $ hg log -r 'divergent()'
+  4:01f36c5a8fda A_3
+  8:7ae126973a96 A_7
+  9:14608b260df8 A_8
+  10:bed64f5d2f5a A_9
+
+fix the divergence
+
+  $ mkcommit A_A; hg up 0
+  created new head
+  0 files updated, 0 files merged, 1 files removed, 0 files unresolved
+  $ hg debugobsolete `getid A_9` `getid A_A`
+  $ hg debugobsolete `getid A_7` `getid A_A`
+  $ hg debugobsolete `getid A_8` `getid A_A`
+  $ hg log -G --hidden
+  o  11:a139f71be9da A_A
+  |
+  | x  10:bed64f5d2f5a A_9
+  |/
+  | x  9:14608b260df8 A_8
+  |/
+  | x  8:7ae126973a96 A_7
+  |/
+  | x  7:3750ebee865d B_0
+  | |
+  | x  6:e442cfc57690 A_5
+  |/
+  | x  5:6a411f0d7a0a A_4
+  |/
+  | o  4:01f36c5a8fda A_3
+  |/
+  | x  3:392fd25390da A_2
+  |/
+  | x  2:82623d38b9ba A_1
+  |/
+  | x  1:007dc284c1f8 A_0
+  |/
+  @  0:d20a80d4def3 base
+  
+  $ hg debugsuccessorssets --hidden 'all()'
+  d20a80d4def3
+      d20a80d4def3
+  007dc284c1f8
+      01f36c5a8fda a139f71be9da
+  82623d38b9ba
+      01f36c5a8fda
+  392fd25390da
+      a139f71be9da
+  01f36c5a8fda
+      01f36c5a8fda
+  6a411f0d7a0a
+      a139f71be9da
+  e442cfc57690
+      a139f71be9da
+  3750ebee865d
+      a139f71be9da
+  7ae126973a96
+      a139f71be9da
+  14608b260df8
+      a139f71be9da
+  bed64f5d2f5a
+      a139f71be9da
+  a139f71be9da
+      a139f71be9da
+  $ hg log -r 'divergent()'
+
+  $ cd ..
+
+
+Subset does not diverge
+------------------------------
+
+Do not report divergent successors-set if it is a subset of another
+successors-set. (report [A,B] not [A] + [A,B])
+
+  $ newcase subset
+  $ hg debugobsolete `getid A_0` `getid A_2`
+  $ hg debugobsolete `getid A_0` `getid A_1` `getid A_2`
+  invalid branchheads cache (served): tip differs
+  $ hg debugsuccessorssets --hidden 'desc('A_0')'
+  007dc284c1f8
+      82623d38b9ba 392fd25390da
+
+  $ cd ..
--- a/tests/test-obsolete.t	Mon Jan 14 23:14:45 2013 +0900
+++ b/tests/test-obsolete.t	Sat Jan 19 17:24:33 2013 -0600
@@ -11,7 +11,7 @@
   >    hg ci -m "add $1"
   > }
   $ getid() {
-  >    hg id --debug -ir "desc('$1')"
+  >    hg id --debug --hidden -ir "desc('$1')"
   > }
 
   $ cat > debugkeys.py <<EOF
@@ -128,9 +128,84 @@
      summary:     add a
   
 
+check that heads does not report them
+
+  $ hg heads
+  changeset:   5:5601fb93a350
+  tag:         tip
+  parent:      1:7c3bad9141dc
+  user:        test
+  date:        Thu Jan 01 00:00:00 1970 +0000
+  summary:     add new_3_c
+  
+  $ hg heads --hidden
+  changeset:   5:5601fb93a350
+  tag:         tip
+  parent:      1:7c3bad9141dc
+  user:        test
+  date:        Thu Jan 01 00:00:00 1970 +0000
+  summary:     add new_3_c
+  
+  changeset:   4:ca819180edb9
+  parent:      1:7c3bad9141dc
+  user:        test
+  date:        Thu Jan 01 00:00:00 1970 +0000
+  summary:     add new_2_c
+  
+  changeset:   3:cdbce2fbb163
+  parent:      1:7c3bad9141dc
+  user:        test
+  date:        Thu Jan 01 00:00:00 1970 +0000
+  summary:     add new_c
+  
+  changeset:   2:245bde4270cd
+  user:        test
+  date:        Thu Jan 01 00:00:00 1970 +0000
+  summary:     add original_c
+  
+
+
+check that summary does not report them
+
+  $ hg init ../sink
+  $ echo '[paths]' >> .hg/hgrc
+  $ echo 'default=../sink' >> .hg/hgrc
+  $ hg summary --remote
+  parent: 5:5601fb93a350 tip
+   add new_3_c
+  branch: default
+  commit: (clean)
+  update: (current)
+  remote: 3 outgoing
+
+  $ hg summary --remote --hidden
+  parent: 5:5601fb93a350 tip
+   add new_3_c
+  branch: default
+  commit: (clean)
+  update: 3 new changesets, 4 branch heads (merge)
+  remote: 3 outgoing
+
+check that various commands work well with filtering
+
+  $ hg tip
+  changeset:   5:5601fb93a350
+  tag:         tip
+  parent:      1:7c3bad9141dc
+  user:        test
+  date:        Thu Jan 01 00:00:00 1970 +0000
+  summary:     add new_3_c
+  
+  $ hg log -r 6
+  abort: unknown revision '6'!
+  [255]
+  $ hg log -r 4
+  abort: unknown revision '4'!
+  [255]
+
 Check that public changeset are not accounted as obsolete:
 
-  $ hg phase --public 2
+  $ hg --hidden phase --public 2
   $ hg --config 'extensions.graphlog=' glog
   @  changeset:   5:5601fb93a350
   |  tag:         tip
@@ -173,6 +248,14 @@
 
 And that we can't push bumped changeset
 
+  $ hg push ../tmpa -r 0 --force #(make repo related)
+  pushing to ../tmpa
+  searching for changes
+  warning: repository is unrelated
+  adding changesets
+  adding manifests
+  adding file changes
+  added 1 changesets with 1 changes to 1 files (+1 heads)
   $ hg push ../tmpa
   pushing to ../tmpa
   searching for changes
@@ -577,16 +660,51 @@
   adding file changes
   added 1 changesets with 1 changes to 1 files (+1 heads)
 
+check hgweb does not explode
+====================================
+
+  $ hg unbundle $TESTDIR/bundles/hgweb+obs.hg
+  adding changesets
+  adding manifests
+  adding file changes
+  added 62 changesets with 63 changes to 9 files (+60 heads)
+  (run 'hg heads .' to see heads, 'hg merge' to merge)
+  $ for node in `hg log -r 'desc(babar_)' --template '{node}\n'`;
+  > do
+  >    hg debugobsolete $node
+  > done
+  $ hg up tip
+  2 files updated, 0 files merged, 0 files removed, 0 files unresolved
+
+  $ hg serve -n test -p $HGPORT -d --pid-file=hg.pid -A access.log -E errors.log
+  $ cat hg.pid >> $DAEMON_PIDS
+
+check changelog view
+
+  $ "$TESTDIR/get-with-headers.py" --headeronly localhost:$HGPORT 'shortlog/'
+  200 Script output follows
+
+check graph view
+
+  $ "$TESTDIR/get-with-headers.py" --headeronly localhost:$HGPORT 'graph'
+  200 Script output follows
+
+check filelog view
+
+  $ "$TESTDIR/get-with-headers.py" --headeronly localhost:$HGPORT 'log/'`hg id --debug --id`/'babar'
+  200 Script output follows
+  $ kill `cat hg.pid`
+
 Checking _enable=False warning if obsolete marker exists
 
   $ echo '[extensions]' >> $HGRCPATH
   $ echo "obs=!" >> $HGRCPATH
   $ hg log -r tip
-  obsolete feature not enabled but 8 markers found!
-  changeset:   6:3de5eca88c00
+  obsolete feature not enabled but 68 markers found!
+  changeset:   68:c15e9edfca13
   tag:         tip
-  parent:      3:6f9641995072
+  parent:      7:50c51b361e60
   user:        test
   date:        Thu Jan 01 00:00:00 1970 +0000
-  summary:     add obsolete_e
+  summary:     add celestine
   
--- a/tests/test-parents.t	Mon Jan 14 23:14:45 2013 +0900
+++ b/tests/test-parents.t	Sat Jan 19 17:24:33 2013 -0600
@@ -71,7 +71,7 @@
   
 
   $ hg parents -r 2 ../a
-  abort: ../a not under root
+  abort: ../a not under root '$TESTTMP/repo'
   [255]
 
 
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tests/test-pathencode.py	Sat Jan 19 17:24:33 2013 -0600
@@ -0,0 +1,198 @@
+# 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)
+
+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):
+        h = store._pathencode(p)    # uses C implementation, if available
+        r = store._hybridencode(p, True) # reference implementation in Python
+        if h != r:
+            if nerrs == 0:
+                print >> sys.stderr, 'seed:', hex(seed)[:-1]
+            print >> sys.stderr, "\np: '%s'" % p.encode("string_escape")
+            print >> sys.stderr, "h: '%s'" % h.encode("string_escape")
+            print >> sys.stderr, "r: '%s'" % r.encode("string_escape")
+            nerrs += 1
+    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, base=0) # accepts base 10 or 16 strings
+        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-phases.t	Mon Jan 14 23:14:45 2013 +0900
+++ b/tests/test-phases.t	Sat Jan 19 17:24:33 2013 -0600
@@ -173,6 +173,31 @@
 
 :note: The "(+1 heads)" is wrong as we do not had any visible head
 
+check that branch cache with "unserved" filter are properly computed and stored
+
+  $ ls ../push-dest/.hg/cache/branchheads*
+  ../push-dest/.hg/cache/branchheads-served
+  $ cat ../push-dest/.hg/cache/branchheads-served
+  6d6770faffce199f1fddd1cf87f6f026138cf061 6 465891ffab3c47a3c23792f7dc84156e19a90722
+  b3325c91a4d916bcc4cdc83ea3fe4ece46a42f6e default
+  6d6770faffce199f1fddd1cf87f6f026138cf061 default
+  $ hg heads -R ../push-dest --template '{rev}:{node} {phase}\n'  #update visible cache too
+  6:6d6770faffce199f1fddd1cf87f6f026138cf061 draft
+  5:2713879da13d6eea1ff22b442a5a87cb31a7ce6a secret
+  3:b3325c91a4d916bcc4cdc83ea3fe4ece46a42f6e draft
+  $ ls ../push-dest/.hg/cache/branchheads*
+  ../push-dest/.hg/cache/branchheads-served
+  ../push-dest/.hg/cache/branchheads-visible
+  $ cat ../push-dest/.hg/cache/branchheads-served
+  6d6770faffce199f1fddd1cf87f6f026138cf061 6 465891ffab3c47a3c23792f7dc84156e19a90722
+  b3325c91a4d916bcc4cdc83ea3fe4ece46a42f6e default
+  6d6770faffce199f1fddd1cf87f6f026138cf061 default
+  $ cat ../push-dest/.hg/cache/branchheads-visible
+  6d6770faffce199f1fddd1cf87f6f026138cf061 6
+  b3325c91a4d916bcc4cdc83ea3fe4ece46a42f6e default
+  2713879da13d6eea1ff22b442a5a87cb31a7ce6a default
+  6d6770faffce199f1fddd1cf87f6f026138cf061 default
+
 
 Restore condition prior extra insertion.
   $ hg -q --config extensions.mq= strip .
--- a/tests/test-push-cgi.t	Mon Jan 14 23:14:45 2013 +0900
+++ b/tests/test-push-cgi.t	Sat Jan 19 17:24:33 2013 -0600
@@ -54,6 +54,7 @@
   $ cat page2
   Status: 200 Script output follows\r (esc)
   Content-Type: application/mercurial-0.1\r (esc)
+  Content-Length: 102\r (esc)
   \r (esc)
   1
   adding changesets
@@ -68,6 +69,7 @@
   $ cat page3
   Status: 200 Script output follows\r (esc)
   Content-Type: application/mercurial-0.1\r (esc)
+  Content-Length: 102\r (esc)
   \r (esc)
   1
   adding changesets
@@ -82,6 +84,7 @@
   $ cat page4
   Status: 200 Script output follows\r (esc)
   Content-Type: application/mercurial-0.1\r (esc)
+  Content-Length: 102\r (esc)
   \r (esc)
   1
   adding changesets
--- a/tests/test-push-validation.t	Mon Jan 14 23:14:45 2013 +0900
+++ b/tests/test-push-validation.t	Sat Jan 19 17:24:33 2013 -0600
@@ -18,7 +18,48 @@
   updating to branch default
   2 files updated, 0 files merged, 0 files removed, 0 files unresolved
 
+Test spurious filelog entries:
+
   $ cd test-clone
+  $ echo blah >> beta
+  $ cp .hg/store/data/beta.i tmp1
+  $ hg ci -m 2
+  $ cp .hg/store/data/beta.i tmp2
+  $ hg -q rollback
+  $ mv tmp2 .hg/store/data/beta.i
+  $ echo blah >> beta
+  $ hg ci -m '2 (corrupt)'
+
+Expected to fail:
+
+  $ hg verify
+  checking changesets
+  checking manifests
+  crosschecking files in changesets and manifests
+  checking files
+   beta@1: dddc47b3ba30 not in manifests
+  2 files, 2 changesets, 4 total revisions
+  1 integrity errors encountered!
+  (first damaged changeset appears to be 1)
+  [1]
+
+  $ hg push
+  pushing to $TESTTMP/test
+  searching for changes
+  adding changesets
+  adding manifests
+  adding file changes
+  transaction abort!
+  rollback completed
+  abort: received spurious file revlog entry
+  [255]
+
+  $ hg -q rollback
+  $ mv tmp1 .hg/store/data/beta.i
+  $ echo beta > beta
+
+Test missing filelog entries:
+
   $ cp .hg/store/data/beta.i tmp
   $ echo blah >> beta
   $ hg ci -m '2 (corrupt)'
@@ -37,8 +78,6 @@
   (first damaged changeset appears to be 1)
   [1]
 
-Expected to fail:
-
   $ hg push
   pushing to $TESTTMP/test (glob)
   searching for changes
--- a/tests/test-push-warn.t	Mon Jan 14 23:14:45 2013 +0900
+++ b/tests/test-push-warn.t	Sat Jan 19 17:24:33 2013 -0600
@@ -457,7 +457,7 @@
   $ hg push ../l -b b
   pushing to ../l
   searching for changes
-  abort: push creates new remote head e7e31d71180f on branch 'a'!
+  abort: push creates new remote head 451211cc22b0 on branch 'a'!
   (did you forget to merge? use push -f to force)
   [255]
 
--- a/tests/test-rebase-collapse.t	Mon Jan 14 23:14:45 2013 +0900
+++ b/tests/test-rebase-collapse.t	Sat Jan 19 17:24:33 2013 -0600
@@ -263,9 +263,10 @@
 
 
 Test that branchheads cache is updated correctly when doing a strip in which
-the parent of the ancestor node to be stripped does not become a head and
-also, the parent of a node that is a child of the node stripped becomes a head
-(node 3).
+the parent of the ancestor node to be stripped does not become a head and also,
+the parent of a node that is a child of the node stripped becomes a head (node
+3). The code is now much simpler and we could just test a simpler scenario
+We keep it the test this way in case new complexity is injected.
 
   $ hg clone -q -u . b b2
   $ cd b2
@@ -274,7 +275,7 @@
   7:c65502d4178782309ce0574c5ae6ee9485a9bafa default
   6:c772a8b2dc17629cec88a19d09c926c4814b12c7 default
 
-  $ cat $TESTTMP/b2/.hg/cache/branchheads
+  $ cat $TESTTMP/b2/.hg/cache/branchheads-served
   c65502d4178782309ce0574c5ae6ee9485a9bafa 7
   c772a8b2dc17629cec88a19d09c926c4814b12c7 default
   c65502d4178782309ce0574c5ae6ee9485a9bafa default
@@ -282,7 +283,7 @@
   $ hg strip 4
   saved backup bundle to $TESTTMP/b2/.hg/strip-backup/8a5212ebc852-backup.hg (glob)
 
-  $ cat $TESTTMP/b2/.hg/cache/branchheads
+  $ cat $TESTTMP/b2/.hg/cache/branchheads-served
   c65502d4178782309ce0574c5ae6ee9485a9bafa 4
   2870ad076e541e714f3c2bc32826b5c6a6e5b040 default
   c65502d4178782309ce0574c5ae6ee9485a9bafa default
--- a/tests/test-rebase-obsolete.t	Mon Jan 14 23:14:45 2013 +0900
+++ b/tests/test-rebase-obsolete.t	Sat Jan 19 17:24:33 2013 -0600
@@ -166,10 +166,65 @@
   o  0:cd010b8cd998 A
   
   $ hg debugobsolete
-  42ccdea3bb16d28e1848c95fe2e44c000f3f21b1 08483444fef91d6224f6655ee586a65d263ad34c 0 {'date': '*', 'user': 'test'} (glob)
+  42ccdea3bb16d28e1848c95fe2e44c000f3f21b1 0 {'date': '*', 'user': 'test'} (glob)
+  5fddd98957c8a54a4d436dfe1da9d87f21a1b97b 5ae4c968c6aca831df823664e706c9d4aa34473d 0 {'date': '*', 'user': 'test'} (glob)
+  32af7686d403cf45b5d95f2d70cebea587ac806a 0 {'date': '*', 'user': 'test'} (glob)
+
+
+More complex case were part of the rebase set were already rebased
+
+  $ hg rebase --rev 'desc(D)' --dest 'desc(H)'
+  $ hg debugobsolete
+  42ccdea3bb16d28e1848c95fe2e44c000f3f21b1 0 {'date': '*', 'user': 'test'} (glob)
   5fddd98957c8a54a4d436dfe1da9d87f21a1b97b 5ae4c968c6aca831df823664e706c9d4aa34473d 0 {'date': '*', 'user': 'test'} (glob)
-  32af7686d403cf45b5d95f2d70cebea587ac806a 5ae4c968c6aca831df823664e706c9d4aa34473d 0 {'date': '*', 'user': 'test'} (glob)
-
+  32af7686d403cf45b5d95f2d70cebea587ac806a 0 {'date': '*', 'user': 'test'} (glob)
+  08483444fef91d6224f6655ee586a65d263ad34c cbc07f26687521cecfc9a141bf5ecfc0fd2b8531 0 {'date': '* *', 'user': 'test'} (glob)
+  $ hg log -G
+  @  11:cbc07f266875 D
+  |
+  | o  10:5ae4c968c6ac C
+  | |
+  | x  9:08483444fef9 D
+  | |
+  | o  8:8877864f1edb B
+  | |
+  o |  7:02de42196ebe H
+  | |
+  | o  6:eea13746799a G
+  |/|
+  o |  5:24b6387c8c8c F
+  | |
+  | o  4:9520eea781bc E
+  |/
+  o  0:cd010b8cd998 A
+  
+  $ hg rebase --source 'desc(B)' --dest 'tip'
+  $ hg debugobsolete
+  42ccdea3bb16d28e1848c95fe2e44c000f3f21b1 0 {'date': '* *', 'user': 'test'} (glob)
+  5fddd98957c8a54a4d436dfe1da9d87f21a1b97b 5ae4c968c6aca831df823664e706c9d4aa34473d 0 {'date': '* *', 'user': 'test'} (glob)
+  32af7686d403cf45b5d95f2d70cebea587ac806a 0 {'date': '* *', 'user': 'test'} (glob)
+  08483444fef91d6224f6655ee586a65d263ad34c cbc07f26687521cecfc9a141bf5ecfc0fd2b8531 0 {'date': '* *', 'user': 'test'} (glob)
+  8877864f1edb05d0e07dc4ba77b67a80a7b86672 b1861c79d66ec3aa1b607ac3c9fb819e38b12238 0 {'date': '* *', 'user': 'test'} (glob)
+  08483444fef91d6224f6655ee586a65d263ad34c 0 {'date': '* *', 'user': 'test'} (glob)
+  5ae4c968c6aca831df823664e706c9d4aa34473d dd4be135457a404ce5541de427ae1d98a28f4acd 0 {'date': '* *', 'user': 'test'} (glob)
+  $ hg log --rev 'divergent()'
+  $ hg log -G
+  @  13:dd4be135457a C
+  |
+  o  12:b1861c79d66e B
+  |
+  o  11:cbc07f266875 D
+  |
+  o  7:02de42196ebe H
+  |
+  | o  6:eea13746799a G
+  |/|
+  o |  5:24b6387c8c8c F
+  | |
+  | o  4:9520eea781bc E
+  |/
+  o  0:cd010b8cd998 A
+  
 
   $ cd ..
 
@@ -279,4 +334,54 @@
   32af7686d403cf45b5d95f2d70cebea587ac806a cf44d2f5a9f4297a62be94cbdd3dff7c7dc54258 0 {'date': '*', 'user': 'test'} (glob)
   42ccdea3bb16d28e1848c95fe2e44c000f3f21b1 7c6027df6a99d93f461868e5433f63bde20b6dfb 0 {'date': '*', 'user': 'test'} (glob)
 
+Test that rewriting leaving instability behind is allowed
+---------------------------------------------------------------------
+
+  $ hg log -r 'children(8)'
+  9:cf44d2f5a9f4 D (no-eol)
+  $ hg rebase -r 8
+  $ hg log -G
+  @  11:0d8f238b634c C
+  |
+  o  10:7c6027df6a99 B
+  |
+  | o  9:cf44d2f5a9f4 D
+  | |
+  | x  8:e273c5e7d2d2 C
+  | |
+  o |  7:02de42196ebe H
+  | |
+  | o  6:eea13746799a G
+  |/|
+  o |  5:24b6387c8c8c F
+  | |
+  | o  4:9520eea781bc E
+  |/
+  o  0:cd010b8cd998 A
+  
+
+
+Test multiple root handling
+------------------------------------
+
+  $ hg rebase --dest 4 --rev '7+11+9'
+  $ hg log -G
+  @  14:1e8370e38cca C
+  |
+  | o  13:102b4c1d889b D
+  | |
+  o |  12:bfe264faf697 H
+  |/
+  | o  10:7c6027df6a99 B
+  | |
+  | x  7:02de42196ebe H
+  | |
+  +---o  6:eea13746799a G
+  | |/
+  | o  5:24b6387c8c8c F
+  | |
+  o |  4:9520eea781bc E
+  |/
+  o  0:cd010b8cd998 A
+  
   $ cd ..
--- a/tests/test-rebase-rename.t	Mon Jan 14 23:14:45 2013 +0900
+++ b/tests/test-rebase-rename.t	Sat Jan 19 17:24:33 2013 -0600
@@ -20,7 +20,10 @@
   $ hg ci -Am B
   adding b
 
-  $ hg up -q -C 0
+  $ hg mv b b-renamed
+  $ hg ci -m 'rename B'
+
+  $ hg up -q -C 1
 
   $ hg mv a a-renamed
 
@@ -28,28 +31,32 @@
   created new head
 
   $ hg tglog
-  @  2: 'rename A'
+  @  3: 'rename A'
   |
-  | o  1: 'B'
+  | o  2: 'rename B'
   |/
+  o  1: 'B'
+  |
   o  0: 'A'
   
 
 Rename is tracked:
 
   $ hg tlog -p --git -r tip
-  2: 'rename A' 
+  3: 'rename A' 
   diff --git a/a b/a-renamed
   rename from a
   rename to a-renamed
   
 Rebase the revision containing the rename:
 
-  $ hg rebase -s 2 -d 1
+  $ hg rebase -s 3 -d 2
   saved backup bundle to $TESTTMP/a/.hg/strip-backup/*-backup.hg (glob)
 
   $ hg tglog
-  @  2: 'rename A'
+  @  3: 'rename A'
+  |
+  o  2: 'rename B'
   |
   o  1: 'B'
   |
@@ -59,11 +66,32 @@
 Rename is not lost:
 
   $ hg tlog -p --git -r tip
-  2: 'rename A' 
+  3: 'rename A' 
   diff --git a/a b/a-renamed
   rename from a
   rename to a-renamed
   
+
+Rebased revision does not contain information about b (issue3739)
+
+  $ hg log -r 3 --debug
+  changeset:   3:3b905b1064f14ace3ad02353b79dd42d32981655
+  tag:         tip
+  phase:       draft
+  parent:      2:920a371a5635af23a26a011ca346cecd1cfcb942
+  parent:      -1:0000000000000000000000000000000000000000
+  manifest:    3:c4a62b2b64593c8fe0523d4c1ba2e243a8bd4dce
+  user:        test
+  date:        Thu Jan 01 00:00:00 1970 +0000
+  files+:      a-renamed
+  files-:      a
+  extra:       branch=default
+  extra:       rebase_source=89af05cb38a281f891c6f5581dd027092da29166
+  description:
+  rename A
+  
+  
+
   $ cd ..
 
 
@@ -78,47 +106,75 @@
   $ hg ci -Am B
   adding b
 
-  $ hg up -q -C 0
+  $ hg cp b b-copied
+  $ hg ci -Am 'copy B'
+
+  $ hg up -q -C 1
 
   $ hg cp a a-copied
   $ hg ci -m 'copy A'
   created new head
 
   $ hg tglog
-  @  2: 'copy A'
+  @  3: 'copy A'
   |
-  | o  1: 'B'
+  | o  2: 'copy B'
   |/
+  o  1: 'B'
+  |
   o  0: 'A'
   
 Copy is tracked:
 
   $ hg tlog -p --git -r tip
-  2: 'copy A' 
+  3: 'copy A' 
   diff --git a/a b/a-copied
   copy from a
   copy to a-copied
   
 Rebase the revision containing the copy:
 
-  $ hg rebase -s 2 -d 1
+  $ hg rebase -s 3 -d 2
   saved backup bundle to $TESTTMP/b/.hg/strip-backup/*-backup.hg (glob)
 
   $ hg tglog
-  @  2: 'copy A'
+  @  3: 'copy A'
+  |
+  o  2: 'copy B'
   |
   o  1: 'B'
   |
   o  0: 'A'
   
+
 Copy is not lost:
 
   $ hg tlog -p --git -r tip
-  2: 'copy A' 
+  3: 'copy A' 
   diff --git a/a b/a-copied
   copy from a
   copy to a-copied
   
+
+Rebased revision does not contain information about b (issue3739)
+
+  $ hg log -r 3 --debug
+  changeset:   3:98f6e6dbf45ab54079c2237fbd11066a5c41a11d
+  tag:         tip
+  phase:       draft
+  parent:      2:39e588434882ff77d01229d169cdc77f29e8855e
+  parent:      -1:0000000000000000000000000000000000000000
+  manifest:    3:2232f329d66fffe3930d43479ae624f66322b04d
+  user:        test
+  date:        Thu Jan 01 00:00:00 1970 +0000
+  files+:      a-copied
+  extra:       branch=default
+  extra:       rebase_source=0a8162ff18a8900df8df8ef7ac0046955205613e
+  description:
+  copy A
+  
+  
+
   $ cd ..
 
 
--- a/tests/test-rebase-scenario-global.t	Mon Jan 14 23:14:45 2013 +0900
+++ b/tests/test-rebase-scenario-global.t	Sat Jan 19 17:24:33 2013 -0600
@@ -542,6 +542,108 @@
   $ hg clone -q -u . ah ah6
   $ cd ah6
   $ hg rebase -r '(4+6)::' -d 1
-  abort: can't rebase multiple roots
-  [255]
+  saved backup bundle to $TESTTMP/ah6/.hg/strip-backup/3d8a618087a7-backup.hg (glob)
+  $ hg tglog
+  @  8: 'I'
+  |
+  o  7: 'H'
+  |
+  o  6: 'G'
+  |
+  | o  5: 'F'
+  | |
+  | o  4: 'E'
+  |/
+  | o  3: 'D'
+  | |
+  | o  2: 'C'
+  | |
+  o |  1: 'B'
+  |/
+  o  0: 'A'
+  
   $ cd ..
+
+More complexe rebase with multiple roots
+each root have a different common ancestor with the destination and this is a detach
+
+(setup)
+
+  $ hg clone -q -u . a a8
+  $ cd a8
+  $ echo I > I
+  $ hg add I
+  $ hg commit -m I
+  $ hg up 4
+  1 files updated, 0 files merged, 3 files removed, 0 files unresolved
+  $ echo I > J
+  $ hg add J
+  $ hg commit -m J
+  created new head
+  $ echo I > K
+  $ hg add K
+  $ hg commit -m K
+  $ hg tglog
+  @  10: 'K'
+  |
+  o  9: 'J'
+  |
+  | o  8: 'I'
+  | |
+  | o  7: 'H'
+  | |
+  +---o  6: 'G'
+  | |/
+  | o  5: 'F'
+  | |
+  o |  4: 'E'
+  |/
+  | o  3: 'D'
+  | |
+  | o  2: 'C'
+  | |
+  | o  1: 'B'
+  |/
+  o  0: 'A'
+  
+(actual test)
+
+  $ hg rebase --dest 'desc(G)' --rev 'desc(K) + desc(I)'
+  saved backup bundle to $TESTTMP/a8/.hg/strip-backup/23a4ace37988-backup.hg (glob)
+  $ hg log --rev 'children(desc(G))'
+  changeset:   9:adb617877056
+  parent:      6:eea13746799a
+  user:        test
+  date:        Thu Jan 01 00:00:00 1970 +0000
+  summary:     I
+  
+  changeset:   10:882431a34a0e
+  tag:         tip
+  parent:      6:eea13746799a
+  user:        test
+  date:        Thu Jan 01 00:00:00 1970 +0000
+  summary:     K
+  
+  $ hg tglog
+  @  10: 'K'
+  |
+  | o  9: 'I'
+  |/
+  | o  8: 'J'
+  | |
+  | | o  7: 'H'
+  | | |
+  o---+  6: 'G'
+  |/ /
+  | o  5: 'F'
+  | |
+  o |  4: 'E'
+  |/
+  | o  3: 'D'
+  | |
+  | o  2: 'C'
+  | |
+  | o  1: 'B'
+  |/
+  o  0: 'A'
+  
--- a/tests/test-record.t	Mon Jan 14 23:14:45 2013 +0900
+++ b/tests/test-record.t	Sat Jan 19 17:24:33 2013 -0600
@@ -255,18 +255,18 @@
   [255]
 
 
-Modify end of plain file
+Modify end of plain file, also test that diffopts are accounted for
 
   $ HGUSER="test"
   $ export HGUSER
-  $ hg record -d '8 0' -m end plain <<EOF
+  $ hg record --config diff.showfunc=true -d '8 0' -m end plain <<EOF
   > y
   > y
   > EOF
   diff --git a/plain b/plain
   1 hunks, 1 lines changed
   examine changes to 'plain'? [Ynesfdaq?] 
-  @@ -8,3 +8,4 @@
+  @@ -8,3 +8,4 @@ 7
    8
    9
    10
--- a/tests/test-remove.t	Mon Jan 14 23:14:45 2013 +0900
+++ b/tests/test-remove.t	Sat Jan 19 17:24:33 2013 -0600
@@ -107,7 +107,7 @@
   $ echo b > bar
   $ hg add bar
   $ remove -A bar
-  not removing bar: file still exists (use -f to force removal)
+  not removing bar: file still exists
   exit code: 1
   A bar
   ./bar
@@ -117,7 +117,7 @@
 21 state clean, options -A
 
   $ remove -A foo
-  not removing foo: file still exists (use -f to force removal)
+  not removing foo: file still exists
   exit code: 1
   ? bar
   ./bar
@@ -128,7 +128,7 @@
 
   $ echo b >> foo
   $ remove -A foo
-  not removing foo: file still exists (use -f to force removal)
+  not removing foo: file still exists
   exit code: 1
   M foo
   ? bar
@@ -220,7 +220,7 @@
 
   $ rm test/bar
   $ remove -A test
-  not removing test/foo: file still exists (use -f to force removal) (glob)
+  not removing test/foo: file still exists (glob)
   removing test/bar (glob)
   exit code: 1
   R test/bar
--- a/tests/test-rename-dir-merge.t	Mon Jan 14 23:14:45 2013 +0900
+++ b/tests/test-rename-dir-merge.t	Sat Jan 19 17:24:33 2013 -0600
@@ -31,17 +31,17 @@
      b/a
      b/b
     all copies found (* = to merge, ! = divergent, % = renamed and deleted):
-     b/a -> a/a 
-     b/b -> a/b 
+     src: 'a/a' -> dst: 'b/a' 
+     src: 'a/b' -> dst: 'b/b' 
     checking for directory renames
-    dir a/ -> b/
-    file a/c -> b/c
+     discovered dir src: 'a/' -> dst: 'b/'
+     pending file src: 'a/c' -> dst: 'b/c'
   resolving manifests
    overwrite: False, partial: False
    ancestor: f9b20c0d4c51, local: ce36d17b18fb+, remote: 397f8b00a740
-   a/c: remote renamed directory to b/c -> d
+   a/a: other deleted -> r
    a/b: other deleted -> r
-   a/a: other deleted -> r
+   a/c: remote renamed directory to b/c -> d
    b/a: remote created -> g
    b/b: remote created -> g
   updating: a/a 1/5 files (20.00%)
@@ -82,11 +82,11 @@
     unmatched files in other:
      a/c
     all copies found (* = to merge, ! = divergent, % = renamed and deleted):
-     b/a -> a/a 
-     b/b -> a/b 
+     src: 'a/a' -> dst: 'b/a' 
+     src: 'a/b' -> dst: 'b/b' 
     checking for directory renames
-    dir a/ -> b/
-    file a/c -> b/c
+     discovered dir src: 'a/' -> dst: 'b/'
+     pending file src: 'a/c' -> dst: 'b/c'
   resolving manifests
    overwrite: False, partial: False
    ancestor: f9b20c0d4c51, local: 397f8b00a740+, remote: ce36d17b18fb
--- a/tests/test-rename-merge1.t	Mon Jan 14 23:14:45 2013 +0900
+++ b/tests/test-rename-merge1.t	Sat Jan 19 17:24:33 2013 -0600
@@ -29,9 +29,9 @@
      b
      b2
     all copies found (* = to merge, ! = divergent, % = renamed and deleted):
-     c2 -> a2 !
-     b -> a *
-     b2 -> a2 !
+     src: 'a' -> dst: 'b' *
+     src: 'a2' -> dst: 'b2' !
+     src: 'a2' -> dst: 'c2' !
     checking for directory renames
    a2: divergent renames -> dr
   resolving manifests
@@ -176,7 +176,7 @@
     unmatched files in other:
      newfile
     all copies found (* = to merge, ! = divergent, % = renamed and deleted):
-     newfile -> file %
+     src: 'file' -> dst: 'newfile' %
     checking for directory renames
    file: rename and delete -> rd
   resolving manifests
--- a/tests/test-rename-merge2.t	Mon Jan 14 23:14:45 2013 +0900
+++ b/tests/test-rename-merge2.t	Sat Jan 19 17:24:33 2013 -0600
@@ -81,7 +81,7 @@
     unmatched files in other:
      b
     all copies found (* = to merge, ! = divergent, % = renamed and deleted):
-     b -> a *
+     src: 'a' -> dst: 'b' *
     checking for directory renames
   resolving manifests
    overwrite: False, partial: False
@@ -116,7 +116,7 @@
     unmatched files in local:
      b
     all copies found (* = to merge, ! = divergent, % = renamed and deleted):
-     b -> a *
+     src: 'a' -> dst: 'b' *
     checking for directory renames
   resolving manifests
    overwrite: False, partial: False
@@ -154,7 +154,7 @@
     unmatched files in other:
      b
     all copies found (* = to merge, ! = divergent, % = renamed and deleted):
-     b -> a *
+     src: 'a' -> dst: 'b' *
     checking for directory renames
   resolving manifests
    overwrite: False, partial: False
@@ -189,7 +189,7 @@
     unmatched files in local:
      b
     all copies found (* = to merge, ! = divergent, % = renamed and deleted):
-     b -> a *
+     src: 'a' -> dst: 'b' *
     checking for directory renames
   resolving manifests
    overwrite: False, partial: False
@@ -223,7 +223,7 @@
     unmatched files in other:
      b
     all copies found (* = to merge, ! = divergent, % = renamed and deleted):
-     b -> a 
+     src: 'a' -> dst: 'b' 
     checking for directory renames
   resolving manifests
    overwrite: False, partial: False
@@ -253,7 +253,7 @@
     unmatched files in local:
      b
     all copies found (* = to merge, ! = divergent, % = renamed and deleted):
-     b -> a 
+     src: 'a' -> dst: 'b' 
     checking for directory renames
   resolving manifests
    overwrite: False, partial: False
@@ -280,7 +280,7 @@
     unmatched files in other:
      b
     all copies found (* = to merge, ! = divergent, % = renamed and deleted):
-     b -> a 
+     src: 'a' -> dst: 'b' 
     checking for directory renames
   resolving manifests
    overwrite: False, partial: False
@@ -312,7 +312,7 @@
     unmatched files in local:
      b
     all copies found (* = to merge, ! = divergent, % = renamed and deleted):
-     b -> a 
+     src: 'a' -> dst: 'b' 
     checking for directory renames
   resolving manifests
    overwrite: False, partial: False
@@ -370,8 +370,8 @@
     unmatched files in other:
      c
     all copies found (* = to merge, ! = divergent, % = renamed and deleted):
-     c -> a !
-     b -> a !
+     src: 'a' -> dst: 'b' !
+     src: 'a' -> dst: 'c' !
     checking for directory renames
    a: divergent renames -> dr
   resolving manifests
@@ -649,7 +649,7 @@
     unmatched files in other:
      b
     all copies found (* = to merge, ! = divergent, % = renamed and deleted):
-     b -> a *
+     src: 'a' -> dst: 'b' *
     checking for directory renames
   resolving manifests
    overwrite: False, partial: False
@@ -683,7 +683,7 @@
     unmatched files in local:
      b
     all copies found (* = to merge, ! = divergent, % = renamed and deleted):
-     b -> a *
+     src: 'a' -> dst: 'b' *
     checking for directory renames
   resolving manifests
    overwrite: False, partial: False
@@ -721,7 +721,7 @@
     unmatched files in other:
      c
     all copies found (* = to merge, ! = divergent, % = renamed and deleted):
-     b -> a *
+     src: 'a' -> dst: 'b' *
     checking for directory renames
   resolving manifests
    overwrite: False, partial: False
--- a/tests/test-rename.t	Mon Jan 14 23:14:45 2013 +0900
+++ b/tests/test-rename.t	Sat Jan 19 17:24:33 2013 -0600
@@ -593,7 +593,7 @@
   [255]
   $ hg status -C
   $ hg rename d1/d11/a1 ../foo
-  abort: ../foo not under root
+  abort: ../foo not under root '$TESTTMP'
   [255]
   $ hg status -C
 
@@ -612,7 +612,7 @@
   [255]
   $ hg status -C
   $ hg rename d1/d11/a1 ..
-  abort: ../a1 not under root (glob)
+  abort: ../a1 not under root '$TESTTMP'
   [255]
   $ hg status -C
 
@@ -631,7 +631,7 @@
   [255]
   $ hg status -C
   $ (cd d1/d11; hg rename ../../d2/b ../../../foo)
-  abort: ../../../foo not under root
+  abort: ../../../foo not under root '$TESTTMP'
   [255]
   $ hg status -C
 
--- a/tests/test-revlog-ancestry.py	Mon Jan 14 23:14:45 2013 +0900
+++ b/tests/test-revlog-ancestry.py	Sat Jan 19 17:24:33 2013 -0600
@@ -62,6 +62,14 @@
     for r in repo.changelog.ancestors([7], 6):
         print r,
 
+    print '\nAncestors of 7, including revs'
+    for r in repo.changelog.ancestors([7], inclusive=True):
+        print r,
+
+    print '\nAncestors of 7, 5 and 3, including revs'
+    for r in repo.changelog.ancestors([7, 5, 3], inclusive=True):
+        print r,
+
     # Descendants
     print '\n\nDescendants of 5'
     for r in repo.changelog.descendants([5]):
--- a/tests/test-revlog-ancestry.py.out	Mon Jan 14 23:14:45 2013 +0900
+++ b/tests/test-revlog-ancestry.py.out	Sat Jan 19 17:24:33 2013 -0600
@@ -6,6 +6,10 @@
 4 2 0 
 Ancestors of 7, stop at 6
 6 
+Ancestors of 7, including revs
+7 6 5 3 4 2 1 0 
+Ancestors of 7, 5 and 3, including revs
+7 5 3 6 4 2 1 0 
 
 Descendants of 5
 7 8 
--- a/tests/test-setdiscovery.t	Mon Jan 14 23:14:45 2013 +0900
+++ b/tests/test-setdiscovery.t	Sat Jan 19 17:24:33 2013 -0600
@@ -36,8 +36,8 @@
   % -- a -> b tree
   comparing with b
   searching for changes
-  unpruned common: b5714e113bc0 66f7d451a68b 01241442b3c2
-  common heads: b5714e113bc0 01241442b3c2
+  unpruned common: 01241442b3c2 66f7d451a68b b5714e113bc0
+  common heads: 01241442b3c2 b5714e113bc0
   local is subset
   
   % -- a -> b set
@@ -45,14 +45,14 @@
   query 1; heads
   searching for changes
   all local heads known remotely
-  common heads: b5714e113bc0 01241442b3c2
+  common heads: 01241442b3c2 b5714e113bc0
   local is subset
   
   % -- b -> a tree
   comparing with a
   searching for changes
-  unpruned common: b5714e113bc0 01241442b3c2
-  common heads: b5714e113bc0 01241442b3c2
+  unpruned common: 01241442b3c2 b5714e113bc0
+  common heads: 01241442b3c2 b5714e113bc0
   remote is subset
   
   % -- b -> a set
@@ -60,7 +60,7 @@
   query 1; heads
   searching for changes
   all remote heads known locally
-  common heads: b5714e113bc0 01241442b3c2
+  common heads: 01241442b3c2 b5714e113bc0
   remote is subset
 
 
@@ -89,7 +89,7 @@
   % -- b -> a tree
   comparing with a
   searching for changes
-  unpruned common: bebd167eb94d 66f7d451a68b
+  unpruned common: 66f7d451a68b bebd167eb94d
   common heads: bebd167eb94d
   
   % -- b -> a set
@@ -128,7 +128,7 @@
   % -- b -> a tree
   comparing with a
   searching for changes
-  unpruned common: 66f7d451a68b 2dc09a01254d
+  unpruned common: 2dc09a01254d 66f7d451a68b
   common heads: 2dc09a01254d
   
   % -- b -> a set
--- a/tests/test-ssh.t	Mon Jan 14 23:14:45 2013 +0900
+++ b/tests/test-ssh.t	Sat Jan 19 17:24:33 2013 -0600
@@ -165,8 +165,8 @@
   $ cd ../local
   $ hg debugpushkey --config ui.ssh="python \"$TESTDIR/dummyssh\"" ssh://user@dummy/remote namespaces
   bookmarks	
+  namespaces	
   phases	
-  namespaces	
   $ hg book foo -r 0
   $ hg out -B
   comparing with ssh://user@dummy/remote
--- a/tests/test-status-color.t	Mon Jan 14 23:14:45 2013 +0900
+++ b/tests/test-status-color.t	Sat Jan 19 17:24:33 2013 -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-git.t	Mon Jan 14 23:14:45 2013 +0900
+++ b/tests/test-subrepo-git.t	Sat Jan 19 17:24:33 2013 -0600
@@ -331,10 +331,10 @@
   $ hg sum | grep commit
   commit: 1 subrepos
   $ hg push -q
-  abort: subrepo s is missing
+  abort: subrepo s is missing (in subrepo s)
   [255]
   $ hg commit --subrepos -qm missing
-  abort: subrepo s is missing
+  abort: subrepo s is missing (in subrepo s)
   [255]
   $ hg update -C
   cloning subrepo s from $TESTTMP/gitroot
--- a/tests/test-subrepo-recursion.t	Mon Jan 14 23:14:45 2013 +0900
+++ b/tests/test-subrepo-recursion.t	Sat Jan 19 17:24:33 2013 -0600
@@ -386,7 +386,7 @@
   $ echo f > foo/f
   $ hg archive --subrepos -r tip archive
   cloning subrepo foo from $TESTTMP/empty/foo
-  abort: destination '$TESTTMP/almost-empty/foo' is not empty (glob)
+  abort: destination '$TESTTMP/almost-empty/foo' is not empty (in subrepo foo) (glob)
   [255]
 
 Clone and test outgoing:
--- a/tests/test-subrepo-svn.t	Mon Jan 14 23:14:45 2013 +0900
+++ b/tests/test-subrepo-svn.t	Sat Jan 19 17:24:33 2013 -0600
@@ -119,7 +119,7 @@
   $ rm s/alpha
   $ hg commit --subrepos -m 'abort on missing file'
   committing subrepository s
-  abort: cannot commit missing svn entries
+  abort: cannot commit missing svn entries (in subrepo s)
   [255]
   $ svn revert s/alpha > /dev/null
 
@@ -180,7 +180,7 @@
   $ echo zzz > s/externals/other
   $ hg ci --subrepos -m 'amend externals from hg'
   committing subrepository s
-  abort: cannot commit svn externals
+  abort: cannot commit svn externals (in subrepo s)
   [255]
   $ hg diff --subrepos -r 1:2 | grep -v diff
   --- a/.hgsubstate	Thu Jan 01 00:00:00 1970 +0000
@@ -202,7 +202,7 @@
   property 'svn:mime-type' set on 's/externals/other' (glob)
   $ hg ci --subrepos -m 'amend externals from hg'
   committing subrepository s
-  abort: cannot commit svn externals
+  abort: cannot commit svn externals (in subrepo s)
   [255]
   $ svn revert -q s/externals/other
 
@@ -544,11 +544,11 @@
   archiving: .hgsubstate 2/2 files (100.00%)
   archiving (obstruct): 0/1 files (0.00%)
   archiving (obstruct): 1/1 files (100.00%)
+  archiving (recreated): 0/1 files (0.00%)
+  archiving (recreated): 1/1 files (100.00%)
   archiving (s): 0/2 files (0.00%)
   archiving (s): 1/2 files (50.00%)
   archiving (s): 2/2 files (100.00%)
-  archiving (recreated): 0/1 files (0.00%)
-  archiving (recreated): 1/1 files (100.00%)
 
   $ hg archive -S ../archive-exclude --debug -X **old
   archiving: 0/2 files (0.00%)
@@ -556,10 +556,10 @@
   archiving: .hgsubstate 2/2 files (100.00%)
   archiving (obstruct): 0/1 files (0.00%)
   archiving (obstruct): 1/1 files (100.00%)
+  archiving (recreated): 0 files
   archiving (s): 0/2 files (0.00%)
   archiving (s): 1/2 files (50.00%)
   archiving (s): 2/2 files (100.00%)
-  archiving (recreated): 0 files
   $ find ../archive-exclude | sort
   ../archive-exclude
   ../archive-exclude/.hg_archival.txt
--- a/tests/test-subrepo.t	Mon Jan 14 23:14:45 2013 +0900
+++ b/tests/test-subrepo.t	Sat Jan 19 17:24:33 2013 -0600
@@ -320,7 +320,7 @@
   no changes found
   pushing subrepo s to $TESTTMP/t/s (glob)
   searching for changes
-  abort: push creates new remote head 12a213df6fa9!
+  abort: push creates new remote head 12a213df6fa9! (in subrepo s)
   (did you forget to merge? use push -f to force)
   [255]
   $ hg push -f
@@ -587,7 +587,7 @@
   created new head
   $ hg -R repo2 ci -m3
   $ hg -q -R repo2 push
-  abort: push creates new remote head cc505f09a8b2!
+  abort: push creates new remote head cc505f09a8b2! (in subrepo s)
   (did you forget to merge? use push -f to force)
   [255]
   $ hg -R repo update
@@ -599,7 +599,7 @@
   $ hg -R repo2 push -f -q
   $ hg -R repo update
   b: untracked file differs
-  abort: untracked files in working directory differ from files in requested revision
+  abort: untracked files in working directory differ from files in requested revision (in subrepo s)
   [255]
 
   $ cat repo/s/b
@@ -645,7 +645,7 @@
   added 2 changesets with 3 changes to 2 files
   (run 'hg update' to get a working copy)
   $ hg -R issue1852b update
-  abort: default path for subrepository sub/repo not found (glob)
+  abort: default path for subrepository not found (in subrepo sub/repo) (glob)
   [255]
 
 Pull -u now doesn't help
@@ -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-symlink-os-yes-fs-no.py	Mon Jan 14 23:14:45 2013 +0900
+++ b/tests/test-symlink-os-yes-fs-no.py	Sat Jan 19 17:24:33 2013 -0600
@@ -24,7 +24,7 @@
 # now disable symlink support -- this is what os.symlink would do on a
 # non-symlink file system
 def symlink_failure(src, dst):
-    raise OSError, (1, "Operation not permitted")
+    raise OSError(1, "Operation not permitted")
 os.symlink = symlink_failure
 
 # dereference links as if a Samba server has exported this to a
--- a/tests/test-symlinks.t	Mon Jan 14 23:14:45 2013 +0900
+++ b/tests/test-symlinks.t	Sat Jan 19 17:24:33 2013 -0600
@@ -82,7 +82,7 @@
 this should fail
 
   $ hg status ../z && { echo hg mistakenly exited with status 0; exit 1; } || :
-  abort: ../z not under root
+  abort: ../z not under root '$TESTTMP/x'
   $ cd ..
 
 
--- a/tests/test-ui-color.py	Mon Jan 14 23:14:45 2013 +0900
+++ b/tests/test-ui-color.py	Sat Jan 19 17:24:33 2013 -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())
 
--- a/tests/test-update-issue1456.t	Mon Jan 14 23:14:45 2013 +0900
+++ b/tests/test-update-issue1456.t	Sat Jan 19 17:24:33 2013 -0600
@@ -30,7 +30,7 @@
   $ hg up -C 0
   0 files updated, 0 files merged, 0 files removed, 0 files unresolved
   $ hg up
-  0 files updated, 0 files merged, 0 files removed, 0 files unresolved
+  1 files updated, 0 files merged, 0 files removed, 0 files unresolved
   $ hg st
 
   $ cd ..
--- a/tests/test-update-reverse.t	Mon Jan 14 23:14:45 2013 +0900
+++ b/tests/test-update-reverse.t	Sat Jan 19 17:24:33 2013 -0600
@@ -68,8 +68,8 @@
   resolving manifests
    overwrite: True, partial: False
    ancestor: 91ebc10ed028+, local: 91ebc10ed028+, remote: 71a760306caf
+   side1: other deleted -> r
    side2: other deleted -> r
-   side1: other deleted -> r
    main: remote created -> g
   updating: side1 1/3 files (33.33%)
   removing side1
--- a/tests/test-walk.t	Mon Jan 14 23:14:45 2013 +0900
+++ b/tests/test-walk.t	Sat Jan 19 17:24:33 2013 -0600
@@ -181,10 +181,10 @@
   f  mammals/Procyonidae/raccoon     mammals/Procyonidae/raccoon
   f  mammals/skunk                   mammals/skunk
   $ hg debugwalk ..
-  abort: .. not under root
+  abort: .. not under root '$TESTTMP/t'
   [255]
   $ hg debugwalk beans/../..
-  abort: beans/../.. not under root
+  abort: beans/../.. not under root '$TESTTMP/t'
   [255]
   $ hg debugwalk .hg
   abort: path contains illegal component: .hg (glob)
@@ -209,7 +209,7 @@
   f  beans/pinto     beans/pinto
   f  beans/turtle    beans/turtle
   $ hg debugwalk `pwd`/..
-  abort: $TESTTMP/t/.. not under root
+  abort: $TESTTMP/t/.. not under root '$TESTTMP/t'
   [255]
 
 Test patterns:
--- a/tests/test-wireproto.py	Mon Jan 14 23:14:45 2013 +0900
+++ b/tests/test-wireproto.py	Sat Jan 19 17:24:33 2013 -0600
@@ -25,6 +25,9 @@
     def greet(self, name):
         return "Hello, " + name
 
+    def filtered(self, name):
+        return self
+
 def mangle(s):
     return ''.join(chr(ord(c) + 1) for c in s)
 def unmangle(s):