--- 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 = '> <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 = '> <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 = '> <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> </th>
+ <th> </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}"> ↓{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 = '> <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 = '> <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> </th>
+ <th> </th>
</tr>
<tr class="parity0">
@@ -215,6 +216,11 @@
<td>Foo Bar <foo.bar@example.com></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>Foo Bar <foo.bar@example.com></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>Foo Bar <foo.bar@example.com></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>Foo Bar <foo.bar@example.com></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>Foo Bar <foo.bar@example.com></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>Foo Bar <foo.bar@example.com></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>Foo Bar <foo.bar@example.com></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>Foo Bar <foo.bar@example.com></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>Foo Bar <foo.bar@example.com></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>Foo Bar <foo.bar@example.com></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>Foo Bar <foo.bar@example.com></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>Foo Bar <foo.bar@example.com></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>Foo Bar <foo.bar@example.com></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>Foo Bar <foo.bar@example.com></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>Foo Bar <foo.bar@example.com></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>Foo Bar <foo.bar@example.com></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>Foo Bar <foo.bar@example.com></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>Foo Bar <foo.bar@example.com></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>Foo Bar <foo.bar@example.com></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>Foo Bar <foo.bar@example.com></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>Foo Bar <foo.bar@example.com></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>Foo Bar <foo.bar@example.com></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>Foo Bar <foo.bar@example.com></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>Foo Bar <foo.bar@example.com></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>Foo Bar <foo.bar@example.com></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>Foo Bar <foo.bar@example.com></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>Foo Bar <foo.bar@example.com></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>Foo Bar <foo.bar@example.com></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>Foo Bar <foo.bar@example.com></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>Foo Bar <foo.bar@example.com></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>Foo Bar <foo.bar@example.com></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>Foo Bar <foo.bar@example.com></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>Foo Bar <foo.bar@example.com></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>Foo Bar <foo.bar@example.com></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> > <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> </th>
+ <th> </th>
</tr>
<tr class="parity0">
@@ -540,6 +712,11 @@
<td>Foo Bar <foo.bar@example.com></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> </th>
+ <th> </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):