Mercurial > hg
changeset 18453:f5fbe15ca744 stable 2.5-rc
merge default into stable for 2.5 code freeze
author | Matt Mackall <mpm@selenic.com> |
---|---|
date | Sat, 19 Jan 2013 17:24:33 -0600 |
parents | 7648b87e76db (current diff) 8bd338c7c4c9 (diff) |
children | a9475e8936bc |
files | tests/test-inotify-issue1208.t tests/test-mq-caches.t |
diffstat | 308 files changed, 7572 insertions(+), 3124 deletions(-) [+] |
line wrap: on
line diff
--- a/Makefile Mon Jan 14 23:14:45 2013 +0900 +++ b/Makefile Sat Jan 19 17:24:33 2013 -0600 @@ -11,6 +11,9 @@ PYFILES:=$(shell find mercurial hgext doc -name '*.py') DOCFILES=mercurial/help/*.txt +# Set this to e.g. "mingw32" to use a non-default compiler. +COMPILER= + help: @echo 'Commonly used make targets:' @echo ' all - build program and documentation' @@ -33,11 +36,15 @@ all: build doc local: - $(PYTHON) setup.py $(PURE) build_py -c -d . build_ext -i build_hgexe -i build_mo - $(PYTHON) hg version + $(PYTHON) setup.py $(PURE) \ + build_py -c -d . \ + build_ext $(COMPILER:%=-c %) -i \ + build_hgexe $(COMPILER:%=-c %) -i \ + build_mo + env HGRCPATH= $(PYTHON) hg version build: - $(PYTHON) setup.py $(PURE) build + $(PYTHON) setup.py $(PURE) build $(COMPILER:%=-c %) doc: $(MAKE) -C doc
--- a/contrib/check-code.py Mon Jan 14 23:14:45 2013 +0900 +++ b/contrib/check-code.py Sat Jan 19 17:24:33 2013 -0600 @@ -129,13 +129,14 @@ (r'(?<!def)\s+(cmp)\(', "cmp is not available in Python 3+"), (r'\breduce\s*\(.*', "reduce is not available in Python 3+"), (r'\.has_key\b', "dict.has_key is not available in Python 3+"), + (r'\s<>\s', '<> operator is not available in Python 3+, use !='), (r'^\s*\t', "don't use tabs"), (r'\S;\s*\n', "semicolon"), (r'[^_]_\("[^"]+"\s*%', "don't use % inside _()"), (r"[^_]_\('[^']+'\s*%", "don't use % inside _()"), - (r'\w,\w', "missing whitespace after ,"), - (r'\w[+/*\-<>]\w', "missing whitespace in expression"), - (r'^\s+\w+=\w+[^,)\n]$', "missing whitespace in assignment"), + (r'(\w|\)),\w', "missing whitespace after ,"), + (r'(\w|\))[+/*\-<>]\w', "missing whitespace in expression"), + (r'^\s+(\w|\.)+=\w[^,()\n]*$', "missing whitespace in assignment"), (r'(\s+)try:\n((?:\n|\1\s.*\n)+?)\1except.*?:\n' r'((?:\n|\1\s.*\n)+?)\1finally:', 'no try/except/finally in Python 2.4'), (r'(\s+)try:\n((?:\n|\1\s.*\n)*?)\1\s*yield\b.*?' @@ -185,6 +186,8 @@ (r'[^^+=*/!<>&| %-](\s=|=\s)[^= ]', "wrong whitespace around ="), (r'raise Exception', "don't raise generic exceptions"), + (r'raise [^,(]+, (\([^\)]+\)|[^,\(\)]+)$', + "don't use old-style two-argument raise, use Exception(message)"), (r' is\s+(not\s+)?["\'0-9-]', "object comparison with literal"), (r' [=!]=\s+(True|False|None)', "comparison with singleton, use 'is' or 'is not' instead"), @@ -211,11 +214,11 @@ (r'\.strip\(\)\.split\(\)', "no need to strip before splitting"), (r'^\s*except\s*:', "warning: naked except clause", r'#.*re-raises'), (r':\n( )*( ){1,3}[^ ]', "must indent 4 spaces"), + (r'ui\.(status|progress|write|note|warn)\([\'\"]x', + "missing _() in ui message (use () to hide false-positives)"), ], # warnings [ - (r'ui\.(status|progress|write|note|warn)\([\'\"]x', - "warning: unwrapped ui message"), ] ]
--- a/contrib/hgk Mon Jan 14 23:14:45 2013 +0900 +++ b/contrib/hgk Sat Jan 19 17:24:33 2013 -0600 @@ -15,8 +15,43 @@ # The whole snipped is activated only under windows, mouse wheel # bindings working already under MacOSX and Linux. +if {[catch {package require Ttk}]} { + # use a shim + namespace eval ttk { + proc style args {} + + proc entry args { + eval [linsert $args 0 ::entry] -relief flat + } + } + + interp alias {} ttk::button {} button + interp alias {} ttk::frame {} frame + interp alias {} ttk::label {} label + interp alias {} ttk::scrollbar {} scrollbar + interp alias {} ttk::optionMenu {} tk_optionMenu +} else { + proc ::ttk::optionMenu {w varName firstValue args} { + upvar #0 $varName var + + if {![info exists var]} { + set var $firstValue + } + ttk::menubutton $w -textvariable $varName -menu $w.menu \ + -direction flush + menu $w.menu -tearoff 0 + $w.menu add radiobutton -label $firstValue -variable $varName + foreach i $args { + $w.menu add radiobutton -label $i -variable $varName + } + return $w.menu + } +} + if {[tk windowingsystem] eq "win32"} { +ttk::style theme use xpnative + set mw_classes [list Text Listbox Table TreeCtrl] foreach class $mw_classes { bind $class <MouseWheel> {} } @@ -72,6 +107,12 @@ bind all <MouseWheel> [list ::tk::MouseWheel %W %X %Y %D 0] # end of win32 section +} else { + +if {[ttk::style theme use] eq "default"} { + ttk::style theme use clam +} + } @@ -480,7 +521,7 @@ wm transient $w . message $w.m -text $msg -justify center -aspect 400 pack $w.m -side top -fill x -padx 20 -pady 20 - button $w.ok -text OK -command "destroy $w" + ttk::button $w.ok -text OK -command "destroy $w" pack $w.ok -side bottom -fill x bind $w <Visibility> "grab $w; focus $w" tkwait window $w @@ -526,11 +567,11 @@ set geometry(ctexth) [expr {($texth - 8) / [font metrics $textfont -linespace]}] } - frame .ctop.top - frame .ctop.top.bar + ttk::frame .ctop.top + ttk::frame .ctop.top.bar pack .ctop.top.bar -side bottom -fill x set cscroll .ctop.top.csb - scrollbar $cscroll -command {allcanvs yview} -highlightthickness 0 + ttk::scrollbar $cscroll -command {allcanvs yview} pack $cscroll -side right -fill y panedwindow .ctop.top.clist -orient horizontal -sashpad 0 -handlesize 4 pack .ctop.top.clist -side top -fill both -expand 1 @@ -538,15 +579,15 @@ set canv .ctop.top.clist.canv canvas $canv -height $geometry(canvh) -width $geometry(canv1) \ -bg $bgcolor -bd 0 \ - -yscrollincr $linespc -yscrollcommand "$cscroll set" -selectbackground grey + -yscrollincr $linespc -yscrollcommand "$cscroll set" -selectbackground "#c0c0c0" .ctop.top.clist add $canv set canv2 .ctop.top.clist.canv2 canvas $canv2 -height $geometry(canvh) -width $geometry(canv2) \ - -bg $bgcolor -bd 0 -yscrollincr $linespc -selectbackground grey + -bg $bgcolor -bd 0 -yscrollincr $linespc -selectbackground "#c0c0c0" .ctop.top.clist add $canv2 set canv3 .ctop.top.clist.canv3 canvas $canv3 -height $geometry(canvh) -width $geometry(canv3) \ - -bg $bgcolor -bd 0 -yscrollincr $linespc -selectbackground grey + -bg $bgcolor -bd 0 -yscrollincr $linespc -selectbackground "#c0c0c0" .ctop.top.clist add $canv3 bind .ctop.top.clist <Configure> {resizeclistpanes %W %w} @@ -557,7 +598,7 @@ -command gotocommit -width 8 $sha1but conf -disabledforeground [$sha1but cget -foreground] pack .ctop.top.bar.sha1label -side left - entry $sha1entry -width 40 -font $textfont -textvariable sha1string + ttk::entry $sha1entry -width 40 -font $textfont -textvariable sha1string trace add variable sha1string write sha1change pack $sha1entry -side left -pady 2 @@ -577,25 +618,25 @@ 0x00, 0x38, 0xff, 0x7f, 0xff, 0x7f, 0xff, 0x7f, 0x00, 0x38, 0x00, 0x1c, 0x00, 0x0e, 0x00, 0x07, 0x80, 0x03, 0xc0, 0x01}; } - button .ctop.top.bar.leftbut -image bm-left -command goback \ + ttk::button .ctop.top.bar.leftbut -image bm-left -command goback \ -state disabled -width 26 pack .ctop.top.bar.leftbut -side left -fill y - button .ctop.top.bar.rightbut -image bm-right -command goforw \ + ttk::button .ctop.top.bar.rightbut -image bm-right -command goforw \ -state disabled -width 26 pack .ctop.top.bar.rightbut -side left -fill y - button .ctop.top.bar.findbut -text "Find" -command dofind + ttk::button .ctop.top.bar.findbut -text "Find" -command dofind pack .ctop.top.bar.findbut -side left set findstring {} set fstring .ctop.top.bar.findstring lappend entries $fstring - entry $fstring -width 30 -font $textfont -textvariable findstring + ttk::entry $fstring -width 30 -font $textfont -textvariable findstring pack $fstring -side left -expand 1 -fill x set findtype Exact - set findtypemenu [tk_optionMenu .ctop.top.bar.findtype \ + set findtypemenu [ttk::optionMenu .ctop.top.bar.findtype \ findtype Exact IgnCase Regexp] set findloc "All fields" - tk_optionMenu .ctop.top.bar.findloc findloc "All fields" Headline \ + ttk::optionMenu .ctop.top.bar.findloc findloc "All fields" Headline \ Comments Author Committer Files Pickaxe pack .ctop.top.bar.findloc -side right pack .ctop.top.bar.findtype -side right @@ -604,14 +645,14 @@ panedwindow .ctop.cdet -orient horizontal .ctop add .ctop.cdet - frame .ctop.cdet.left + ttk::frame .ctop.cdet.left set ctext .ctop.cdet.left.ctext text $ctext -fg $fgcolor -bg $bgcolor -state disabled -font $textfont \ -width $geometry(ctextw) -height $geometry(ctexth) \ -yscrollcommand ".ctop.cdet.left.sb set" \ -xscrollcommand ".ctop.cdet.left.hb set" -wrap none - scrollbar .ctop.cdet.left.sb -command "$ctext yview" - scrollbar .ctop.cdet.left.hb -orient horizontal -command "$ctext xview" + ttk::scrollbar .ctop.cdet.left.sb -command "$ctext yview" + ttk::scrollbar .ctop.cdet.left.hb -orient horizontal -command "$ctext xview" pack .ctop.cdet.left.sb -side right -fill y pack .ctop.cdet.left.hb -side bottom -fill x pack $ctext -side left -fill both -expand 1 @@ -643,12 +684,12 @@ $ctext tag conf found -back yellow } - frame .ctop.cdet.right + ttk::frame .ctop.cdet.right set cflist .ctop.cdet.right.cfiles listbox $cflist -fg $fgcolor -bg $bgcolor \ -selectmode extended -width $geometry(cflistw) \ -yscrollcommand ".ctop.cdet.right.sb set" - scrollbar .ctop.cdet.right.sb -command "$cflist yview" + ttk::scrollbar .ctop.cdet.right.sb -command "$cflist yview" pack .ctop.cdet.right.sb -side right -fill y pack $cflist -side left -fill both -expand 1 .ctop.cdet add .ctop.cdet.right @@ -901,7 +942,7 @@ Use and redistribute under the terms of the GNU General Public License} \ -justify center -aspect 400 pack $w.m -side top -fill x -padx 20 -pady 20 - button $w.ok -text Close -command "destroy $w" + ttk::button $w.ok -text Close -command "destroy $w" pack $w.ok -side bottom } @@ -1219,7 +1260,7 @@ } else { # draw a head or other ref if {[incr nheads -1] >= 0} { - set col green + set col "#00ff00" } else { set col "#ddddff" } @@ -2417,8 +2458,7 @@ set currentid $id $sha1entry delete 0 end $sha1entry insert 0 $id - $sha1entry selection from 0 - $sha1entry selection to end + $sha1entry selection range 0 end $ctext conf -state normal $ctext delete 0.0 end @@ -3675,36 +3715,36 @@ set patchtop $top catch {destroy $top} toplevel $top - label $top.title -text "Generate patch" + ttk::label $top.title -text "Generate patch" grid $top.title - -pady 10 - label $top.from -text "From:" - entry $top.fromsha1 -width 40 -relief flat + ttk::label $top.from -text "From:" + ttk::entry $top.fromsha1 -width 40 $top.fromsha1 insert 0 $oldid $top.fromsha1 conf -state readonly grid $top.from $top.fromsha1 -sticky w - entry $top.fromhead -width 60 -relief flat + ttk::entry $top.fromhead -width 60 $top.fromhead insert 0 $oldhead $top.fromhead conf -state readonly grid x $top.fromhead -sticky w - label $top.to -text "To:" - entry $top.tosha1 -width 40 -relief flat + ttk::label $top.to -text "To:" + ttk::entry $top.tosha1 -width 40 $top.tosha1 insert 0 $newid $top.tosha1 conf -state readonly grid $top.to $top.tosha1 -sticky w - entry $top.tohead -width 60 -relief flat + ttk::entry $top.tohead -width 60 $top.tohead insert 0 $newhead $top.tohead conf -state readonly grid x $top.tohead -sticky w - button $top.rev -text "Reverse" -command mkpatchrev -padx 5 + ttk::button $top.rev -text "Reverse" -command mkpatchrev grid $top.rev x -pady 10 - label $top.flab -text "Output file:" - entry $top.fname -width 60 + ttk::label $top.flab -text "Output file:" + ttk::entry $top.fname -width 60 $top.fname insert 0 [file normalize "patch$patchnum.patch"] incr patchnum grid $top.flab $top.fname -sticky w - frame $top.buts - button $top.buts.gen -text "Generate" -command mkpatchgo - button $top.buts.can -text "Cancel" -command mkpatchcan + ttk::frame $top.buts + ttk::button $top.buts.gen -text "Generate" -command mkpatchgo + ttk::button $top.buts.can -text "Cancel" -command mkpatchcan grid $top.buts.gen $top.buts.can grid columnconfigure $top.buts 0 -weight 1 -uniform a grid columnconfigure $top.buts 1 -weight 1 -uniform a @@ -3755,23 +3795,23 @@ set mktagtop $top catch {destroy $top} toplevel $top - label $top.title -text "Create tag" + ttk::label $top.title -text "Create tag" grid $top.title - -pady 10 - label $top.id -text "ID:" - entry $top.sha1 -width 40 -relief flat + ttk::label $top.id -text "ID:" + ttk::entry $top.sha1 -width 40 $top.sha1 insert 0 $rowmenuid $top.sha1 conf -state readonly grid $top.id $top.sha1 -sticky w - entry $top.head -width 60 -relief flat + ttk::entry $top.head -width 60 $top.head insert 0 [lindex $commitinfo($rowmenuid) 0] $top.head conf -state readonly grid x $top.head -sticky w - label $top.tlab -text "Tag name:" - entry $top.tag -width 60 + ttk::label $top.tlab -text "Tag name:" + ttk::entry $top.tag -width 60 grid $top.tlab $top.tag -sticky w - frame $top.buts - button $top.buts.gen -text "Create" -command mktaggo - button $top.buts.can -text "Cancel" -command mktagcan + ttk::frame $top.buts + ttk::button $top.buts.gen -text "Create" -command mktaggo + ttk::button $top.buts.can -text "Cancel" -command mktagcan grid $top.buts.gen $top.buts.can grid columnconfigure $top.buts 0 -weight 1 -uniform a grid columnconfigure $top.buts 1 -weight 1 -uniform a @@ -3835,27 +3875,27 @@ set wrcomtop $top catch {destroy $top} toplevel $top - label $top.title -text "Write commit to file" + ttk::label $top.title -text "Write commit to file" grid $top.title - -pady 10 - label $top.id -text "ID:" - entry $top.sha1 -width 40 -relief flat + ttk::label $top.id -text "ID:" + ttk::entry $top.sha1 -width 40 $top.sha1 insert 0 $rowmenuid $top.sha1 conf -state readonly grid $top.id $top.sha1 -sticky w - entry $top.head -width 60 -relief flat + ttk::entry $top.head -width 60 $top.head insert 0 [lindex $commitinfo($rowmenuid) 0] $top.head conf -state readonly grid x $top.head -sticky w - label $top.clab -text "Command:" - entry $top.cmd -width 60 -textvariable wrcomcmd + ttk::label $top.clab -text "Command:" + ttk::entry $top.cmd -width 60 -textvariable wrcomcmd grid $top.clab $top.cmd -sticky w -pady 10 - label $top.flab -text "Output file:" - entry $top.fname -width 60 + ttk::label $top.flab -text "Output file:" + ttk::entry $top.fname -width 60 $top.fname insert 0 [file normalize "commit-[string range $rowmenuid 0 6]"] grid $top.flab $top.fname -sticky w - frame $top.buts - button $top.buts.gen -text "Write" -command wrcomgo - button $top.buts.can -text "Cancel" -command wrcomcan + ttk::frame $top.buts + ttk::button $top.buts.gen -text "Write" -command wrcomgo + ttk::button $top.buts.can -text "Cancel" -command wrcomcan grid $top.buts.gen $top.buts.can grid columnconfigure $top.buts 0 -weight 1 -uniform a grid columnconfigure $top.buts 1 -weight 1 -uniform a
--- a/contrib/mergetools.hgrc Mon Jan 14 23:14:45 2013 +0900 +++ b/contrib/mergetools.hgrc Sat Jan 19 17:24:33 2013 -0600 @@ -19,7 +19,7 @@ vimdiff.check=changed vimdiff.priority=-10 -merge.checkconflicts=True +merge.check=conflicts merge.priority=-100 gpyfm.gui=True @@ -43,7 +43,7 @@ diffmerge.regname=Location diffmerge.priority=-7 diffmerge.args=-nosplash -merge -title1=local -title2=merged -title3=other $local $base $other -result=$output -diffmerge.checkchanged=True +diffmerge.check=changed diffmerge.gui=True diffmerge.diffargs=--nosplash --title1='$plabel1' --title2='$clabel' $parent $child @@ -59,7 +59,7 @@ tortoisemerge.args=/base:$base /mine:$local /theirs:$other /merged:$output tortoisemerge.regkey=Software\TortoiseSVN tortoisemerge.regkeyalt=Software\Wow6432Node\TortoiseSVN -tortoisemerge.checkchanged=True +tortoisemerge.check=changed tortoisemerge.gui=True tortoisemerge.priority=-8 tortoisemerge.diffargs=/base:$parent /mine:$child /basename:'$plabel1' /minename:'$clabel' @@ -93,7 +93,7 @@ winmerge.regkey=Software\Thingamahoochie\WinMerge winmerge.regkeyalt=Software\Wow6432Node\Thingamahoochie\WinMerge\ winmerge.regname=Executable -winmerge.checkchanged=True +winmerge.check=changed winmerge.gui=True winmerge.priority=-10 winmerge.diffargs=/r /e /x /ub /wl /dl '$plabel1' /dr '$clabel' $parent $child @@ -119,6 +119,5 @@ UltraCompare.priority = -2 UltraCompare.gui = True UltraCompare.binary = True -UltraCompare.checkconflicts = True -UltraCompare.checkchanged = True +UltraCompare.check = conflicts,changed UltraCompare.diffargs=$child $parent -title1 $clabel -title2 $plabel1
--- a/contrib/perf.py Mon Jan 14 23:14:45 2013 +0900 +++ b/contrib/perf.py Sat Jan 19 17:24:33 2013 -0600 @@ -1,9 +1,13 @@ # perf.py - performance test routines '''helper extension to measure performance''' -from mercurial import cmdutil, scmutil, util, match, commands +from mercurial import cmdutil, scmutil, util, match, commands, obsolete +from mercurial import repoview, branchmap import time, os, sys +cmdtable = {} +command = cmdutil.command(cmdtable) + def timer(func, title=None): results = [] begin = time.time() @@ -29,6 +33,7 @@ sys.stderr.write("! wall %f comb %f user %f sys %f (best of %d)\n" % (m[0], m[1] + m[2], m[1], m[2], count)) +@command('perfwalk') def perfwalk(ui, repo, *pats): try: m = scmutil.match(repo[None], pats, {}) @@ -40,11 +45,14 @@ except Exception: timer(lambda: len(list(cmdutil.walk(repo, pats, {})))) -def perfstatus(ui, repo, *pats): +@command('perfstatus', + [('u', 'unknown', False, + 'ask status to look for unknown files')]) +def perfstatus(ui, repo, **opts): #m = match.always(repo.root, repo.getcwd()) #timer(lambda: sum(map(len, repo.dirstate.status(m, [], False, False, # False)))) - timer(lambda: sum(map(len, repo.status()))) + timer(lambda: sum(map(len, repo.status(**opts)))) def clearcaches(cl): # behave somewhat consistently across internal API changes @@ -55,6 +63,7 @@ cl._nodecache = {nullid: nullrev} cl._nodepos = None +@command('perfheads') def perfheads(ui, repo): cl = repo.changelog def d(): @@ -62,6 +71,7 @@ clearcaches(cl) timer(d) +@command('perftags') def perftags(ui, repo): import mercurial.changelog, mercurial.manifest def t(): @@ -71,6 +81,7 @@ return len(repo.tags()) timer(t) +@command('perfancestors') def perfancestors(ui, repo): heads = repo.changelog.headrevs() def d(): @@ -78,6 +89,17 @@ pass timer(d) +@command('perfancestorset') +def perfancestorset(ui, repo, revset): + revs = repo.revs(revset) + heads = repo.changelog.headrevs() + def d(): + s = repo.changelog.ancestors(heads) + for rev in revs: + rev in s + timer(d) + +@command('perfdirstate') def perfdirstate(ui, repo): "a" in repo.dirstate def d(): @@ -85,6 +107,7 @@ "a" in repo.dirstate timer(d) +@command('perfdirstatedirs') def perfdirstatedirs(ui, repo): "a" in repo.dirstate def d(): @@ -92,6 +115,7 @@ del repo.dirstate._dirs timer(d) +@command('perfdirstatewrite') def perfdirstatewrite(ui, repo): ds = repo.dirstate "a" in ds @@ -100,6 +124,7 @@ ds.write() timer(d) +@command('perfmanifest') def perfmanifest(ui, repo): def d(): t = repo.manifest.tip() @@ -108,6 +133,7 @@ repo.manifest._cache = None timer(d) +@command('perfchangeset') def perfchangeset(ui, repo, rev): n = repo[rev].node() def d(): @@ -115,6 +141,7 @@ #repo.changelog._cache = None timer(d) +@command('perfindex') def perfindex(ui, repo): import mercurial.revlog mercurial.revlog._prereadsize = 2**24 # disable lazy parser in old hg @@ -124,12 +151,14 @@ cl.rev(n) timer(d) +@command('perfstartup') def perfstartup(ui, repo): cmd = sys.argv[0] def d(): os.system("HGRCPATH= %s version -q > /dev/null" % cmd) timer(d) +@command('perfparents') def perfparents(ui, repo): nl = [repo.changelog.node(i) for i in xrange(1000)] def d(): @@ -137,22 +166,16 @@ repo.changelog.parents(n) timer(d) +@command('perflookup') def perflookup(ui, repo, rev): timer(lambda: len(repo.lookup(rev))) +@command('perfrevrange') def perfrevrange(ui, repo, *specs): revrange = scmutil.revrange timer(lambda: len(revrange(repo, specs))) -def perfnodelookup(ui, repo, rev): - import mercurial.revlog - mercurial.revlog._prereadsize = 2**24 # disable lazy parser in old hg - n = repo[rev].node() - def d(): - cl = mercurial.revlog.revlog(repo.sopener, "00changelog.i") - cl.rev(n) - timer(d) - +@command('perfnodelookup') def perfnodelookup(ui, repo, rev): import mercurial.revlog mercurial.revlog._prereadsize = 2**24 # disable lazy parser in old hg @@ -163,12 +186,15 @@ clearcaches(cl) timer(d) +@command('perflog', + [('', 'rename', False, 'ask log to follow renames')]) def perflog(ui, repo, **opts): ui.pushbuffer() timer(lambda: commands.log(ui, repo, rev=[], date='', user='', copies=opts.get('rename'))) ui.popbuffer() +@command('perftemplating') def perftemplating(ui, repo): ui.pushbuffer() timer(lambda: commands.log(ui, repo, rev=[], date='', user='', @@ -176,15 +202,18 @@ ' {author|person}: {desc|firstline}\n')) ui.popbuffer() +@command('perfcca') def perfcca(ui, repo): timer(lambda: scmutil.casecollisionauditor(ui, False, repo.dirstate)) +@command('perffncacheload') def perffncacheload(ui, repo): s = repo.store def d(): s.fncache._load() timer(d) +@command('perffncachewrite') def perffncachewrite(ui, repo): s = repo.store s.fncache._load() @@ -193,6 +222,7 @@ s.fncache.write() timer(d) +@command('perffncacheencode') def perffncacheencode(ui, repo): s = repo.store s.fncache._load() @@ -201,6 +231,7 @@ s.encode(p) timer(d) +@command('perfdiffwd') def perfdiffwd(ui, repo): """Profile diff of working directory changes""" options = { @@ -218,6 +249,9 @@ title = 'diffopts: %s' % (diffopt and ('-' + diffopt) or 'none') timer(d, title) +@command('perfrevlog', + [('d', 'dist', 100, 'distance between the revisions')], + "[INDEXFILE]") def perfrevlog(ui, repo, file_, **opts): from mercurial import revlog dist = opts['dist'] @@ -228,32 +262,105 @@ timer(d) -cmdtable = { - 'perfcca': (perfcca, []), - 'perffncacheload': (perffncacheload, []), - 'perffncachewrite': (perffncachewrite, []), - 'perffncacheencode': (perffncacheencode, []), - 'perflookup': (perflookup, []), - 'perfrevrange': (perfrevrange, []), - 'perfnodelookup': (perfnodelookup, []), - 'perfparents': (perfparents, []), - 'perfstartup': (perfstartup, []), - 'perfstatus': (perfstatus, []), - 'perfwalk': (perfwalk, []), - 'perfmanifest': (perfmanifest, []), - 'perfchangeset': (perfchangeset, []), - 'perfindex': (perfindex, []), - 'perfheads': (perfheads, []), - 'perftags': (perftags, []), - 'perfancestors': (perfancestors, []), - 'perfdirstate': (perfdirstate, []), - 'perfdirstatedirs': (perfdirstate, []), - 'perfdirstatewrite': (perfdirstatewrite, []), - 'perflog': (perflog, - [('', 'rename', False, 'ask log to follow renames')]), - 'perftemplating': (perftemplating, []), - 'perfdiffwd': (perfdiffwd, []), - 'perfrevlog': (perfrevlog, - [('d', 'dist', 100, 'distance between the revisions')], - "[INDEXFILE]"), -} +@command('perfrevset', + [('C', 'clear', False, 'clear volatile cache between each call.')], + "REVSET") +def perfrevset(ui, repo, expr, clear=False): + """benchmark the execution time of a revset + + Use the --clean option if need to evaluate the impact of build volative + revisions set cache on the revset execution. Volatile cache hold filtered + and obsolete related cache.""" + def d(): + if clear: + repo.invalidatevolatilesets() + repo.revs(expr) + timer(d) + +@command('perfvolatilesets') +def perfvolatilesets(ui, repo, *names): + """benchmark the computation of various volatile set + + Volatile set computes element related to filtering and obsolescence.""" + repo = repo.unfiltered() + + def getobs(name): + def d(): + repo.invalidatevolatilesets() + obsolete.getrevs(repo, name) + return d + + allobs = sorted(obsolete.cachefuncs) + if names: + allobs = [n for n in allobs if n in names] + + for name in allobs: + timer(getobs(name), title=name) + + def getfiltered(name): + def d(): + repo.invalidatevolatilesets() + repoview.filteredrevs(repo, name) + return d + + allfilter = sorted(repoview.filtertable) + if names: + allfilter = [n for n in allfilter if n in names] + + for name in allfilter: + timer(getfiltered(name), title=name) + +@command('perfbranchmap', + [('f', 'full', False, + 'Includes build time of subset'), + ]) +def perfbranchmap(ui, repo, full=False): + """benchmark the update of a branchmap + + This benchmarks the full repo.branchmap() call with read and write disabled + """ + def getbranchmap(filtername): + """generate a benchmark function for the filtername""" + if filtername is None: + view = repo + else: + view = repo.filtered(filtername) + def d(): + if full: + view._branchcaches.clear() + else: + view._branchcaches.pop(filtername, None) + view.branchmap() + return d + # add filter in smaller subset to bigger subset + possiblefilters = set(repoview.filtertable) + allfilters = [] + while possiblefilters: + for name in possiblefilters: + subset = repoview.subsettable.get(name) + if subset not in possiblefilters: + break + else: + assert False, 'subset cycle %s!' % possiblefilters + allfilters.append(name) + possiblefilters.remove(name) + + # warm the cache + if not full: + for name in allfilters: + repo.filtered(name).branchmap() + # add unfiltered + allfilters.append(None) + oldread = branchmap.read + oldwrite = branchmap.branchcache.write + try: + branchmap.read = lambda repo: None + branchmap.write = lambda repo: None + for name in allfilters: + timer(getbranchmap(name), title=str(name)) + finally: + branchmap.read = oldread + branchmap.branchcache.write = oldwrite + + +
--- a/contrib/synthrepo.py Mon Jan 14 23:14:45 2013 +0900 +++ b/contrib/synthrepo.py Sat Jan 19 17:24:33 2013 -0600 @@ -231,6 +231,8 @@ fp.close() def cdf(l): + if not l: + return [], [] vals, probs = zip(*sorted(l, key=lambda x: x[1], reverse=True)) t = float(sum(probs, 0)) s, cdfs = 0, []
--- a/contrib/vim/hgtest.vim Mon Jan 14 23:14:45 2013 +0900 +++ b/contrib/vim/hgtest.vim Sat Jan 19 17:24:33 2013 -0600 @@ -2,7 +2,8 @@ " Language: Mercurial unified tests " Author: Steve Losh (steve@stevelosh.com) " -" Add the following line to your ~/.vimrc to enable: +" Place this file in ~/.vim/syntax/ and add the following line to your +" ~/.vimrc to enable: " au BufNewFile,BufRead *.t set filetype=hgtest " " If you want folding you'll need the following line as well:
--- a/contrib/zsh_completion Mon Jan 14 23:14:45 2013 +0900 +++ b/contrib/zsh_completion Sat Jan 19 17:24:33 2013 -0600 @@ -174,11 +174,10 @@ _hg_cmd tags | while read tag do - tags+=(${tag/ # [0-9]#:*}) + tags+=(${tag/ #[0-9]#:*}) done - (( $#tags )) && _describe -t tags 'tags' tags + (( $#tags )) && _describe -t tags 'tags' tags } - _hg_bookmarks() { typeset -a bookmark bookmarks @@ -198,7 +197,7 @@ _hg_cmd branches | while read branch do - branches+=(${branch/ # [0-9]#:*}) + branches+=(${branch/ #[0-9]#:*}) done (( $#branches )) && _describe -t branches 'branches' branches } @@ -208,12 +207,19 @@ typeset -a heads local myrev - heads=(${(f)"$(_hg_cmd heads --template '{rev}\\n')"}) + heads=(${(f)"$(_hg_cmd heads --template '{rev}:{branch}\\n')"}) # exclude own revision - myrev=$(_hg_cmd log -r . --template '{rev}\\n') + myrev=$(_hg_cmd log -r . --template '{rev}:{branch}\\n') heads=(${heads:#$myrev}) (( $#heads )) && _describe -t heads 'heads' heads + + branches=(${(f)"$(_hg_cmd heads --template '{branch}\\n')"}) + # exclude own revision + myrev=$(_hg_cmd log -r . --template '{branch}\\n') + branches=(${branches:#$myrev}) + + (( $#branches )) && _describe -t branches 'branches' branches } _hg_files() {
--- a/doc/hgmanpage.py Mon Jan 14 23:14:45 2013 +0900 +++ b/doc/hgmanpage.py Sat Jan 19 17:24:33 2013 -0600 @@ -146,7 +146,7 @@ text.extend(cell) if not text[-1].endswith('\n'): text[-1] += '\n' - if i < len(row)-1: + if i < len(row) - 1: text.append('T}'+self._tab_char+'T{\n') else: text.append('T}\n') @@ -258,7 +258,7 @@ # ensure we get a ".TH" as viewers require it. self.head.append(self.header()) # filter body - for i in xrange(len(self.body)-1, 0, -1): + for i in xrange(len(self.body) - 1, 0, -1): # remove superfluous vertical gaps. if self.body[i] == '.sp\n': if self.body[i - 1][:4] in ('.BI ','.IP '): @@ -880,7 +880,7 @@ self.context[-3] = '.BI' # bold/italic alternate if node['delimiter'] != ' ': self.body.append('\\fB%s ' % node['delimiter']) - elif self.body[len(self.body)-1].endswith('='): + elif self.body[len(self.body) - 1].endswith('='): # a blank only means no blank in output, just changing font self.body.append(' ') else:
--- a/hgext/churn.py Mon Jan 14 23:14:45 2013 +0900 +++ b/hgext/churn.py Sat Jan 19 17:24:33 2013 -0600 @@ -144,8 +144,10 @@ if not rate: return - sortkey = ((not opts.get('sort')) and (lambda x: -sum(x[1])) or None) - rate.sort(key=sortkey) + if opts.get('sort'): + rate.sort() + else: + rate.sort(key=lambda x: (-sum(x[1]), x)) # Be careful not to have a zero maxcount (issue833) maxcount = float(max(sum(v) for k, v in rate)) or 1.0
--- a/hgext/color.py Mon Jan 14 23:14:45 2013 +0900 +++ b/hgext/color.py Sat Jan 19 17:24:33 2013 -0600 @@ -103,6 +103,7 @@ import os from mercurial import commands, dispatch, extensions, ui as uimod, util +from mercurial import templater from mercurial.i18n import _ testedwith = 'internal' @@ -354,6 +355,28 @@ for s in msg.split('\n')]) return msg +def templatelabel(context, mapping, args): + if len(args) != 2: + # i18n: "label" is a keyword + raise error.ParseError(_("label expects two arguments")) + + thing = templater.stringify(args[1][0](context, mapping, args[1][1])) + thing = templater.runtemplate(context, mapping, + templater.compiletemplate(thing, context)) + + # apparently, repo could be a string that is the favicon? + repo = mapping.get('repo', '') + if isinstance(repo, str): + return thing + + label = templater.stringify(args[0][0](context, mapping, args[0][1])) + label = templater.runtemplate(context, mapping, + templater.compiletemplate(label, context)) + + thing = templater.stringify(thing) + label = templater.stringify(label) + + return repo.ui.label(thing, label) def uisetup(ui): global _terminfo_params @@ -370,6 +393,7 @@ configstyles(ui_) return orig(ui_, opts, cmd, cmdfunc) extensions.wrapfunction(dispatch, '_runcommand', colorcmd) + templater.funcs['label'] = templatelabel def extsetup(ui): commands.globalopts.append(
--- a/hgext/convert/__init__.py Mon Jan 14 23:14:45 2013 +0900 +++ b/hgext/convert/__init__.py Sat Jan 19 17:24:33 2013 -0600 @@ -191,6 +191,10 @@ branch indicated in the regex as the second parent of the changeset. Default is ``{{mergefrombranch ([-\\w]+)}}`` + :convert.localtimezone: use local time (as determined by the TZ + environment variable) for changeset date/times. The default + is False (use UTC). + :hooks.cvslog: Specify a Python function to be called at the end of gathering the CVS log. The function is passed a list with the log entries, and can modify the entries in-place, or add or @@ -231,6 +235,10 @@ :convert.svn.trunk: specify the name of the trunk branch. The default is ``trunk``. + :convert.localtimezone: use local time (as determined by the TZ + environment variable) for changeset date/times. The default + is False (use UTC). + Source history can be retrieved starting at a specific revision, instead of being integrally converted. Only single branch conversions are supported.
--- a/hgext/convert/common.py Mon Jan 14 23:14:45 2013 +0900 +++ b/hgext/convert/common.py Sat Jan 19 17:24:33 2013 -0600 @@ -5,7 +5,7 @@ # This software may be used and distributed according to the terms of the # GNU General Public License version 2 or any later version. -import base64, errno, subprocess, os +import base64, errno, subprocess, os, datetime import cPickle as pickle from mercurial import util from mercurial.i18n import _ @@ -446,3 +446,10 @@ if e.errno != errno.ENOENT: raise return m + +def makedatetimestamp(t): + """Like util.makedate() but for time t instead of current time""" + delta = (datetime.datetime.utcfromtimestamp(t) - + datetime.datetime.fromtimestamp(t)) + tz = delta.days * 86400 + delta.seconds + return t, tz
--- a/hgext/convert/convcmd.py Mon Jan 14 23:14:45 2013 +0900 +++ b/hgext/convert/convcmd.py Sat Jan 19 17:24:33 2013 -0600 @@ -147,7 +147,7 @@ map contains valid revision identifiers and merge the new links in the source graph. """ - for c in splicemap: + for c in sorted(splicemap): if c not in parents: if not self.dest.hascommit(self.map.get(c, c)): # Could be in source but not converted during this run @@ -175,7 +175,7 @@ revisions without parents. 'parents' must be a mapping of revision identifier to its parents ones. """ - visit = parents.keys() + visit = sorted(parents) seen = set() children = {} roots = []
--- a/hgext/convert/cvs.py Mon Jan 14 23:14:45 2013 +0900 +++ b/hgext/convert/cvs.py Sat Jan 19 17:24:33 2013 -0600 @@ -11,6 +11,7 @@ from mercurial.i18n import _ from common import NoRepo, commit, converter_source, checktool +from common import makedatetimestamp import cvsps class convert_cvs(converter_source): @@ -70,6 +71,8 @@ cs.author = self.recode(cs.author) self.lastbranch[cs.branch] = id cs.comment = self.recode(cs.comment) + if self.ui.configbool('convert', 'localtimezone'): + cs.date = makedatetimestamp(cs.date[0]) date = util.datestr(cs.date, '%Y-%m-%d %H:%M:%S %1%2') self.tags.update(dict.fromkeys(cs.tags, id))
--- a/hgext/convert/cvsps.py Mon Jan 14 23:14:45 2013 +0900 +++ b/hgext/convert/cvsps.py Sat Jan 19 17:24:33 2013 -0600 @@ -19,6 +19,7 @@ .branch - name of branch this revision is on .branches - revision tuple of branches starting at this revision .comment - commit message + .commitid - CVS commitid or None .date - the commit date as a (time, tz) tuple .dead - true if file revision is dead .file - Name of file @@ -28,19 +29,17 @@ .revision - revision number as tuple .tags - list of tags on the file .synthetic - is this a synthetic "file ... added on ..." revision? - .mergepoint- the branch that has been merged from - (if present in rlog output) - .branchpoints- the branches that start at the current entry + .mergepoint - the branch that has been merged from (if present in + rlog output) or None + .branchpoints - the branches that start at the current entry or empty ''' def __init__(self, **entries): self.synthetic = False self.__dict__.update(entries) def __repr__(self): - return "<%s at 0x%x: %s %s>" % (self.__class__.__name__, - id(self), - self.file, - ".".join(map(str, self.revision))) + items = ("%s=%r"%(k, self.__dict__[k]) for k in sorted(self.__dict__)) + return "%s(%s)"%(type(self).__name__, ", ".join(items)) class logerror(Exception): pass @@ -113,6 +112,7 @@ re_50 = re.compile('revision ([\\d.]+)(\s+locked by:\s+.+;)?$') re_60 = re.compile(r'date:\s+(.+);\s+author:\s+(.+);\s+state:\s+(.+?);' r'(\s+lines:\s+(\+\d+)?\s+(-\d+)?;)?' + r'(\s+commitid:\s+([^;]+);)?' r'(.*mergepoint:\s+([^;]+);)?') re_70 = re.compile('branches: (.+);$') @@ -171,6 +171,14 @@ try: ui.note(_('reading cvs log cache %s\n') % cachefile) oldlog = pickle.load(open(cachefile)) + for e in oldlog: + if not (util.safehasattr(e, 'branchpoints') and + util.safehasattr(e, 'commitid') and + util.safehasattr(e, 'mergepoint')): + ui.status(_('ignoring old cache\n')) + oldlog = [] + break + ui.note(_('cache has %d log entries\n') % len(oldlog)) except Exception, e: ui.note(_('error reading cache: %r\n') % e) @@ -296,9 +304,16 @@ # as this state is re-entered for subsequent revisions of a file. match = re_50.match(line) assert match, _('expected revision number') - e = logentry(rcs=scache(rcs), file=scache(filename), - revision=tuple([int(x) for x in match.group(1).split('.')]), - branches=[], parent=None) + e = logentry(rcs=scache(rcs), + file=scache(filename), + revision=tuple([int(x) for x in + match.group(1).split('.')]), + branches=[], + parent=None, + commitid=None, + mergepoint=None, + branchpoints=set()) + state = 6 elif state == 6: @@ -329,8 +344,11 @@ else: e.lines = None - if match.group(7): # cvsnt mergepoint - myrev = match.group(8).split('.') + if match.group(7): # cvs 1.12 commitid + e.commitid = match.group(8) + + if match.group(9): # cvsnt mergepoint + myrev = match.group(10).split('.') if len(myrev) == 2: # head e.mergepoint = 'HEAD' else: @@ -339,8 +357,7 @@ assert len(branches) == 1, ('unknown branch: %s' % e.mergepoint) e.mergepoint = branches[0] - else: - e.mergepoint = None + e.comment = [] state = 7 @@ -469,23 +486,22 @@ .author - author name as CVS knows it .branch - name of branch this changeset is on, or None .comment - commit message + .commitid - CVS commitid or None .date - the commit date as a (time,tz) tuple .entries - list of logentry objects in this changeset .parents - list of one or two parent changesets .tags - list of tags on this changeset .synthetic - from synthetic revision "file ... added on branch ..." - .mergepoint- the branch that has been merged from - (if present in rlog output) - .branchpoints- the branches that start at the current entry + .mergepoint- the branch that has been merged from or None + .branchpoints- the branches that start at the current entry or empty ''' def __init__(self, **entries): self.synthetic = False self.__dict__.update(entries) def __repr__(self): - return "<%s at 0x%x: %s>" % (self.__class__.__name__, - id(self), - getattr(self, 'id', "(no id)")) + items = ("%s=%r"%(k, self.__dict__[k]) for k in sorted(self.__dict__)) + return "%s(%s)"%(type(self).__name__, ", ".join(items)) def createchangeset(ui, log, fuzz=60, mergefrom=None, mergeto=None): '''Convert log into changesets.''' @@ -493,8 +509,8 @@ ui.status(_('creating changesets\n')) # Merge changesets - - log.sort(key=lambda x: (x.comment, x.author, x.branch, x.date)) + log.sort(key=lambda x: (x.commitid, x.comment, x.author, x.branch, x.date, + x.branchpoints)) changesets = [] files = set() @@ -517,22 +533,24 @@ # first changeset and bar the next and MYBRANCH and MYBRANCH2 # should both start off of the bar changeset. No provisions are # made to ensure that this is, in fact, what happens. - if not (c and - e.comment == c.comment and - e.author == c.author and - e.branch == c.branch and - (not util.safehasattr(e, 'branchpoints') or - not util.safehasattr (c, 'branchpoints') or - e.branchpoints == c.branchpoints) and - ((c.date[0] + c.date[1]) <= - (e.date[0] + e.date[1]) <= - (c.date[0] + c.date[1]) + fuzz) and - e.file not in files): + if not (c and e.branchpoints == c.branchpoints and + (# cvs commitids + (e.commitid is not None and e.commitid == c.commitid) or + (# no commitids, use fuzzy commit detection + (e.commitid is None or c.commitid is None) and + e.comment == c.comment and + e.author == c.author and + e.branch == c.branch and + ((c.date[0] + c.date[1]) <= + (e.date[0] + e.date[1]) <= + (c.date[0] + c.date[1]) + fuzz) and + e.file not in files))): c = changeset(comment=e.comment, author=e.author, - branch=e.branch, date=e.date, entries=[], - mergepoint=getattr(e, 'mergepoint', None), - branchpoints=getattr(e, 'branchpoints', set())) + branch=e.branch, date=e.date, + entries=[], mergepoint=e.mergepoint, + branchpoints=e.branchpoints, commitid=e.commitid) changesets.append(c) + files = set() if len(changesets) % 100 == 0: t = '%d %s' % (len(changesets), repr(e.comment)[1:-1]) @@ -801,22 +819,22 @@ # Note: trailing spaces on several lines here are needed to have # bug-for-bug compatibility with cvsps. ui.write('---------------------\n') - ui.write('PatchSet %d \n' % cs.id) - ui.write('Date: %s\n' % util.datestr(cs.date, - '%Y/%m/%d %H:%M:%S %1%2')) - ui.write('Author: %s\n' % cs.author) - ui.write('Branch: %s\n' % (cs.branch or 'HEAD')) - ui.write('Tag%s: %s \n' % (['', 's'][len(cs.tags) > 1], - ','.join(cs.tags) or '(none)')) - branchpoints = getattr(cs, 'branchpoints', None) - if branchpoints: - ui.write('Branchpoints: %s \n' % ', '.join(branchpoints)) + ui.write(('PatchSet %d \n' % cs.id)) + ui.write(('Date: %s\n' % util.datestr(cs.date, + '%Y/%m/%d %H:%M:%S %1%2'))) + ui.write(('Author: %s\n' % cs.author)) + ui.write(('Branch: %s\n' % (cs.branch or 'HEAD'))) + ui.write(('Tag%s: %s \n' % (['', 's'][len(cs.tags) > 1], + ','.join(cs.tags) or '(none)'))) + if cs.branchpoints: + ui.write(('Branchpoints: %s \n') % + ', '.join(sorted(cs.branchpoints))) if opts["parents"] and cs.parents: if len(cs.parents) > 1: - ui.write('Parents: %s\n' % - (','.join([str(p.id) for p in cs.parents]))) + ui.write(('Parents: %s\n' % + (','.join([str(p.id) for p in cs.parents])))) else: - ui.write('Parent: %d\n' % cs.parents[0].id) + ui.write(('Parent: %d\n' % cs.parents[0].id)) if opts["ancestors"]: b = cs.branch @@ -825,11 +843,11 @@ b, c = ancestors[b] r.append('%s:%d:%d' % (b or "HEAD", c, branches[b])) if r: - ui.write('Ancestors: %s\n' % (','.join(r))) + ui.write(('Ancestors: %s\n' % (','.join(r)))) - ui.write('Log:\n') + ui.write(('Log:\n')) ui.write('%s\n\n' % cs.comment) - ui.write('Members: \n') + ui.write(('Members: \n')) for f in cs.entries: fn = f.file if fn.startswith(opts["prefix"]):
--- a/hgext/convert/git.py Mon Jan 14 23:14:45 2013 +0900 +++ b/hgext/convert/git.py Sat Jan 19 17:24:33 2013 -0600 @@ -6,12 +6,24 @@ # GNU General Public License version 2 or any later version. import os -from mercurial import util +from mercurial import util, config from mercurial.node import hex, nullid from mercurial.i18n import _ from common import NoRepo, commit, converter_source, checktool +class submodule(object): + def __init__(self, path, node, url): + self.path = path + self.node = node + self.url = url + + def hgsub(self): + return "%s = [git]%s" % (self.path, self.url) + + def hgsubstate(self): + return "%s %s" % (self.node, self.path) + class convert_git(converter_source): # Windows does not support GIT_DIR= construct while other systems # cannot remove environment variable. Just assume none have @@ -55,6 +67,7 @@ checktool('git', 'git') self.path = path + self.submodules = [] def getheads(self): if not self.rev: @@ -76,16 +89,57 @@ return data def getfile(self, name, rev): - data = self.catfile(rev, "blob") - mode = self.modecache[(name, rev)] + if name == '.hgsub': + data = '\n'.join([m.hgsub() for m in self.submoditer()]) + mode = '' + elif name == '.hgsubstate': + data = '\n'.join([m.hgsubstate() for m in self.submoditer()]) + mode = '' + else: + data = self.catfile(rev, "blob") + mode = self.modecache[(name, rev)] return data, mode + def submoditer(self): + null = hex(nullid) + for m in sorted(self.submodules, key=lambda p: p.path): + if m.node != null: + yield m + + def parsegitmodules(self, content): + """Parse the formatted .gitmodules file, example file format: + [submodule "sub"]\n + \tpath = sub\n + \turl = git://giturl\n + """ + self.submodules = [] + c = config.config() + # Each item in .gitmodules starts with \t that cant be parsed + c.parse('.gitmodules', content.replace('\t','')) + for sec in c.sections(): + s = c[sec] + if 'url' in s and 'path' in s: + self.submodules.append(submodule(s['path'], '', s['url'])) + + def retrievegitmodules(self, version): + modules, ret = self.gitread("git show %s:%s" % (version, '.gitmodules')) + if ret: + raise util.Abort(_('cannot read submodules config file in %s') % + version) + self.parsegitmodules(modules) + for m in self.submodules: + node, ret = self.gitread("git rev-parse %s:%s" % (version, m.path)) + if ret: + continue + m.node = node.strip() + def getchanges(self, version): self.modecache = {} fh = self.gitopen("git diff-tree -z --root -m -r %s" % version) changes = [] seen = set() entry = None + subexists = False for l in fh.read().split('\x00'): if not entry: if not l.startswith(':'): @@ -97,15 +151,24 @@ seen.add(f) entry = entry.split() h = entry[3] - if entry[1] == '160000': - raise util.Abort('git submodules are not supported!') p = (entry[1] == "100755") s = (entry[1] == "120000") - self.modecache[(f, h)] = (p and "x") or (s and "l") or "" - changes.append((f, h)) + + if f == '.gitmodules': + subexists = True + changes.append(('.hgsub', '')) + elif entry[1] == '160000' or entry[0] == ':160000': + subexists = True + else: + self.modecache[(f, h)] = (p and "x") or (s and "l") or "" + changes.append((f, h)) entry = None if fh.close(): raise util.Abort(_('cannot read changes in %s') % version) + + if subexists: + self.retrievegitmodules(version) + changes.append(('.hgsubstate', '')) return (changes, {}) def getcommit(self, version):
--- a/hgext/convert/hg.py Mon Jan 14 23:14:45 2013 +0900 +++ b/hgext/convert/hg.py Sat Jan 19 17:24:33 2013 -0600 @@ -110,7 +110,7 @@ if missings: self.after() - for pbranch, heads in missings.iteritems(): + for pbranch, heads in sorted(missings.iteritems()): pbranchpath = os.path.join(self.path, pbranch) prepo = hg.peer(self.ui, {}, pbranchpath) self.ui.note(_('pulling from %s into %s\n') % (pbranch, branch)) @@ -219,9 +219,10 @@ return self.ui.status(_("updating bookmarks\n")) + destmarks = self.repo._bookmarks for bookmark in updatedbookmark: - self.repo._bookmarks[bookmark] = bin(updatedbookmark[bookmark]) - bookmarks.write(self.repo) + destmarks[bookmark] = bin(updatedbookmark[bookmark]) + destmarks.write() def hascommit(self, rev): if rev not in self.repo and self.clonebranches:
--- a/hgext/convert/subversion.py Mon Jan 14 23:14:45 2013 +0900 +++ b/hgext/convert/subversion.py Sat Jan 19 17:24:33 2013 -0600 @@ -18,6 +18,7 @@ from common import NoRepo, MissingTool, commit, encodeargs, decodeargs from common import commandline, converter_source, converter_sink, mapfile +from common import makedatetimestamp try: from svn.core import SubversionException, Pool @@ -376,7 +377,7 @@ rpath = self.url.strip('/') branchnames = svn.client.ls(rpath + '/' + quote(branches), rev, False, self.ctx) - for branch in branchnames.keys(): + for branch in sorted(branchnames): module = '%s/%s/%s' % (oldmodule, branches, branch) if not isdir(module, self.last_changed): continue @@ -802,6 +803,8 @@ # ISO-8601 conformant # '2007-01-04T17:35:00.902377Z' date = util.parsedate(date[:19] + " UTC", ["%Y-%m-%dT%H:%M:%S"]) + if self.ui.configbool('convert', 'localtimezone'): + date = makedatetimestamp(date[0]) log = message and self.recode(message) or '' author = author and self.recode(author) or ''
--- a/hgext/eol.py Mon Jan 14 23:14:45 2013 +0900 +++ b/hgext/eol.py Sat Jan 19 17:24:33 2013 -0600 @@ -307,7 +307,7 @@ eolmtime = 0 if eolmtime > cachemtime: - ui.debug("eol: detected change in .hgeol\n") + self.ui.debug("eol: detected change in .hgeol\n") wlock = None try: wlock = self.wlock()
--- a/hgext/graphlog.py Mon Jan 14 23:14:45 2013 +0900 +++ b/hgext/graphlog.py Sat Jan 19 17:24:33 2013 -0600 @@ -39,7 +39,6 @@ _('show changesets within the given named branch'), _('BRANCH')), ('P', 'prune', [], _('do not display revision or any of its ancestors'), _('REV')), - ('', 'hidden', False, _('show hidden changesets (DEPRECATED)')), ] + commands.logopts + commands.walkopts, _('[OPTION]... [FILE]')) def graphlog(ui, repo, *pats, **opts):
--- a/hgext/hgk.py Mon Jan 14 23:14:45 2013 +0900 +++ b/hgext/hgk.py Sat Jan 19 17:24:33 2013 -0600 @@ -98,9 +98,9 @@ if ctx is None: ctx = repo[n] # use ctx.node() instead ?? - ui.write("tree %s\n" % short(ctx.changeset()[0])) + ui.write(("tree %s\n" % short(ctx.changeset()[0]))) for p in ctx.parents(): - ui.write("parent %s\n" % p) + ui.write(("parent %s\n" % p)) date = ctx.date() description = ctx.description().replace("\0", "") @@ -108,12 +108,13 @@ if lines and lines[-1].startswith('committer:'): committer = lines[-1].split(': ')[1].rstrip() else: - committer = ctx.user() + committer = "" - ui.write("author %s %s %s\n" % (ctx.user(), int(date[0]), date[1])) - ui.write("committer %s %s %s\n" % (committer, int(date[0]), date[1])) - ui.write("revision %d\n" % ctx.rev()) - ui.write("branch %s\n\n" % ctx.branch()) + ui.write(("author %s %s %s\n" % (ctx.user(), int(date[0]), date[1]))) + if committer != '': + ui.write(("committer %s %s %s\n" % (committer, int(date[0]), date[1]))) + ui.write(("revision %d\n" % ctx.rev())) + ui.write(("branch %s\n\n" % ctx.branch())) if prefix != "": ui.write("%s%s\n" % (prefix, @@ -302,7 +303,7 @@ def config(ui, repo, **opts): """print extension options""" def writeopt(name, value): - ui.write('k=%s\nv=%s\n' % (name, value)) + ui.write(('k=%s\nv=%s\n' % (name, value))) writeopt('vdiff', ui.config('hgk', 'vdiff', ''))
--- a/hgext/highlight/highlight.py Mon Jan 14 23:14:45 2013 +0900 +++ b/hgext/highlight/highlight.py Sat Jan 19 17:24:33 2013 -0600 @@ -50,7 +50,7 @@ colorized = highlight(text, lexer, formatter) # strip wrapping div colorized = colorized[:colorized.find('\n</pre>')] - colorized = colorized[colorized.find('<pre>')+5:] + colorized = colorized[colorized.find('<pre>') + 5:] coloriter = (s.encode(encoding.encoding, 'replace') for s in colorized.splitlines())
--- a/hgext/histedit.py Mon Jan 14 23:14:45 2013 +0900 +++ b/hgext/histedit.py Sat Jan 19 17:24:33 2013 -0600 @@ -144,7 +144,6 @@ import pickle import os -from mercurial import bookmarks from mercurial import cmdutil from mercurial import discovery from mercurial import error @@ -177,6 +176,31 @@ # """) +def commitfuncfor(repo, src): + """Build a commit function for the replacement of <src> + + This function ensure we apply the same treatement to all changesets. + + - Add a 'histedit_source' entry in extra. + + Note that fold have its own separated logic because its handling is a bit + different and not easily factored out of the fold method. + """ + phasemin = src.phase() + def commitfunc(**kwargs): + phasebackup = repo.ui.backupconfig('phases', 'new-commit') + try: + repo.ui.setconfig('phases', 'new-commit', phasemin) + extra = kwargs.get('extra', {}).copy() + extra['histedit_source'] = src.hex() + kwargs['extra'] = extra + return repo.commit(**kwargs) + finally: + repo.ui.restoreconfig(phasebackup) + return commitfunc + + + def applychanges(ui, repo, ctx, opts): """Merge changeset from ctx (only) in the current working directory""" wcpar = repo.dirstate.parents()[0] @@ -255,7 +279,7 @@ message = first.description() user = commitopts.get('user') date = commitopts.get('date') - extra = first.extra() + extra = commitopts.get('extra') parents = (first.p1().node(), first.p2().node()) new = context.memctx(repo, @@ -280,8 +304,9 @@ raise util.Abort(_('Fix up the change and run ' 'hg histedit --continue')) # drop the second merge parent - n = repo.commit(text=oldctx.description(), user=oldctx.user(), - date=oldctx.date(), extra=oldctx.extra()) + commit = commitfuncfor(repo, oldctx) + n = commit(text=oldctx.description(), user=oldctx.user(), + date=oldctx.date(), extra=oldctx.extra()) if n is None: ui.warn(_('%s: empty changeset\n') % node.hex(ha)) @@ -332,7 +357,19 @@ commitopts['message'] = newmessage # date commitopts['date'] = max(ctx.date(), oldctx.date()) - n = collapse(repo, ctx, repo[newnode], commitopts) + extra = ctx.extra().copy() + # histedit_source + # note: ctx is likely a temporary commit but that the best we can do here + # This is sufficient to solve issue3681 anyway + extra['histedit_source'] = '%s,%s' % (ctx.hex(), oldctx.hex()) + commitopts['extra'] = extra + phasebackup = repo.ui.backupconfig('phases', 'new-commit') + try: + phasemin = max(ctx.phase(), oldctx.phase()) + repo.ui.setconfig('phases', 'new-commit', phasemin) + n = collapse(repo, ctx, repo[newnode], commitopts) + finally: + repo.ui.restoreconfig(phasebackup) if n is None: return ctx, [] hg.update(repo, n) @@ -357,8 +394,9 @@ 'hg histedit --continue')) message = oldctx.description() + '\n' message = ui.edit(message, ui.username()) - new = repo.commit(text=message, user=oldctx.user(), date=oldctx.date(), - extra=oldctx.extra()) + commit = commitfuncfor(repo, oldctx) + new = commit(text=message, user=oldctx.user(), date=oldctx.date(), + extra=oldctx.extra()) newctx = repo[new] if oldctx.node() != newctx.node(): return newctx, [(oldctx.node(), (new,))] @@ -559,9 +597,10 @@ editor = cmdutil.commitforceeditor else: editor = False - new = repo.commit(text=message, user=ctx.user(), - date=ctx.date(), extra=ctx.extra(), - editor=editor) + commit = commitfuncfor(repo, ctx) + new = commit(text=message, user=ctx.user(), + date=ctx.date(), extra=ctx.extra(), + editor=editor) if new is not None: newchildren.append(new) @@ -594,7 +633,8 @@ When keep is false, the specified set can't have children.""" ctxs = list(repo.set('%n::%n', old, new)) if ctxs and not keep: - if repo.revs('(%ld::) - (%ld + hidden())', ctxs, ctxs): + if (not obsolete._enabled and + repo.revs('(%ld::) - (%ld)', ctxs, ctxs)): raise util.Abort(_('cannot edit history that would orphan nodes')) root = ctxs[0] # list is already sorted by repo.set if not root.phase(): @@ -720,9 +760,9 @@ # if nothing got rewritten there is not purpose for this function return moves = [] - for bk, old in repo._bookmarks.iteritems(): + for bk, old in sorted(repo._bookmarks.iteritems()): if old == oldtopmost: - # special case ensure bookmark stay on tip. + # special case ensure bookmark stay on tip. # # This is arguably a feature and we may only want that for the # active bookmark. But the behavior is kept compatible with the old @@ -740,12 +780,13 @@ # nothing to move moves.append((bk, new[-1])) if moves: + marks = repo._bookmarks for mark, new in moves: - old = repo._bookmarks[mark] + old = marks[mark] ui.note(_('histedit: moving bookmarks %s from %s to %s\n') % (mark, node.short(old), node.short(new))) - repo._bookmarks[mark] = new - bookmarks.write(repo) + marks[mark] = new + marks.write() def cleanupnode(ui, repo, name, nodes): """strip a group of nodes from the repository
--- a/hgext/inotify/linux/watcher.py Mon Jan 14 23:14:45 2013 +0900 +++ b/hgext/inotify/linux/watcher.py Sat Jan 19 17:24:33 2013 -0600 @@ -72,7 +72,7 @@ def __repr__(self): r = repr(self.raw) - return 'event(path=' + repr(self.path) + ', ' + r[r.find('(')+1:] + return 'event(path=' + repr(self.path) + ', ' + r[r.find('(') + 1:] _event_props = {
--- a/hgext/inotify/linuxserver.py Mon Jan 14 23:14:45 2013 +0900 +++ b/hgext/inotify/linuxserver.py Sat Jan 19 17:24:33 2013 -0600 @@ -405,14 +405,7 @@ def shutdown(self): self.sock.close() - try: - os.unlink(self.sockpath) - if self.realsockpath: - os.unlink(self.realsockpath) - os.rmdir(os.path.dirname(self.realsockpath)) - except OSError, err: - if err.errno != errno.ENOENT: - raise + self.sock.cleanup() def answer_stat_query(self, cs): if self.repowatcher.timeout:
--- a/hgext/inotify/server.py Mon Jan 14 23:14:45 2013 +0900 +++ b/hgext/inotify/server.py Sat Jan 19 17:24:33 2013 -0600 @@ -6,7 +6,7 @@ # GNU General Public License version 2 or any later version. from mercurial.i18n import _ -from mercurial import cmdutil, osutil, util +from mercurial import cmdutil, posix, osutil, util import common import errno @@ -15,7 +15,6 @@ import stat import struct import sys -import tempfile class AlreadyStartedException(Exception): pass @@ -330,42 +329,15 @@ def __init__(self, ui, root, repowatcher, timeout): self.ui = ui self.repowatcher = repowatcher - self.sock = socket.socket(socket.AF_UNIX) - self.sockpath = join(root, '.hg/inotify.sock') - - self.realsockpath = self.sockpath - if os.path.islink(self.sockpath): - if os.path.exists(self.sockpath): - self.realsockpath = os.readlink(self.sockpath) - else: - raise util.Abort('inotify-server: cannot start: ' - '.hg/inotify.sock is a broken symlink') try: - self.sock.bind(self.realsockpath) - except socket.error, err: + self.sock = posix.unixdomainserver( + lambda p: os.path.join(root, '.hg', p), + 'inotify') + except (OSError, socket.error), err: if err.args[0] == errno.EADDRINUSE: - raise AlreadyStartedException(_('cannot start: socket is ' - 'already bound')) - if err.args[0] == "AF_UNIX path too long": - tempdir = tempfile.mkdtemp(prefix="hg-inotify-") - self.realsockpath = os.path.join(tempdir, "inotify.sock") - try: - self.sock.bind(self.realsockpath) - os.symlink(self.realsockpath, self.sockpath) - except (OSError, socket.error), inst: - try: - os.unlink(self.realsockpath) - except OSError: - pass - os.rmdir(tempdir) - if inst.errno == errno.EEXIST: - raise AlreadyStartedException(_('cannot start: tried ' - 'linking .hg/inotify.sock to a temporary socket but' - ' .hg/inotify.sock already exists')) - raise - else: - raise - self.sock.listen(5) + raise AlreadyStartedException(_('cannot start: ' + 'socket is already bound')) + raise self.fileno = self.sock.fileno def answer_stat_query(self, cs):
--- a/hgext/largefiles/basestore.py Mon Jan 14 23:14:45 2013 +0900 +++ b/hgext/largefiles/basestore.py Sat Jan 19 17:24:33 2013 -0600 @@ -26,14 +26,8 @@ self.detail = detail def longmessage(self): - if self.url: - return ('%s: %s\n' - '(failed URL: %s)\n' - % (self.filename, self.detail, self.url)) - else: - return ('%s: %s\n' - '(no default or default-push path set in hgrc)\n' - % (self.filename, self.detail)) + return (_("error getting %s from %s for %s: %s\n") % + (self.hash, self.url, self.filename, self.detail)) def __str__(self): return "%s: %s" % (self.url, self.detail)
--- a/hgext/largefiles/lfcommands.py Mon Jan 14 23:14:45 2013 +0900 +++ b/hgext/largefiles/lfcommands.py Sat Jan 19 17:24:33 2013 -0600 @@ -383,6 +383,13 @@ store = basestore._openstore(repo) return store.verify(revs, contents=contents) +def debugdirstate(ui, repo): + '''Show basic information for the largefiles dirstate''' + lfdirstate = lfutil.openlfdirstate(ui, repo) + for file_, ent in sorted(lfdirstate._map.iteritems()): + mode = '%3o' % (ent[1] & 0777 & ~util.umask) + ui.write("%c %s %10d %s\n" % (ent[0], mode, ent[2], file_)) + def cachelfiles(ui, repo, node, filelist=None): '''cachelfiles ensures that all largefiles needed by the specified revision are present in the repository's largefile cache.
--- a/hgext/largefiles/lfutil.py Mon Jan 14 23:14:45 2013 +0900 +++ b/hgext/largefiles/lfutil.py Sat Jan 19 17:24:33 2013 -0600 @@ -18,43 +18,10 @@ from mercurial.i18n import _ shortname = '.hglf' +shortnameslash = shortname + '/' longname = 'largefiles' -# -- Portability wrappers ---------------------------------------------- - -def dirstatewalk(dirstate, matcher, unknown=False, ignored=False): - return dirstate.walk(matcher, [], unknown, ignored) - -def repoadd(repo, list): - add = repo[None].add - return add(list) - -def reporemove(repo, list, unlink=False): - def remove(list, unlink): - wlock = repo.wlock() - try: - if unlink: - for f in list: - try: - util.unlinkpath(repo.wjoin(f)) - except OSError, inst: - if inst.errno != errno.ENOENT: - raise - repo[None].forget(list) - finally: - wlock.release() - return remove(list, unlink=unlink) - -def repoforget(repo, list): - forget = repo[None].forget - return forget(list) - -def findoutgoing(repo, remote, force): - from mercurial import discovery - outgoing = discovery.findcommonoutgoing(repo, remote.peer(), force=force) - return outgoing.missing - # -- Private worker functions ------------------------------------------ def getminsize(ui, assumelfiles, opt, default=10): @@ -139,24 +106,26 @@ return super(largefilesdirstate, self).forget(unixpath(f)) def normallookup(self, f): return super(largefilesdirstate, self).normallookup(unixpath(f)) + def _ignore(self): + return False def openlfdirstate(ui, repo, create=True): ''' Return a dirstate object that tracks largefiles: i.e. its root is the repo root, but it is saved in .hg/largefiles/dirstate. ''' - admin = repo.join(longname) - opener = scmutil.opener(admin) + lfstoredir = repo.join(longname) + opener = scmutil.opener(lfstoredir) lfdirstate = largefilesdirstate(opener, ui, repo.root, repo.dirstate._validate) # If the largefiles dirstate does not exist, populate and create # it. This ensures that we create it on the first meaningful # largefiles operation in a new clone. - if create and not os.path.exists(os.path.join(admin, 'dirstate')): - util.makedirs(admin) + if create and not os.path.exists(os.path.join(lfstoredir, 'dirstate')): + util.makedirs(lfstoredir) matcher = getstandinmatcher(repo) - for standin in dirstatewalk(repo.dirstate, matcher): + for standin in repo.dirstate.walk(matcher, [], False, False): lfile = splitstandin(standin) hash = readstandin(repo, lfile) lfdirstate.normallookup(lfile) @@ -173,8 +142,11 @@ s = lfdirstate.status(match, [], False, False, False) unsure, modified, added, removed, missing, unknown, ignored, clean = s for lfile in unsure: - if repo[rev][standin(lfile)].data().strip() != \ - hashfile(repo.wjoin(lfile)): + try: + fctx = repo[rev][standin(lfile)] + except LookupError: + fctx = None + if not fctx or fctx.data().strip() != hashfile(repo.wjoin(lfile)): modified.append(lfile) else: clean.append(lfile) @@ -250,7 +222,7 @@ def getstandinmatcher(repo, pats=[], opts={}): '''Return a match object that applies pats to the standin directory''' - standindir = repo.pathto(shortname) + standindir = repo.wjoin(shortname) if pats: # patterns supplied: search standin directory relative to current dir cwd = repo.getcwd() @@ -264,19 +236,11 @@ pats = [standindir] else: # no patterns and no standin dir: return matcher that matches nothing - match = match_.match(repo.root, None, [], exact=True) - match.matchfn = lambda f: False - return match - return getmatcher(repo, pats, opts, showbad=False) + return match_.match(repo.root, None, [], exact=True) -def getmatcher(repo, pats=[], opts={}, showbad=True): - '''Wrapper around scmutil.match() that adds showbad: if false, - neuter the match object's bad() method so it does not print any - warnings about missing files or directories.''' + # no warnings about missing files or directories match = scmutil.match(repo[None], pats, opts) - - if not showbad: - match.bad = lambda f, msg: None + match.bad = lambda f, msg: None return match def composestandinmatcher(repo, rmatcher): @@ -296,17 +260,17 @@ file.''' # Notes: # 1) Some callers want an absolute path, but for instance addlargefiles - # needs it repo-relative so it can be passed to repoadd(). So leave - # it up to the caller to use repo.wjoin() to get an absolute path. + # needs it repo-relative so it can be passed to repo[None].add(). So + # leave it up to the caller to use repo.wjoin() to get an absolute path. # 2) Join with '/' because that's what dirstate always uses, even on # Windows. Change existing separator to '/' first in case we are # passed filenames from an external source (like the command line). - return shortname + '/' + util.pconvert(filename) + return shortnameslash + util.pconvert(filename) def isstandin(filename): '''Return true if filename is a big file standin. filename must be in Mercurial's internal form (slash-separated).''' - return filename.startswith(shortname + '/') + return filename.startswith(shortnameslash) def splitstandin(filename): # Split on / because that's what dirstate always uses, even on Windows. @@ -435,7 +399,7 @@ def islfilesrepo(repo): if ('largefiles' in repo.requirements and - util.any(shortname + '/' in f[0] for f in repo.store.datafiles())): + util.any(shortnameslash in f[0] for f in repo.store.datafiles())): return True return util.any(openlfdirstate(repo.ui, repo, False)) @@ -455,9 +419,13 @@ def getstandinsstate(repo): standins = [] matcher = getstandinmatcher(repo) - for standin in dirstatewalk(repo.dirstate, matcher): + for standin in repo.dirstate.walk(matcher, [], False, False): lfile = splitstandin(standin) - standins.append((lfile, readstandin(repo, lfile))) + try: + hash = readstandin(repo, lfile) + except IOError: + hash = None + standins.append((lfile, hash)) return standins def getlfilestoupdate(oldstandins, newstandins):
--- a/hgext/largefiles/localstore.py Mon Jan 14 23:14:45 2013 +0900 +++ b/hgext/largefiles/localstore.py Sat Jan 19 17:24:33 2013 -0600 @@ -22,9 +22,8 @@ the user cache.''' def __init__(self, ui, repo, remote): - url = os.path.join(remote.local().path, '.hg', lfutil.longname) - super(localstore, self).__init__(ui, repo, util.expandpath(url)) self.remote = remote.local() + super(localstore, self).__init__(ui, repo, self.remote.url()) def put(self, source, hash): util.makedirs(os.path.dirname(lfutil.storepath(self.remote, hash))) @@ -46,7 +45,7 @@ elif lfutil.inusercache(self.ui, hash): path = lfutil.usercachepath(self.ui, hash) else: - raise basestore.StoreError(filename, hash, '', + raise basestore.StoreError(filename, hash, self.url, _("can't get file locally")) fd = open(path, 'rb') try:
--- a/hgext/largefiles/overrides.py Mon Jan 14 23:14:45 2013 +0900 +++ b/hgext/largefiles/overrides.py Sat Jan 19 17:24:33 2013 -0600 @@ -12,7 +12,7 @@ import copy from mercurial import hg, commands, util, cmdutil, scmutil, match as match_, \ - node, archival, error, merge + node, archival, error, merge, discovery from mercurial.i18n import _ from mercurial.node import hex from hgext import rebase @@ -116,7 +116,7 @@ lfdirstate.add(f) lfdirstate.write() bad += [lfutil.splitstandin(f) - for f in lfutil.repoadd(repo, standins) + for f in repo[None].add(standins) if f in m.files()] finally: wlock.release() @@ -137,21 +137,23 @@ if lfutil.standin(f) in manifest] for list in [s[0], s[1], s[3], s[6]]] - def warn(files, reason): + def warn(files, msg): for f in files: - ui.warn(_('not removing %s: %s (use forget to undo)\n') - % (m.rel(f), reason)) + ui.warn(msg % m.rel(f)) return int(len(files) > 0) result = 0 if after: remove, forget = deleted, [] - result = warn(modified + added + clean, _('file still exists')) + result = warn(modified + added + clean, + _('not removing %s: file still exists\n')) else: remove, forget = deleted + clean, [] - result = warn(modified, _('file is modified')) - result = warn(added, _('file has been marked for add')) or result + result = warn(modified, _('not removing %s: file is modified (use -f' + ' to force removal)\n')) + result = warn(added, _('not removing %s: file has been marked for add' + ' (use forget to undo)\n')) or result for f in sorted(remove + forget): if ui.verbose or not m.exact(f): @@ -168,19 +170,18 @@ # are removing the file. if getattr(repo, "_isaddremove", False): ui.status(_('removing %s\n') % f) - if os.path.exists(repo.wjoin(f)): - util.unlinkpath(repo.wjoin(f)) + util.unlinkpath(repo.wjoin(f), ignoremissing=True) lfdirstate.remove(f) lfdirstate.write() forget = [lfutil.standin(f) for f in forget] remove = [lfutil.standin(f) for f in remove] - lfutil.repoforget(repo, forget) + repo[None].forget(forget) # If this is being called by addremove, let the original addremove # function handle this. if not getattr(repo, "_isaddremove", False): - lfutil.reporemove(repo, remove, unlink=True) - else: - lfutil.reporemove(repo, remove, unlink=False) + for f in remove: + util.unlinkpath(repo.wjoin(f), ignoremissing=True) + repo[None].forget(remove) finally: wlock.release() @@ -238,11 +239,34 @@ repo._repo.lfstatus = False def overridelog(orig, ui, repo, *pats, **opts): + def overridematch(ctx, pats=[], opts={}, globbed=False, + default='relpath'): + """Matcher that merges root directory with .hglf, suitable for log. + It is still possible to match .hglf directly. + For any listed files run log on the standin too. + matchfn tries both the given filename and with .hglf stripped. + """ + match = oldmatch(ctx, pats, opts, globbed, default) + m = copy.copy(match) + standins = [lfutil.standin(f) for f in m._files] + m._files.extend(standins) + m._fmap = set(m._files) + origmatchfn = m.matchfn + def lfmatchfn(f): + lf = lfutil.splitstandin(f) + if lf is not None and origmatchfn(lf): + return True + r = origmatchfn(f) + return r + m.matchfn = lfmatchfn + return m + oldmatch = installmatchfn(overridematch) try: repo.lfstatus = True return orig(ui, repo, *pats, **opts) finally: repo.lfstatus = False + restorematchfn() def overrideverify(orig, ui, repo, *pats, **opts): large = opts.pop('large', False) @@ -254,6 +278,13 @@ result = result or lfcommands.verifylfiles(ui, repo, all, contents) return result +def overridedebugstate(orig, ui, repo, *pats, **opts): + large = opts.pop('large', False) + if large: + lfcommands.debugdirstate(ui, repo) + else: + orig(ui, repo, *pats, **opts) + # Override needs to refresh standins so that update's normal merge # will go through properly. Then the other update hook (overriding repo.update) # will get the new files. Filemerge is also overridden so that the merge @@ -746,7 +777,7 @@ # .hg/largefiles, and the standin matcher won't match anything anyway.) if 'largefiles' in repo.requirements: if opts.get('noupdate'): - util.makedirs(repo.pathto(lfutil.shortname)) + util.makedirs(repo.wjoin(lfutil.shortname)) util.makedirs(repo.join(lfutil.longname)) # Caching is implicitly limited to 'rev' option, since the dest repo was @@ -839,7 +870,7 @@ write(f, 'x' in ff and 0755 or 0644, 'l' in ff, getdata) if subrepos: - for subpath in ctx.substate: + for subpath in sorted(ctx.substate): sub = ctx.sub(subpath) submatch = match_.narrowmatcher(subpath, matchfn) sub.archive(repo.ui, archiver, prefix, submatch) @@ -886,7 +917,7 @@ write(f, 'x' in ff and 0755 or 0644, 'l' in ff, getdata) - for subpath in ctx.substate: + for subpath in sorted(ctx.substate): sub = ctx.sub(subpath) submatch = match_.narrowmatcher(subpath, match) sub.archive(ui, archiver, os.path.join(prefix, repo._path) + '/', @@ -949,8 +980,10 @@ else: lfdirstate.remove(f) lfdirstate.write() - lfutil.reporemove(repo, [lfutil.standin(f) for f in forget], - unlink=True) + standins = [lfutil.standin(f) for f in forget] + for f in standins: + util.unlinkpath(repo.wjoin(f), ignoremissing=True) + repo[None].forget(standins) finally: wlock.release() @@ -967,10 +1000,10 @@ remote = hg.peer(repo, opts, dest) except error.RepoError: return None - o = lfutil.findoutgoing(repo, remote, False) - if not o: - return o - o = repo.changelog.nodesbetween(o, revs)[0] + outgoing = discovery.findcommonoutgoing(repo, remote.peer(), force=False) + if not outgoing.missing: + return outgoing.missing + o = repo.changelog.nodesbetween(outgoing.missing, revs)[0] if opts.get('newest_first'): o.reverse() @@ -994,7 +1027,7 @@ files.add(f) toupload = toupload.union( set([f for f in files if lfutil.isstandin(f) and f in ctx])) - return toupload + return sorted(toupload) def overrideoutgoing(orig, ui, repo, dest=None, **opts): result = orig(ui, repo, dest, **opts) @@ -1065,6 +1098,9 @@ # Calling purge with --all will cause the largefiles to be deleted. # Override repo.status to prevent this from happening. def overridepurge(orig, ui, repo, *dirs, **opts): + # XXX large file status is buggy when used on repo proxy. + # XXX this needs to be investigate. + repo = repo.unfiltered() oldstatus = repo.status def overridestatus(node1='.', node2=None, match=None, ignored=False, clean=False, unknown=False, listsubrepos=False):
--- a/hgext/largefiles/proto.py Mon Jan 14 23:14:45 2013 +0900 +++ b/hgext/largefiles/proto.py Sat Jan 19 17:24:33 2013 -0600 @@ -140,19 +140,6 @@ def capabilities(repo, proto): return capabilitiesorig(repo, proto) + ' largefiles=serve' -# duplicate what Mercurial's new out-of-band errors mechanism does, because -# clients old and new alike both handle it well -def webprotorefuseclient(self, message): - self.req.header([('Content-Type', 'application/hg-error')]) - return message - -def sshprotorefuseclient(self, message): - self.ui.write_err('%s\n-\n' % message) - self.fout.write('\n') - self.fout.flush() - - return '' - def heads(repo, proto): if lfutil.islfilesrepo(repo): return wireproto.ooberror(LARGEFILES_REQUIRED_MSG)
--- a/hgext/largefiles/reposetup.py Mon Jan 14 23:14:45 2013 +0900 +++ b/hgext/largefiles/reposetup.py Sat Jan 19 17:24:33 2013 -0600 @@ -11,9 +11,11 @@ import types import os -from mercurial import context, error, manifest, match as match_, util +from mercurial import context, error, manifest, match as match_, util, \ + discovery from mercurial import node as node_ from mercurial.i18n import _ +from mercurial import localrepo import lfcommands import proto @@ -88,6 +90,9 @@ # appropriate list in the result. Also removes standin files # from the listing. Revert to the original status if # self.lfstatus is False. + # XXX large file status is buggy when used on repo proxy. + # XXX this needs to be investigated. + @localrepo.unfilteredmethod def status(self, node1='.', node2=None, match=None, ignored=False, clean=False, unknown=False, listsubrepos=False): listignored, listclean, listunknown = ignored, clean, unknown @@ -153,78 +158,54 @@ newfiles.append(f) return newfiles - # Create a function that we can use to override what is - # normally the ignore matcher. We've already checked - # for ignored files on the first dirstate walk, and - # unnecessarily re-checking here causes a huge performance - # hit because lfdirstate only knows about largefiles - def _ignoreoverride(self): - return False - m = copy.copy(match) m._files = tostandins(m._files) result = super(lfilesrepo, self).status(node1, node2, m, ignored, clean, unknown, listsubrepos) if working: - try: - # Any non-largefiles that were explicitly listed must be - # taken out or lfdirstate.status will report an error. - # The status of these files was already computed using - # super's status. - # Override lfdirstate's ignore matcher to not do - # anything - origignore = lfdirstate._ignore - lfdirstate._ignore = _ignoreoverride + + def sfindirstate(f): + sf = lfutil.standin(f) + dirstate = self.dirstate + return sf in dirstate or sf in dirstate.dirs() - def sfindirstate(f): - sf = lfutil.standin(f) - dirstate = self.dirstate - return sf in dirstate or sf in dirstate.dirs() - match._files = [f for f in match._files - if sfindirstate(f)] - # Don't waste time getting the ignored and unknown - # files again; we already have them - s = lfdirstate.status(match, [], False, - listclean, False) - (unsure, modified, added, removed, missing, unknown, - ignored, clean) = s - # Replace the list of ignored and unknown files with - # the previously calculated lists, and strip out the - # largefiles - lfiles = set(lfdirstate._map) - ignored = set(result[5]).difference(lfiles) - unknown = set(result[4]).difference(lfiles) - if parentworking: - for lfile in unsure: - standin = lfutil.standin(lfile) - if standin not in ctx1: - # from second parent - modified.append(lfile) - elif ctx1[standin].data().strip() \ - != lfutil.hashfile(self.wjoin(lfile)): + match._files = [f for f in match._files + if sfindirstate(f)] + # Don't waste time getting the ignored and unknown + # files from lfdirstate + s = lfdirstate.status(match, [], False, + listclean, False) + (unsure, modified, added, removed, missing, _unknown, + _ignored, clean) = s + if parentworking: + for lfile in unsure: + standin = lfutil.standin(lfile) + if standin not in ctx1: + # from second parent + modified.append(lfile) + elif ctx1[standin].data().strip() \ + != lfutil.hashfile(self.wjoin(lfile)): + modified.append(lfile) + else: + clean.append(lfile) + lfdirstate.normal(lfile) + else: + tocheck = unsure + modified + added + clean + modified, added, clean = [], [], [] + + for lfile in tocheck: + standin = lfutil.standin(lfile) + if inctx(standin, ctx1): + if ctx1[standin].data().strip() != \ + lfutil.hashfile(self.wjoin(lfile)): modified.append(lfile) else: clean.append(lfile) - lfdirstate.normal(lfile) - else: - tocheck = unsure + modified + added + clean - modified, added, clean = [], [], [] + else: + added.append(lfile) - for lfile in tocheck: - standin = lfutil.standin(lfile) - if inctx(standin, ctx1): - if ctx1[standin].data().strip() != \ - lfutil.hashfile(self.wjoin(lfile)): - modified.append(lfile) - else: - clean.append(lfile) - else: - added.append(lfile) - finally: - # Replace the original ignore function - lfdirstate._ignore = origignore - + # Standins no longer found in lfdirstate has been removed for standin in ctx1.manifest(): if not lfutil.isstandin(standin): continue @@ -239,20 +220,17 @@ # Largefiles are not really removed when they're # still in the normal dirstate. Likewise, normal - # files are not really removed if it's still in + # files are not really removed if they are still in # lfdirstate. This happens in merges where files # change type. removed = [f for f in removed if f not in self.dirstate] result[2] = [f for f in result[2] if f not in lfdirstate] + lfiles = set(lfdirstate._map) # Unknown files - unknown = set(unknown).difference(ignored) - result[4] = [f for f in unknown - if (self.dirstate[f] == '?' and - not lfutil.isstandin(f))] - # Ignored files were calculated earlier by the dirstate, - # and we already stripped out the largefiles from the list - result[5] = ignored + result[4] = set(result[4]).difference(lfiles) + # Ignored files + result[5] = set(result[5]).difference(lfiles) # combine normal files and largefiles normals = [[fn for fn in filelist if not lfutil.isstandin(fn)] @@ -361,7 +339,7 @@ # Case 2: user calls commit with specified patterns: refresh # any matching big files. smatcher = lfutil.composestandinmatcher(self, match) - standins = lfutil.dirstatewalk(self.dirstate, smatcher) + standins = self.dirstate.walk(smatcher, [], False, False) # No matching big files: get out of the way and pass control to # the usual commit() method. @@ -377,7 +355,7 @@ lfdirstate = lfutil.openlfdirstate(ui, self) for standin in standins: lfile = lfutil.splitstandin(standin) - if lfdirstate[lfile] <> 'r': + if lfdirstate[lfile] != 'r': lfutil.updatestandin(self, standin) lfdirstate.normal(lfile) else: @@ -427,10 +405,11 @@ wlock.release() def push(self, remote, force=False, revs=None, newbranch=False): - o = lfutil.findoutgoing(self, remote, force) - if o: + outgoing = discovery.findcommonoutgoing(repo, remote.peer(), + force=force) + if outgoing.missing: toupload = set() - o = self.changelog.nodesbetween(o, revs)[0] + o = self.changelog.nodesbetween(outgoing.missing, revs)[0] for n in o: parents = [p for p in self.changelog.parents(n) if p != node_.nullid]
--- a/hgext/largefiles/uisetup.py Mon Jan 14 23:14:45 2013 +0900 +++ b/hgext/largefiles/uisetup.py Sat Jan 19 17:24:33 2013 -0600 @@ -9,9 +9,9 @@ '''setup for largefiles extension: uisetup''' from mercurial import archival, cmdutil, commands, extensions, filemerge, hg, \ - httppeer, localrepo, merge, scmutil, sshpeer, sshserver, wireproto + httppeer, localrepo, merge, scmutil, sshpeer, wireproto from mercurial.i18n import _ -from mercurial.hgweb import hgweb_mod, protocol, webcommands +from mercurial.hgweb import hgweb_mod, webcommands from mercurial.subrepo import hgsubrepo import overrides @@ -59,6 +59,11 @@ _('verify largefile contents not just existence'))] entry[1].extend(verifyopt) + entry = extensions.wrapcommand(commands.table, 'debugstate', + overrides.overridedebugstate) + debugstateopt = [('', 'large', None, _('display largefiles dirstate'))] + entry[1].extend(debugstateopt) + entry = extensions.wrapcommand(commands.table, 'outgoing', overrides.overrideoutgoing) outgoingopt = [('', 'large', None, _('display outgoing largefiles'))] @@ -139,11 +144,6 @@ proto.capabilitiesorig = wireproto.capabilities wireproto.capabilities = proto.capabilities - # these let us reject non-largefiles clients and make them display - # our error messages - protocol.webproto.refuseclient = proto.webprotorefuseclient - sshserver.sshserver.refuseclient = proto.sshprotorefuseclient - # can't do this in reposetup because it needs to have happened before # wirerepo.__init__ is called proto.ssholdcallstream = sshpeer.sshpeer._callstream
--- a/hgext/mq.py Mon Jan 14 23:14:45 2013 +0900 +++ b/hgext/mq.py Sat Jan 19 17:24:33 2013 -0600 @@ -63,7 +63,7 @@ from mercurial.node import bin, hex, short, nullid, nullrev from mercurial.lock import release from mercurial import commands, cmdutil, hg, scmutil, util, revset -from mercurial import repair, extensions, error, phases, bookmarks +from mercurial import repair, extensions, error, phases from mercurial import patch as patchmod import os, re, errno, shutil @@ -275,6 +275,7 @@ It should be used instead of repo.commit inside the mq source for operation creating new changeset. """ + repo = repo.unfiltered() if phase is None: if repo.ui.configbool('mq', 'secret', False): phase = phases.secret @@ -826,7 +827,11 @@ if r: r[None].forget(patches) for p in patches: - os.unlink(self.join(p)) + try: + os.unlink(self.join(p)) + except OSError, inst: + if inst.errno != errno.ENOENT: + raise qfinished = [] if numrevs: @@ -924,11 +929,11 @@ self._cleanup(realpatches, numrevs, opts.get('keep')) def checktoppatch(self, repo): + '''check that working directory is at qtip''' if self.applied: top = self.applied[-1].node patch = self.applied[-1].name - pp = repo.dirstate.parents() - if top not in pp: + if repo.dirstate.p1() != top: raise util.Abort(_("working directory revision is not qtip")) return top, patch return None, None @@ -942,7 +947,7 @@ bctx = repo[baserev] else: bctx = wctx.parents()[0] - for s in wctx.substate: + for s in sorted(wctx.substate): if wctx.sub(s).dirty(True): raise util.Abort( _("uncommitted changes in subrepository %s") % s) @@ -1146,7 +1151,7 @@ return matches[0] if self.series and self.applied: if s == 'qtip': - return self.series[self.seriesend(True)-1] + return self.series[self.seriesend(True) - 1] if s == 'qbase': return self.series[0] return None @@ -1324,11 +1329,7 @@ # created while patching for f in all_files: if f not in repo.dirstate: - try: - util.unlinkpath(repo.wjoin(f)) - except OSError, inst: - if inst.errno != errno.ENOENT: - raise + util.unlinkpath(repo.wjoin(f), ignoremissing=True) self.ui.warn(_('done\n')) raise @@ -1405,8 +1406,6 @@ self.applieddirty = True end = len(self.applied) rev = self.applied[start].node - if update: - top = self.checktoppatch(repo)[0] try: heads = repo.changelog.heads(rev) @@ -1427,7 +1426,7 @@ if update: qp = self.qparents(repo, rev) ctx = repo[qp] - m, a, r, d = repo.status(qp, top)[:4] + m, a, r, d = repo.status(qp, '.')[:4] if d: raise util.Abort(_("deletions found between repo revs")) @@ -1437,11 +1436,7 @@ self.backup(repo, tobackup) for f in a: - try: - util.unlinkpath(repo.wjoin(f)) - except OSError, e: - if e.errno != errno.ENOENT: - raise + util.unlinkpath(repo.wjoin(f), ignoremissing=True) repo.dirstate.drop(f) for f in m + r: fctx = ctx[f] @@ -1625,7 +1620,7 @@ # if the patch excludes a modified file, mark that # file with mtime=0 so status can see it. mm = [] - for i in xrange(len(m)-1, -1, -1): + for i in xrange(len(m) - 1, -1, -1): if not matchfn(m[i]): mm.append(m[i]) del m[i] @@ -1675,9 +1670,10 @@ patchf.write(chunk) patchf.close() + marks = repo._bookmarks for bm in bmlist: - repo._bookmarks[bm] = n - bookmarks.write(repo) + marks[bm] = n + marks.write() self.applied.append(statusentry(n, patchfn)) except: # re-raises @@ -2999,7 +2995,7 @@ revs.update(set(rsrevs)) if not revs: del marks[mark] - repo._writebookmarks(mark) + marks.write() ui.write(_("bookmark '%s' deleted\n") % mark) if not revs: @@ -3036,7 +3032,7 @@ del q.applied[start:end] q.savedirty() - revs = list(rootnodes) + revs = sorted(rootnodes) if update and opts.get('keep'): wlock = repo.wlock() try: @@ -3049,7 +3045,7 @@ if opts.get('bookmark'): del marks[mark] - repo._writebookmarks(marks) + marks.write() ui.write(_("bookmark '%s' deleted\n") % mark) repo.mq.strip(repo, revs, backup=backup, update=update, @@ -3435,7 +3431,7 @@ outapplied.pop() # looking for pushed and shared changeset for node in outapplied: - if repo[node].phase() < phases.secret: + if self[node].phase() < phases.secret: raise util.Abort(_('source has mq patches applied')) # no non-secret patches pushed super(mqrepo, self).checkpush(force, revs) @@ -3451,7 +3447,8 @@ mqtags = [(patch.node, patch.name) for patch in q.applied] try: - self.changelog.rev(mqtags[-1][0]) + # for now ignore filtering business + self.unfiltered().changelog.rev(mqtags[-1][0]) except error.LookupError: self.ui.warn(_('mq status file refers to unknown node %s\n') % short(mqtags[-1][0])) @@ -3470,41 +3467,6 @@ return result - def _branchtags(self, partial, lrev): - q = self.mq - cl = self.changelog - qbase = None - if not q.applied: - if getattr(self, '_committingpatch', False): - # Committing a new patch, must be tip - qbase = len(cl) - 1 - else: - qbasenode = q.applied[0].node - try: - qbase = cl.rev(qbasenode) - except error.LookupError: - self.ui.warn(_('mq status file refers to unknown node %s\n') - % short(qbasenode)) - if qbase is None: - return super(mqrepo, self)._branchtags(partial, lrev) - - start = lrev + 1 - if start < qbase: - # update the cache (excluding the patches) and save it - ctxgen = (self[r] for r in xrange(lrev + 1, qbase)) - self._updatebranchcache(partial, ctxgen) - self._writebranchcache(partial, cl.node(qbase - 1), qbase - 1) - start = qbase - # if start = qbase, the cache is as updated as it should be. - # if start > qbase, the cache includes (part of) the patches. - # we might as well use it, but we won't save it. - - # update the cache up to the tip - ctxgen = (self[r] for r in xrange(start, len(cl))) - self._updatebranchcache(partial, ctxgen) - - return partial - if repo.local(): repo.__class__ = mqrepo
--- a/hgext/patchbomb.py Mon Jan 14 23:14:45 2013 +0900 +++ b/hgext/patchbomb.py Sat Jan 19 17:24:33 2013 -0600 @@ -474,11 +474,11 @@ if opts.get('diffstat') or opts.get('confirm'): ui.write(_('\nFinal summary:\n\n')) - ui.write('From: %s\n' % sender) + ui.write(('From: %s\n' % sender)) for addr in showaddrs: ui.write('%s\n' % addr) for m, subj, ds in msgs: - ui.write('Subject: %s\n' % subj) + ui.write(('Subject: %s\n' % subj)) if ds: ui.write(ds) ui.write('\n')
--- a/hgext/rebase.py Mon Jan 14 23:14:45 2013 +0900 +++ b/hgext/rebase.py Sat Jan 19 17:24:33 2013 -0600 @@ -23,6 +23,7 @@ import os, errno nullmerge = -2 +revignored = -3 cmdtable = {} command = cmdutil.command(cmdtable) @@ -184,8 +185,6 @@ rebaseset = repo.revs( '(children(ancestor(%ld, %d)) and ::(%ld))::', base, dest, base) - # temporary top level filtering of extinct revisions - rebaseset = repo.revs('%ld - hidden()', rebaseset) if rebaseset: root = min(rebaseset) else: @@ -194,8 +193,9 @@ if not rebaseset: repo.ui.debug('base is ancestor of destination\n') result = None - elif not keepf and repo.revs('first(children(%ld) - %ld)-hidden()', - rebaseset, rebaseset): + elif (not (keepf or obsolete._enabled) + and repo.revs('first(children(%ld) - %ld)', + rebaseset, rebaseset)): raise util.Abort( _("can't remove original changesets with" " unrebased descendants"), @@ -214,8 +214,8 @@ else: originalwd, target, state = result if collapsef: - targetancestors = set(repo.changelog.ancestors([target])) - targetancestors.add(target) + targetancestors = repo.changelog.ancestors([target], + inclusive=True) external = checkexternal(repo, state, targetancestors) if keepbranchesf: @@ -233,8 +233,7 @@ # Rebase if not targetancestors: - targetancestors = set(repo.changelog.ancestors([target])) - targetancestors.add(target) + targetancestors = repo.changelog.ancestors([target], inclusive=True) # Keep track of the current bookmarks in order to reset them later currentbookmarks = repo._bookmarks.copy() @@ -294,7 +293,7 @@ else: commitmsg = 'Collapsed revision' for rebased in state: - if rebased not in skipped and state[rebased] != nullmerge: + if rebased not in skipped and state[rebased] > nullmerge: commitmsg += '\n* %s' % repo[rebased].description() commitmsg = ui.edit(commitmsg, repo.ui.username()) newrev = concludenode(repo, rev, p1, external, commitmsg=commitmsg, @@ -307,22 +306,21 @@ # Nodeids are needed to reset bookmarks nstate = {} for k, v in state.iteritems(): - if v != nullmerge: + if v > nullmerge: nstate[repo[k].node()] = repo[v].node() if not keepf: collapsedas = None if collapsef: collapsedas = newrev - clearrebased(ui, repo, state, collapsedas) + clearrebased(ui, repo, state, skipped, collapsedas) if currentbookmarks: updatebookmarks(repo, nstate, currentbookmarks, **opts) clearstatus(repo) ui.note(_("rebase completed\n")) - if os.path.exists(repo.sjoin('undo')): - util.unlinkpath(repo.sjoin('undo')) + util.unlinkpath(repo.sjoin('undo'), ignoremissing=True) if skipped: ui.note(_("%d revisions have been skipped\n") % len(skipped)) @@ -395,6 +393,15 @@ # have to allow merging with it. return merge.update(repo, rev, True, True, False, base, collapse) +def nearestrebased(repo, rev, state): + """return the nearest ancestors of rev in the rebase result""" + rebased = [r for r in state if state[r] > nullmerge] + candidates = repo.revs('max(%ld and (::%d))', rebased, rev) + if candidates: + return state[candidates[0]] + else: + return None + def defineparents(repo, rev, target, state, targetancestors): 'Return the new parent relationship of the revision that will be rebased' parents = repo[rev].parents() @@ -406,6 +413,10 @@ elif P1n in state: if state[P1n] == nullmerge: p1 = target + elif state[P1n] == revignored: + p1 = nearestrebased(repo, P1n, state) + if p1 is None: + p1 = target else: p1 = state[P1n] else: # P1n external @@ -418,6 +429,11 @@ if P2n in state: if p1 == target: # P1n in targetancestors or external p1 = state[P2n] + elif state[P2n] == revignored: + p2 = nearestrebased(repo, P2n, state) + if p2 is None: + # no ancestors rebased yet, detach + p2 = target else: p2 = state[P2n] else: # P2n external @@ -479,13 +495,14 @@ def updatebookmarks(repo, nstate, originalbookmarks, **opts): 'Move bookmarks to their correct changesets' + marks = repo._bookmarks for k, v in originalbookmarks.iteritems(): if v in nstate: - if nstate[v] != nullmerge: + if nstate[v] > nullmerge: # update the bookmarks for revs that have moved - repo._bookmarks[k] = nstate[v] + marks[k] = nstate[v] - bookmarks.write(repo) + marks.write() def storestatus(repo, originalwd, target, state, collapse, keep, keepbranches, external): @@ -499,7 +516,7 @@ f.write('%d\n' % int(keepbranches)) for d, v in state.iteritems(): oldrev = repo[d].hex() - if v != nullmerge: + if v > nullmerge: newrev = repo[v].hex() else: newrev = v @@ -509,8 +526,7 @@ def clearstatus(repo): 'Remove the status files' - if os.path.exists(repo.join("rebasestate")): - util.unlinkpath(repo.join("rebasestate")) + util.unlinkpath(repo.join("rebasestate"), ignoremissing=True) def restorestatus(repo): 'Restore a previously stored status' @@ -535,10 +551,10 @@ keepbranches = bool(int(l)) else: oldrev, newrev = l.split(':') - if newrev != str(nullmerge): + if newrev in (str(nullmerge), str(revignored)): + state[repo[oldrev].rev()] = int(newrev) + else: state[repo[oldrev].rev()] = repo[newrev].rev() - else: - state[repo[oldrev].rev()] = int(newrev) skipped = set() # recompute the set of skipped revs if not collapse: @@ -577,9 +593,9 @@ merge.update(repo, repo[originalwd].rev(), False, True, False) rebased = filter(lambda x: x > -1 and x != target, state.values()) if rebased: - strippoint = min(rebased) + strippoints = [c.node() for c in repo.set('roots(%ld)', rebased)] # no backup of rebased cset versions needed - repair.strip(repo.ui, repo, repo[strippoint].node()) + repair.strip(repo.ui, repo, strippoints) clearstatus(repo) repo.ui.warn(_('rebase aborted\n')) return 0 @@ -602,65 +618,77 @@ roots = list(repo.set('roots(%ld)', rebaseset)) if not roots: raise util.Abort(_('no matching revisions')) - if len(roots) > 1: - raise util.Abort(_("can't rebase multiple roots")) - root = roots[0] - - commonbase = root.ancestor(dest) - if commonbase == root: - raise util.Abort(_('source is ancestor of destination')) - if commonbase == dest: - samebranch = root.branch() == dest.branch() - if not collapse and samebranch and root in dest.children(): - repo.ui.debug('source is a child of destination\n') - return None + roots.sort() + state = {} + detachset = set() + for root in roots: + commonbase = root.ancestor(dest) + if commonbase == root: + raise util.Abort(_('source is ancestor of destination')) + if commonbase == dest: + samebranch = root.branch() == dest.branch() + if not collapse and samebranch and root in dest.children(): + repo.ui.debug('source is a child of destination\n') + return None - repo.ui.debug('rebase onto %d starting from %d\n' % (dest, root)) - state = dict.fromkeys(rebaseset, nullrev) - # Rebase tries to turn <dest> into a parent of <root> while - # preserving the number of parents of rebased changesets: - # - # - A changeset with a single parent will always be rebased as a - # changeset with a single parent. - # - # - A merge will be rebased as merge unless its parents are both - # ancestors of <dest> or are themselves in the rebased set and - # pruned while rebased. - # - # If one parent of <root> is an ancestor of <dest>, the rebased - # version of this parent will be <dest>. This is always true with - # --base option. - # - # Otherwise, we need to *replace* the original parents with - # <dest>. This "detaches" the rebased set from its former location - # and rebases it onto <dest>. Changes introduced by ancestors of - # <root> not common with <dest> (the detachset, marked as - # nullmerge) are "removed" from the rebased changesets. - # - # - If <root> has a single parent, set it to <dest>. - # - # - If <root> is a merge, we cannot decide which parent to - # replace, the rebase operation is not clearly defined. - # - # The table below sums up this behavior: - # - # +--------------------+----------------------+-------------------------+ - # | | one parent | merge | - # +--------------------+----------------------+-------------------------+ - # | parent in ::<dest> | new parent is <dest> | parents in ::<dest> are | - # | | | remapped to <dest> | - # +--------------------+----------------------+-------------------------+ - # | unrelated source | new parent is <dest> | ambiguous, abort | - # +--------------------+----------------------+-------------------------+ - # - # The actual abort is handled by `defineparents` - if len(root.parents()) <= 1: - # (strict) ancestors of <root> not ancestors of <dest> - detachset = repo.revs('::%d - ::%d - %d', root, commonbase, root) - state.update(dict.fromkeys(detachset, nullmerge)) + repo.ui.debug('rebase onto %d starting from %s\n' % (dest, roots)) + state.update(dict.fromkeys(rebaseset, nullrev)) + # Rebase tries to turn <dest> into a parent of <root> while + # preserving the number of parents of rebased changesets: + # + # - A changeset with a single parent will always be rebased as a + # changeset with a single parent. + # + # - A merge will be rebased as merge unless its parents are both + # ancestors of <dest> or are themselves in the rebased set and + # pruned while rebased. + # + # If one parent of <root> is an ancestor of <dest>, the rebased + # version of this parent will be <dest>. This is always true with + # --base option. + # + # Otherwise, we need to *replace* the original parents with + # <dest>. This "detaches" the rebased set from its former location + # and rebases it onto <dest>. Changes introduced by ancestors of + # <root> not common with <dest> (the detachset, marked as + # nullmerge) are "removed" from the rebased changesets. + # + # - If <root> has a single parent, set it to <dest>. + # + # - If <root> is a merge, we cannot decide which parent to + # replace, the rebase operation is not clearly defined. + # + # The table below sums up this behavior: + # + # +------------------+----------------------+-------------------------+ + # | | one parent | merge | + # +------------------+----------------------+-------------------------+ + # | parent in | new parent is <dest> | parents in ::<dest> are | + # | ::<dest> | | remapped to <dest> | + # +------------------+----------------------+-------------------------+ + # | unrelated source | new parent is <dest> | ambiguous, abort | + # +------------------+----------------------+-------------------------+ + # + # The actual abort is handled by `defineparents` + if len(root.parents()) <= 1: + # ancestors of <root> not ancestors of <dest> + detachset.update(repo.changelog.findmissingrevs([commonbase.rev()], + [root.rev()])) + for r in detachset: + if r not in state: + state[r] = nullmerge + if len(roots) > 1: + # If we have multiple roots, we may have "hole" in the rebase set. + # Rebase roots that descend from those "hole" should not be detached as + # other root are. We use the special `revignored` to inform rebase that + # the revision should be ignored but that `defineparent` should search + # a rebase destination that make sense regarding rebaset topology. + rebasedomain = set(repo.revs('%ld::%ld', rebaseset, rebaseset)) + for ignored in set(rebasedomain) - set(rebaseset): + state[ignored] = revignored return repo['.'].rev(), dest.rev(), state -def clearrebased(ui, repo, state, collapsedas=None): +def clearrebased(ui, repo, state, skipped, collapsedas=None): """dispose of rebased revision at the end of the rebase If `collapsedas` is not None, the rebase was a collapse whose result if the @@ -669,20 +697,28 @@ markers = [] for rev, newrev in sorted(state.items()): if newrev >= 0: - if collapsedas is not None: - newrev = collapsedas - markers.append((repo[rev], (repo[newrev],))) + if rev in skipped: + succs = () + elif collapsedas is not None: + succs = (repo[collapsedas],) + else: + succs = (repo[newrev],) + markers.append((repo[rev], succs)) if markers: obsolete.createmarkers(repo, markers) else: - rebased = [rev for rev in state if state[rev] != nullmerge] + rebased = [rev for rev in state if state[rev] > nullmerge] if rebased: - if set(repo.changelog.descendants([min(rebased)])) - set(state): - ui.warn(_("warning: new changesets detected " - "on source branch, not stripping\n")) - else: + stripped = [] + for root in repo.set('roots(%ld)', rebased): + if set(repo.changelog.descendants([root.rev()])) - set(state): + ui.warn(_("warning: new changesets detected " + "on source branch, not stripping\n")) + else: + stripped.append(root.node()) + if stripped: # backup the old csets by default - repair.strip(ui, repo, repo[min(rebased)].node(), "all") + repair.strip(ui, repo, stripped, "all") def pullrebase(orig, ui, repo, *args, **opts):
--- a/hgext/record.py Mon Jan 14 23:14:45 2013 +0900 +++ b/hgext/record.py Sat Jan 19 17:24:33 2013 -0600 @@ -8,7 +8,7 @@ '''commands to interactively select changes for commit/qrefresh''' from mercurial.i18n import gettext, _ -from mercurial import cmdutil, commands, extensions, hg, mdiff, patch +from mercurial import cmdutil, commands, extensions, hg, patch from mercurial import util import copy, cStringIO, errno, os, re, shutil, tempfile @@ -520,11 +520,11 @@ '(use "hg commit" instead)')) changes = repo.status(match=match)[:3] - diffopts = mdiff.diffopts( + diffopts = patch.diffopts(ui, opts=dict( git=True, nodates=True, ignorews=opts.get('ignore_all_space'), ignorewsamount=opts.get('ignore_space_change'), - ignoreblanklines=opts.get('ignore_blank_lines')) + ignoreblanklines=opts.get('ignore_blank_lines'))) chunks = patch.diff(repo, changes=changes, opts=diffopts) fp = cStringIO.StringIO() fp.write(''.join(chunks))
--- a/hgext/transplant.py Mon Jan 14 23:14:45 2013 +0900 +++ b/hgext/transplant.py Sat Jan 19 17:24:33 2013 -0600 @@ -94,7 +94,8 @@ parentrev = repo.changelog.rev(parent) if hasnode(repo, node): rev = repo.changelog.rev(node) - reachable = repo.changelog.incancestors([parentrev], rev) + reachable = repo.changelog.ancestors([parentrev], rev, + inclusive=True) if rev in reachable: return True for t in self.transplants.get(node): @@ -103,7 +104,8 @@ self.transplants.remove(t) return False lnoderev = repo.changelog.rev(t.lnode) - if lnoderev in repo.changelog.incancestors([parentrev], lnoderev): + if lnoderev in repo.changelog.ancestors([parentrev], lnoderev, + inclusive=True): return True return False
--- a/hgext/win32text.py Mon Jan 14 23:14:45 2013 +0900 +++ b/hgext/win32text.py Sat Jan 19 17:24:33 2013 -0600 @@ -121,7 +121,7 @@ # changegroup that contains an unacceptable commit followed later # by a commit that fixes the problem. tip = repo['tip'] - for rev in xrange(len(repo)-1, repo[node].rev()-1, -1): + for rev in xrange(len(repo) - 1, repo[node].rev() - 1, -1): c = repo[rev] for f in c.files(): if f in seen or f not in tip or f not in c:
--- a/mercurial/ancestor.py Mon Jan 14 23:14:45 2013 +0900 +++ b/mercurial/ancestor.py Sat Jan 19 17:24:33 2013 -0600 @@ -5,7 +5,8 @@ # This software may be used and distributed according to the terms of the # GNU General Public License version 2 or any later version. -import heapq +import heapq, util +from node import nullrev def ancestor(a, b, pfunc): """ @@ -89,3 +90,175 @@ gx = x.next() except StopIteration: return None + +def missingancestors(revs, bases, pfunc): + """Return all the ancestors of revs that are not ancestors of bases. + + This may include elements from revs. + + Equivalent to the revset (::revs - ::bases). Revs are returned in + revision number order, which is a topological order. + + revs and bases should both be iterables. pfunc must return a list of + parent revs for a given revs. + """ + + revsvisit = set(revs) + basesvisit = set(bases) + if not revsvisit: + return [] + if not basesvisit: + basesvisit.add(nullrev) + start = max(max(revsvisit), max(basesvisit)) + bothvisit = revsvisit.intersection(basesvisit) + revsvisit.difference_update(bothvisit) + basesvisit.difference_update(bothvisit) + # At this point, we hold the invariants that: + # - revsvisit is the set of nodes we know are an ancestor of at least one + # of the nodes in revs + # - basesvisit is the same for bases + # - bothvisit is the set of nodes we know are ancestors of at least one of + # the nodes in revs and one of the nodes in bases + # - a node may be in none or one, but not more, of revsvisit, basesvisit + # and bothvisit at any given time + # Now we walk down in reverse topo order, adding parents of nodes already + # visited to the sets while maintaining the invariants. When a node is + # found in both revsvisit and basesvisit, it is removed from them and + # added to bothvisit instead. When revsvisit becomes empty, there are no + # more ancestors of revs that aren't also ancestors of bases, so exit. + + missing = [] + for curr in xrange(start, nullrev, -1): + if not revsvisit: + break + + if curr in bothvisit: + bothvisit.remove(curr) + # curr's parents might have made it into revsvisit or basesvisit + # through another path + for p in pfunc(curr): + revsvisit.discard(p) + basesvisit.discard(p) + bothvisit.add(p) + continue + + # curr will never be in both revsvisit and basesvisit, since if it + # were it'd have been pushed to bothvisit + if curr in revsvisit: + missing.append(curr) + thisvisit = revsvisit + othervisit = basesvisit + elif curr in basesvisit: + thisvisit = basesvisit + othervisit = revsvisit + else: + # not an ancestor of revs or bases: ignore + continue + + thisvisit.remove(curr) + for p in pfunc(curr): + if p == nullrev: + pass + elif p in othervisit or p in bothvisit: + # p is implicitly in thisvisit. This means p is or should be + # in bothvisit + revsvisit.discard(p) + basesvisit.discard(p) + bothvisit.add(p) + else: + # visit later + thisvisit.add(p) + + missing.reverse() + return missing + +class lazyancestors(object): + def __init__(self, cl, revs, stoprev=0, inclusive=False): + """Create a new object generating ancestors for the given revs. Does + not generate revs lower than stoprev. + + This is computed lazily starting from revs. The object supports + iteration and membership. + + cl should be a changelog and revs should be an iterable. inclusive is + a boolean that indicates whether revs should be included. Revs lower + than stoprev will not be generated. + + Result does not include the null revision.""" + self._parentrevs = cl.parentrevs + self._initrevs = revs + self._stoprev = stoprev + self._inclusive = inclusive + + # Initialize data structures for __contains__. + # For __contains__, we use a heap rather than a deque because + # (a) it minimizes the number of parentrevs calls made + # (b) it makes the loop termination condition obvious + # Python's heap is a min-heap. Multiply all values by -1 to convert it + # into a max-heap. + self._containsvisit = [-rev for rev in revs] + heapq.heapify(self._containsvisit) + if inclusive: + self._containsseen = set(revs) + else: + self._containsseen = set() + + def __iter__(self): + """Generate the ancestors of _initrevs in reverse topological order. + + If inclusive is False, yield a sequence of revision numbers starting + with the parents of each revision in revs, i.e., each revision is *not* + considered an ancestor of itself. Results are in breadth-first order: + parents of each rev in revs, then parents of those, etc. + + If inclusive is True, yield all the revs first (ignoring stoprev), + then yield all the ancestors of revs as when inclusive is False. + If an element in revs is an ancestor of a different rev it is not + yielded again.""" + seen = set() + revs = self._initrevs + if self._inclusive: + for rev in revs: + yield rev + seen.update(revs) + + parentrevs = self._parentrevs + stoprev = self._stoprev + visit = util.deque(revs) + + while visit: + for parent in parentrevs(visit.popleft()): + if parent >= stoprev and parent not in seen: + visit.append(parent) + seen.add(parent) + yield parent + + def __contains__(self, target): + """Test whether target is an ancestor of self._initrevs.""" + # Trying to do both __iter__ and __contains__ using the same visit + # heap and seen set is complex enough that it slows down both. Keep + # them separate. + seen = self._containsseen + if target in seen: + return True + + parentrevs = self._parentrevs + visit = self._containsvisit + stoprev = self._stoprev + heappop = heapq.heappop + heappush = heapq.heappush + + targetseen = False + + while visit and -visit[0] > target and not targetseen: + for parent in parentrevs(-heappop(visit)): + if parent < stoprev or parent in seen: + continue + # We need to make sure we push all parents into the heap so + # that we leave it in a consistent state for future calls. + heappush(visit, -parent) + seen.add(parent) + if parent == target: + targetseen = True + + return targetseen
--- a/mercurial/archival.py Mon Jan 14 23:14:45 2013 +0900 +++ b/mercurial/archival.py Sat Jan 19 17:24:33 2013 -0600 @@ -74,8 +74,11 @@ def _write_gzip_header(self): self.fileobj.write('\037\213') # magic header self.fileobj.write('\010') # compression method - # Python 2.6 deprecates self.filename - fname = getattr(self, 'name', None) or self.filename + # Python 2.6 introduced self.name and deprecated self.filename + try: + fname = self.name + except AttributeError: + fname = self.filename if fname and fname.endswith('.gz'): fname = fname[:-3] flags = 0 @@ -103,7 +106,6 @@ self.fileobj = gzfileobj return tarfile.TarFile.taropen(name, mode, gzfileobj) else: - self.fileobj = fileobj return tarfile.open(name, mode + kind, fileobj) if isinstance(dest, str): @@ -191,7 +193,7 @@ 0x5455, # block type: "extended-timestamp" 1 + 4, # size of this block 1, # "modification time is present" - self.mtime) # time of last modification (UTC) + int(self.mtime)) # last modification (UTC) self.z.writestr(i, data) def done(self): @@ -297,7 +299,7 @@ repo.ui.progress(_('archiving'), None) if subrepos: - for subpath in ctx.substate: + for subpath in sorted(ctx.substate): sub = ctx.sub(subpath) submatch = matchmod.narrowmatcher(subpath, matchfn) sub.archive(repo.ui, archiver, prefix, submatch)
--- a/mercurial/bookmarks.py Mon Jan 14 23:14:45 2013 +0900 +++ b/mercurial/bookmarks.py Sat Jan 19 17:24:33 2013 -0600 @@ -7,40 +7,80 @@ from mercurial.i18n import _ from mercurial.node import hex -from mercurial import encoding, error, util, obsolete, phases +from mercurial import encoding, error, util, obsolete import errno, os -def read(repo): - '''Parse .hg/bookmarks file and return a dictionary +class bmstore(dict): + """Storage for bookmarks. + + This object should do all bookmark reads and writes, so that it's + fairly simple to replace the storage underlying bookmarks without + having to clone the logic surrounding bookmarks. + + This particular bmstore implementation stores bookmarks as + {hash}\s{name}\n (the same format as localtags) in + .hg/bookmarks. The mapping is stored as {name: nodeid}. + + This class does NOT handle the "current" bookmark state at this + time. + """ - Bookmarks are stored as {HASH}\\s{NAME}\\n (localtags format) values - in the .hg/bookmarks file. - Read the file and return a (name=>nodeid) dictionary - ''' - bookmarks = {} - try: - for line in repo.opener('bookmarks'): - line = line.strip() - if not line: - continue - if ' ' not in line: - repo.ui.warn(_('malformed line in .hg/bookmarks: %r\n') % line) - continue - sha, refspec = line.split(' ', 1) - refspec = encoding.tolocal(refspec) + def __init__(self, repo): + dict.__init__(self) + self._repo = repo + try: + for line in repo.vfs('bookmarks'): + line = line.strip() + if not line: + continue + if ' ' not in line: + repo.ui.warn(_('malformed line in .hg/bookmarks: %r\n') + % line) + continue + sha, refspec = line.split(' ', 1) + refspec = encoding.tolocal(refspec) + try: + self[refspec] = repo.changelog.lookup(sha) + except LookupError: + pass + except IOError, inst: + if inst.errno != errno.ENOENT: + raise + + def write(self): + '''Write bookmarks + + Write the given bookmark => hash dictionary to the .hg/bookmarks file + in a format equal to those of localtags. + + We also store a backup of the previous state in undo.bookmarks that + can be copied back on rollback. + ''' + repo = self._repo + if repo._bookmarkcurrent not in self: + setcurrent(repo, None) + + wlock = repo.wlock() + try: + + file = repo.vfs('bookmarks', 'w', atomictemp=True) + for name, node in self.iteritems(): + file.write("%s %s\n" % (hex(node), encoding.fromlocal(name))) + file.close() + + # touch 00changelog.i so hgweb reloads bookmarks (no lock needed) try: - bookmarks[refspec] = repo.changelog.lookup(sha) - except LookupError: + os.utime(repo.sjoin('00changelog.i'), None) + except OSError: pass - except IOError, inst: - if inst.errno != errno.ENOENT: - raise - return bookmarks + + finally: + wlock.release() def readcurrent(repo): '''Get the current bookmark - If we use gittishsh branches we have a current bookmark that + If we use gittish branches we have a current bookmark that we are on. This function returns the name of the bookmark. It is stored in .hg/bookmarks.current ''' @@ -60,37 +100,6 @@ file.close() return mark -def write(repo): - '''Write bookmarks - - Write the given bookmark => hash dictionary to the .hg/bookmarks file - in a format equal to those of localtags. - - We also store a backup of the previous state in undo.bookmarks that - can be copied back on rollback. - ''' - refs = repo._bookmarks - - if repo._bookmarkcurrent not in refs: - setcurrent(repo, None) - - wlock = repo.wlock() - try: - - file = repo.opener('bookmarks', 'w', atomictemp=True) - for refspec, node in refs.iteritems(): - file.write("%s %s\n" % (hex(node), encoding.fromlocal(refspec))) - file.close() - - # touch 00changelog.i so hgweb reloads bookmarks (no lock needed) - try: - os.utime(repo.sjoin('00changelog.i'), None) - except OSError: - pass - - finally: - wlock.release() - def setcurrent(repo, mark): '''Set the name of the bookmark that we are currently on @@ -152,7 +161,7 @@ if mark != cur: del marks[mark] if update: - repo._writebookmarks(marks) + marks.write() return update def listbookmarks(repo): @@ -179,7 +188,7 @@ if new not in repo: return False marks[key] = repo[new].node() - write(repo) + marks.write() return True finally: w.release() @@ -188,16 +197,17 @@ ui.debug("checking for updated bookmarks\n") rb = remote.listkeys('bookmarks') changed = False - for k in rb.keys(): - if k in repo._bookmarks: - nr, nl = rb[k], repo._bookmarks[k] + localmarks = repo._bookmarks + for k in sorted(rb): + if k in localmarks: + nr, nl = rb[k], localmarks[k] if nr in repo: cr = repo[nr] cl = repo[nl] if cl.rev() >= cr.rev(): continue if validdest(repo, cl, cr): - repo._bookmarks[k] = cr.node() + localmarks[k] = cr.node() changed = True ui.status(_("updating bookmark %s\n") % k) else: @@ -208,7 +218,7 @@ # find a unique @ suffix for x in range(1, 100): n = '%s@%d' % (kd, x) - if n not in repo._bookmarks: + if n not in localmarks: break # try to use an @pathalias suffix # if an @pathalias already exists, we overwrite (update) it @@ -216,17 +226,17 @@ if path == u: n = '%s@%s' % (kd, p) - repo._bookmarks[n] = cr.node() + localmarks[n] = cr.node() changed = True ui.warn(_("divergent bookmark %s stored as %s\n") % (k, n)) elif rb[k] in repo: # add remote bookmarks for changes we already have - repo._bookmarks[k] = repo[rb[k]].node() + localmarks[k] = repo[rb[k]].node() changed = True ui.status(_("adding remote bookmark %s\n") % k) if changed: - write(repo) + localmarks.write() def diff(ui, dst, src): ui.status(_("searching for changed bookmarks\n")) @@ -246,6 +256,7 @@ def validdest(repo, old, new): """Is the new bookmark destination a valid update from the old one""" + repo = repo.unfiltered() if old == new: # Old == new -> nothing to update. return False @@ -263,14 +274,10 @@ while len(validdests) != plen: plen = len(validdests) succs = set(c.node() for c in validdests) - for c in validdests: - if c.phase() > phases.public: - # obsolescence marker does not apply to public changeset - succs.update(obsolete.allsuccessors(repo.obsstore, - [c.node()])) + mutable = [c.node() for c in validdests if c.mutable()] + succs.update(obsolete.allsuccessors(repo.obsstore, mutable)) known = (n for n in succs if n in nm) validdests = set(repo.set('%ln::', known)) - validdests.remove(old) return new in validdests else: return old.descendant(new)
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/mercurial/branchmap.py Sat Jan 19 17:24:33 2013 -0600 @@ -0,0 +1,223 @@ +# branchmap.py - logic to computes, maintain and stores branchmap for local repo +# +# Copyright 2005-2007 Matt Mackall <mpm@selenic.com> +# +# This software may be used and distributed according to the terms of the +# GNU General Public License version 2 or any later version. + +from node import bin, hex, nullid, nullrev +import encoding +import util, repoview + +def _filename(repo): + """name of a branchcache file for a given repo or repoview""" + filename = "cache/branchheads" + if repo.filtername: + filename = '%s-%s' % (filename, repo.filtername) + return filename + +def read(repo): + try: + f = repo.opener(_filename(repo)) + lines = f.read().split('\n') + f.close() + except (IOError, OSError): + return None + + try: + cachekey = lines.pop(0).split(" ", 2) + last, lrev = cachekey[:2] + last, lrev = bin(last), int(lrev) + filteredhash = None + if len(cachekey) > 2: + filteredhash = bin(cachekey[2]) + partial = branchcache(tipnode=last, tiprev=lrev, + filteredhash=filteredhash) + if not partial.validfor(repo): + # invalidate the cache + raise ValueError('tip differs') + for l in lines: + if not l: + continue + node, label = l.split(" ", 1) + label = encoding.tolocal(label.strip()) + if not node in repo: + raise ValueError('node %s does not exist' % node) + partial.setdefault(label, []).append(bin(node)) + except KeyboardInterrupt: + raise + except Exception, inst: + if repo.ui.debugflag: + msg = 'invalid branchheads cache' + if repo.filtername is not None: + msg += ' (%s)' % repo.filtername + msg += ': %s\n' + repo.ui.warn(msg % inst) + partial = None + return partial + + + +def updatecache(repo): + cl = repo.changelog + filtername = repo.filtername + partial = repo._branchcaches.get(filtername) + + revs = [] + if partial is None or not partial.validfor(repo): + partial = read(repo) + if partial is None: + subsetname = repoview.subsettable.get(filtername) + if subsetname is None: + partial = branchcache() + else: + subset = repo.filtered(subsetname) + partial = subset.branchmap().copy() + extrarevs = subset.changelog.filteredrevs - cl.filteredrevs + revs.extend(r for r in extrarevs if r <= partial.tiprev) + revs.extend(cl.revs(start=partial.tiprev + 1)) + if revs: + partial.update(repo, revs) + partial.write(repo) + assert partial.validfor(repo), filtername + repo._branchcaches[repo.filtername] = partial + +class branchcache(dict): + """A dict like object that hold branches heads cache""" + + def __init__(self, entries=(), tipnode=nullid, tiprev=nullrev, + filteredhash=None): + super(branchcache, self).__init__(entries) + self.tipnode = tipnode + self.tiprev = tiprev + self.filteredhash = filteredhash + + def _hashfiltered(self, repo): + """build hash of revision filtered in the current cache + + Tracking tipnode and tiprev is not enough to ensure validaty of the + cache as they do not help to distinct cache that ignored various + revision bellow tiprev. + + To detect such difference, we build a cache of all ignored revisions. + """ + cl = repo.changelog + if not cl.filteredrevs: + return None + key = None + revs = sorted(r for r in cl.filteredrevs if r <= self.tiprev) + if revs: + s = util.sha1() + for rev in revs: + s.update('%s;' % rev) + key = s.digest() + return key + + def validfor(self, repo): + """Is the cache content valide regarding a repo + + - False when cached tipnode are unknown or if we detect a strip. + - True when cache is up to date or a subset of current repo.""" + try: + return ((self.tipnode == repo.changelog.node(self.tiprev)) + and (self.filteredhash == self._hashfiltered(repo))) + except IndexError: + return False + + def copy(self): + """return an deep copy of the branchcache object""" + return branchcache(self, self.tipnode, self.tiprev, self.filteredhash) + + def write(self, repo): + try: + f = repo.opener(_filename(repo), "w", atomictemp=True) + cachekey = [hex(self.tipnode), str(self.tiprev)] + if self.filteredhash is not None: + cachekey.append(hex(self.filteredhash)) + f.write(" ".join(cachekey) + '\n') + for label, nodes in sorted(self.iteritems()): + for node in nodes: + f.write("%s %s\n" % (hex(node), encoding.fromlocal(label))) + f.close() + except (IOError, OSError, util.Abort): + # Abort may be raise by read only opener + pass + + def update(self, repo, revgen): + """Given a branchhead cache, self, that may have extra nodes or be + missing heads, and a generator of nodes that are at least a superset of + heads missing, this function updates self to be correct. + """ + cl = repo.changelog + # collect new branch entries + newbranches = {} + getbranch = cl.branch + for r in revgen: + newbranches.setdefault(getbranch(r), []).append(cl.node(r)) + # if older branchheads are reachable from new ones, they aren't + # really branchheads. Note checking parents is insufficient: + # 1 (branch a) -> 2 (branch b) -> 3 (branch a) + for branch, newnodes in newbranches.iteritems(): + bheads = self.setdefault(branch, []) + # Remove candidate heads that no longer are in the repo (e.g., as + # the result of a strip that just happened). Avoid using 'node in + # self' here because that dives down into branchcache code somewhat + # recursively. + bheadrevs = [cl.rev(node) for node in bheads + if cl.hasnode(node)] + newheadrevs = [cl.rev(node) for node in newnodes + if cl.hasnode(node)] + ctxisnew = bheadrevs and min(newheadrevs) > max(bheadrevs) + # Remove duplicates - nodes that are in newheadrevs and are already + # in bheadrevs. This can happen if you strip a node whose parent + # was already a head (because they're on different branches). + bheadrevs = sorted(set(bheadrevs).union(newheadrevs)) + + # Starting from tip means fewer passes over reachable. If we know + # the new candidates are not ancestors of existing heads, we don't + # have to examine ancestors of existing heads + if ctxisnew: + iterrevs = sorted(newheadrevs) + else: + iterrevs = list(bheadrevs) + + # This loop prunes out two kinds of heads - heads that are + # superseded by a head in newheadrevs, and newheadrevs that are not + # heads because an existing head is their descendant. + while iterrevs: + latest = iterrevs.pop() + if latest not in bheadrevs: + continue + ancestors = set(cl.ancestors([latest], + bheadrevs[0])) + if ancestors: + bheadrevs = [b for b in bheadrevs if b not in ancestors] + self[branch] = [cl.node(rev) for rev in bheadrevs] + tiprev = max(bheadrevs) + if tiprev > self.tiprev: + self.tipnode = cl.node(tiprev) + self.tiprev = tiprev + + # There may be branches that cease to exist when the last commit in the + # branch was stripped. This code filters them out. Note that the + # branch that ceased to exist may not be in newbranches because + # newbranches is the set of candidate heads, which when you strip the + # last commit in a branch will be the parent branch. + droppednodes = [] + for branch in self.keys(): + nodes = [head for head in self[branch] + if cl.hasnode(head)] + if not nodes: + droppednodes.extend(nodes) + del self[branch] + if ((not self.validfor(repo)) or (self.tipnode in droppednodes)): + + # cache key are not valid anymore + self.tipnode = nullid + self.tiprev = nullrev + for heads in self.values(): + tiprev = max(cl.rev(node) for node in heads) + if tiprev > self.tiprev: + self.tipnode = cl.node(tiprev) + self.tiprev = tiprev + self.filteredhash = self._hashfiltered(repo)
--- a/mercurial/bundlerepo.py Mon Jan 14 23:14:45 2013 +0900 +++ b/mercurial/bundlerepo.py Sat Jan 19 17:24:33 2013 -0600 @@ -14,25 +14,28 @@ from node import nullid from i18n import _ import os, tempfile, shutil -import changegroup, util, mdiff, discovery, cmdutil +import changegroup, util, mdiff, discovery, cmdutil, scmutil import localrepo, changelog, manifest, filelog, revlog, error class bundlerevlog(revlog.revlog): def __init__(self, opener, indexfile, bundle, linkmapper): # How it works: - # to retrieve a revision, we need to know the offset of - # the revision in the bundle (an unbundle object). + # To retrieve a revision, we need to know the offset of the revision in + # the bundle (an unbundle object). We store this offset in the index + # (start). # - # We store this offset in the index (start), to differentiate a - # rev in the bundle and from a rev in the revlog, we check - # len(index[r]). If the tuple is bigger than 7, it is a bundle - # (it is bigger since we store the node to which the delta is) + # basemap is indexed with revisions coming from the bundle, and it + # maps to the revision that is the base of the corresponding delta. # + # To differentiate a rev in the bundle from a rev in the revlog, we + # check revision against basemap. + opener = scmutil.readonlyvfs(opener) revlog.revlog.__init__(self, opener, indexfile) self.bundle = bundle - self.basemap = {} + self.basemap = {} # mapping rev to delta base rev n = len(self) chain = None + self.bundlerevs = set() # used by 'bundle()' revset expression while True: chunkdata = bundle.deltachunk(chain) if not chunkdata: @@ -51,49 +54,50 @@ if node in self.nodemap: # this can happen if two branches make the same change chain = node + self.bundlerevs.add(self.nodemap[node]) continue for p in (p1, p2): if p not in self.nodemap: raise error.LookupError(p, self.indexfile, _("unknown parent")) + + if deltabase not in self.nodemap: + raise LookupError(deltabase, self.indexfile, + _('unknown delta base')) + + baserev = self.rev(deltabase) # start, size, full unc. size, base (unused), link, p1, p2, node e = (revlog.offset_type(start, 0), size, -1, -1, link, self.rev(p1), self.rev(p2), node) - self.basemap[n] = deltabase + self.basemap[n] = baserev self.index.insert(-1, e) self.nodemap[node] = n + self.bundlerevs.add(n) chain = node n += 1 - def inbundle(self, rev): - """is rev from the bundle""" - if rev < 0: - return False - return rev in self.basemap - def bundlebase(self, rev): - return self.basemap[rev] def _chunk(self, rev): - # Warning: in case of bundle, the diff is against bundlebase, + # Warning: in case of bundle, the diff is against self.basemap, # not against rev - 1 # XXX: could use some caching - if not self.inbundle(rev): + if rev not in self.basemap: return revlog.revlog._chunk(self, rev) self.bundle.seek(self.start(rev)) return self.bundle.read(self.length(rev)) def revdiff(self, rev1, rev2): """return or calculate a delta between two revisions""" - if self.inbundle(rev1) and self.inbundle(rev2): + if rev1 in self.basemap and rev2 in self.basemap: # hot path for bundle - revb = self.rev(self.bundlebase(rev2)) + revb = self.basemap[rev2] if revb == rev1: return self._chunk(rev2) - elif not self.inbundle(rev1) and not self.inbundle(rev2): + elif rev1 not in self.basemap and rev2 not in self.basemap: return revlog.revlog.revdiff(self, rev1, rev2) return mdiff.textdiff(self.revision(self.node(rev1)), - self.revision(self.node(rev2))) + self.revision(self.node(rev2))) def revision(self, nodeorrev): """return an uncompressed revision of a given node or revision @@ -111,28 +115,23 @@ text = None chain = [] - iter_node = node + iterrev = rev # reconstruct the revision if it is from a changegroup - while self.inbundle(rev): - if self._cache and self._cache[0] == iter_node: + while iterrev in self.basemap: + if self._cache and self._cache[1] == iterrev: text = self._cache[2] break - chain.append(rev) - iter_node = self.bundlebase(rev) - rev = self.rev(iter_node) + chain.append(iterrev) + iterrev = self.basemap[iterrev] if text is None: - text = revlog.revlog.revision(self, iter_node) + text = revlog.revlog.revision(self, iterrev) while chain: delta = self._chunk(chain.pop()) text = mdiff.patches(text, [delta]) - p1, p2 = self.parents(node) - if node != revlog.hash(text, p1, p2): - raise error.RevlogError(_("integrity check failed on %s:%d") - % (self.datafile, self.rev(node))) - - self._cache = (node, self.rev(node), text) + self._checkhash(text, node, rev) + self._cache = (node, rev, text) return text def addrevision(self, text, transaction, link, p1=None, p2=None, d=None): @@ -212,7 +211,7 @@ # dict with the mapping 'filename' -> position in the bundle self.bundlefilespos = {} - @util.propertycache + @localrepo.unfilteredpropertycache def changelog(self): # consume the header if it exists self.bundle.changelogheader() @@ -220,7 +219,7 @@ self.manstart = self.bundle.tell() return c - @util.propertycache + @localrepo.unfilteredpropertycache def manifest(self): self.bundle.seek(self.manstart) # consume the header if it exists @@ -229,12 +228,12 @@ self.filestart = self.bundle.tell() return m - @util.propertycache + @localrepo.unfilteredpropertycache def manstart(self): self.changelog return self.manstart - @util.propertycache + @localrepo.unfilteredpropertycache def filestart(self): self.manifest return self.filestart @@ -256,8 +255,6 @@ if not c: break - if f[0] == '/': - f = f[1:] if f in self.bundlefilespos: self.bundle.seek(self.bundlefilespos[f]) return bundlefilelog(self.sopener, f, self.bundle, @@ -282,9 +279,6 @@ def getcwd(self): return os.getcwd() # always outside the repo - def _writebranchcache(self, branches, tip, tiprev): - # don't overwrite the disk cache with bundle-augmented data - pass def instance(ui, path, create): if create: @@ -384,4 +378,3 @@ other.close() return (localrepo, csets, cleanup) -
--- a/mercurial/changelog.py Mon Jan 14 23:14:45 2013 +0900 +++ b/mercurial/changelog.py Sat Jan 19 17:24:33 2013 -0600 @@ -27,10 +27,13 @@ def decodeextra(text): """ - >>> decodeextra(encodeextra({'foo': 'bar', 'baz': chr(0) + '2'})) - {'foo': 'bar', 'baz': '\\x002', 'branch': 'default'} - >>> decodeextra(encodeextra({'foo': 'bar', 'baz': chr(92) + chr(0) + '2'})) - {'foo': 'bar', 'baz': '\\\\\\x002', 'branch': 'default'} + >>> sorted(decodeextra(encodeextra({'foo': 'bar', 'baz': chr(0) + '2'}) + ... ).iteritems()) + [('baz', '\\x002'), ('branch', 'default'), ('foo', 'bar')] + >>> sorted(decodeextra(encodeextra({'foo': 'bar', + ... 'baz': chr(92) + chr(0) + '2'}) + ... ).iteritems()) + [('baz', '\\\\\\x002'), ('branch', 'default'), ('foo', 'bar')] """ extra = _defaultextra.copy() for l in text.split('\0'): @@ -124,7 +127,7 @@ self._realopener = opener self._delayed = False self._divert = False - self.filteredrevs = () + self.filteredrevs = frozenset() def tip(self): """filtered version of revlog.tip""" @@ -337,3 +340,10 @@ l = [hex(manifest), user, parseddate] + sorted(files) + ["", desc] text = "\n".join(l) return self.addrevision(text, transaction, len(self), p1, p2) + + def branch(self, rev): + """return the branch of a revision + + This function exists because creating a changectx object + just to access this is costly.""" + return encoding.tolocal(self.read(rev)[5].get("branch"))
--- a/mercurial/cmdutil.py Mon Jan 14 23:14:45 2013 +0900 +++ b/mercurial/cmdutil.py Sat Jan 19 17:24:33 2013 -0600 @@ -10,7 +10,7 @@ import os, sys, errno, re, tempfile import util, scmutil, templater, patch, error, templatekw, revlog, copies import match as matchmod -import subrepo, context, repair, bookmarks, graphmod, revset, phases, obsolete +import subrepo, context, repair, graphmod, revset, phases, obsolete import changelog import lock as lockmod @@ -85,7 +85,7 @@ if modified or added or removed or deleted: raise util.Abort(_("outstanding uncommitted changes")) ctx = repo[None] - for s in ctx.substate: + for s in sorted(ctx.substate): if ctx.sub(s).dirty(): raise util.Abort(_("uncommitted changes in subrepo %s") % s) @@ -1137,8 +1137,8 @@ for path in match.files(): if path == '.' or path in repo.store: break - else: - return [] + else: + return [] if slowpath: # We have to read the changelog to match filenames against @@ -1399,39 +1399,18 @@ callable taking a revision number and returning a match objects filtering the files to be detailed when displaying the revision. """ - def increasingrevs(repo, revs, matcher): - # The sorted input rev sequence is chopped in sub-sequences - # which are sorted in ascending order and passed to the - # matcher. The filtered revs are sorted again as they were in - # the original sub-sequence. This achieve several things: - # - # - getlogrevs() now returns a generator which behaviour is - # adapted to log need. First results come fast, last ones - # are batched for performances. - # - # - revset matchers often operate faster on revision in - # changelog order, because most filters deal with the - # changelog. - # - # - revset matchers can reorder revisions. "A or B" typically - # returns returns the revision matching A then the revision - # matching B. We want to hide this internal implementation - # detail from the caller, and sorting the filtered revision - # again achieves this. - for i, window in increasingwindows(0, len(revs), windowsize=1): - orevs = revs[i:i + window] - nrevs = set(matcher(repo, sorted(orevs))) - for rev in orevs: - if rev in nrevs: - yield rev - if not len(repo): - return iter([]), None, None + return [], None, None + limit = loglimit(opts) # Default --rev value depends on --follow but --follow behaviour # depends on revisions resolved from --rev... follow = opts.get('follow') or opts.get('follow_first') + possiblyunsorted = False # whether revs might need sorting if opts.get('rev'): revs = scmutil.revrange(repo, opts['rev']) + # Don't sort here because _makegraphlogrevset might depend on the + # order of revs + possiblyunsorted = True else: if follow and len(repo) > 0: revs = repo.revs('reverse(:.)') @@ -1439,17 +1418,23 @@ revs = list(repo.changelog) revs.reverse() if not revs: - return iter([]), None, None + return [], None, None expr, filematcher = _makegraphlogrevset(repo, pats, opts, revs) + if possiblyunsorted: + revs.sort(reverse=True) if expr: + # Revset matchers often operate faster on revisions in changelog + # order, because most filters deal with the changelog. + revs.reverse() matcher = revset.match(repo.ui, expr) - revs = increasingrevs(repo, revs, matcher) - if not opts.get('hidden'): - # --hidden is still experimental and not worth a dedicated revset - # yet. Fortunately, filtering revision number is fast. - revs = (r for r in revs if r not in repo.hiddenrevs) - else: - revs = iter(revs) + # Revset matches can reorder revisions. "A or B" typically returns + # returns the revision matching A then the revision matching B. Sort + # again to fix that. + revs = matcher(repo, revs) + revs.sort(reverse=True) + if limit is not None: + revs = revs[:limit] + return revs, expr, filematcher def displaygraph(ui, dag, displayer, showparents, edgefn, getrenamed=None, @@ -1484,10 +1469,6 @@ def graphlog(ui, repo, *pats, **opts): # Parameters are identical to log command ones revs, expr, filematcher = getgraphlogrevs(repo, pats, opts) - revs = sorted(revs, reverse=1) - limit = loglimit(opts) - if limit is not None: - revs = revs[:limit] revdag = graphmod.dagwalker(repo, revs) getrenamed = None @@ -1534,7 +1515,7 @@ if ui.verbose or not exact: ui.status(_('adding %s\n') % match.rel(join(f))) - for subpath in wctx.substate: + for subpath in sorted(wctx.substate): sub = wctx.sub(subpath) try: submatch = matchmod.narrowmatcher(subpath, match) @@ -1565,7 +1546,7 @@ if explicitonly: forget = [f for f in forget if match.exact(f)] - for subpath in wctx.substate: + for subpath in sorted(wctx.substate): sub = wctx.sub(subpath) try: submatch = matchmod.narrowmatcher(subpath, match) @@ -1762,9 +1743,10 @@ # Move bookmarks from old parent to amend commit bms = repo.nodebookmarks(old.node()) if bms: + marks = repo._bookmarks for bm in bms: - repo._bookmarks[bm] = newid - bookmarks.write(repo) + marks[bm] = newid + marks.write() #commit the whole amend process if obsolete._enabled and newid != old.node(): # mark the new changeset as successor of the rewritten one @@ -1875,7 +1857,7 @@ names[abs] = m.rel(abs), m.exact(abs) # get the list of subrepos that must be reverted - targetsubs = [s for s in ctx.substate if m(s)] + targetsubs = sorted(s for s in ctx.substate if m(s)) m = scmutil.matchfiles(repo, names) changes = repo.status(match=m)[:4] modified, added, removed, deleted = map(set, changes) @@ -2015,12 +1997,12 @@ '''returns a function object bound to table which can be used as a decorator for populating table as a command table''' - def cmd(name, options, synopsis=None): + def cmd(name, options=(), synopsis=None): def decorator(func): if synopsis: - table[name] = func, options[:], synopsis + table[name] = func, list(options), synopsis else: - table[name] = func, options[:] + table[name] = func, list(options) return func return decorator
--- a/mercurial/commands.py Mon Jan 14 23:14:45 2013 +0900 +++ b/mercurial/commands.py Sat Jan 19 17:24:33 2013 -0600 @@ -49,6 +49,7 @@ ('', 'profile', None, _('print command execution profile')), ('', 'version', None, _('output version information and exit')), ('h', 'help', None, _('display help and exit')), + ('', 'hidden', False, _('consider hidden changesets')), ] dryrunopts = [('n', 'dry-run', None, @@ -549,6 +550,10 @@ hg bisect --skip hg bisect --skip 23 + - skip all revisions that do not touch directories ``foo`` or ``bar`` + + hg bisect --skip '!( file("path:foo") & file("path:bar") )' + - forget the current bisection:: hg bisect --reset @@ -754,7 +759,7 @@ cmdutil.bailifchanged(repo) return hg.clean(repo, node) -@command('bookmarks', +@command('bookmarks|bookmark', [('f', 'force', False, _('force')), ('r', 'rev', '', _('revision'), _('REV')), ('d', 'delete', False, _('delete a given bookmark')), @@ -821,7 +826,7 @@ if mark == repo._bookmarkcurrent: bookmarks.setcurrent(repo, None) del marks[mark] - bookmarks.write(repo) + marks.write() elif rename: if mark is None: @@ -834,7 +839,7 @@ if repo._bookmarkcurrent == rename and not inactive: bookmarks.setcurrent(repo, mark) del marks[rename] - bookmarks.write(repo) + marks.write() elif mark is not None: mark = checkformat(mark) @@ -848,7 +853,7 @@ marks[mark] = cur if not inactive and cur == marks[mark]: bookmarks.setcurrent(repo, mark) - bookmarks.write(repo) + marks.write() # Same message whether trying to deactivate the current bookmark (-i # with no NAME) or listing bookmarks @@ -924,7 +929,7 @@ ' exists'), # i18n: "it" refers to an existing branch hint=_("use 'hg update' to switch to it")) - scmutil.checknewlabel(None, label, 'branch') + scmutil.checknewlabel(repo, label, 'branch') repo.dirstate.setbranch(label) ui.status(_('marked working directory as branch %s\n') % label) ui.status(_('(branches are permanent and global, ' @@ -1292,7 +1297,7 @@ raise util.Abort(_('cannot amend merge changesets')) if len(repo[None].parents()) > 1: raise util.Abort(_('cannot amend while merging')) - if old.children(): + if (not obsolete._enabled) and old.children(): raise util.Abort(_('cannot amend changeset with children')) e = cmdutil.commiteditor @@ -1322,11 +1327,12 @@ elif marks: ui.debug('moving bookmarks %r from %s to %s\n' % (marks, old.hex(), hex(node))) + newmarks = repo._bookmarks for bm in marks: - repo._bookmarks[bm] = node + newmarks[bm] = node if bm == current: bookmarks.setcurrent(repo, bm) - bookmarks.write(repo) + newmarks.write() else: e = cmdutil.commiteditor if opts.get('force_editor'): @@ -1513,7 +1519,7 @@ ui.progress(_('building'), id, unit=_('revisions'), total=total) for type, data in dagparser.parsedag(text): if type == 'n': - ui.note('node %s\n' % str(data)) + ui.note(('node %s\n' % str(data))) id, ps = data files = [] @@ -1526,7 +1532,8 @@ if len(ps) > 1: p2 = repo[ps[1]] pa = p1.ancestor(p2) - base, local, other = [x[fn].data() for x in pa, p1, p2] + base, local, other = [x[fn].data() for x in (pa, p1, + p2)] m3 = simplemerge.Merge3Text(base, local, other) ml = [l.strip() for l in m3.merge_lines()] ml.append("") @@ -1574,10 +1581,10 @@ at = id elif type == 'l': id, name = data - ui.note('tag %s\n' % name) + ui.note(('tag %s\n' % name)) tags.append("%s %s\n" % (hex(repo.changelog.node(id)), name)) elif type == 'a': - ui.note('branch %s\n' % data) + ui.note(('branch %s\n' % data)) atbranch = data ui.progress(_('building'), id, unit=_('revisions'), total=total) tr.close() @@ -1595,7 +1602,7 @@ try: gen = changegroup.readbundle(f, bundlepath) if all: - ui.write("format: id, p1, p2, cset, delta base, len(delta)\n") + ui.write(("format: id, p1, p2, cset, delta base, len(delta)\n")) def showchunks(named): ui.write("\n%s\n" % named) @@ -1787,11 +1794,11 @@ d = util.parsedate(date, util.extendeddateformats) else: d = util.parsedate(date) - ui.write("internal: %s %s\n" % d) - ui.write("standard: %s\n" % util.datestr(d)) + ui.write(("internal: %s %s\n") % d) + ui.write(("standard: %s\n") % util.datestr(d)) if range: m = util.matchdate(range) - ui.write("match: %s\n" % m(d[0])) + ui.write(("match: %s\n") % m(d[0])) @command('debugdiscovery', [('', 'old', None, _('use old-style discovery')), @@ -1821,8 +1828,8 @@ force=True) common = set(common) if not opts.get('nonheads'): - ui.write("unpruned common: %s\n" % " ".join([short(n) - for n in common])) + ui.write(("unpruned common: %s\n") % + " ".join(sorted(short(n) for n in common))) dag = dagutil.revlogdag(repo.changelog) all = dag.ancestorset(dag.internalizeall(common)) common = dag.externalizeall(dag.headsetofconnecteds(all)) @@ -1831,11 +1838,12 @@ common = set(common) rheads = set(hds) lheads = set(repo.heads()) - ui.write("common heads: %s\n" % " ".join([short(n) for n in common])) + ui.write(("common heads: %s\n") % + " ".join(sorted(short(n) for n in common))) if lheads <= common: - ui.write("local is subset\n") + ui.write(("local is subset\n")) elif rheads <= common: - ui.write("remote is subset\n") + ui.write(("remote is subset\n")) serverlogs = opts.get('serverlog') if serverlogs: @@ -1879,9 +1887,9 @@ def debugfsinfo(ui, path = "."): """show information detected about current filesystem""" util.writefile('.debugfsinfo', '') - ui.write('exec: %s\n' % (util.checkexec(path) and 'yes' or 'no')) - ui.write('symlink: %s\n' % (util.checklink(path) and 'yes' or 'no')) - ui.write('case-sensitive: %s\n' % (util.checkcase('.debugfsinfo') + ui.write(('exec: %s\n') % (util.checkexec(path) and 'yes' or 'no')) + ui.write(('symlink: %s\n') % (util.checklink(path) and 'yes' or 'no')) + ui.write(('case-sensitive: %s\n') % (util.checkcase('.debugfsinfo') and 'yes' or 'no')) os.unlink('.debugfsinfo') @@ -1979,7 +1987,7 @@ r = filelog if not r: r = revlog.revlog(scmutil.opener(os.getcwd(), audit=False), file_) - ui.write("digraph G {\n") + ui.write(("digraph G {\n")) for i in r: node = r.node(i) pp = r.parents(node) @@ -2128,7 +2136,8 @@ ui.write(' ') ui.write(hex(repl)) ui.write(' %X ' % m._data[2]) - ui.write(m.metadata()) + ui.write('{%s}' % (', '.join('%r: %r' % t for t in + sorted(m.metadata().items())))) ui.write('\n') @command('debugpushkey', [], _('REPO NAMESPACE [KEY OLD NEW]')) @@ -2148,7 +2157,7 @@ ui.status(str(r) + '\n') return not r else: - for k, v in target.listkeys(namespace).iteritems(): + for k, v in sorted(target.listkeys(namespace).iteritems()): ui.write("%s\t%s\n" % (k.encode('string-escape'), v.encode('string-escape'))) @@ -2325,52 +2334,54 @@ def pcfmt(value, total): return (value, 100 * float(value) / total) - ui.write('format : %d\n' % format) - ui.write('flags : %s\n' % ', '.join(flags)) + ui.write(('format : %d\n') % format) + ui.write(('flags : %s\n') % ', '.join(flags)) ui.write('\n') fmt = pcfmtstr(totalsize) fmt2 = dfmtstr(totalsize) - ui.write('revisions : ' + fmt2 % numrevs) - ui.write(' merges : ' + fmt % pcfmt(nummerges, numrevs)) - ui.write(' normal : ' + fmt % pcfmt(numrevs - nummerges, numrevs)) - ui.write('revisions : ' + fmt2 % numrevs) - ui.write(' full : ' + fmt % pcfmt(numfull, numrevs)) - ui.write(' deltas : ' + fmt % pcfmt(numdeltas, numrevs)) - ui.write('revision size : ' + fmt2 % totalsize) - ui.write(' full : ' + fmt % pcfmt(fulltotal, totalsize)) - ui.write(' deltas : ' + fmt % pcfmt(deltatotal, totalsize)) + ui.write(('revisions : ') + fmt2 % numrevs) + ui.write((' merges : ') + fmt % pcfmt(nummerges, numrevs)) + ui.write((' normal : ') + fmt % pcfmt(numrevs - nummerges, numrevs)) + ui.write(('revisions : ') + fmt2 % numrevs) + ui.write((' full : ') + fmt % pcfmt(numfull, numrevs)) + ui.write((' deltas : ') + fmt % pcfmt(numdeltas, numrevs)) + ui.write(('revision size : ') + fmt2 % totalsize) + ui.write((' full : ') + fmt % pcfmt(fulltotal, totalsize)) + ui.write((' deltas : ') + fmt % pcfmt(deltatotal, totalsize)) ui.write('\n') fmt = dfmtstr(max(avgchainlen, compratio)) - ui.write('avg chain length : ' + fmt % avgchainlen) - ui.write('compression ratio : ' + fmt % compratio) + ui.write(('avg chain length : ') + fmt % avgchainlen) + ui.write(('compression ratio : ') + fmt % compratio) if format > 0: ui.write('\n') - ui.write('uncompressed data size (min/max/avg) : %d / %d / %d\n' + ui.write(('uncompressed data size (min/max/avg) : %d / %d / %d\n') % tuple(datasize)) - ui.write('full revision size (min/max/avg) : %d / %d / %d\n' + ui.write(('full revision size (min/max/avg) : %d / %d / %d\n') % tuple(fullsize)) - ui.write('delta size (min/max/avg) : %d / %d / %d\n' + ui.write(('delta size (min/max/avg) : %d / %d / %d\n') % tuple(deltasize)) if numdeltas > 0: ui.write('\n') fmt = pcfmtstr(numdeltas) fmt2 = pcfmtstr(numdeltas, 4) - ui.write('deltas against prev : ' + fmt % pcfmt(numprev, numdeltas)) + ui.write(('deltas against prev : ') + fmt % pcfmt(numprev, numdeltas)) if numprev > 0: - ui.write(' where prev = p1 : ' + fmt2 % pcfmt(nump1prev, + ui.write((' where prev = p1 : ') + fmt2 % pcfmt(nump1prev, numprev)) - ui.write(' where prev = p2 : ' + fmt2 % pcfmt(nump2prev, + ui.write((' where prev = p2 : ') + fmt2 % pcfmt(nump2prev, numprev)) - ui.write(' other : ' + fmt2 % pcfmt(numoprev, + ui.write((' other : ') + fmt2 % pcfmt(numoprev, numprev)) if gdelta: - ui.write('deltas against p1 : ' + fmt % pcfmt(nump1, numdeltas)) - ui.write('deltas against p2 : ' + fmt % pcfmt(nump2, numdeltas)) - ui.write('deltas against other : ' + fmt % pcfmt(numother, + ui.write(('deltas against p1 : ') + + fmt % pcfmt(nump1, numdeltas)) + ui.write(('deltas against p2 : ') + + fmt % pcfmt(nump2, numdeltas)) + ui.write(('deltas against other : ') + fmt % pcfmt(numother, numdeltas)) @command('debugrevspec', [], ('REVSPEC')) @@ -2448,9 +2459,63 @@ def debugsub(ui, repo, rev=None): ctx = scmutil.revsingle(repo, rev, None) for k, v in sorted(ctx.substate.items()): - ui.write('path %s\n' % k) - ui.write(' source %s\n' % v[0]) - ui.write(' revision %s\n' % v[1]) + ui.write(('path %s\n') % k) + ui.write((' source %s\n') % v[0]) + ui.write((' revision %s\n') % v[1]) + +@command('debugsuccessorssets', + [], + _('[REV]')) +def debugsuccessorssets(ui, repo, *revs): + """show set of successors for revision + + A successors set of changeset A is a consistent group of revisions that + succeed A. It contains non-obsolete changesets only. + + In most cases a changeset A has a single successors set containing a single + successors (changeset A replaced by A'). + + A changeset that is made obsolete with no successors are called "pruned". + Such changesets have no successors sets at all. + + A changeset that has been "split" will have a successors set containing + more than one successors. + + A changeset that has been rewritten in multiple different ways is called + "divergent". Such changesets have multiple successor sets (each of which + may also be split, i.e. have multiple successors). + + Results are displayed as follows:: + + <rev1> + <successors-1A> + <rev2> + <successors-2A> + <successors-2B1> <successors-2B2> <successors-2B3> + + Here rev2 has two possible (i.e. divergent) successors sets. The first + holds one element, whereas the second holds three (i.e. the changeset has + been split). + """ + # passed to successorssets caching computation from one call to another + cache = {} + ctx2str = str + node2str = short + if ui.debug(): + def ctx2str(ctx): + return ctx.hex() + node2str = hex + for rev in scmutil.revrange(repo, revs): + ctx = repo[rev] + ui.write('%s\n'% ctx2str(ctx)) + for succsset in obsolete.successorssets(repo, ctx.node(), cache): + if succsset: + ui.write(' ') + ui.write(node2str(succsset[0])) + for node in succsset[1:]: + ui.write(' ') + ui.write(node2str(node)) + ui.write('\n') @command('debugwalk', walkopts, _('[OPTION]... [FILE]...')) def debugwalk(ui, repo, *pats, **opts): @@ -2823,13 +2888,27 @@ wlock = repo.wlock() try: + current = repo['.'] for pos, ctx in enumerate(repo.set("%ld", revs)): - current = repo['.'] ui.status(_('grafting revision %s\n') % ctx.rev()) if opts.get('dry_run'): continue + source = ctx.extra().get('source') + if not source: + source = ctx.hex() + extra = {'source': source} + user = ctx.user() + if opts.get('user'): + user = opts['user'] + date = ctx.date() + if opts.get('date'): + date = opts['date'] + message = ctx.description() + if opts.get('log'): + message += '\n(grafted from %s)' % ctx.hex() + # we don't merge the first commit when continuing if not cont: # perform the graft merge with p1(rev) as 'ancestor' @@ -2858,29 +2937,18 @@ cmdutil.duplicatecopies(repo, ctx.rev(), ctx.p1().rev()) # commit - source = ctx.extra().get('source') - if not source: - source = ctx.hex() - extra = {'source': source} - user = ctx.user() - if opts.get('user'): - user = opts['user'] - date = ctx.date() - if opts.get('date'): - date = opts['date'] - message = ctx.description() - if opts.get('log'): - message += '\n(grafted from %s)' % ctx.hex() node = repo.commit(text=message, user=user, date=date, extra=extra, editor=editor) if node is None: ui.status(_('graft for revision %s is empty\n') % ctx.rev()) + else: + current = repo[node] finally: wlock.release() # remove state when we complete successfully - if not opts.get('dry_run') and os.path.exists(repo.join('graftstate')): - util.unlinkpath(repo.join('graftstate')) + if not opts.get('dry_run'): + util.unlinkpath(repo.join('graftstate'), ignoremissing=True) return 0 @@ -3564,7 +3632,7 @@ bms = [bm for bm, bmr in peer.listkeys('bookmarks').iteritems() if bmr == hexremoterev] - return bms + return sorted(bms) if bookmarks: output.extend(getbms()) @@ -4024,7 +4092,6 @@ _('show changesets within the given named branch'), _('BRANCH')), ('P', 'prune', [], _('do not display revision or any of its ancestors'), _('REV')), - ('', 'hidden', False, _('show hidden changesets (DEPRECATED)')), ] + logopts + walkopts, _('[OPTION]... [FILE]')) def log(ui, repo, *pats, **opts): @@ -4140,8 +4207,6 @@ return if opts.get('branch') and ctx.branch() not in opts['branch']: return - if not opts.get('hidden') and ctx.hidden(): - return if df and not df(ctx.date()[0]): return @@ -4207,6 +4272,9 @@ Returns 0 on success. """ + + fm = ui.formatter('manifest', opts) + if opts.get('all'): if rev or node: raise util.Abort(_("can't specify a revision with --all")) @@ -4224,7 +4292,9 @@ finally: lock.release() for f in res: - ui.write("%s\n" % f) + fm.startitem() + fm.write("path", '%s\n', f) + fm.end() return if rev and node: @@ -4233,14 +4303,17 @@ if not node: node = rev - decor = {'l':'644 @ ', 'x':'755 * ', '':'644 '} + char = {'l': '@', 'x': '*', '': ''} + mode = {'l': '644', 'x': '755', '': '644'} ctx = scmutil.revsingle(repo, node) + mf = ctx.manifest() for f in ctx: - if ui.debugflag: - ui.write("%40s " % hex(ctx.manifest()[f])) - if ui.verbose: - ui.write(decor[ctx.flags(f)]) - ui.write("%s\n" % f) + fm.startitem() + fl = ctx[f].flags() + fm.condwrite(ui.debugflag, 'hash', '%s ', hex(mf[f])) + fm.condwrite(ui.verbose, 'mode type', '%s %1s ', mode[fl], char[fl]) + fm.write('path', '%s\n', f) + fm.end() @command('^merge', [('f', 'force', None, _('force a merge with outstanding changes')), @@ -4556,10 +4629,14 @@ phases.retractboundary(repo, targetphase, nodes) finally: lock.release() - newdata = repo._phasecache.getphaserevs(repo) + # moving revision from public to draft may hide them + # We have to check result on an unfiltered repository + unfi = repo.unfiltered() + newdata = repo._phasecache.getphaserevs(unfi) changes = sum(o != newdata[i] for i, o in enumerate(olddata)) + cl = unfi.changelog rejected = [n for n in nodes - if newdata[repo[n].rev()] < targetphase] + if newdata[cl.rev(n)] < targetphase] if rejected: ui.warn(_('cannot move %i changesets to a more permissive ' 'phase, use --force\n') % len(rejected)) @@ -4666,11 +4743,12 @@ # update specified bookmarks if opts.get('bookmark'): + marks = repo._bookmarks for b in opts['bookmark']: # explicit pull overrides local bookmark if any ui.status(_("importing bookmark %s\n") % b) - repo._bookmarks[b] = repo[rb[b]].node() - bookmarks.write(repo) + marks[b] = repo[rb[b]].node() + marks.write() return ret @@ -4861,8 +4939,7 @@ elif after: list = deleted for f in modified + added + clean: - ui.warn(_('not removing %s: file still exists (use -f' - ' to force removal)\n') % m.rel(f)) + ui.warn(_('not removing %s: file still exists\n') % m.rel(f)) ret = 1 else: list = deleted + clean @@ -4885,11 +4962,7 @@ for f in list: if f in added: continue # we never unlink added files on remove - try: - util.unlinkpath(repo.wjoin(f)) - except OSError, inst: - if inst.errno != errno.ENOENT: - raise + util.unlinkpath(repo.wjoin(f), ignoremissing=True) repo[None].forget(list) finally: wlock.release() @@ -5427,17 +5500,16 @@ copy = copies.pathcopies(repo[node1], repo[node2]) fm = ui.formatter('status', opts) - format = '%s %s' + end - if opts.get('no_status'): - format = '%.0s%s' + end + fmt = '%s' + end + showchar = not opts.get('no_status') for state, char, files in changestates: if state in show: label = 'status.' + state for f in files: fm.startitem() - fm.write("status path", format, char, - repo.pathto(f, cwd), label=label) + fm.condwrite(showchar, 'status', '%s ', char, label=label) + fm.write('path', fmt, repo.pathto(f, cwd), label=label) if f in copy: fm.write("copy", ' %s' + end, repo.pathto(copy[f], cwd), label='status.copied') @@ -5743,7 +5815,7 @@ release(lock, wlock) @command('tags', [], '') -def tags(ui, repo): +def tags(ui, repo, **opts): """list repository tags This lists both regular and local tags. When the -v/--verbose @@ -5752,27 +5824,27 @@ Returns 0 on success. """ + fm = ui.formatter('tags', opts) hexfunc = ui.debugflag and hex or short tagtype = "" for t, n in reversed(repo.tagslist()): - if ui.quiet: - ui.write("%s\n" % t, label='tags.normal') - continue - hn = hexfunc(n) - r = "%5d:%s" % (repo.changelog.rev(n), hn) - rev = ui.label(r, 'log.changeset changeset.%s' % repo[n].phasestr()) - spaces = " " * (30 - encoding.colwidth(t)) - - tag = ui.label(t, 'tags.normal') - if ui.verbose: - if repo.tagtype(t) == 'local': - tagtype = " local" - tag = ui.label(t, 'tags.local') - else: - tagtype = "" - ui.write("%s%s %s%s\n" % (tag, spaces, rev, tagtype)) + label = 'tags.normal' + tagtype = '' + if repo.tagtype(t) == 'local': + label = 'tags.local' + tagtype = 'local' + + fm.startitem() + fm.write('tag', '%s', t, label=label) + fmt = " " * (30 - encoding.colwidth(t)) + ' %5d:%s' + fm.condwrite(not ui.quiet, 'rev id', fmt, + repo.changelog.rev(n), hn, label=label) + fm.condwrite(ui.verbose and tagtype, 'type', ' %s', + tagtype, label=label) + fm.plain('\n') + fm.end() @command('tip', [('p', 'patch', None, _('show patch')),
--- a/mercurial/commandserver.py Mon Jan 14 23:14:45 2013 +0900 +++ b/mercurial/commandserver.py Sat Jan 19 17:24:33 2013 -0600 @@ -42,7 +42,7 @@ def __getattr__(self, attr): if attr in ('isatty', 'fileno'): - raise AttributeError, attr + raise AttributeError(attr) return getattr(self.in_, attr) class channeledinput(object): @@ -122,7 +122,7 @@ def __getattr__(self, attr): if attr in ('isatty', 'fileno'): - raise AttributeError, attr + raise AttributeError(attr) return getattr(self.in_, attr) class server(object): @@ -220,7 +220,7 @@ 'getencoding' : getencoding} def serve(self): - hellomsg = 'capabilities: ' + ' '.join(self.capabilities.keys()) + hellomsg = 'capabilities: ' + ' '.join(sorted(self.capabilities)) hellomsg += '\n' hellomsg += 'encoding: ' + encoding.encoding
--- a/mercurial/context.py Mon Jan 14 23:14:45 2013 +0900 +++ b/mercurial/context.py Sat Jan 19 17:24:33 2013 -0600 @@ -12,6 +12,7 @@ import match as matchmod import os, errno, stat import obsolete as obsmod +import repoview propertycache = util.propertycache @@ -25,8 +26,12 @@ self._repo = repo if isinstance(changeid, int): + try: + self._node = repo.changelog.node(changeid) + except IndexError: + raise error.RepoLookupError( + _("unknown revision '%s'") % changeid) self._rev = changeid - self._node = repo.changelog.node(changeid) return if isinstance(changeid, long): changeid = str(changeid) @@ -62,7 +67,7 @@ self._rev = r self._node = repo.changelog.node(r) return - except (ValueError, OverflowError): + except (ValueError, OverflowError, IndexError): pass if len(changeid) == 40: @@ -95,7 +100,10 @@ # lookup failed # check if it might have come from damaged dirstate - if changeid in repo.dirstate.parents(): + # + # XXX we could avoid the unfiltered if we had a recognizable exception + # for filtered changeset access + if changeid in repo.unfiltered().dirstate.parents(): raise error.Abort(_("working directory has unknown parent '%s'!") % short(changeid)) try: @@ -204,7 +212,7 @@ def mutable(self): return self.phase() > phases.public def hidden(self): - return self._rev in self._repo.hiddenrevs + return self._rev in repoview.filterrevs(self._repo, 'visible') def parents(self): """return contexts for each parent changeset""" @@ -250,6 +258,34 @@ """ return self.rev() in obsmod.getrevs(self._repo, 'bumped') + def divergent(self): + """Is a successors of a changeset with multiple possible successors set + + Only non-public and non-obsolete changesets may be divergent. + """ + return self.rev() in obsmod.getrevs(self._repo, 'divergent') + + def troubled(self): + """True if the changeset is either unstable, bumped or divergent""" + return self.unstable() or self.bumped() or self.divergent() + + def troubles(self): + """return the list of troubles affecting this changesets. + + Troubles are returned as strings. possible values are: + - unstable, + - bumped, + - divergent. + """ + troubles = [] + if self.unstable(): + troubles.append('unstable') + if self.bumped(): + troubles.append('bumped') + if self.divergent(): + troubles.append('divergent') + return troubles + def _fileinfo(self, path): if '_manifest' in self.__dict__: try: @@ -352,6 +388,9 @@ def dirs(self): return self._dirs + def dirty(self): + return False + class filectx(object): """A filecontext object makes access to data related to a particular filerevision convenient.""" @@ -380,7 +419,26 @@ @propertycache def _changectx(self): - return changectx(self._repo, self._changeid) + try: + return changectx(self._repo, self._changeid) + except error.RepoLookupError: + # Linkrev may point to any revision in the repository. When the + # repository is filtered this may lead to `filectx` trying to build + # `changectx` for filtered revision. In such case we fallback to + # creating `changectx` on the unfiltered version of the reposition. + # This fallback should not be an issue because`changectx` from + # `filectx` are not used in complexe operation that care about + # filtering. + # + # This fallback is a cheap and dirty fix that prevent several + # crash. It does not ensure the behavior is correct. However the + # behavior was not correct before filtering either and "incorrect + # behavior" is seen as better as "crash" + # + # Linkrevs have several serious troubles with filtering that are + # complicated to solve. Proper handling of the issue here should be + # considered when solving linkrev issue are on the table. + return changectx(self._repo.unfiltered(), self._changeid) @propertycache def _filelog(self): @@ -977,13 +1035,13 @@ return self._parents[0].ancestor(c2) # punt on two parents for now def walk(self, match): - return sorted(self._repo.dirstate.walk(match, self.substate.keys(), + return sorted(self._repo.dirstate.walk(match, sorted(self.substate), True, False)) def dirty(self, missing=False, merge=True, branch=True): "check whether a working directory is modified" # check subrepos first - for s in self.substate: + for s in sorted(self.substate): if self.sub(s).dirty(): return True # check current working dir
--- a/mercurial/copies.py Mon Jan 14 23:14:45 2013 +0900 +++ b/mercurial/copies.py Sat Jan 19 17:24:33 2013 -0600 @@ -145,12 +145,16 @@ return cm -def _backwardcopies(a, b): - # because the forward mapping is 1:n, we can lose renames here - # in particular, we find renames better than copies +def _backwardrenames(a, b): + # Even though we're not taking copies into account, 1:n rename situations + # can still exist (e.g. hg cp a b; hg mv a c). In those cases we + # arbitrarily pick one of the renames. f = _forwardcopies(b, a) r = {} - for k, v in f.iteritems(): + for k, v in sorted(f.iteritems()): + # remove copies + if v in a: + continue r[v] = k return r @@ -162,19 +166,25 @@ if a == x: return _forwardcopies(x, y) if a == y: - return _backwardcopies(x, y) - return _chain(x, y, _backwardcopies(x, a), _forwardcopies(a, y)) + return _backwardrenames(x, y) + return _chain(x, y, _backwardrenames(x, a), _forwardcopies(a, y)) def mergecopies(repo, c1, c2, ca): """ Find moves and copies between context c1 and c2 that are relevant for merging. - Returns two dicts, "copy" and "diverge". + Returns four dicts: "copy", "movewithdir", "diverge", and + "renamedelete". "copy" is a mapping from destination name -> source name, where source is in c1 and destination is in c2 or vice-versa. + "movewithdir" is a mapping from source name -> destination name, + where the file at source present in one context but not the other + needs to be moved to destination by the merge process, because the + other context moved the directory it is in. + "diverge" is a mapping of source name -> list of destination names for divergent renames. @@ -183,16 +193,16 @@ """ # avoid silly behavior for update from empty dir if not c1 or not c2 or c1 == c2: - return {}, {}, {} + return {}, {}, {}, {} # avoid silly behavior for parent -> working dir if c2.node() is None and c1.node() == repo.dirstate.p1(): - return repo.dirstate.copies(), {}, {} + return repo.dirstate.copies(), {}, {}, {} limit = _findlimit(repo, c1.rev(), c2.rev()) if limit is None: # no common ancestor, no copies - return {}, {}, {} + return {}, {}, {}, {} m1 = c1.manifest() m2 = c2.manifest() ma = ca.manifest() @@ -206,6 +216,7 @@ ctx = util.lrucachefunc(makectx) copy = {} + movewithdir = {} fullcopy = {} diverge = {} @@ -303,7 +314,7 @@ if fullcopy: repo.ui.debug(" all copies found (* = to merge, ! = divergent, " "% = renamed and deleted):\n") - for f in fullcopy: + for f in sorted(fullcopy): note = "" if f in copy: note += "*" @@ -311,11 +322,12 @@ note += "!" if f in renamedelete2: note += "%" - repo.ui.debug(" %s -> %s %s\n" % (f, fullcopy[f], note)) + repo.ui.debug(" src: '%s' -> dst: '%s' %s\n" % (fullcopy[f], f, + note)) del diverge2 if not fullcopy: - return copy, diverge, renamedelete + return copy, movewithdir, diverge, renamedelete repo.ui.debug(" checking for directory renames\n") @@ -352,10 +364,11 @@ del d1, d2, invalid if not dirmove: - return copy, diverge, renamedelete + return copy, movewithdir, diverge, renamedelete for d in dirmove: - repo.ui.debug(" dir %s -> %s\n" % (d, dirmove[d])) + repo.ui.debug(" discovered dir src: '%s' -> dst: '%s'\n" % + (d, dirmove[d])) # check unaccounted nonoverlapping files against directory moves for f in u1 + u2: @@ -365,8 +378,9 @@ # new file added in a directory that was moved, move it df = dirmove[d] + f[len(d):] if df not in copy: - copy[f] = df - repo.ui.debug(" file %s -> %s\n" % (f, copy[f])) + movewithdir[f] = df + repo.ui.debug((" pending file src: '%s' -> " + "dst: '%s'\n") % (f, df)) break - return copy, diverge, renamedelete + return copy, movewithdir, diverge, renamedelete
--- a/mercurial/dirstate.py Mon Jan 14 23:14:45 2013 +0900 +++ b/mercurial/dirstate.py Sat Jan 19 17:24:33 2013 -0600 @@ -265,6 +265,12 @@ try: f.write(self._branch + '\n') f.close() + + # make sure filecache has the correct stat info for _branch after + # replacing the underlying file + ce = self._filecache['_branch'] + if ce: + ce.refresh() except: # re-raises f.discard() raise @@ -607,7 +613,7 @@ normalize = self._normalize skipstep3 = False else: - normalize = lambda x, y, z: x + normalize = None files = sorted(match.files()) subrepos.sort() @@ -628,7 +634,10 @@ # step 1: find all explicit files for ff in files: - nf = normalize(normpath(ff), False, True) + if normalize: + nf = normalize(normpath(ff), False, True) + else: + nf = normpath(ff) if nf in results: continue @@ -678,7 +687,10 @@ continue raise for f, kind, st in entries: - nf = normalize(nd and (nd + "/" + f) or f, True, True) + if normalize: + nf = normalize(nd and (nd + "/" + f) or f, True, True) + else: + nf = nd and (nd + "/" + f) or f if nf not in results: if kind == dirkind: if not ignore(nf): @@ -698,11 +710,9 @@ # step 3: report unseen items in the dmap hash if not skipstep3 and not exact: visit = sorted([f for f in dmap if f not in results and matchfn(f)]) - for nf, st in zip(visit, util.statfiles([join(i) for i in visit])): - if (not st is None and - getkind(st.st_mode) not in (regkind, lnkkind)): - st = None - results[nf] = st + nf = iter(visit).next + for st in util.statfiles([join(i) for i in visit]): + results[nf()] = st for s in subrepos: del results[s] del results['.hg'] @@ -748,13 +758,19 @@ radd = removed.append dadd = deleted.append cadd = clean.append + mexact = match.exact + dirignore = self._dirignore + checkexec = self._checkexec + checklink = self._checklink + copymap = self._copymap + lastnormaltime = self._lastnormaltime lnkkind = stat.S_IFLNK for fn, st in self.walk(match, subrepos, listunknown, listignored).iteritems(): if fn not in dmap: - if (listignored or match.exact(fn)) and self._dirignore(fn): + if (listignored or mexact(fn)) and dirignore(fn): if listignored: iadd(fn) elif listunknown: @@ -773,15 +789,15 @@ mtime = int(st.st_mtime) if (size >= 0 and ((size != st.st_size and size != st.st_size & _rangemask) - or ((mode ^ st.st_mode) & 0100 and self._checkexec)) - and (mode & lnkkind != lnkkind or self._checklink) + or ((mode ^ st.st_mode) & 0100 and checkexec)) + and (mode & lnkkind != lnkkind or checklink) or size == -2 # other parent - or fn in self._copymap): + or fn in copymap): madd(fn) elif ((time != mtime and time != mtime & _rangemask) - and (mode & lnkkind != lnkkind or self._checklink)): + and (mode & lnkkind != lnkkind or checklink)): ladd(fn) - elif mtime == self._lastnormaltime: + elif mtime == lastnormaltime: # fn may have been changed in the same timeslot without # changing its size. This can happen if we quickly do # multiple commits in a single transaction.
--- a/mercurial/discovery.py Mon Jan 14 23:14:45 2013 +0900 +++ b/mercurial/discovery.py Sat Jan 19 17:24:33 2013 -0600 @@ -8,6 +8,7 @@ from node import nullid, short from i18n import _ import util, setdiscovery, treediscovery, phases, obsolete, bookmarks +import branchmap def findcommonincoming(repo, remote, heads=None, force=False): """Return a tuple (common, anyincoming, heads) used to identify the common @@ -114,7 +115,7 @@ og.missingheads = onlyheads or repo.heads() elif onlyheads is None: # use visible heads as it should be cached - og.missingheads = visibleheads(repo) + og.missingheads = repo.filtered("served").heads() og.excluded = [ctx.node() for ctx in repo.set('secret() or extinct()')] else: # compute common, missing and exclude secret stuff @@ -192,9 +193,10 @@ # D. Update newmap with outgoing changes. # This will possibly add new heads and remove existing ones. - newmap = dict((branch, heads[1]) for branch, heads in headssum.iteritems() - if heads[0] is not None) - repo._updatebranchcache(newmap, missingctx) + newmap = branchmap.branchcache((branch, heads[1]) + for branch, heads in headssum.iteritems() + if heads[0] is not None) + newmap.update(repo, (ctx.rev() for ctx in missingctx)) for branch, newheads in newmap.iteritems(): headssum[branch][1][:] = newheads return headssum @@ -205,7 +207,7 @@ cl = repo.changelog # 1-4b. old servers: Check for new topological heads. # Construct {old,new}map with branch = None (topological branch). - # (code based on _updatebranchcache) + # (code based on update) oldheads = set(h for h in remoteheads if h in cl.nodemap) # all nodes in outgoing.missing are children of either: # - an element of oldheads @@ -266,7 +268,7 @@ allmissing = set(outgoing.missing) allfuturecommon = set(c.node() for c in repo.set('%ld', outgoing.common)) allfuturecommon.update(allmissing) - for branch, heads in headssum.iteritems(): + for branch, heads in sorted(headssum.iteritems()): if heads[0] is None: # Maybe we should abort if we push more that one head # for new branches ? @@ -310,7 +312,7 @@ unsynced = True if len(newhs) > len(oldhs): # strip updates to existing remote heads from the new heads list - dhs = list(newhs - bookmarkedheads - oldhs) + dhs = sorted(newhs - bookmarkedheads - oldhs) if dhs: if error is None: if branch not in ('default', None): @@ -335,43 +337,3 @@ # 6. Check for unsynced changes on involved branches. if unsynced: repo.ui.warn(_("note: unsynced remote changes!\n")) - -def visibleheads(repo): - """return the set of visible head of this repo""" - # XXX we want a cache on this - sroots = repo._phasecache.phaseroots[phases.secret] - if sroots or repo.obsstore: - # XXX very slow revset. storing heads or secret "boundary" - # would help. - revset = repo.set('heads(not (%ln:: + extinct()))', sroots) - - vheads = [ctx.node() for ctx in revset] - if not vheads: - vheads.append(nullid) - else: - vheads = repo.heads() - return vheads - - -def visiblebranchmap(repo): - """return a branchmap for the visible set""" - # XXX Recomputing this data on the fly is very slow. We should build a - # XXX cached version while computing the standard branchmap version. - sroots = repo._phasecache.phaseroots[phases.secret] - if sroots or repo.obsstore: - vbranchmap = {} - for branch, nodes in repo.branchmap().iteritems(): - # search for secret heads. - for n in nodes: - if repo[n].phase() >= phases.secret: - nodes = None - break - # if secret heads were found we must compute them again - if nodes is None: - s = repo.set('heads(branch(%s) - secret() - extinct())', - branch) - nodes = [c.node() for c in s] - vbranchmap[branch] = nodes - else: - vbranchmap = repo.branchmap() - return vbranchmap
--- a/mercurial/dispatch.py Mon Jan 14 23:14:45 2013 +0900 +++ b/mercurial/dispatch.py Sat Jan 19 17:24:33 2013 -0600 @@ -183,8 +183,8 @@ else: raise except OSError, inst: - if getattr(inst, "filename", None): - ui.warn(_("abort: %s: %s\n") % (inst.strerror, inst.filename)) + if getattr(inst, "filename", None) is not None: + ui.warn(_("abort: %s: '%s'\n") % (inst.strerror, inst.filename)) else: ui.warn(_("abort: %s\n") % inst.strerror) except KeyboardInterrupt: @@ -710,6 +710,8 @@ repo = hg.repository(ui, path=path) if not repo.local(): raise util.Abort(_("repository '%s' is not local") % path) + if options['hidden']: + repo = repo.unfiltered() repo.ui.setconfig("bundle", "mainreporoot", repo.root) except error.RequirementError: raise
--- a/mercurial/encoding.py Mon Jan 14 23:14:45 2013 +0900 +++ b/mercurial/encoding.py Sat Jan 19 17:24:33 2013 -0600 @@ -80,8 +80,8 @@ 'foo: \\xc3\\xa4' >>> u2 = 'foo: \\xc3\\xa1' >>> d = { l: 1, tolocal(u2): 2 } - >>> d # no collision - {'foo: ?': 1, 'foo: ?': 2} + >>> len(d) # no collision + 2 >>> 'foo: ?' in d False >>> l1 = 'foo: \\xe4' # historical latin1 fallback
--- a/mercurial/filemerge.py Mon Jan 14 23:14:45 2013 +0900 +++ b/mercurial/filemerge.py Sat Jan 19 17:24:33 2013 -0600 @@ -171,13 +171,15 @@ def _premerge(repo, toolconf, files): tool, toolpath, binary, symlink = toolconf + if symlink: + return 1 a, b, c, back = files ui = repo.ui # do we attempt to simplemerge first? try: - premerge = _toolbool(ui, tool, "premerge", not (binary or symlink)) + premerge = _toolbool(ui, tool, "premerge", not binary) except error.ConfigError: premerge = _toolstr(ui, tool, "premerge").lower() valid = 'keep'.split() @@ -204,6 +206,12 @@ Uses the internal non-interactive simple merge algorithm for merging files. It will fail if there are any conflicts and leave markers in the partially merged file.""" + tool, toolpath, binary, symlink = toolconf + if symlink: + repo.ui.warn(_('warning: internal:merge cannot merge symlinks ' + 'for %s\n') % fcd.path()) + return False, 1 + r = _premerge(repo, toolconf, files) if r: a, b, c, back = files
--- a/mercurial/fileset.py Mon Jan 14 23:14:45 2013 +0900 +++ b/mercurial/fileset.py Sat Jan 19 17:24:33 2013 -0600 @@ -373,7 +373,7 @@ # i18n: "subrepo" is a keyword getargs(x, 0, 1, _("subrepo takes at most one argument")) ctx = mctx.ctx - sstate = ctx.substate + sstate = sorted(ctx.substate) if x: pat = getstring(x, _("subrepo requires a pattern or no arguments"))
--- a/mercurial/formatter.py Mon Jan 14 23:14:45 2013 +0900 +++ b/mercurial/formatter.py Sat Jan 19 17:24:33 2013 -0600 @@ -31,6 +31,10 @@ '''do default text output while assigning data to item''' for k, v in zip(fields.split(), fielddata): self._item[k] = v + def condwrite(self, cond, fields, deftext, *fielddata, **opts): + '''do conditional write (primarily for plain formatter)''' + for k, v in zip(fields.split(), fielddata): + self._item[k] = v def plain(self, text, **opts): '''show raw text for non-templated mode''' pass @@ -51,6 +55,10 @@ pass def write(self, fields, deftext, *fielddata, **opts): self._ui.write(deftext % fielddata, **opts) + def condwrite(self, cond, fields, deftext, *fielddata, **opts): + '''do conditional write''' + if cond: + self._ui.write(deftext % fielddata, **opts) def plain(self, text, **opts): self._ui.write(text, **opts) def end(self):
--- a/mercurial/hbisect.py Mon Jan 14 23:14:45 2013 +0900 +++ b/mercurial/hbisect.py Sat Jan 19 17:24:33 2013 -0600 @@ -147,7 +147,7 @@ f = repo.opener("bisect.state", "w", atomictemp=True) wlock = repo.wlock() try: - for kind in state: + for kind in sorted(state): for node in state[kind]: f.write("%s %s\n" % (kind, hex(node))) f.close()
--- a/mercurial/help/config.txt Mon Jan 14 23:14:45 2013 +0900 +++ b/mercurial/help/config.txt Sat Jan 19 17:24:33 2013 -0600 @@ -850,14 +850,6 @@ ``prompt`` Always prompt for merge success, regardless of success reported by tool. -``checkchanged`` - True is equivalent to ``check = changed``. - Default: False - -``checkconflicts`` - True is equivalent to ``check = conflicts``. - Default: False - ``fixeol`` Attempt to fix up EOL changes caused by the merge tool. Default: False @@ -1295,6 +1287,10 @@ (DEPRECATED) Whether to allow .zip downloading of repository revisions. Default is False. This feature creates temporary files. +``archivesubrepos`` + Whether to recurse into subrepositories when archiving. Default is + False. + ``baseurl`` Base URL to use when publishing URLs in other locations, so third-party tools like email notification hooks can construct
--- a/mercurial/hg.py Mon Jan 14 23:14:45 2013 +0900 +++ b/mercurial/hg.py Sat Jan 19 17:24:33 2013 -0600 @@ -113,7 +113,7 @@ if not repo: raise util.Abort(_("repository '%s' is not local") % (path or peer.url())) - return repo + return repo.filtered('visible') def peer(uiorrepo, opts, path, create=False): '''return a repository peer for the specified path''' @@ -171,11 +171,14 @@ r = repository(ui, root) default = srcrepo.ui.config('paths', 'default') - if default: - fp = r.opener("hgrc", "w", text=True) - fp.write("[paths]\n") - fp.write("default = %s\n" % default) - fp.close() + if not default: + # set default to source for being able to clone subrepos + default = os.path.abspath(util.urllocalpath(origsource)) + fp = r.opener("hgrc", "w", text=True) + fp.write("[paths]\n") + fp.write("default = %s\n" % default) + fp.close() + r.ui.setconfig('paths', 'default', default) if update: r.ui.status(_("updating working directory\n")) @@ -288,17 +291,7 @@ elif os.listdir(dest): raise util.Abort(_("destination '%s' is not empty") % dest) - class DirCleanup(object): - def __init__(self, dir_): - self.rmtree = shutil.rmtree - self.dir_ = dir_ - def close(self): - self.dir_ = None - def cleanup(self): - if self.dir_: - self.rmtree(self.dir_, True) - - srclock = destlock = dircleanup = None + srclock = destlock = cleandir = None srcrepo = srcpeer.local() try: abspath = origsource @@ -306,7 +299,7 @@ abspath = os.path.abspath(util.urllocalpath(origsource)) if islocal(dest): - dircleanup = DirCleanup(dest) + cleandir = dest copy = False if (srcrepo and srcrepo.cancopy() and islocal(dest) @@ -330,13 +323,13 @@ os.mkdir(dest) else: # only clean up directories we create ourselves - dircleanup.dir_ = hgdir + cleandir = hgdir try: destpath = hgdir util.makedir(destpath, notindexed=True) except OSError, inst: if inst.errno == errno.EEXIST: - dircleanup.close() + cleandir = None raise util.Abort(_("destination '%s' already exists") % dest) raise @@ -364,7 +357,7 @@ # only pass ui when no srcrepo except OSError, inst: if inst.errno == errno.EEXIST: - dircleanup.close() + cleandir = None raise util.Abort(_("destination '%s' already exists") % dest) raise @@ -384,21 +377,21 @@ else: raise util.Abort(_("clone from remote to remote not supported")) - if dircleanup: - dircleanup.close() + cleandir = None # clone all bookmarks except divergent ones destrepo = destpeer.local() if destrepo and srcpeer.capable("pushkey"): rb = srcpeer.listkeys('bookmarks') + marks = destrepo._bookmarks for k, n in rb.iteritems(): try: m = destrepo.lookup(n) - destrepo._bookmarks[k] = m + marks[k] = m except error.RepoLookupError: pass if rb: - bookmarks.write(destrepo) + marks.write() elif srcrepo and destpeer.capable("pushkey"): for k, n in srcrepo._bookmarks.iteritems(): destpeer.pushkey('bookmarks', k, '', hex(n)) @@ -450,8 +443,8 @@ return srcpeer, destpeer finally: release(srclock, destlock) - if dircleanup is not None: - dircleanup.cleanup() + if cleandir is not None: + shutil.rmtree(cleandir, True) if srcpeer is not None: srcpeer.close()
--- a/mercurial/hgweb/common.py Mon Jan 14 23:14:45 2013 +0900 +++ b/mercurial/hgweb/common.py Sat Jan 19 17:24:33 2013 -0600 @@ -140,11 +140,11 @@ try: os.stat(path) ct = mimetypes.guess_type(path)[0] or "text/plain" - req.respond(HTTP_OK, ct, length = os.path.getsize(path)) fp = open(path, 'rb') data = fp.read() fp.close() - return data + req.respond(HTTP_OK, ct, body=data) + return "" except TypeError: raise ErrorResponse(HTTP_SERVER_ERROR, 'illegal filename') except OSError, err:
--- a/mercurial/hgweb/hgweb_mod.py Mon Jan 14 23:14:45 2013 +0900 +++ b/mercurial/hgweb/hgweb_mod.py Sat Jan 19 17:24:33 2013 -0600 @@ -24,6 +24,30 @@ 'pushkey': 'push', } +def makebreadcrumb(url): + '''Return a 'URL breadcrumb' list + + A 'URL breadcrumb' is a list of URL-name pairs, + corresponding to each of the path items on a URL. + This can be used to create path navigation entries. + ''' + if url.endswith('/'): + url = url[:-1] + relpath = url + if relpath.startswith('/'): + relpath = relpath[1:] + + breadcrumb = [] + urlel = url + pathitems = [''] + relpath.split('/') + for pathel in reversed(pathitems): + if not pathel or not urlel: + break + breadcrumb.append({'url': urlel, 'name': pathel}) + urlel = os.path.dirname(urlel) + return reversed(breadcrumb) + + class hgweb(object): def __init__(self, repo, name=None, baseui=None): if isinstance(repo, str): @@ -35,6 +59,7 @@ else: self.repo = repo + self.repo = self.repo.filtered('served') self.repo.ui.setconfig('ui', 'report_untrusted', 'off') self.repo.ui.setconfig('ui', 'nontty', 'true') hook.redirect(True) @@ -71,6 +96,7 @@ self.mtime = st.st_mtime self.size = st.st_size self.repo = hg.repository(self.repo.ui, self.repo.root) + self.repo = self.repo.filtered('served') self.maxchanges = int(self.config("web", "maxchanges", 10)) self.stripecount = int(self.config("web", "stripes", 1)) self.maxshortchanges = int(self.config("web", "maxshortchanges", @@ -134,8 +160,9 @@ '').lower() != '100-continue') or req.env.get('X-HgHttp2', '')): req.drain() - req.respond(inst, protocol.HGTYPE) - return '0\n%s\n' % inst.message + req.respond(inst, protocol.HGTYPE, + body='0\n%s\n' % inst.message) + return '' # translate user-visible url structure to internal structure @@ -285,7 +312,8 @@ "header": header, "footer": footer, "motd": motd, - "sessionvars": sessionvars + "sessionvars": sessionvars, + "pathdef": makebreadcrumb(req.url), }) return tmpl
--- a/mercurial/hgweb/hgwebdir_mod.py Mon Jan 14 23:14:45 2013 +0900 +++ b/mercurial/hgweb/hgwebdir_mod.py Sat Jan 19 17:24:33 2013 -0600 @@ -12,7 +12,7 @@ from mercurial import error, encoding from common import ErrorResponse, get_mtime, staticfile, paritygen, \ get_contact, HTTP_OK, HTTP_NOT_FOUND, HTTP_SERVER_ERROR -from hgweb_mod import hgweb +from hgweb_mod import hgweb, makebreadcrumb from request import wsgirequest import webutil @@ -310,7 +310,8 @@ description_sort="", lastchange=d, lastchange_sort=d[1]-d[0], - archives=[]) + archives=[], + isdirectory=True) seendirs.add(name) yield row @@ -394,6 +395,7 @@ self.updatereqenv(req.env) return tmpl("index", entries=entries, subdir=subdir, + pathdef=makebreadcrumb('/' + subdir), sortcolumn=sortcolumn, descending=descending, **dict(sort))
--- a/mercurial/hgweb/protocol.py Mon Jan 14 23:14:45 2013 +0900 +++ b/mercurial/hgweb/protocol.py Sat Jan 19 17:24:33 2013 -0600 @@ -75,23 +75,24 @@ p = webproto(req, repo.ui) rsp = wireproto.dispatch(repo, p, cmd) if isinstance(rsp, str): - req.respond(HTTP_OK, HGTYPE, length=len(rsp)) - return [rsp] + req.respond(HTTP_OK, HGTYPE, body=rsp) + return [] elif isinstance(rsp, wireproto.streamres): req.respond(HTTP_OK, HGTYPE) return rsp.gen elif isinstance(rsp, wireproto.pushres): val = p.restore() - req.respond(HTTP_OK, HGTYPE) - return ['%d\n%s' % (rsp.res, val)] + rsp = '%d\n%s' % (rsp.res, val) + req.respond(HTTP_OK, HGTYPE, body=rsp) + return [] elif isinstance(rsp, wireproto.pusherr): # drain the incoming bundle req.drain() p.restore() rsp = '0\n%s\n' % rsp.res - req.respond(HTTP_OK, HGTYPE, length=len(rsp)) - return [rsp] + req.respond(HTTP_OK, HGTYPE, body=rsp) + return [] elif isinstance(rsp, wireproto.ooberror): rsp = rsp.message - req.respond(HTTP_OK, HGERRTYPE, length=len(rsp)) - return [rsp] + req.respond(HTTP_OK, HGERRTYPE, body=rsp) + return []
--- a/mercurial/hgweb/request.py Mon Jan 14 23:14:45 2013 +0900 +++ b/mercurial/hgweb/request.py Sat Jan 19 17:24:33 2013 -0600 @@ -70,19 +70,23 @@ for s in util.filechunkiter(self.inp, limit=length): pass - def respond(self, status, type=None, filename=None, length=0): + def respond(self, status, type, filename=None, body=None): if self._start_response is not None: - - self.httphdr(type, filename, length) - if not self.headers: - raise RuntimeError("request.write called before headers sent") + self.headers.append(('Content-Type', type)) + if filename: + filename = (filename.split('/')[-1] + .replace('\\', '\\\\').replace('"', '\\"')) + self.headers.append(('Content-Disposition', + 'inline; filename="%s"' % filename)) + if body is not None: + self.headers.append(('Content-Length', str(len(body)))) for k, v in self.headers: if not isinstance(v, str): - raise TypeError('header value must be string: %r' % v) + raise TypeError('header value must be string: %r' % (v,)) if isinstance(status, ErrorResponse): - self.header(status.headers) + self.headers.extend(status.headers) if status.code == HTTP_NOT_MODIFIED: # RFC 2616 Section 10.3.5: 304 Not Modified has cases where # it MUST NOT include any headers other than these and no @@ -99,13 +103,12 @@ self.server_write = self._start_response(status, self.headers) self._start_response = None self.headers = [] + if body is not None: + self.write(body) + self.server_write = None def write(self, thing): - if util.safehasattr(thing, "__iter__"): - for part in thing: - self.write(part) - else: - thing = str(thing) + if thing: try: self.server_write(thing) except socket.error, inst: @@ -122,22 +125,6 @@ def close(self): return None - def header(self, headers=[('Content-Type','text/html')]): - self.headers.extend(headers) - - def httphdr(self, type=None, filename=None, length=0, headers={}): - headers = headers.items() - if type is not None: - headers.append(('Content-Type', type)) - if filename: - filename = (filename.split('/')[-1] - .replace('\\', '\\\\').replace('"', '\\"')) - headers.append(('Content-Disposition', - 'inline; filename="%s"' % filename)) - if length: - headers.append(('Content-Length', str(length))) - self.header(headers) - def wsgiapplication(app_maker): '''For compatibility with old CGI scripts. A plain hgweb() or hgwebdir() can and should now be used as a WSGI application.'''
--- a/mercurial/hgweb/server.py Mon Jan 14 23:14:45 2013 +0900 +++ b/mercurial/hgweb/server.py Sat Jan 19 17:24:33 2013 -0600 @@ -129,13 +129,16 @@ SocketServer.ForkingMixIn) env['wsgi.run_once'] = 0 - self.close_connection = True self.saved_status = None self.saved_headers = [] self.sent_headers = False self.length = None + self._chunked = None for chunk in self.server.application(env, self._start_response): self._write(chunk) + if not self.sent_headers: + self.send_headers() + self._done() def send_headers(self): if not self.saved_status: @@ -144,20 +147,20 @@ saved_status = self.saved_status.split(None, 1) saved_status[0] = int(saved_status[0]) self.send_response(*saved_status) - should_close = True + self.length = None + self._chunked = False for h in self.saved_headers: self.send_header(*h) if h[0].lower() == 'content-length': - should_close = False self.length = int(h[1]) - # The value of the Connection header is a list of case-insensitive - # tokens separated by commas and optional whitespace. - if 'close' in [token.strip().lower() for token in - self.headers.get('connection', '').split(',')]: - should_close = True - if should_close: - self.send_header('Connection', 'close') - self.close_connection = should_close + if (self.length is None and + saved_status[0] != common.HTTP_NOT_MODIFIED): + self._chunked = (not self.close_connection and + self.request_version == "HTTP/1.1") + if self._chunked: + self.send_header('Transfer-Encoding', 'chunked') + else: + self.send_header('Connection', 'close') self.end_headers() self.sent_headers = True @@ -180,9 +183,16 @@ raise AssertionError("Content-length header sent, but more " "bytes than specified are being written.") self.length = self.length - len(data) + elif self._chunked and data: + data = '%x\r\n%s\r\n' % (len(data), data) self.wfile.write(data) self.wfile.flush() + def _done(self): + if self._chunked: + self.wfile.write('0\r\n\r\n') + self.wfile.flush() + class _httprequesthandleropenssl(_httprequesthandler): """HTTPS handler based on pyOpenSSL"""
--- a/mercurial/hgweb/webcommands.py Mon Jan 14 23:14:45 2013 +0900 +++ b/mercurial/hgweb/webcommands.py Sat Jan 19 17:24:33 2013 -0600 @@ -14,6 +14,7 @@ from common import HTTP_OK, HTTP_FORBIDDEN, HTTP_NOT_FOUND from mercurial import graphmod, patch from mercurial import help as helpmod +from mercurial import scmutil from mercurial.i18n import _ # __all__ is populated with the allowed commands. Be sure to add to it if @@ -60,8 +61,8 @@ if mt.startswith('text/'): mt += '; charset="%s"' % encoding.encoding - req.respond(HTTP_OK, mt, path, len(text)) - return [text] + req.respond(HTTP_OK, mt, path, body=text) + return [] def _filerevision(web, tmpl, fctx): f = fctx.path() @@ -193,34 +194,37 @@ except error.RepoError: return _search(web, req, tmpl) # XXX redirect to 404 page? - def changelist(limit=0, **map): + def changelist(latestonly, **map): l = [] # build a list in forward order for efficiency - for i in xrange(start, end): + revs = [] + if start < end: + revs = web.repo.changelog.revs(start, end - 1) + if latestonly: + for r in revs: + pass + revs = (r,) + for i in revs: ctx = web.repo[i] n = ctx.node() showtags = webutil.showtag(web.repo, tmpl, 'changelogtag', n) files = webutil.listfilediffs(tmpl, ctx.files(), n, web.maxfiles) - l.insert(0, {"parity": parity.next(), - "author": ctx.user(), - "parent": webutil.parents(ctx, i - 1), - "child": webutil.children(ctx, i + 1), - "changelogtag": showtags, - "desc": ctx.description(), - "date": ctx.date(), - "files": files, - "rev": i, - "node": hex(n), - "tags": webutil.nodetagsdict(web.repo, n), - "bookmarks": webutil.nodebookmarksdict(web.repo, n), - "inbranch": webutil.nodeinbranch(web.repo, ctx), - "branches": webutil.nodebranchdict(web.repo, ctx) - }) - - if limit > 0: - l = l[:limit] - - for e in l: + l.append({"parity": parity.next(), + "author": ctx.user(), + "parent": webutil.parents(ctx, i - 1), + "child": webutil.children(ctx, i + 1), + "changelogtag": showtags, + "desc": ctx.description(), + "date": ctx.date(), + "files": files, + "rev": i, + "node": hex(n), + "tags": webutil.nodetagsdict(web.repo, n), + "bookmarks": webutil.nodebookmarksdict(web.repo, n), + "inbranch": webutil.nodeinbranch(web.repo, ctx), + "branches": webutil.nodebranchdict(web.repo, ctx) + }) + for e in reversed(l): yield e revcount = shortlog and web.maxshortchanges or web.maxchanges @@ -241,12 +245,12 @@ pos = end - 1 parity = paritygen(web.stripecount, offset=start - end) - changenav = webutil.revnavgen(pos, revcount, count, web.repo.changectx) + changenav = webutil.revnav(web.repo).gen(pos, revcount, count) return tmpl(shortlog and 'shortlog' or 'changelog', changenav=changenav, node=ctx.hex(), rev=pos, changesets=count, - entries=lambda **x: changelist(limit=0,**x), - latestentry=lambda **x: changelist(limit=1,**x), + entries=lambda **x: changelist(latestonly=False, **x), + latestentry=lambda **x: changelist(latestonly=True, **x), archives=web.archivelist("tip"), revcount=revcount, morevars=morevars, lessvars=lessvars) @@ -255,6 +259,9 @@ def changeset(web, req, tmpl): ctx = webutil.changectx(web.repo, req) + basectx = webutil.basechangectx(web.repo, req) + if basectx is None: + basectx = ctx.p1() showtags = webutil.showtag(web.repo, tmpl, 'changesettag', ctx.node()) showbookmarks = webutil.showbookmark(web.repo, tmpl, 'changesetbookmark', ctx.node()) @@ -273,10 +280,10 @@ style = req.form['style'][0] parity = paritygen(web.stripecount) - diffs = webutil.diffs(web.repo, tmpl, ctx, None, parity, style) + diffs = webutil.diffs(web.repo, tmpl, ctx, basectx, None, parity, style) parity = paritygen(web.stripecount) - diffstatgen = webutil.diffstatgen(ctx) + diffstatgen = webutil.diffstatgen(ctx, basectx) diffstat = webutil.diffstat(tmpl, ctx, diffstatgen, parity) return tmpl('changeset', @@ -285,6 +292,7 @@ node=ctx.hex(), parent=webutil.parents(ctx), child=webutil.children(ctx), + currentbaseline=basectx.hex(), changesettag=showtags, changesetbookmark=showbookmarks, changesetbranch=showbranch, @@ -397,14 +405,13 @@ i = list(reversed(web.repo.tagslist())) parity = paritygen(web.stripecount) - def entries(notip=False, limit=0, **map): - count = 0 - for k, n in i: - if notip and k == "tip": - continue - if limit > 0 and count >= limit: - continue - count = count + 1 + def entries(notip, latestonly, **map): + t = i + if notip: + t = [(k, n) for k, n in i if k != "tip"] + if latestonly: + t = t[:1] + for k, n in t: yield {"parity": parity.next(), "tag": k, "date": web.repo[n].date(), @@ -412,20 +419,20 @@ return tmpl("tags", node=hex(web.repo.changelog.tip()), - entries=lambda **x: entries(False, 0, **x), - entriesnotip=lambda **x: entries(True, 0, **x), - latestentry=lambda **x: entries(True, 1, **x)) + entries=lambda **x: entries(False, False, **x), + entriesnotip=lambda **x: entries(True, False, **x), + latestentry=lambda **x: entries(True, True, **x)) def bookmarks(web, req, tmpl): i = web.repo._bookmarks.items() parity = paritygen(web.stripecount) - def entries(limit=0, **map): - count = 0 - for k, n in sorted(i): - if limit > 0 and count >= limit: - continue - count = count + 1 + def entries(latestonly, **map): + if latestonly: + t = [min(i)] + else: + t = sorted(i) + for k, n in t: yield {"parity": parity.next(), "bookmark": k, "date": web.repo[n].date(), @@ -433,8 +440,8 @@ return tmpl("bookmarks", node=hex(web.repo.changelog.tip()), - entries=lambda **x: entries(0, **x), - latestentry=lambda **x: entries(1, **x)) + entries=lambda **x: entries(latestonly=False, **x), + latestentry=lambda **x: entries(latestonly=True, **x)) def branches(web, req, tmpl): tips = [] @@ -515,7 +522,7 @@ n = ctx.node() hn = hex(n) - l.insert(0, tmpl( + l.append(tmpl( 'shortlogentry', parity=parity.next(), author=ctx.user(), @@ -528,6 +535,7 @@ inbranch=webutil.nodeinbranch(web.repo, ctx), branches=webutil.nodebranchdict(web.repo, ctx))) + l.reverse() yield l tip = web.repo['tip'] @@ -569,7 +577,7 @@ if 'style' in req.form: style = req.form['style'][0] - diffs = webutil.diffs(web.repo, tmpl, ctx, [path], parity, style) + diffs = webutil.diffs(web.repo, tmpl, ctx, None, [path], parity, style) rename = fctx and webutil.renamelink(fctx) or [] ctx = fctx and fctx or ctx return tmpl("filediff", @@ -736,41 +744,42 @@ end = min(count, start + revcount) # last rev on this page parity = paritygen(web.stripecount, offset=start - end) - def entries(limit=0, **map): + def entries(latestonly, **map): l = [] repo = web.repo - for i in xrange(start, end): + revs = repo.changelog.revs(start, end - 1) + if latestonly: + for r in revs: + pass + revs = (r,) + for i in revs: iterfctx = fctx.filectx(i) - l.insert(0, {"parity": parity.next(), - "filerev": i, - "file": f, - "node": iterfctx.hex(), - "author": iterfctx.user(), - "date": iterfctx.date(), - "rename": webutil.renamelink(iterfctx), - "parent": webutil.parents(iterfctx), - "child": webutil.children(iterfctx), - "desc": iterfctx.description(), - "tags": webutil.nodetagsdict(repo, iterfctx.node()), - "bookmarks": webutil.nodebookmarksdict( - repo, iterfctx.node()), - "branch": webutil.nodebranchnodefault(iterfctx), - "inbranch": webutil.nodeinbranch(repo, iterfctx), - "branches": webutil.nodebranchdict(repo, iterfctx)}) - - if limit > 0: - l = l[:limit] - - for e in l: + l.append({"parity": parity.next(), + "filerev": i, + "file": f, + "node": iterfctx.hex(), + "author": iterfctx.user(), + "date": iterfctx.date(), + "rename": webutil.renamelink(iterfctx), + "parent": webutil.parents(iterfctx), + "child": webutil.children(iterfctx), + "desc": iterfctx.description(), + "tags": webutil.nodetagsdict(repo, iterfctx.node()), + "bookmarks": webutil.nodebookmarksdict( + repo, iterfctx.node()), + "branch": webutil.nodebranchnodefault(iterfctx), + "inbranch": webutil.nodeinbranch(repo, iterfctx), + "branches": webutil.nodebranchdict(repo, iterfctx)}) + for e in reversed(l): yield e - nodefunc = lambda x: fctx.filectx(fileid=x) - nav = webutil.revnavgen(end - 1, revcount, count, nodefunc) + revnav = webutil.filerevnav(web.repo, fctx.path()) + nav = revnav.gen(end - 1, revcount, count) return tmpl("filelog", file=f, node=fctx.hex(), nav=nav, - entries=lambda **x: entries(limit=0, **x), - latestentry=lambda **x: entries(limit=1, **x), + entries=lambda **x: entries(latestonly=False, **x), + latestentry=lambda **x: entries(latestonly=True, **x), revcount=revcount, morevars=morevars, lessvars=lessvars) def archive(web, req, tmpl): @@ -795,14 +804,17 @@ name = "%s-%s" % (reponame, arch_version) mimetype, artype, extension, encoding = web.archive_specs[type_] headers = [ - ('Content-Type', mimetype), ('Content-Disposition', 'attachment; filename=%s%s' % (name, extension)) - ] + ] if encoding: headers.append(('Content-Encoding', encoding)) - req.header(headers) - req.respond(HTTP_OK) - archival.archive(web.repo, req, cnode, artype, prefix=name) + req.headers.extend(headers) + req.respond(HTTP_OK, mimetype) + + ctx = webutil.changectx(web.repo, req) + archival.archive(web.repo, req, cnode, artype, prefix=name, + matchfn=scmutil.match(ctx, []), + subrepos=web.configbool("web", "archivesubrepos")) return [] @@ -843,10 +855,13 @@ uprev = min(max(0, count - 1), rev + revcount) downrev = max(0, rev - revcount) - changenav = webutil.revnavgen(pos, revcount, count, web.repo.changectx) + changenav = webutil.revnav(web.repo).gen(pos, revcount, count) - dag = graphmod.dagwalker(web.repo, range(start, end)[::-1]) - tree = list(graphmod.colored(dag, web.repo)) + tree = [] + if start < end: + revs = list(web.repo.changelog.revs(end - 1, start)) + dag = graphmod.dagwalker(web.repo, revs) + tree = list(graphmod.colored(dag, web.repo)) def getcolumns(tree): cols = 0
--- a/mercurial/hgweb/webutil.py Mon Jan 14 23:14:45 2013 +0900 +++ b/mercurial/hgweb/webutil.py Sat Jan 19 17:24:33 2013 -0600 @@ -24,46 +24,100 @@ return "/" return up + "/" -def revnavgen(pos, pagelen, limit, nodefunc): - def seq(factor, limit=None): - if limit: - yield limit - if limit >= 20 and limit <= 40: - yield 50 - else: - yield 1 * factor - yield 3 * factor - for f in seq(factor * 10): - yield f +def _navseq(step, firststep=None): + if firststep: + yield firststep + if firststep >= 20 and firststep <= 40: + firststep = 50 + yield firststep + assert step > 0 + assert firststep > 0 + while step <= firststep: + step *= 10 + while True: + yield 1 * step + yield 3 * step + step *= 10 + +class revnav(object): + + def __init__(self, repo): + """Navigation generation object - navbefore = [] - navafter = [] + :repo: repo object we generate nav for + """ + # used for hex generation + self._revlog = repo.changelog + + def __nonzero__(self): + """return True if any revision to navigate over""" + try: + self._revlog.node(0) + return True + except error.RepoError: + return False + + def hex(self, rev): + return hex(self._revlog.node(rev)) + + def gen(self, pos, pagelen, limit): + """computes label and revision id for navigation link + + :pos: is the revision relative to which we generate navigation. + :pagelen: the size of each navigation page + :limit: how far shall we link - last = 0 - for f in seq(1, pagelen): - if f < pagelen or f <= last: - continue - if f > limit: - break - last = f - if pos + f < limit: - navafter.append(("+%d" % f, hex(nodefunc(pos + f).node()))) - if pos - f >= 0: - navbefore.insert(0, ("-%d" % f, hex(nodefunc(pos - f).node()))) + The return is: + - a single element tuple + - containing a dictionary with a `before` and `after` key + - values are generator functions taking arbitrary number of kwargs + - yield items are dictionaries with `label` and `node` keys + """ + if not self: + # empty repo + return ({'before': (), 'after': ()},) + + targets = [] + for f in _navseq(1, pagelen): + if f > limit: + break + targets.append(pos + f) + targets.append(pos - f) + targets.sort() - navafter.append(("tip", "tip")) - try: - navbefore.insert(0, ("(0)", hex(nodefunc('0').node()))) - except error.RepoError: - pass + navbefore = [("(0)", self.hex(0))] + navafter = [] + for rev in targets: + if rev not in self._revlog: + continue + if pos < rev < limit: + navafter.append(("+%d" % f, self.hex(rev))) + if 0 < rev < pos: + navbefore.append(("-%d" % f, self.hex(rev))) + + + navafter.append(("tip", "tip")) + + data = lambda i: {"label": i[0], "node": i[1]} + return ({'before': lambda **map: (data(i) for i in navbefore), + 'after': lambda **map: (data(i) for i in navafter)},) - def gen(l): - def f(**map): - for label, node in l: - yield {"label": label, "node": node} - return f +class filerevnav(revnav): + + def __init__(self, repo, path): + """Navigation generation object - return (dict(before=gen(navbefore), after=gen(navafter)),) + :repo: repo object we generate nav for + :path: path of the file we generate nav for + """ + # used for iteration + self._changelog = repo.unfiltered().changelog + # used for hex generation + self._revlog = repo.file(path) + + def hex(self, rev): + return hex(self._changelog.node(self._revlog.linkrev(rev))) + def _siblings(siblings=[], hiderev=None): siblings = [s for s in siblings if s.node() != nullid] @@ -140,13 +194,7 @@ path = path.lstrip('/') return scmutil.canonpath(repo.root, '', path) -def changectx(repo, req): - changeid = "tip" - if 'node' in req.form: - changeid = req.form['node'][0] - elif 'manifest' in req.form: - changeid = req.form['manifest'][0] - +def changeidctx (repo, changeid): try: ctx = repo[changeid] except error.RepoError: @@ -155,6 +203,28 @@ return ctx +def changectx (repo, req): + changeid = "tip" + if 'node' in req.form: + changeid = req.form['node'][0] + ipos=changeid.find(':') + if ipos != -1: + changeid = changeid[(ipos + 1):] + elif 'manifest' in req.form: + changeid = req.form['manifest'][0] + + return changeidctx(repo, changeid) + +def basechangectx(repo, req): + if 'node' in req.form: + changeid = req.form['node'][0] + ipos=changeid.find(':') + if ipos != -1: + changeid = changeid[:ipos] + return changeidctx(repo, changeid) + + return None + def filectx(repo, req): if 'file' not in req.form: raise ErrorResponse(HTTP_NOT_FOUND, 'file not given') @@ -178,7 +248,7 @@ if len(files) > max: yield tmpl('fileellipses') -def diffs(repo, tmpl, ctx, files, parity, style): +def diffs(repo, tmpl, ctx, basectx, files, parity, style): def countgen(): start = 1 @@ -209,8 +279,11 @@ m = match.always(repo.root, repo.getcwd()) diffopts = patch.diffopts(repo.ui, untrusted=True) - parents = ctx.parents() - node1 = parents and parents[0].node() or nullid + if basectx is None: + parents = ctx.parents() + node1 = parents and parents[0].node() or nullid + else: + node1 = basectx.node() node2 = ctx.node() block = [] @@ -274,10 +347,10 @@ for oc in s.get_grouped_opcodes(n=context): yield tmpl('comparisonblock', lines=getblock(oc)) -def diffstatgen(ctx): +def diffstatgen(ctx, basectx): '''Generator function that provides the diffstat data.''' - stats = patch.diffstatdata(util.iterlines(ctx.diff())) + stats = patch.diffstatdata(util.iterlines(ctx.diff(basectx))) maxname, maxtotal, addtotal, removetotal, binary = patch.diffstatsum(stats) while True: yield stats, maxname, maxtotal, addtotal, removetotal, binary @@ -321,7 +394,7 @@ return sessionvars(copy.copy(self.vars), self.start) def __iter__(self): separator = self.start - for key, value in self.vars.iteritems(): + for key, value in sorted(self.vars.iteritems()): yield {'name': key, 'value': str(value), 'separator': separator} separator = '&'
--- a/mercurial/hook.py Mon Jan 14 23:14:45 2013 +0900 +++ b/mercurial/hook.py Sat Jan 19 17:24:33 2013 -0600 @@ -7,7 +7,7 @@ from i18n import _ import os, sys -import extensions, util +import extensions, util, demandimport def _pythonhook(ui, repo, name, hname, funcname, args, throw): '''call python hook. hook is callable object, looked up as @@ -35,13 +35,17 @@ sys.path = sys.path[:] + [modpath] modname = modfile try: + demandimport.disable() obj = __import__(modname) + demandimport.enable() except ImportError: e1 = sys.exc_type, sys.exc_value, sys.exc_traceback try: # extensions are loaded with hgext_ prefix obj = __import__("hgext_%s" % modname) + demandimport.enable() except ImportError: + demandimport.enable() e2 = sys.exc_type, sys.exc_value, sys.exc_traceback if ui.tracebackflag: ui.warn(_('exception from first failed import attempt:\n'))
--- a/mercurial/httpclient/socketutil.py Mon Jan 14 23:14:45 2013 +0900 +++ b/mercurial/httpclient/socketutil.py Sat Jan 19 17:24:33 2013 -0600 @@ -70,7 +70,7 @@ continue break if not sock: - raise socket.error, msg + raise socket.error(msg) return sock if ssl:
--- a/mercurial/ignore.py Mon Jan 14 23:14:45 2013 +0900 +++ b/mercurial/ignore.py Sat Jan 19 17:24:33 2013 -0600 @@ -46,12 +46,32 @@ pat = line break elif line.startswith(s+':'): - pat = rels + line[len(s)+1:] + pat = rels + line[len(s) + 1:] break patterns.append(pat) return patterns, warnings +def readpats(root, files, warn): + '''return a dict mapping ignore-file-name to list-of-patterns''' + + pats = {} + for f in files: + if f in pats: + continue + try: + pats[f] = [] + fp = open(f) + pats[f], warnings = ignorepats(fp) + fp.close() + for warning in warnings: + warn("%s: %s\n" % (f, warning)) + except IOError, inst: + if f != files[0]: + warn(_("skipping unreadable ignore file '%s': %s\n") % + (f, inst.strerror)) + return [(f, pats[f]) for f in files if f in pats] + def ignore(root, files, warn): '''return matcher covering patterns in 'files'. @@ -72,22 +92,10 @@ glob:pattern # non-rooted glob pattern # pattern of the current default type''' - pats = {} - for f in files: - try: - pats[f] = [] - fp = open(f) - pats[f], warnings = ignorepats(fp) - fp.close() - for warning in warnings: - warn("%s: %s\n" % (f, warning)) - except IOError, inst: - if f != files[0]: - warn(_("skipping unreadable ignore file '%s': %s\n") % - (f, inst.strerror)) + pats = readpats(root, files, warn) allpats = [] - for patlist in pats.values(): + for f, patlist in pats: allpats.extend(patlist) if not allpats: return util.never @@ -96,7 +104,7 @@ ignorefunc = match.match(root, '', [], allpats) except util.Abort: # Re-raise an exception where the src is the right file - for f, patlist in pats.iteritems(): + for f, patlist in pats: try: match.match(root, '', [], patlist) except util.Abort, inst:
--- a/mercurial/localrepo.py Mon Jan 14 23:14:45 2013 +0900 +++ b/mercurial/localrepo.py Sat Jan 19 17:24:33 2013 -0600 @@ -4,9 +4,9 @@ # # This software may be used and distributed according to the terms of the # GNU General Public License version 2 or any later version. -from node import bin, hex, nullid, nullrev, short +from node import hex, nullid, short from i18n import _ -import peer, changegroup, subrepo, discovery, pushkey, obsolete +import peer, changegroup, subrepo, discovery, pushkey, obsolete, repoview import changelog, dirstate, filelog, manifest, context, bookmarks, phases import lock, transaction, store, encoding, base85 import scmutil, util, extensions, hook, error, revset @@ -15,14 +15,49 @@ import tags as tagsmod from lock import release import weakref, errno, os, time, inspect +import branchmap propertycache = util.propertycache filecache = scmutil.filecache -class storecache(filecache): +class repofilecache(filecache): + """All filecache usage on repo are done for logic that should be unfiltered + """ + + def __get__(self, repo, type=None): + return super(repofilecache, self).__get__(repo.unfiltered(), type) + def __set__(self, repo, value): + return super(repofilecache, self).__set__(repo.unfiltered(), value) + def __delete__(self, repo): + return super(repofilecache, self).__delete__(repo.unfiltered()) + +class storecache(repofilecache): """filecache for files in the store""" def join(self, obj, fname): return obj.sjoin(fname) +class unfilteredpropertycache(propertycache): + """propertycache that apply to unfiltered repo only""" + + def __get__(self, repo, type=None): + return super(unfilteredpropertycache, self).__get__(repo.unfiltered()) + +class filteredpropertycache(propertycache): + """propertycache that must take filtering in account""" + + def cachevalue(self, obj, value): + object.__setattr__(obj, self.name, value) + + +def hasunfilteredcache(repo, name): + """check if an repo and a unfilteredproperty cached value for <name>""" + return name in vars(repo.unfiltered()) + +def unfilteredmethod(orig): + """decorate method that always need to be run on unfiltered version""" + def wrapper(repo, *args, **kwargs): + return orig(repo.unfiltered(), *args, **kwargs) + return wrapper + MODERNCAPS = set(('lookup', 'branchmap', 'pushkey', 'known', 'getbundle')) LEGACYCAPS = MODERNCAPS.union(set(['changegroupsubset'])) @@ -31,7 +66,7 @@ def __init__(self, repo, caps=MODERNCAPS): peer.peerrepository.__init__(self) - self._repo = repo + self._repo = repo.filtered('served') self.ui = repo.ui self._caps = repo._restrictcapabilities(caps) self.requirements = repo.requirements @@ -56,10 +91,10 @@ return self._repo.lookup(key) def branchmap(self): - return discovery.visiblebranchmap(self._repo) + return self._repo.branchmap() def heads(self): - return discovery.visibleheads(self._repo) + return self._repo.heads() def known(self, nodes): return self._repo.known(nodes) @@ -112,6 +147,7 @@ 'dotencode')) openerreqs = set(('revlogv1', 'generaldelta')) requirements = ['revlogv1'] + filtername = None def _baserequirements(self, create): return self.requirements[:] @@ -193,8 +229,7 @@ self._writerequirements() - self._branchcache = None - self._branchcachetip = None + self._branchcaches = {} self.filterpats = {} self._datafilters = {} self._transref = self._lockref = self._wlockref = None @@ -205,6 +240,15 @@ # Maps a property name to its util.filecacheentry self._filecache = {} + # hold sets of revision to be filtered + # should be cleared when something might have changed the filter value: + # - new changesets, + # - phase change, + # - new obsolescence marker, + # - working directory parent change, + # - bookmark changes + self.filteredrevcache = {} + def close(self): pass @@ -218,7 +262,7 @@ def _writerequirements(self): reqfile = self.opener("requires", "w") - for r in self.requirements: + for r in sorted(self.requirements): reqfile.write("%s\n" % r) reqfile.close() @@ -263,17 +307,28 @@ def peer(self): return localpeer(self) # not cached to avoid reference cycle - @filecache('bookmarks') + def unfiltered(self): + """Return unfiltered version of the repository + + Intended to be ovewritten by filtered repo.""" + return self + + def filtered(self, name): + """Return a filtered version of a repository""" + # build a new class with the mixin and the current class + # (possibily subclass of the repo) + class proxycls(repoview.repoview, self.unfiltered().__class__): + pass + return proxycls(self, name) + + @repofilecache('bookmarks') def _bookmarks(self): - return bookmarks.read(self) + return bookmarks.bmstore(self) - @filecache('bookmarks.current') + @repofilecache('bookmarks.current') def _bookmarkcurrent(self): return bookmarks.readcurrent(self) - def _writebookmarks(self, marks): - bookmarks.write(self) - def bookmarkheads(self, bookmark): name = bookmark.split('@', 1)[0] heads = [] @@ -295,27 +350,6 @@ self.ui.warn(msg % len(list(store))) return store - @propertycache - def hiddenrevs(self): - """hiddenrevs: revs that should be hidden by command and tools - - This set is carried on the repo to ease initialization and lazy - loading; it'll probably move back to changelog for efficiency and - consistency reasons. - - Note that the hiddenrevs will needs invalidations when - - a new changesets is added (possible unstable above extinct) - - a new obsolete marker is added (possible new extinct changeset) - - hidden changesets cannot have non-hidden descendants - """ - hidden = set() - if self.obsstore: - ### hide extinct changeset that are not accessible by any mean - hiddenquery = 'extinct() - ::(. + bookmark())' - hidden.update(self.revs(hiddenquery)) - return hidden - @storecache('00changelog.i') def changelog(self): c = changelog.changelog(self.sopener) @@ -329,7 +363,7 @@ def manifest(self): return manifest.manifest(self.sopener) - @filecache('dirstate') + @repofilecache('dirstate') def dirstate(self): warned = [0] def validate(node): @@ -385,6 +419,7 @@ def hook(self, name, throw=False, **args): return hook.hook(self.ui, self, name, throw, **args) + @unfilteredmethod def _tag(self, names, node, message, local, user, date, extra={}): if isinstance(names, str): names = (names,) @@ -482,7 +517,7 @@ self.tags() # instantiate the cache self._tag(names, node, message, local, user, date) - @propertycache + @filteredpropertycache def _tagscache(self): '''Returns a tagscache object that contains various tags related caches.''' @@ -594,43 +629,10 @@ marks.append(bookmark) return sorted(marks) - def _branchtags(self, partial, lrev): - # TODO: rename this function? - tiprev = len(self) - 1 - if lrev != tiprev: - ctxgen = (self[r] for r in self.changelog.revs(lrev + 1, tiprev)) - self._updatebranchcache(partial, ctxgen) - self._writebranchcache(partial, self.changelog.tip(), tiprev) - - return partial - - def updatebranchcache(self): - tip = self.changelog.tip() - if self._branchcache is not None and self._branchcachetip == tip: - return - - oldtip = self._branchcachetip - self._branchcachetip = tip - if oldtip is None or oldtip not in self.changelog.nodemap: - partial, last, lrev = self._readbranchcache() - else: - lrev = self.changelog.rev(oldtip) - partial = self._branchcache - - self._branchtags(partial, lrev) - # this private cache holds all heads (not just the branch tips) - self._branchcache = partial - def branchmap(self): '''returns a dictionary {branch: [branchheads]}''' - if self.changelog.filteredrevs: - # some changeset are excluded we can't use the cache - branchmap = {} - self._updatebranchcache(branchmap, (self[r] for r in self)) - return branchmap - else: - self.updatebranchcache() - return self._branchcache + branchmap.updatecache(self) + return self._branchcaches[self.filtername] def _branchtip(self, heads): @@ -656,109 +658,6 @@ bt[bn] = self._branchtip(heads) return bt - def _readbranchcache(self): - partial = {} - try: - f = self.opener("cache/branchheads") - lines = f.read().split('\n') - f.close() - except (IOError, OSError): - return {}, nullid, nullrev - - try: - last, lrev = lines.pop(0).split(" ", 1) - last, lrev = bin(last), int(lrev) - if lrev >= len(self) or self[lrev].node() != last: - # invalidate the cache - raise ValueError('invalidating branch cache (tip differs)') - for l in lines: - if not l: - continue - node, label = l.split(" ", 1) - label = encoding.tolocal(label.strip()) - if not node in self: - raise ValueError('invalidating branch cache because node '+ - '%s does not exist' % node) - partial.setdefault(label, []).append(bin(node)) - except KeyboardInterrupt: - raise - except Exception, inst: - if self.ui.debugflag: - self.ui.warn(str(inst), '\n') - partial, last, lrev = {}, nullid, nullrev - return partial, last, lrev - - def _writebranchcache(self, branches, tip, tiprev): - try: - f = self.opener("cache/branchheads", "w", atomictemp=True) - f.write("%s %s\n" % (hex(tip), tiprev)) - for label, nodes in branches.iteritems(): - for node in nodes: - f.write("%s %s\n" % (hex(node), encoding.fromlocal(label))) - f.close() - except (IOError, OSError): - pass - - def _updatebranchcache(self, partial, ctxgen): - """Given a branchhead cache, partial, that may have extra nodes or be - missing heads, and a generator of nodes that are at least a superset of - heads missing, this function updates partial to be correct. - """ - # collect new branch entries - newbranches = {} - for c in ctxgen: - newbranches.setdefault(c.branch(), []).append(c.node()) - # if older branchheads are reachable from new ones, they aren't - # really branchheads. Note checking parents is insufficient: - # 1 (branch a) -> 2 (branch b) -> 3 (branch a) - for branch, newnodes in newbranches.iteritems(): - bheads = partial.setdefault(branch, []) - # Remove candidate heads that no longer are in the repo (e.g., as - # the result of a strip that just happened). Avoid using 'node in - # self' here because that dives down into branchcache code somewhat - # recursively. - bheadrevs = [self.changelog.rev(node) for node in bheads - if self.changelog.hasnode(node)] - newheadrevs = [self.changelog.rev(node) for node in newnodes - if self.changelog.hasnode(node)] - ctxisnew = bheadrevs and min(newheadrevs) > max(bheadrevs) - # Remove duplicates - nodes that are in newheadrevs and are already - # in bheadrevs. This can happen if you strip a node whose parent - # was already a head (because they're on different branches). - bheadrevs = sorted(set(bheadrevs).union(newheadrevs)) - - # Starting from tip means fewer passes over reachable. If we know - # the new candidates are not ancestors of existing heads, we don't - # have to examine ancestors of existing heads - if ctxisnew: - iterrevs = sorted(newheadrevs) - else: - iterrevs = list(bheadrevs) - - # This loop prunes out two kinds of heads - heads that are - # superseded by a head in newheadrevs, and newheadrevs that are not - # heads because an existing head is their descendant. - while iterrevs: - latest = iterrevs.pop() - if latest not in bheadrevs: - continue - ancestors = set(self.changelog.ancestors([latest], - bheadrevs[0])) - if ancestors: - bheadrevs = [b for b in bheadrevs if b not in ancestors] - partial[branch] = [self.changelog.node(rev) for rev in bheadrevs] - - # There may be branches that cease to exist when the last commit in the - # branch was stripped. This code filters them out. Note that the - # branch that ceased to exist may not be in newbranches because - # newbranches is the set of candidate heads, which when you strip the - # last commit in a branch will be the parent branch. - for branch in partial.keys(): - nodes = [head for head in partial[branch] - if self.changelog.hasnode(head)] - if not nodes: - del partial[branch] - def lookup(self, key): return self[key].node() @@ -865,11 +764,11 @@ return data - @propertycache + @unfilteredpropertycache def _encodefilterpats(self): return self._loadfilter('encode') - @propertycache + @unfilteredpropertycache def _decodefilterpats(self): return self._loadfilter('decode') @@ -964,6 +863,7 @@ finally: release(lock, wlock) + @unfilteredmethod # Until we get smarter cache management def _rollback(self, dryrun, force): ui = self.ui try: @@ -995,6 +895,7 @@ return 0 parents = self.dirstate.parents() + self.destroying() transaction.rollback(self.sopener, self.sjoin('undo'), ui.warn) if os.path.exists(self.join('undo.bookmarks')): util.rename(self.join('undo.bookmarks'), @@ -1004,9 +905,6 @@ self.sjoin('phaseroots')) self.invalidate() - # Discard all cache entries to force reloading everything. - self._filecache.clear() - parentgone = (parents[0] not in self.changelog.nodemap or parents[1] not in self.changelog.nodemap) if parentgone: @@ -1034,16 +932,16 @@ return 0 def invalidatecaches(self): - def delcache(name): - try: - delattr(self, name) - except AttributeError: - pass + + if '_tagscache' in vars(self): + # can't use delattr on proxy + del self.__dict__['_tagscache'] - delcache('_tagscache') + self.unfiltered()._branchcaches.clear() + self.invalidatevolatilesets() - self._branchcache = None # in UTF-8 - self._branchcachetip = None + def invalidatevolatilesets(self): + self.filteredrevcache.clear() obsolete.clearobscaches(self) def invalidatedirstate(self): @@ -1055,22 +953,23 @@ rereads the dirstate. Use dirstate.invalidate() if you want to explicitly read the dirstate again (i.e. restoring it to a previous known good state).''' - if 'dirstate' in self.__dict__: + if hasunfilteredcache(self, 'dirstate'): for k in self.dirstate._filecache: try: delattr(self.dirstate, k) except AttributeError: pass - delattr(self, 'dirstate') + delattr(self.unfiltered(), 'dirstate') def invalidate(self): + unfiltered = self.unfiltered() # all filecaches are stored on unfiltered for k in self._filecache: # dirstate is invalidated separately in invalidatedirstate() if k == 'dirstate': continue try: - delattr(self, k) + delattr(unfiltered, k) except AttributeError: pass self.invalidatecaches() @@ -1111,10 +1010,10 @@ def unlock(): self.store.write() - if '_phasecache' in vars(self): + if hasunfilteredcache(self, '_phasecache'): self._phasecache.write() for k, ce in self._filecache.items(): - if k == 'dirstate': + if k == 'dirstate' or k not in self.__dict__: continue ce.refresh() @@ -1134,9 +1033,7 @@ def unlock(): self.dirstate.write() - ce = self._filecache.get('dirstate') - if ce: - ce.refresh() + self._filecache['dirstate'].refresh() l = self._lock(self.join("wlock"), wait, unlock, self.invalidatedirstate, _('working directory of %s') % @@ -1224,6 +1121,7 @@ return fparent1 + @unfilteredmethod def commit(self, text="", user=None, date=None, match=None, force=False, editor=False, extra={}): """Add a new revision to current repository. @@ -1394,6 +1292,7 @@ self._afterlock(commithook) return ret + @unfilteredmethod def commitctx(self, ctx, error=False): """Add a new revision to current repository. Revision information is passed via the context argument. @@ -1468,14 +1367,33 @@ # if minimal phase was 0 we don't need to retract anything phases.retractboundary(self, targetphase, [n]) tr.close() - self.updatebranchcache() + branchmap.updatecache(self.filtered('served')) return n finally: if tr: tr.release() lock.release() - def destroyed(self, newheadnodes=None): + @unfilteredmethod + def destroying(self): + '''Inform the repository that nodes are about to be destroyed. + Intended for use by strip and rollback, so there's a common + place for anything that has to be done before destroying history. + + This is mostly useful for saving state that is in memory and waiting + to be flushed when the current lock is released. Because a call to + destroyed is imminent, the repo will be invalidated causing those + changes to stay in memory (waiting for the next unlock), or vanish + completely. + ''' + # When using the same lock to commit and strip, the phasecache is left + # dirty after committing. Then when we strip, the repo is invalidated, + # causing those changes to disappear. + if '_phasecache' in vars(self): + self._phasecache.write() + + @unfilteredmethod + def destroyed(self): '''Inform the repository that nodes have been destroyed. Intended for use by strip and rollback, so there's a common place for anything that has to be done after destroying history. @@ -1486,16 +1404,22 @@ code to update the branchheads cache, rather than having future code decide it's invalid and regenerating it from scratch. ''' - # If we have info, newheadnodes, on how to update the branch cache, do - # it, Otherwise, since nodes were destroyed, the cache is stale and this - # will be caught the next time it is read. - if newheadnodes: - tiprev = len(self) - 1 - ctxgen = (self[node] for node in newheadnodes - if self.changelog.hasnode(node)) - self._updatebranchcache(self._branchcache, ctxgen) - self._writebranchcache(self._branchcache, self.changelog.tip(), - tiprev) + # When one tries to: + # 1) destroy nodes thus calling this method (e.g. strip) + # 2) use phasecache somewhere (e.g. commit) + # + # then 2) will fail because the phasecache contains nodes that were + # removed. We can either remove phasecache from the filecache, + # causing it to reload next time it is accessed, or simply filter + # the removed nodes now and write the updated cache. + if '_phasecache' in self._filecache: + self._phasecache.filterunknown(self) + self._phasecache.write() + + # update the 'served' branch cache to help read only server process + # Thanks to branchcach collaboration this is done from the nearest + # filtered subset and it is expected to be fast. + branchmap.updatecache(self.filtered('served')) # Ensure the persistent tag cache is updated. Doing it now # means that the tag cache only has to worry about destroyed @@ -1507,10 +1431,7 @@ # head, refresh the tag cache, then immediately add a new head. # But I think doing it this way is necessary for the "instant # tag cache retrieval" case to work. - self.invalidatecaches() - - # Discard all cache entries to force reloading everything. - self._filecache.clear() + self.invalidate() def walk(self, match, node=None): ''' @@ -1568,7 +1489,7 @@ if working: # we need to scan the working dir subrepos = [] if '.hgsub' in self.dirstate: - subrepos = ctx2.substate.keys() + subrepos = sorted(ctx2.substate) s = self.dirstate.status(match, subrepos, listignored, listclean, listunknown) cmp, modified, added, removed, deleted, unknown, ignored, clean = s @@ -1806,6 +1727,7 @@ if key.startswith('dump'): data = base85.b85decode(remoteobs[key]) self.obsstore.mergemarkers(tr, data) + self.invalidatevolatilesets() if tr is not None: tr.close() finally: @@ -1841,6 +1763,7 @@ if not remote.canpush(): raise util.Abort(_("destination does not support push")) + unfi = self.unfiltered() # get local lock as we might write phase data locallock = self.lock() try: @@ -1852,40 +1775,43 @@ try: # discovery fci = discovery.findcommonincoming - commoninc = fci(self, remote, force=force) + commoninc = fci(unfi, remote, force=force) common, inc, remoteheads = commoninc fco = discovery.findcommonoutgoing - outgoing = fco(self, remote, onlyheads=revs, + outgoing = fco(unfi, remote, onlyheads=revs, commoninc=commoninc, force=force) if not outgoing.missing: # nothing to push - scmutil.nochangesfound(self.ui, self, outgoing.excluded) + scmutil.nochangesfound(unfi.ui, unfi, outgoing.excluded) ret = None else: # something to push if not force: # if self.obsstore == False --> no obsolete # then, save the iteration - if self.obsstore: + if unfi.obsstore: # this message are here for 80 char limit reason mso = _("push includes obsolete changeset: %s!") - msu = _("push includes unstable changeset: %s!") - msb = _("push includes bumped changeset: %s!") + mst = "push includes %s changeset: %s!" + # plain versions for i18n tool to detect them + _("push includes unstable changeset: %s!") + _("push includes bumped changeset: %s!") + _("push includes divergent changeset: %s!") # If we are to push if there is at least one # obsolete or unstable changeset in missing, at # least one of the missinghead will be obsolete or # unstable. So checking heads only is ok for node in outgoing.missingheads: - ctx = self[node] + ctx = unfi[node] if ctx.obsolete(): raise util.Abort(mso % ctx) - elif ctx.unstable(): - raise util.Abort(msu % ctx) - elif ctx.bumped(): - raise util.Abort(msb % ctx) - discovery.checkheads(self, remote, outgoing, + elif ctx.troubled(): + raise util.Abort(_(mst) + % (ctx.troubles()[0], + ctx)) + discovery.checkheads(unfi, remote, outgoing, remoteheads, newbranch, bool(inc)) @@ -1938,7 +1864,7 @@ cheads = [node for node in revs if node in common] # and # * commonheads parents on missing - revset = self.set('%ln and parents(roots(%ln))', + revset = unfi.set('%ln and parents(roots(%ln))', outgoing.commonheads, outgoing.missing) cheads.extend(c.node() for c in revset) @@ -1961,7 +1887,7 @@ # Get the list of all revs draft on remote by public here. # XXX Beware that revset break if droots is not strictly # XXX root we may want to ensure it is but it is costly - outdated = self.set('heads((%ln::%ln) and public())', + outdated = unfi.set('heads((%ln::%ln) and public())', droots, cheads) for newremotehead in outdated: r = remote.pushkey('phases', @@ -1992,12 +1918,12 @@ self.ui.debug("checking for updated bookmarks\n") rb = remote.listkeys('bookmarks') for k in rb.keys(): - if k in self._bookmarks: + if k in unfi._bookmarks: nr, nl = rb[k], hex(self._bookmarks[k]) - if nr in self: - cr = self[nr] - cl = self[nl] - if bookmarks.validdest(self, cr, cl): + if nr in unfi: + cr = unfi[nr] + cl = unfi[nl] + if bookmarks.validdest(unfi, cr, cl): r = remote.pushkey('bookmarks', k, nr, nl) if r: self.ui.status(_("updating bookmark %s\n") % k) @@ -2033,7 +1959,7 @@ bases = [nullid] csets, bases, heads = cl.nodesbetween(bases, heads) # We assume that all ancestors of bases are known - common = set(cl.ancestors([cl.rev(n) for n in bases])) + common = cl.ancestors([cl.rev(n) for n in bases]) return self._changegroupsubset(common, csets, heads, source) def getlocalbundle(self, source, outgoing): @@ -2059,8 +1985,8 @@ """ cl = self.changelog if common: - nm = cl.nodemap - common = [n for n in common if n in nm] + hasnode = cl.hasnode + common = [n for n in common if hasnode(n)] else: common = [nullid] if not heads: @@ -2068,6 +1994,7 @@ return self.getlocalbundle(source, discovery.outgoing(cl, common, heads)) + @unfilteredmethod def _changegroupsubset(self, commonrevs, csets, heads, source): cl = self.changelog @@ -2179,6 +2106,7 @@ # to avoid a race we use changegroupsubset() (issue1320) return self.changegroupsubset(basenodes, self.heads(), source) + @unfilteredmethod def _changegroup(self, nodes, source): """Compute the changegroup of all nodes that we have that a recipient doesn't. Return a chunkbuffer object whose read() method will return @@ -2272,6 +2200,7 @@ return changegroup.unbundle10(util.chunkbuffer(gengroup()), 'UN') + @unfilteredmethod def addchangegroup(self, source, srctype, url, emptyok=False): """Add the changegroup returned by source.read() to this repo. srctype is a string like 'push', 'pull', or 'unbundle'. url is @@ -2382,6 +2311,9 @@ n = fl.node(new) if n in needs: needs.remove(n) + else: + raise util.Abort( + _("received spurious file revlog entry")) if not needs: del needfiles[f] self.ui.progress(_('files'), None) @@ -2410,7 +2342,7 @@ self.ui.status(_("added %d changesets" " with %d changes to %d files%s\n") % (changesets, revisions, files, htext)) - obsolete.clearobscaches(self) + self.invalidatevolatilesets() if changesets > 0: p = lambda: cl.writepending() and self.root or "" @@ -2444,7 +2376,11 @@ tr.close() if changesets > 0: - self.updatebranchcache() + if srctype != 'strip': + # During strip, branchcache is invalid but coming call to + # `destroyed` will repair it. + # In other case we can safely update cache on disk. + branchmap.updatecache(self.filtered('served')) def runhooks(): # forcefully update the on-disk branch cache self.ui.debug("updating the branch cache\n") @@ -2538,12 +2474,20 @@ for bheads in rbranchmap.itervalues(): rbheads.extend(bheads) - self.branchcache = rbranchmap if rbheads: rtiprev = max((int(self.changelog.rev(node)) for node in rbheads)) - self._writebranchcache(self.branchcache, - self[rtiprev].node(), rtiprev) + cache = branchmap.branchcache(rbranchmap, + self[rtiprev].node(), + rtiprev) + # Try to stick it as low as possible + # filter above served are unlikely to be fetch from a clone + for candidate in ('base', 'immutable', 'served'): + rview = self.filtered(candidate) + if cache.validfor(rview): + self._branchcaches[candidate] = cache + cache.write(rview) + break self.invalidate() return len(self.heads()) + 1 finally: @@ -2607,7 +2551,7 @@ fp.write(text) finally: fp.close() - return self.pathto(fp.name[len(self.root)+1:]) + return self.pathto(fp.name[len(self.root) + 1:]) # used to avoid circular references so destructors work def aftertrans(files):
--- a/mercurial/manifest.py Mon Jan 14 23:14:45 2013 +0900 +++ b/mercurial/manifest.py Sat Jan 19 17:24:33 2013 -0600 @@ -117,15 +117,23 @@ # apply the changes collected during the bisect loop to our addlist # return a delta suitable for addrevision def addlistdelta(addlist, x): - # start from the bottom up - # so changes to the offsets don't mess things up. - for start, end, content in reversed(x): + # for large addlist arrays, building a new array is cheaper + # than repeatedly modifying the existing one + currentposition = 0 + newaddlist = array.array('c') + + for start, end, content in x: + newaddlist += addlist[currentposition:start] if content: - addlist[start:end] = array.array('c', content) - else: - del addlist[start:end] - return "".join(struct.pack(">lll", start, end, len(content)) + newaddlist += array.array('c', content) + + currentposition = end + + newaddlist += addlist[currentposition:] + + deltatext = "".join(struct.pack(">lll", start, end, len(content)) + content for start, end, content in x) + return deltatext, newaddlist def checkforbidden(l): for f in l: @@ -194,7 +202,8 @@ if dstart is not None: delta.append([dstart, dend, "".join(dline)]) # apply the delta to the addlist, and get a delta for addrevision - cachedelta = (self.rev(p1), addlistdelta(addlist, delta)) + deltatext, addlist = addlistdelta(addlist, delta) + cachedelta = (self.rev(p1), deltatext) arraytext = addlist text = util.buffer(arraytext)
--- a/mercurial/mdiff.py Mon Jan 14 23:14:45 2013 +0900 +++ b/mercurial/mdiff.py Sat Jan 19 17:24:33 2013 -0600 @@ -7,7 +7,7 @@ from i18n import _ import bdiff, mpatch, util -import re, struct +import re, struct, base85, zlib def splitnewlines(text): '''like str.splitlines, but only split on newlines.''' @@ -142,20 +142,7 @@ yield s, type yield s1, '=' -def diffline(revs, a, b, opts): - parts = ['diff'] - if opts.git: - parts.append('--git') - if revs and not opts.git: - parts.append(' '.join(["-r %s" % rev for rev in revs])) - if opts.git: - parts.append('a/%s' % a) - parts.append('b/%s' % b) - else: - parts.append(a) - return ' '.join(parts) + '\n' - -def unidiff(a, ad, b, bd, fn1, fn2, r=None, opts=defaultopts): +def unidiff(a, ad, b, bd, fn1, fn2, opts=defaultopts): def datetag(date, fn=None): if not opts.git and not opts.nodates: return '\t%s\n' % date @@ -206,9 +193,6 @@ if l[ln][-1] != '\n': l[ln] += "\n\ No newline at end of file\n" - if r: - l.insert(0, diffline(r, fn1, fn2, opts)) - return "".join(l) # creates a headerless unified diff @@ -314,6 +298,41 @@ for x in yieldhunk(hunk): yield x +def b85diff(to, tn): + '''print base85-encoded binary diff''' + def fmtline(line): + l = len(line) + if l <= 26: + l = chr(ord('A') + l - 1) + else: + l = chr(l - 26 + ord('a') - 1) + return '%c%s\n' % (l, base85.b85encode(line, True)) + + def chunk(text, csize=52): + l = len(text) + i = 0 + while i < l: + yield text[i:i + csize] + i += csize + + if to is None: + to = '' + if tn is None: + tn = '' + + if to == tn: + return '' + + # TODO: deltas + ret = [] + ret.append('GIT binary patch\n') + ret.append('literal %s\n' % len(tn)) + for l in chunk(zlib.compress(tn)): + ret.append(fmtline(l)) + ret.append('\n') + + return ''.join(ret) + def patchtext(bin): pos = 0 t = []
--- a/mercurial/merge.py Mon Jan 14 23:14:45 2013 +0900 +++ b/mercurial/merge.py Sat Jan 19 17:24:33 2013 -0600 @@ -7,7 +7,7 @@ from node import nullid, nullrev, hex, bin from i18n import _ -import error, scmutil, util, filemerge, copies, subrepo +import error, util, filemerge, copies, subrepo import errno, os, shutil class mergestate(object): @@ -45,11 +45,11 @@ f.write("\0".join([d] + v) + "\n") f.close() self._dirty = False - def add(self, fcl, fco, fca, fd, flags): + def add(self, fcl, fco, fca, fd): hash = util.sha1(fcl.path()).hexdigest() self._repo.opener.write("merge/" + hash, fcl.data()) self._state[fd] = ['u', hash, fcl.path(), fca.path(), - hex(fca.filenode()), fco.path(), flags] + hex(fca.filenode()), fco.path(), fcl.flags()] self._dirty = True def __contains__(self, dfile): return dfile in self._state @@ -67,12 +67,22 @@ if self[dfile] == 'r': return 0 state, hash, lfile, afile, anode, ofile, flags = self._state[dfile] + fcd = wctx[dfile] + fco = octx[ofile] + fca = self._repo.filectx(afile, fileid=anode) + # "premerge" x flags + flo = fco.flags() + fla = fca.flags() + if 'x' in flags + flo + fla and 'l' not in flags + flo + fla: + if fca.node() == nullid: + self._repo.ui.warn(_('warning: cannot merge flags for %s\n') % + afile) + elif flags == fla: + flags = flo + # restore local f = self._repo.opener("merge/" + hash) self._repo.wwrite(dfile, f.read(), flags) f.close() - fcd = wctx[dfile] - fco = octx[ofile] - fca = self._repo.filectx(afile, fileid=anode) r = filemerge.filemerge(self._repo, self._local, lfile, fcd, fco, fca) if r is None: # no real conflict @@ -162,18 +172,18 @@ as removed. """ - action = [] + actions = [] state = branchmerge and 'r' or 'f' for f in wctx.deleted(): if f not in mctx: - action.append((f, state)) + actions.append((f, state)) if not branchmerge: for f in wctx.removed(): if f not in mctx: - action.append((f, "f")) + actions.append((f, "f")) - return action + return actions def manifestmerge(repo, p1, p2, pa, overwrite, partial): """ @@ -183,44 +193,19 @@ partial = function to filter file lists """ - def fmerge(f, f2, fa): - """merge flags""" - a, m, n = ma.flags(fa), m1.flags(f), m2.flags(f2) - if m == n: # flags agree - return m # unchanged - if m and n and not a: # flags set, don't agree, differ from parent - r = repo.ui.promptchoice( - _(" conflicting flags for %s\n" - "(n)one, e(x)ec or sym(l)ink?") % f, - (_("&None"), _("E&xec"), _("Sym&link")), 0) - if r == 1: - return "x" # Exec - if r == 2: - return "l" # Symlink - return "" - if m and m != a: # changed from a to m - return m - if n and n != a: # changed from a to n - if (n == 'l' or a == 'l') and m1.get(f) != ma.get(f): - # can't automatically merge symlink flag when there - # are file-level conflicts here, let filemerge take - # care of it - return m - return n - return '' # flag was cleared - def act(msg, m, f, *args): repo.ui.debug(" %s: %s -> %s\n" % (f, msg, m)) - action.append((f, m) + args) + actions.append((f, m) + args) - action, copy = [], {} + actions, copy, movewithdir = [], {}, {} if overwrite: pa = p1 elif pa == p2: # backwards pa = p1.p1() elif pa and repo.ui.configbool("merge", "followcopies", True): - copy, diverge, renamedelete = copies.mergecopies(repo, p1, p2, pa) + ret = copies.mergecopies(repo, p1, p2, pa) + copy, movewithdir, diverge, renamedelete = ret for of, fl in diverge.iteritems(): act("divergent renames", "dr", of, fl) for of, fl in renamedelete.iteritems(): @@ -233,40 +218,48 @@ m1, m2, ma = p1.manifest(), p2.manifest(), pa.manifest() copied = set(copy.values()) + copied.update(movewithdir.values()) if '.hgsubstate' in m1: # check whether sub state is modified - for s in p1.substate: + for s in sorted(p1.substate): if p1.sub(s).dirty(): m1['.hgsubstate'] += "+" break # Compare manifests - for f, n in m1.iteritems(): + for f, n in sorted(m1.iteritems()): if partial and not partial(f): continue if f in m2: - rflags = fmerge(f, f, f) + n2 = m2[f] + fl1, fl2, fla = m1.flags(f), m2.flags(f), ma.flags(f) + nol = 'l' not in fl1 + fl2 + fla a = ma.get(f, nullid) - if n == m2[f] or m2[f] == a: # same or local newer - # is file locally modified or flags need changing? - # dirstate flags may need to be made current - if m1.flags(f) != rflags or n[20:]: - act("update permissions", "e", f, rflags) - elif n == a: # remote newer - act("remote is newer", "g", f, rflags) - else: # both changed - act("versions differ", "m", f, f, f, rflags, False) + if n == n2 and fl1 == fl2: + pass # same - keep local + elif n2 == a and fl2 == fla: + pass # remote unchanged - keep local + elif n == a and fl1 == fla: # local unchanged - use remote + if n == n2: # optimization: keep local content + act("update permissions", "e", f, fl2) + else: + act("remote is newer", "g", f, fl2) + elif nol and n2 == a: # remote only changed 'x' + act("update permissions", "e", f, fl2) + elif nol and n == a: # local only changed 'x' + act("remote is newer", "g", f, fl) + else: # both changed something + act("versions differ", "m", f, f, f, False) elif f in copied: # files we'll deal with on m2 side pass + elif f in movewithdir: # directory rename + f2 = movewithdir[f] + act("remote renamed directory to " + f2, "d", f, None, f2, + m1.flags(f)) elif f in copy: f2 = copy[f] - if f2 not in m2: # directory rename - act("remote renamed directory to " + f2, "d", - f, None, f2, m1.flags(f)) - else: # case 2 A,B/B/B or case 4,21 A/B/B - act("local copied/moved to " + f2, "m", - f, f2, f, fmerge(f, f2, f2), False) + act("local copied/moved to " + f2, "m", f, f2, f, False) elif f in ma: # clean, a different, no remote if n != ma[f]: if repo.ui.promptchoice( @@ -281,28 +274,28 @@ else: act("other deleted", "r", f) - for f, n in m2.iteritems(): + for f, n in sorted(m2.iteritems()): if partial and not partial(f): continue if f in m1 or f in copied: # files already visited continue - if f in copy: + if f in movewithdir: + f2 = movewithdir[f] + act("local renamed directory to " + f2, "d", None, f, f2, + m2.flags(f)) + elif f in copy: f2 = copy[f] - if f2 not in m1: # directory rename - act("local renamed directory to " + f2, "d", - None, f, f2, m2.flags(f)) - elif f2 in m2: # rename case 1, A/A,B/A + if f2 in m2: act("remote copied to " + f, "m", - f2, f, f, fmerge(f2, f, f2), False) - else: # case 3,20 A/B/A + f2, f, f, False) + else: act("remote moved to " + f, "m", - f2, f, f, fmerge(f2, f, f2), True) + f2, f, f, True) elif f not in ma: if (not overwrite and _checkunknownfile(repo, p1, p2, f)): - rflags = fmerge(f, f, f) act("remote differs from untracked local", - "m", f, f, f, rflags, False) + "m", f, f, f, False) else: act("remote created", "g", f, m2.flags(f)) elif n != ma[f]: @@ -312,12 +305,12 @@ (_("&Changed"), _("&Deleted")), 0) == 0: act("prompt recreating", "g", f, m2.flags(f)) - return action + return actions def actionkey(a): - return a[1] == 'r' and -1 or 0, a + return a[1] == "r" and -1 or 0, a -def applyupdates(repo, action, wctx, mctx, actx, overwrite): +def applyupdates(repo, actions, wctx, mctx, actx, overwrite): """apply the merge action list to the working directory wctx is the working copy context @@ -332,14 +325,14 @@ ms = mergestate(repo) ms.reset(wctx.p1().node()) moves = [] - action.sort(key=actionkey) + actions.sort(key=actionkey) # prescan for merges - for a in action: + for a in actions: f, m = a[:2] - if m == 'm': # merge - f2, fd, flags, move = a[2:] - if f == '.hgsubstate': # merged internally + if m == "m": # merge + f2, fd, move = a[2:] + if fd == '.hgsubstate': # merged internally continue repo.ui.debug("preserving %s for resolve of %s\n" % (f, fd)) fcl = wctx[f] @@ -353,45 +346,42 @@ fca = fcl.ancestor(fco, actx) if not fca: fca = repo.filectx(f, fileid=nullrev) - ms.add(fcl, fco, fca, fd, flags) + ms.add(fcl, fco, fca, fd) if f != fd and move: moves.append(f) - audit = scmutil.pathauditor(repo.root) + audit = repo.wopener.audit # remove renamed files after safely stored for f in moves: if os.path.lexists(repo.wjoin(f)): repo.ui.debug("removing %s\n" % f) audit(f) - os.unlink(repo.wjoin(f)) + util.unlinkpath(repo.wjoin(f)) - numupdates = len(action) - for i, a in enumerate(action): + numupdates = len(actions) + for i, a in enumerate(actions): f, m = a[:2] repo.ui.progress(_('updating'), i + 1, item=f, total=numupdates, unit=_('files')) - if f and f[0] == "/": - continue if m == "r": # remove repo.ui.note(_("removing %s\n") % f) audit(f) if f == '.hgsubstate': # subrepo states need updating subrepo.submerge(repo, wctx, mctx, wctx, overwrite) try: - util.unlinkpath(repo.wjoin(f)) + util.unlinkpath(repo.wjoin(f), ignoremissing=True) except OSError, inst: - if inst.errno != errno.ENOENT: - repo.ui.warn(_("update failed to remove %s: %s!\n") % - (f, inst.strerror)) + repo.ui.warn(_("update failed to remove %s: %s!\n") % + (f, inst.strerror)) removed += 1 elif m == "m": # merge - if f == '.hgsubstate': # subrepo states need updating + if fd == '.hgsubstate': # subrepo states need updating subrepo.submerge(repo, wctx, mctx, wctx.ancestor(mctx), overwrite) continue - f2, fd, flags, move = a[2:] - repo.wopener.audit(fd) + f2, fd, move = a[2:] + audit(fd) r = ms.resolve(fd, wctx, mctx) if r is not None and r > 0: unresolved += 1 @@ -400,17 +390,10 @@ updated += 1 else: merged += 1 - if (move and repo.dirstate.normalize(fd) != f - and os.path.lexists(repo.wjoin(f))): - repo.ui.debug("removing %s\n" % f) - audit(f) - os.unlink(repo.wjoin(f)) elif m == "g": # get flags = a[2] repo.ui.note(_("getting %s\n") % f) - t = mctx.filectx(f).data() - repo.wwrite(f, t, flags) - t = None + repo.wwrite(f, mctx.filectx(f).data(), flags) updated += 1 if f == '.hgsubstate': # subrepo states need updating subrepo.submerge(repo, wctx, mctx, wctx, overwrite) @@ -419,13 +402,11 @@ if f: repo.ui.note(_("moving %s to %s\n") % (f, fd)) audit(f) - t = wctx.filectx(f).data() - repo.wwrite(fd, t, flags) + repo.wwrite(fd, wctx.filectx(f).data(), flags) util.unlinkpath(repo.wjoin(f)) if f2: repo.ui.note(_("getting %s to %s\n") % (f2, fd)) - t = mctx.filectx(f2).data() - repo.wwrite(fd, t, flags) + repo.wwrite(fd, mctx.filectx(f2).data(), flags) updated += 1 elif m == "dr": # divergent renames fl = a[2] @@ -441,17 +422,39 @@ repo.ui.warn(" %s\n" % nf) elif m == "e": # exec flags = a[2] - repo.wopener.audit(f) + audit(f) util.setflags(repo.wjoin(f), 'l' in flags, 'x' in flags) + updated += 1 ms.commit() repo.ui.progress(_('updating'), None, total=numupdates, unit=_('files')) return updated, merged, removed, unresolved -def recordupdates(repo, action, branchmerge): +def calculateupdates(repo, tctx, mctx, ancestor, branchmerge, force, partial): + "Calculate the actions needed to merge mctx into tctx" + actions = [] + folding = not util.checkcase(repo.path) + if folding: + # collision check is not needed for clean update + if (not branchmerge and + (force or not tctx.dirty(missing=True, branch=False))): + _checkcollision(mctx, None) + else: + _checkcollision(mctx, (tctx, ancestor)) + if not force: + _checkunknown(repo, tctx, mctx) + if tctx.rev() is None: + actions += _forgetremoved(tctx, mctx, branchmerge) + actions += manifestmerge(repo, tctx, mctx, + ancestor, + force and not branchmerge, + partial) + return actions + +def recordupdates(repo, actions, branchmerge): "record merge actions to the dirstate" - for a in action: + for a in actions: f, m = a[:2] if m == "r": # remove if branchmerge: @@ -471,7 +474,7 @@ else: repo.dirstate.normal(f) elif m == "m": # merge - f2, fd, flag, move = a[2:] + f2, fd, move = a[2:] if branchmerge: # We've done a branch merge, mark this file as merged # so that we properly record the merger later @@ -590,7 +593,7 @@ if not force and (wc.files() or wc.deleted()): raise util.Abort(_("outstanding uncommitted changes"), hint=_("use 'hg status' to list changes")) - for s in wc.substate: + for s in sorted(wc.substate): if wc.sub(s).dirty(): raise util.Abort(_("outstanding uncommitted changes in " "subrepository '%s'") % s) @@ -609,19 +612,8 @@ pa = p1 ### calculate phase - action = [] - folding = not util.checkcase(repo.path) - if folding: - # collision check is not needed for clean update - if (not branchmerge and - (force or not wc.dirty(missing=True, branch=False))): - _checkcollision(p2, None) - else: - _checkcollision(p2, (wc, pa)) - if not force: - _checkunknown(repo, wc, p2) - action += _forgetremoved(wc, p2, branchmerge) - action += manifestmerge(repo, wc, p2, pa, overwrite, partial) + actions = calculateupdates(repo, wc, p2, pa, + branchmerge, force, partial) ### apply phase if not branchmerge: # just jump to the new rev @@ -629,11 +621,11 @@ if not partial: repo.hook('preupdate', throw=True, parent1=xp1, parent2=xp2) - stats = applyupdates(repo, action, wc, p2, pa, overwrite) + stats = applyupdates(repo, actions, wc, p2, pa, overwrite) if not partial: repo.setparents(fp1, fp2) - recordupdates(repo, action, branchmerge) + recordupdates(repo, actions, branchmerge) if not branchmerge: repo.dirstate.setbranch(p2.branch()) finally:
--- a/mercurial/obsolete.py Mon Jan 14 23:14:45 2013 +0900 +++ b/mercurial/obsolete.py Sat Jan 19 17:24:33 2013 -0600 @@ -402,6 +402,200 @@ seen.add(suc) remaining.add(suc) +def successorssets(repo, initialnode, cache=None): + """Return all set of successors of initial nodes + + Successors set of changeset A are a group of revision that succeed A. It + succeed A as a consistent whole, each revision being only partial + replacement. Successors set contains non-obsolete changeset only. + + In most cases a changeset A have zero (changeset pruned) or a single + successors set that contains a single successor (changeset A replaced by + A') + + When changeset is split, it results successors set containing more than + a single element. Divergent rewriting will result in multiple successors + sets. + + They are returned as a list of tuples containing all valid successors sets. + + Final successors unknown locally are considered plain prune (obsoleted + without successors). + + The optional `cache` parameter is a dictionary that may contains + precomputed successors sets. It is meant to reuse the computation of + previous call to `successorssets` when multiple calls are made at the same + time. The cache dictionary is updated in place. The caller is responsible + for its live spawn. Code that makes multiple calls to `successorssets` + *must* use this cache mechanism or suffer terrible performances.""" + + succmarkers = repo.obsstore.successors + + # Stack of nodes we search successors sets for + toproceed = [initialnode] + # set version of above list for fast loop detection + # element added to "toproceed" must be added here + stackedset = set(toproceed) + if cache is None: + cache = {} + + # This while loop is the flattened version of a recursive search for + # successors sets + # + # def successorssets(x): + # successors = directsuccessors(x) + # ss = [[]] + # for succ in directsuccessors(x): + # # product as in itertools cartesian product + # ss = product(ss, successorssets(succ)) + # return ss + # + # But we can not use plain recursive calls here: + # - that would blow the python call stack + # - obsolescence markers may have cycles, we need to handle them. + # + # The `toproceed` list act as our call stack. Every node we search + # successors set for are stacked there. + # + # The `stackedset` is set version of this stack used to check if a node is + # already stacked. This check is used to detect cycles and prevent infinite + # loop. + # + # successors set of all nodes are stored in the `cache` dictionary. + # + # After this while loop ends we use the cache to return the successors sets + # for the node requested by the caller. + while toproceed: + # Every iteration tries to compute the successors sets of the topmost + # node of the stack: CURRENT. + # + # There are four possible outcomes: + # + # 1) We already know the successors sets of CURRENT: + # -> mission accomplished, pop it from the stack. + # 2) Node is not obsolete: + # -> the node is its own successors sets. Add it to the cache. + # 3) We do not know successors set of direct successors of CURRENT: + # -> We add those successors to the stack. + # 4) We know successors sets of all direct successors of CURRENT: + # -> We can compute CURRENT successors set and add it to the + # cache. + # + current = toproceed[-1] + if current in cache: + # case (1): We already know the successors sets + stackedset.remove(toproceed.pop()) + elif current not in succmarkers: + # case (2): The node is not obsolete. + if current in repo: + # We have a valid last successors. + cache[current] = [(current,)] + else: + # Final obsolete version is unknown locally. + # Do not count that as a valid successors + cache[current] = [] + else: + # cases (3) and (4) + # + # We proceed in two phases. Phase 1 aims to distinguish case (3) + # from case (4): + # + # For each direct successors of CURRENT, we check whether its + # successors sets are known. If they are not, we stack the + # unknown node and proceed to the next iteration of the while + # loop. (case 3) + # + # During this step, we may detect obsolescence cycles: a node + # with unknown successors sets but already in the call stack. + # In such a situation, we arbitrary set the successors sets of + # the node to nothing (node pruned) to break the cycle. + # + # If no break was encountered we proceeed to phase 2. + # + # Phase 2 computes successors sets of CURRENT (case 4); see details + # in phase 2 itself. + # + # Note the two levels of iteration in each phase. + # - The first one handles obsolescence markers using CURRENT as + # precursor (successors markers of CURRENT). + # + # Having multiple entry here means divergence. + # + # - The second one handles successors defined in each marker. + # + # Having none means pruned node, multiple successors means split, + # single successors are standard replacement. + # + for mark in sorted(succmarkers[current]): + for suc in mark[1]: + if suc not in cache: + if suc in stackedset: + # cycle breaking + cache[suc] = [] + else: + # case (3) If we have not computed successors sets + # of one of those successors we add it to the + # `toproceed` stack and stop all work for this + # iteration. + toproceed.append(suc) + stackedset.add(suc) + break + else: + continue + break + else: + # case (4): we know all successors sets of all direct + # successors + # + # Successors set contributed by each marker depends on the + # successors sets of all its "successors" node. + # + # Each different marker is a divergence in the obsolescence + # history. It contributes successors sets dictinct from other + # markers. + # + # Within a marker, a successor may have divergent successors + # sets. In such a case, the marker will contribute multiple + # divergent successors sets. If multiple successors have + # divergents successors sets, a cartesian product is used. + # + # At the end we post-process successors sets to remove + # duplicated entry and successors set that are strict subset of + # another one. + succssets = [] + for mark in sorted(succmarkers[current]): + # successors sets contributed by this marker + markss = [[]] + for suc in mark[1]: + # cardinal product with previous successors + productresult = [] + for prefix in markss: + for suffix in cache[suc]: + newss = list(prefix) + for part in suffix: + # do not duplicated entry in successors set + # first entry wins. + if part not in newss: + newss.append(part) + productresult.append(newss) + markss = productresult + succssets.extend(markss) + # remove duplicated and subset + seen = [] + final = [] + candidate = sorted(((set(s), s) for s in succssets if s), + key=lambda x: len(x[1]), reverse=True) + for setversion, listversion in candidate: + for seenset in seen: + if setversion.issubset(seenset): + break + else: + final.append(listversion) + seen.append(setversion) + final.reverse() # put small successors set first + cache[current] = final + return cache[initialnode] + def _knownrevs(repo, nodes): """yield revision numbers of known nodes passed in parameters @@ -426,6 +620,7 @@ """Return the set of revision that belong to the <name> set Such access may compute the set and cache it for future use""" + repo = repo.unfiltered() if not repo.obsstore: return () if name not in repo.obsstore.caches: @@ -454,27 +649,35 @@ def _computeobsoleteset(repo): """the set of obsolete revisions""" obs = set() - nm = repo.changelog.nodemap + getrev = repo.changelog.nodemap.get + getphase = repo._phasecache.phase for node in repo.obsstore.successors: - rev = nm.get(node) - if rev is not None: + rev = getrev(node) + if rev is not None and getphase(repo, rev): obs.add(rev) - return set(repo.revs('%ld - public()', obs)) + return obs @cachefor('unstable') def _computeunstableset(repo): """the set of non obsolete revisions with obsolete parents""" - return set(repo.revs('(obsolete()::) - obsolete()')) + # revset is not efficient enough here + # we do (obsolete()::) - obsolete() by hand + obs = getrevs(repo, 'obsolete') + if not obs: + return set() + cl = repo.changelog + return set(r for r in cl.descendants(obs) if r not in obs) @cachefor('suspended') def _computesuspendedset(repo): """the set of obsolete parents with non obsolete descendants""" - return set(repo.revs('obsolete() and obsolete()::unstable()')) + suspended = repo.changelog.ancestors(getrevs(repo, 'unstable')) + return set(r for r in getrevs(repo, 'obsolete') if r in suspended) @cachefor('extinct') def _computeextinctset(repo): """the set of obsolete parents without non obsolete descendants""" - return set(repo.revs('obsolete() - obsolete()::unstable()')) + return getrevs(repo, 'obsolete') - getrevs(repo, 'suspended') @cachefor('bumped') @@ -489,6 +692,28 @@ query = '%ld - obsolete() - public()' return set(repo.revs(query, _knownrevs(repo, successors))) +@cachefor('divergent') +def _computedivergentset(repo): + """the set of rev that compete to be the final successors of some revision. + """ + divergent = set() + obsstore = repo.obsstore + newermap = {} + for ctx in repo.set('(not public()) - obsolete()'): + mark = obsstore.precursors.get(ctx.node(), ()) + toprocess = set(mark) + while toprocess: + prec = toprocess.pop()[0] + if prec not in newermap: + successorssets(repo, prec, newermap) + newer = [n for n in newermap[prec] if n] + if len(newer) > 1: + divergent.add(ctx.rev()) + break + toprocess.update(obsstore.precursors.get(prec, ())) + return divergent + + def createmarkers(repo, relations, flag=0, metadata=None): """Add obsolete markers between changesets in a repo @@ -521,6 +746,7 @@ if nprec in nsucs: raise util.Abort("changeset %s cannot obsolete itself" % prec) repo.obsstore.create(tr, nprec, nsucs, flag, metadata) + repo.filteredrevcache.clear() tr.close() finally: tr.release()
--- a/mercurial/osutil.c Mon Jan 14 23:14:45 2013 +0900 +++ b/mercurial/osutil.c Sat Jan 19 17:24:33 2013 -0600 @@ -276,6 +276,16 @@ return -1; } +static PyObject *makestat(const struct stat *st) +{ + PyObject *stat; + + stat = PyObject_CallObject((PyObject *)&listdir_stat_type, NULL); + if (stat) + memcpy(&((struct listdir_stat *)stat)->st, st, sizeof(*st)); + return stat; +} + static PyObject *_listdir(char *path, int pathlen, int keepstat, char *skip) { PyObject *list, *elem, *stat, *ret = NULL; @@ -351,10 +361,9 @@ } if (keepstat) { - stat = PyObject_CallObject((PyObject *)&listdir_stat_type, NULL); + stat = makestat(&st); if (!stat) goto error; - memcpy(&((struct listdir_stat *)stat)->st, &st, sizeof(st)); elem = Py_BuildValue("siN", ent->d_name, kind, stat); } else elem = Py_BuildValue("si", ent->d_name, kind); @@ -380,6 +389,55 @@ return ret; } +static PyObject *statfiles(PyObject *self, PyObject *args) +{ + PyObject *names, *stats; + Py_ssize_t i, count; + + if (!PyArg_ParseTuple(args, "O:statfiles", &names)) + return NULL; + + count = PySequence_Length(names); + if (count == -1) { + PyErr_SetString(PyExc_TypeError, "not a sequence"); + return NULL; + } + + stats = PyList_New(count); + if (stats == NULL) + return NULL; + + for (i = 0; i < count; i++) { + PyObject *stat; + struct stat st; + int ret, kind; + char *path; + + path = PyString_AsString(PySequence_GetItem(names, i)); + if (path == NULL) { + PyErr_SetString(PyExc_TypeError, "not a string"); + goto bail; + } + ret = lstat(path, &st); + kind = st.st_mode & S_IFMT; + if (ret != -1 && (kind == S_IFREG || kind == S_IFLNK)) { + stat = makestat(&st); + if (stat == NULL) + goto bail; + PyList_SET_ITEM(stats, i, stat); + } else { + Py_INCREF(Py_None); + PyList_SET_ITEM(stats, i, Py_None); + } + } + + return stats; + +bail: + Py_DECREF(stats); + return NULL; +} + #endif /* ndef _WIN32 */ static PyObject *listdir(PyObject *self, PyObject *args, PyObject *kwargs) @@ -544,6 +602,10 @@ {"posixfile", (PyCFunction)posixfile, METH_VARARGS | METH_KEYWORDS, "Open a file with POSIX-like semantics.\n" "On error, this function may raise either a WindowsError or an IOError."}, +#else + {"statfiles", (PyCFunction)statfiles, METH_VARARGS | METH_KEYWORDS, + "stat a series of files or symlinks\n" +"Returns None for non-existent entries and entries of other types.\n"}, #endif #ifdef __APPLE__ {
--- a/mercurial/parsers.c Mon Jan 14 23:14:45 2013 +0900 +++ b/mercurial/parsers.c Sat Jan 19 17:24:33 2013 -0600 @@ -1508,6 +1508,7 @@ PyObject *encodedir(PyObject *self, PyObject *args); PyObject *pathencode(PyObject *self, PyObject *args); +PyObject *lowerencode(PyObject *self, PyObject *args); static PyMethodDef methods[] = { {"pack_dirstate", pack_dirstate, METH_VARARGS, "pack a dirstate\n"}, @@ -1516,6 +1517,7 @@ {"parse_index2", parse_index2, METH_VARARGS, "parse a revlog index\n"}, {"encodedir", encodedir, METH_VARARGS, "encodedir a path\n"}, {"pathencode", pathencode, METH_VARARGS, "fncache-encode a path\n"}, + {"lowerencode", lowerencode, METH_VARARGS, "lower-encode a path\n"}, {NULL, NULL} };
--- a/mercurial/patch.py Mon Jan 14 23:14:45 2013 +0900 +++ b/mercurial/patch.py Sat Jan 19 17:24:33 2013 -0600 @@ -6,7 +6,7 @@ # This software may be used and distributed according to the terms of the # GNU General Public License version 2 or any later version. -import cStringIO, email.Parser, os, errno, re +import cStringIO, email.Parser, os, errno, re, posixpath import tempfile, zlib, shutil from i18n import _ @@ -439,11 +439,7 @@ util.setflags(self._join(fname), False, True) def unlink(self, fname): - try: - util.unlinkpath(self._join(fname)) - except OSError, inst: - if inst.errno != errno.ENOENT: - raise + util.unlinkpath(self._join(fname), ignoremissing=True) def writerej(self, fname, failed, total, lines): fname = fname + ".rej" @@ -1007,7 +1003,7 @@ bot = min(fuzz, bot) top = min(fuzz, top) - return old[top:len(old)-bot], new[top:len(new)-bot], top + return old[top:len(old) - bot], new[top:len(new) - bot], top return old, new, 0 def fuzzit(self, fuzz, toponly): @@ -1514,44 +1510,6 @@ finally: fp.close() -def b85diff(to, tn): - '''print base85-encoded binary diff''' - def gitindex(text): - if not text: - return hex(nullid) - l = len(text) - s = util.sha1('blob %d\0' % l) - s.update(text) - return s.hexdigest() - - def fmtline(line): - l = len(line) - if l <= 26: - l = chr(ord('A') + l - 1) - else: - l = chr(l - 26 + ord('a') - 1) - return '%c%s\n' % (l, base85.b85encode(line, True)) - - def chunk(text, csize=52): - l = len(text) - i = 0 - while i < l: - yield text[i:i + csize] - i += csize - - tohash = gitindex(to) - tnhash = gitindex(tn) - if tohash == tnhash: - return "" - - # TODO: deltas - ret = ['index %s..%s\nGIT binary patch\nliteral %s\n' % - (tohash, tnhash, len(tn))] - for l in chunk(zlib.compress(tn)): - ret.append(fmtline(l)) - ret.append('\n') - return ''.join(ret) - class GitDiffRequired(Exception): pass @@ -1622,9 +1580,8 @@ return [] revs = None - if not repo.ui.quiet: - hexfunc = repo.ui.debugflag and hex or short - revs = [hexfunc(node) for node in [node1, node2] if node] + hexfunc = repo.ui.debugflag and hex or short + revs = [hexfunc(node) for node in [node1, node2] if node] copy = {} if opts.git or opts.upgrade: @@ -1690,17 +1647,45 @@ '''like diff(), but yields 2-tuples of (output, label) for ui.write()''' return difflabel(diff, *args, **kw) - -def _addmodehdr(header, omode, nmode): - if omode != nmode: - header.append('old mode %s\n' % omode) - header.append('new mode %s\n' % nmode) - def trydiff(repo, revs, ctx1, ctx2, modified, added, removed, copy, getfilectx, opts, losedatafn, prefix): def join(f): - return os.path.join(prefix, f) + return posixpath.join(prefix, f) + + def addmodehdr(header, omode, nmode): + if omode != nmode: + header.append('old mode %s\n' % omode) + header.append('new mode %s\n' % nmode) + + def addindexmeta(meta, revs): + if opts.git: + i = len(revs) + if i==2: + meta.append('index %s..%s\n' % tuple(revs)) + elif i==3: + meta.append('index %s,%s..%s\n' % tuple(revs)) + + def gitindex(text): + if not text: + return hex(nullid) + l = len(text) + s = util.sha1('blob %d\0' % l) + s.update(text) + return s.hexdigest() + + def diffline(a, b, revs): + if opts.git: + line = 'diff --git a/%s b/%s\n' % (a, b) + elif not repo.ui.quiet: + if revs: + revinfo = ' '.join(["-r %s" % rev for rev in revs]) + line = 'diff %s %s\n' % (revinfo, a) + else: + line = 'diff %s\n' % a + else: + line = '' + return line date1 = util.datestr(ctx1.date()) man1 = ctx1.manifest() @@ -1733,7 +1718,7 @@ else: a = copyto[f] omode = gitmode[man1.flags(a)] - _addmodehdr(header, omode, mode) + addmodehdr(header, omode, mode) if a in removed and a not in gone: op = 'rename' gone.add(a) @@ -1779,22 +1764,24 @@ nflag = ctx2.flags(f) binary = util.binary(to) or util.binary(tn) if opts.git: - _addmodehdr(header, gitmode[oflag], gitmode[nflag]) + addmodehdr(header, gitmode[oflag], gitmode[nflag]) if binary: dodiff = 'binary' elif binary or nflag != oflag: losedatafn(f) - if opts.git: - header.insert(0, mdiff.diffline(revs, join(a), join(b), opts)) if dodiff: + if opts.git or revs: + header.insert(0, diffline(join(a), join(b), revs)) if dodiff == 'binary': - text = b85diff(to, tn) + text = mdiff.b85diff(to, tn) + if text: + addindexmeta(header, [gitindex(to), gitindex(tn)]) else: text = mdiff.unidiff(to, date1, # ctx2 date may be dynamic tn, util.datestr(ctx2.date()), - join(a), join(b), revs, opts=opts) + join(a), join(b), opts=opts) if header and (text or len(header) > 1): yield ''.join(header) if text:
--- a/mercurial/pathencode.c Mon Jan 14 23:14:45 2013 +0900 +++ b/mercurial/pathencode.c Sat Jan 19 17:24:33 2013 -0600 @@ -15,6 +15,7 @@ * required. */ +#define PY_SSIZE_T_CLEAN #include <Python.h> #include <assert.h> #include <ctype.h> @@ -481,12 +482,244 @@ static const Py_ssize_t maxstorepathlen = 120; +static Py_ssize_t _lowerencode(char *dest, size_t destsize, + const char *src, Py_ssize_t len) +{ + static const uint32_t onebyte[8] = { + 1, 0x2bfffbfb, 0xe8000001, 0x2fffffff + }; + + static const uint32_t lower[8] = { 0, 0, 0x7fffffe }; + + Py_ssize_t i, destlen = 0; + + for (i = 0; i < len; i++) { + if (inset(onebyte, src[i])) + charcopy(dest, &destlen, destsize, src[i]); + else if (inset(lower, src[i])) + charcopy(dest, &destlen, destsize, src[i] + 32); + else + escape3(dest, &destlen, destsize, src[i]); + } + + return destlen; +} + +PyObject *lowerencode(PyObject *self, PyObject *args) +{ + char *path; + Py_ssize_t len, newlen; + PyObject *ret; + + if (!PyArg_ParseTuple(args, "s#:lowerencode", &path, &len)) + return NULL; + + newlen = _lowerencode(NULL, 0, path, len); + ret = PyString_FromStringAndSize(NULL, newlen); + if (ret) + newlen = _lowerencode(PyString_AS_STRING(ret), newlen, + path, len); + + return ret; +} + +/* See store.py:_auxencode for a description. */ +static Py_ssize_t auxencode(char *dest, size_t destsize, + const char *src, Py_ssize_t len) +{ + static const uint32_t twobytes[8]; + + static const uint32_t onebyte[8] = { + ~0, 0xffff3ffe, ~0, ~0, ~0, ~0, ~0, ~0, + }; + + return _encode(twobytes, onebyte, dest, 0, destsize, src, len, 0); +} + +static PyObject *hashmangle(const char *src, Py_ssize_t len, const char sha[20]) +{ + static const Py_ssize_t dirprefixlen = 8; + static const Py_ssize_t maxshortdirslen = 68; + char *dest; + PyObject *ret; + + Py_ssize_t i, d, p, lastslash = len - 1, lastdot = -1; + Py_ssize_t destsize, destlen = 0, slop, used; + + while (lastslash >= 0 && src[lastslash] != '/') { + if (src[lastslash] == '.' && lastdot == -1) + lastdot = lastslash; + lastslash--; + } + +#if 0 + /* All paths should end in a suffix of ".i" or ".d". + Unfortunately, the file names in test-hybridencode.py + violate this rule. */ + if (lastdot != len - 3) { + PyErr_SetString(PyExc_ValueError, + "suffix missing or wrong length"); + return NULL; + } +#endif + + /* If src contains a suffix, we will append it to the end of + the new string, so make room. */ + destsize = 120; + if (lastdot >= 0) + destsize += len - lastdot - 1; + + ret = PyString_FromStringAndSize(NULL, destsize); + if (ret == NULL) + return NULL; + + dest = PyString_AS_STRING(ret); + memcopy(dest, &destlen, destsize, "dh/", 3); + + /* Copy up to dirprefixlen bytes of each path component, up to + a limit of maxshortdirslen bytes. */ + for (i = d = p = 0; i < lastslash; i++, p++) { + if (src[i] == '/') { + char d = dest[destlen - 1]; + /* After truncation, a directory name may end + in a space or dot, which are unportable. */ + if (d == '.' || d == ' ') + dest[destlen - 1] = '_'; + if (destlen > maxshortdirslen) + break; + charcopy(dest, &destlen, destsize, src[i]); + p = -1; + } + else if (p < dirprefixlen) + charcopy(dest, &destlen, destsize, src[i]); + } + + /* Rewind to just before the last slash copied. */ + if (destlen > maxshortdirslen + 3) + do { + destlen--; + } while (destlen > 0 && dest[destlen] != '/'); + + if (destlen > 3) { + if (lastslash > 0) { + char d = dest[destlen - 1]; + /* The last directory component may be + truncated, so make it safe. */ + if (d == '.' || d == ' ') + dest[destlen - 1] = '_'; + } + + charcopy(dest, &destlen, destsize, '/'); + } + + /* Add a prefix of the original file's name. Its length + depends on the number of bytes left after accounting for + hash and suffix. */ + used = destlen + 40; + if (lastdot >= 0) + used += len - lastdot - 1; + slop = maxstorepathlen - used; + if (slop > 0) { + Py_ssize_t basenamelen = + lastslash >= 0 ? len - lastslash - 2 : len - 1; + + if (basenamelen > slop) + basenamelen = slop; + if (basenamelen > 0) + memcopy(dest, &destlen, destsize, &src[lastslash + 1], + basenamelen); + } + + /* Add hash and suffix. */ + for (i = 0; i < 20; i++) + hexencode(dest, &destlen, destsize, sha[i]); + + if (lastdot >= 0) + memcopy(dest, &destlen, destsize, &src[lastdot], + len - lastdot - 1); + + PyString_GET_SIZE(ret) = destlen; + + return ret; +} + /* - * We currently implement only basic encoding. - * - * If a name is too long to encode due to Windows path name limits, - * this function returns None. + * Avoiding a trip through Python would improve performance by 50%, + * but we don't encounter enough long names to be worth the code. */ +static int sha1hash(char hash[20], const char *str, Py_ssize_t len) +{ + static PyObject *shafunc; + PyObject *shaobj, *hashobj; + + if (shafunc == NULL) { + PyObject *util, *name = PyString_FromString("mercurial.util"); + + if (name == NULL) + return -1; + + util = PyImport_Import(name); + Py_DECREF(name); + + if (util == NULL) { + PyErr_SetString(PyExc_ImportError, "mercurial.util"); + return -1; + } + shafunc = PyObject_GetAttrString(util, "sha1"); + Py_DECREF(util); + + if (shafunc == NULL) { + PyErr_SetString(PyExc_AttributeError, + "module 'mercurial.util' has no " + "attribute 'sha1'"); + return -1; + } + } + + shaobj = PyObject_CallFunction(shafunc, "s#", str, len); + + if (shaobj == NULL) + return -1; + + hashobj = PyObject_CallMethod(shaobj, "digest", ""); + Py_DECREF(shaobj); + + if (!PyString_Check(hashobj) || PyString_GET_SIZE(hashobj) != 20) { + PyErr_SetString(PyExc_TypeError, + "result of digest is not a 20-byte hash"); + Py_DECREF(hashobj); + return -1; + } + + memcpy(hash, PyString_AS_STRING(hashobj), 20); + Py_DECREF(hashobj); + return 0; +} + +#define MAXENCODE 4096 * 3 + +static PyObject *hashencode(const char *src, Py_ssize_t len) +{ + char dired[MAXENCODE]; + char lowered[MAXENCODE]; + char auxed[MAXENCODE]; + Py_ssize_t dirlen, lowerlen, auxlen, baselen; + char sha[20]; + + baselen = (len - 5) * 3; + if (baselen >= MAXENCODE) { + PyErr_SetString(PyExc_ValueError, "string too long"); + return NULL; + } + + dirlen = _encodedir(dired, baselen, src, len); + if (sha1hash(sha, dired, dirlen - 1) == -1) + return NULL; + lowerlen = _lowerencode(lowered, baselen, dired + 5, dirlen - 5); + auxlen = auxencode(auxed, baselen, lowered, lowerlen); + return hashmangle(auxed, auxlen, sha); +} + PyObject *pathencode(PyObject *self, PyObject *args) { Py_ssize_t len, newlen; @@ -501,13 +734,10 @@ return NULL; } - if (len > maxstorepathlen) { - newobj = Py_None; - Py_INCREF(newobj); - return newobj; - } - - newlen = len ? basicencode(NULL, 0, path, len + 1) : 1; + if (len > maxstorepathlen) + newlen = maxstorepathlen + 2; + else + newlen = len ? basicencode(NULL, 0, path, len + 1) : 1; if (newlen <= maxstorepathlen + 1) { if (newlen == len + 1) { @@ -522,10 +752,9 @@ basicencode(PyString_AS_STRING(newobj), newlen, path, len + 1); } - } else { - newobj = Py_None; - Py_INCREF(newobj); } + else + newobj = hashencode(path, len + 1); return newobj; }
--- a/mercurial/phases.py Mon Jan 14 23:14:45 2013 +0900 +++ b/mercurial/phases.py Sat Jan 19 17:24:33 2013 -0600 @@ -104,30 +104,11 @@ from node import nullid, nullrev, bin, hex, short from i18n import _ import util, error -import obsolete allphases = public, draft, secret = range(3) trackedphases = allphases[1:] phasenames = ['public', 'draft', 'secret'] -def _filterunknown(ui, changelog, phaseroots): - """remove unknown nodes from the phase boundary - - Nothing is lost as unknown nodes only hold data for their descendants. - """ - updated = False - nodemap = changelog.nodemap # to filter unknown nodes - for phase, nodes in enumerate(phaseroots): - missing = [node for node in nodes if node not in nodemap] - if missing: - for mnode in missing: - ui.debug( - 'removing unknown node %s from %i-phase boundary\n' - % (short(mnode), phase)) - nodes.symmetric_difference_update(missing) - updated = True - return updated - def _readroots(repo, phasedefaults=None): """Read phase roots from disk @@ -139,6 +120,7 @@ Return (roots, dirty) where dirty is true if roots differ from what is being stored. """ + repo = repo.unfiltered() dirty = False roots = [set() for i in allphases] try: @@ -156,8 +138,6 @@ for f in phasedefaults: roots = f(repo, roots) dirty = True - if _filterunknown(repo.ui, repo.changelog, roots): - dirty = True return roots, dirty class phasecache(object): @@ -165,8 +145,9 @@ if _load: # Cheap trick to allow shallow-copy without copy module self.phaseroots, self.dirty = _readroots(repo, phasedefaults) + self._phaserevs = None + self.filterunknown(repo) self.opener = repo.sopener - self._phaserevs = None def copy(self): # Shallow copy meant to ensure isolation in @@ -184,6 +165,7 @@ def getphaserevs(self, repo, rebuild=False): if rebuild or self._phaserevs is None: + repo = repo.unfiltered() revs = [public] * len(repo.changelog) for phase in trackedphases: roots = map(repo.changelog.rev, self.phaseroots[phase]) @@ -228,6 +210,7 @@ # Be careful to preserve shallow-copied values: do not update # phaseroots values, replace them. + repo = repo.unfiltered() delroots = [] # set of root deleted by this path for phase in xrange(targetphase + 1, len(allphases)): # filter nodes that are not in a compatible phase already @@ -245,12 +228,13 @@ # declare deleted root in the target phase if targetphase != 0: self.retractboundary(repo, targetphase, delroots) - obsolete.clearobscaches(repo) + repo.invalidatevolatilesets() def retractboundary(self, repo, targetphase, nodes): # Be careful to preserve shallow-copied values: do not update # phaseroots values, replace them. + repo = repo.unfiltered() currentroots = self.phaseroots[targetphase] newroots = [n for n in nodes if self.phase(repo, repo[n].rev()) < targetphase] @@ -262,7 +246,27 @@ ctxs = repo.set('roots(%ln::)', currentroots) currentroots.intersection_update(ctx.node() for ctx in ctxs) self._updateroots(targetphase, currentroots) - obsolete.clearobscaches(repo) + repo.invalidatevolatilesets() + + def filterunknown(self, repo): + """remove unknown nodes from the phase boundary + + Nothing is lost as unknown nodes only hold data for their descendants. + """ + filtered = False + nodemap = repo.changelog.nodemap # to filter unknown nodes + for phase, nodes in enumerate(self.phaseroots): + missing = [node for node in nodes if node not in nodemap] + if missing: + for mnode in missing: + repo.ui.debug( + 'removing unknown node %s from %i-phase boundary\n' + % (short(mnode), phase)) + nodes.symmetric_difference_update(missing) + filtered = True + if filtered: + self.dirty = True + self._phaserevs = None def advanceboundary(repo, targetphase, nodes): """Add nodes to a phase changing other nodes phases if necessary. @@ -316,6 +320,7 @@ def pushphase(repo, nhex, oldphasestr, newphasestr): """List phases root for serialization over pushkey""" + repo = repo.unfiltered() lock = repo.lock() try: currentphase = repo[nhex].phase() @@ -340,6 +345,7 @@ Accept unknown element input """ + repo = repo.unfiltered() # build list from dictionary draftroots = [] nodemap = repo.changelog.nodemap # to filter unknown nodes @@ -367,6 +373,7 @@ * `heads`: define the first subset * `roots`: define the second we subtract from the first""" + repo = repo.unfiltered() revset = repo.set('heads((%ln + parents(%ln)) - (%ln::%ln))', heads, roots, roots, heads) return [c.node() for c in revset]
--- a/mercurial/posix.py Mon Jan 14 23:14:45 2013 +0900 +++ b/mercurial/posix.py Sat Jan 19 17:24:33 2013 -0600 @@ -7,7 +7,7 @@ from i18n import _ import encoding -import os, sys, errno, stat, getpass, pwd, grp, tempfile, unicodedata +import os, sys, errno, stat, getpass, pwd, grp, socket, tempfile, unicodedata posixfile = open normpath = os.path.normpath @@ -21,14 +21,26 @@ os.umask(umask) def split(p): - '''Same as os.path.split, but faster''' + '''Same as posixpath.split, but faster + + >>> import posixpath + >>> for f in ['/absolute/path/to/file', + ... 'relative/path/to/file', + ... 'file_alone', + ... 'path/to/directory/', + ... '/multiple/path//separators', + ... '/file_at_root', + ... '///multiple_leading_separators_at_root', + ... '']: + ... assert split(f) == posixpath.split(f), f + ''' ht = p.rsplit('/', 1) if len(ht) == 1: return '', p nh = ht[0].rstrip('/') if nh: return nh, ht[1] - return ht + return ht[0] + '/', ht[1] def openhardlinks(): '''return true if it is safe to hold open file handles to hardlinks''' @@ -352,12 +364,18 @@ def setsignalhandler(): pass +_wantedkinds = set([stat.S_IFREG, stat.S_IFLNK]) + def statfiles(files): - 'Stat each file in files and yield stat or None if file does not exist.' + '''Stat each file in files. Yield each stat, or None if a file does not + exist or has a type we don't care about.''' lstat = os.lstat + getkind = stat.S_IFMT for nf in files: try: st = lstat(nf) + if getkind(st.st_mode) not in _wantedkinds: + st = None except OSError, err: if err.errno not in (errno.ENOENT, errno.ENOTDIR): raise @@ -437,9 +455,13 @@ def makedir(path, notindexed): os.mkdir(path) -def unlinkpath(f): +def unlinkpath(f, ignoremissing=False): """unlink and remove the directory if it is empty""" - os.unlink(f) + try: + os.unlink(f) + except OSError, e: + if not (ignoremissing and e.errno == errno.ENOENT): + raise # try removing directories that might now be empty try: os.removedirs(os.path.dirname(f)) @@ -468,7 +490,20 @@ def __eq__(self, other): try: - return self.stat == other.stat + # Only dev, ino, size, mtime and atime are likely to change. Out + # of these, we shouldn't compare atime but should compare the + # rest. However, one of the other fields changing indicates + # something fishy going on, so return False if anything but atime + # changes. + return (self.stat.st_mode == other.stat.st_mode and + self.stat.st_ino == other.stat.st_ino and + self.stat.st_dev == other.stat.st_dev and + self.stat.st_nlink == other.stat.st_nlink and + self.stat.st_uid == other.stat.st_uid and + self.stat.st_gid == other.stat.st_gid and + self.stat.st_size == other.stat.st_size and + self.stat.st_mtime == other.stat.st_mtime and + self.stat.st_ctime == other.stat.st_ctime) except AttributeError: return False @@ -477,3 +512,43 @@ def executablepath(): return None # available on Windows only + +class unixdomainserver(socket.socket): + def __init__(self, join, subsystem): + '''Create a unix domain socket with the given prefix.''' + super(unixdomainserver, self).__init__(socket.AF_UNIX) + sockname = subsystem + '.sock' + self.realpath = self.path = join(sockname) + if os.path.islink(self.path): + if os.path.exists(self.path): + self.realpath = os.readlink(self.path) + else: + os.unlink(self.path) + try: + self.bind(self.realpath) + except socket.error, err: + if err.args[0] == 'AF_UNIX path too long': + tmpdir = tempfile.mkdtemp(prefix='hg-%s-' % subsystem) + self.realpath = os.path.join(tmpdir, sockname) + try: + self.bind(self.realpath) + os.symlink(self.realpath, self.path) + except (OSError, socket.error): + self.cleanup() + raise + else: + raise + self.listen(5) + + def cleanup(self): + def okayifmissing(f, path): + try: + f(path) + except OSError, err: + if err.errno != errno.ENOENT: + raise + + okayifmissing(os.unlink, self.path) + if self.realpath != self.path: + okayifmissing(os.unlink, self.realpath) + okayifmissing(os.rmdir, os.path.dirname(self.realpath))
--- a/mercurial/repair.py Mon Jan 14 23:14:45 2013 +0900 +++ b/mercurial/repair.py Sat Jan 19 17:24:33 2013 -0600 @@ -6,7 +6,7 @@ # This software may be used and distributed according to the terms of the # GNU General Public License version 2 or any later version. -from mercurial import changegroup, bookmarks +from mercurial import changegroup from mercurial.node import short from mercurial.i18n import _ import os @@ -56,10 +56,8 @@ return s def strip(ui, repo, nodelist, backup="all", topic='backup'): - # It simplifies the logic around updating the branchheads cache if we only - # have to consider the effect of the stripped revisions and not revisions - # missing because the cache is out-of-date. - repo.updatebranchcache() + repo = repo.unfiltered() + repo.destroying() cl = repo.changelog # TODO handle undo of merge sets @@ -68,17 +66,6 @@ striplist = [cl.rev(node) for node in nodelist] striprev = min(striplist) - # Generate set of branches who will have nodes stripped. - striprevs = repo.revs("%ld::", striplist) - stripbranches = set([repo[rev].branch() for rev in striprevs]) - - # Set of potential new heads resulting from the strip. The parents of any - # node removed could be a new head because the node to be removed could have - # been the only child of the parent. - newheadrevs = repo.revs("parents(%ld::) - %ld::", striprevs, striprevs) - newheadnodes = set([cl.node(rev) for rev in newheadrevs]) - newheadbranches = set([repo[rev].branch() for rev in newheadrevs]) - keeppartialbundle = backup == 'strip' # Some revisions with rev > striprev may not be descendants of striprev. @@ -111,8 +98,10 @@ saverevs.difference_update(descendants) savebases = [cl.node(r) for r in saverevs] stripbases = [cl.node(r) for r in tostrip] - newbmtarget = repo.revs('sort(heads((::%ld) - (%ld)), -rev)', - tostrip, tostrip) + + # For a set s, max(parents(s) - s) is the same as max(heads(::s - s)), but + # is much faster + newbmtarget = repo.revs('max(parents(%ld) - (%ld))', tostrip, tostrip) if newbmtarget: newbmtarget = repo[newbmtarget[0]].node() else: @@ -181,7 +170,7 @@ for m in updatebm: bm[m] = repo[newbmtarget].node() - bookmarks.write(repo) + bm.write() except: # re-raises if backupfile: ui.warn(_("strip failed, full bundle stored in '%s'\n") @@ -191,10 +180,4 @@ % chgrpfile) raise - if len(stripbranches) == 1 and len(newheadbranches) == 1 \ - and stripbranches == newheadbranches: - repo.destroyed(newheadnodes) - else: - # Multiple branches involved in strip. Will allow branchcache to become - # invalid and later on rebuilt from scratch - repo.destroyed() + repo.destroyed()
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/mercurial/repoview.py Sat Jan 19 17:24:33 2013 -0600 @@ -0,0 +1,219 @@ +# repoview.py - Filtered view of a localrepo object +# +# Copyright 2012 Pierre-Yves David <pierre-yves.david@ens-lyon.org> +# Logilab SA <contact@logilab.fr> +# +# This software may be used and distributed according to the terms of the +# GNU General Public License version 2 or any later version. + +import copy +import phases +import util +import obsolete, bookmarks, revset + + +def hideablerevs(repo): + """Revisions candidates to be hidden + + This is a standalone function to help extensions to wrap it.""" + return obsolete.getrevs(repo, 'obsolete') + +def computehidden(repo): + """compute the set of hidden revision to filter + + During most operation hidden should be filtered.""" + assert not repo.changelog.filteredrevs + hideable = hideablerevs(repo) + if hideable: + cl = repo.changelog + firsthideable = min(hideable) + revs = cl.revs(start=firsthideable) + blockers = [r for r in revset._children(repo, revs, hideable) + if r not in hideable] + for par in repo[None].parents(): + blockers.append(par.rev()) + for bm in bookmarks.listbookmarks(repo).values(): + blockers.append(repo[bm].rev()) + blocked = cl.ancestors(blockers, inclusive=True) + return frozenset(r for r in hideable if r not in blocked) + return frozenset() + +def computeunserved(repo): + """compute the set of revision that should be filtered when used a server + + Secret and hidden changeset should not pretend to be here.""" + assert not repo.changelog.filteredrevs + # fast path in simple case to avoid impact of non optimised code + hiddens = filterrevs(repo, 'visible') + if phases.hassecret(repo): + cl = repo.changelog + secret = phases.secret + getphase = repo._phasecache.phase + first = min(cl.rev(n) for n in repo._phasecache.phaseroots[secret]) + revs = cl.revs(start=first) + secrets = set(r for r in revs if getphase(repo, r) >= secret) + return frozenset(hiddens | secrets) + else: + return hiddens + return frozenset() + +def computemutable(repo): + """compute the set of revision that should be filtered when used a server + + Secret and hidden changeset should not pretend to be here.""" + assert not repo.changelog.filteredrevs + # fast check to avoid revset call on huge repo + if util.any(repo._phasecache.phaseroots[1:]): + getphase = repo._phasecache.phase + maymutable = filterrevs(repo, 'base') + return frozenset(r for r in maymutable if getphase(repo, r)) + return frozenset() + +def computeimpactable(repo): + """Everything impactable by mutable revision + + The mutable filter still have some chance to get invalidated. This will + happen when: + + - you garbage collect hidden changeset, + - public phase is moved backward, + - something is changed in the filtering (this could be fixed) + + This filter out any mutable changeset and any public changeset that may be + impacted by something happening to a mutable revision. + + This is achieved by filtered everything with a revision number egal or + higher than the first mutable changeset is filtered.""" + assert not repo.changelog.filteredrevs + cl = repo.changelog + firstmutable = len(cl) + for roots in repo._phasecache.phaseroots[1:]: + if roots: + firstmutable = min(firstmutable, min(cl.rev(r) for r in roots)) + # protect from nullrev root + firstmutable = max(0, firstmutable) + return frozenset(xrange(firstmutable, len(cl))) + +# function to compute filtered set +filtertable = {'visible': computehidden, + 'served': computeunserved, + 'immutable': computemutable, + 'base': computeimpactable} +### Nearest subset relation +# Nearest subset of filter X is a filter Y so that: +# * Y is included in X, +# * X - Y is as small as possible. +# This create and ordering used for branchmap purpose. +# the ordering may be partial +subsettable = {None: 'visible', + 'visible': 'served', + 'served': 'immutable', + 'immutable': 'base'} + +def filterrevs(repo, filtername): + """returns set of filtered revision for this filter name""" + if filtername not in repo.filteredrevcache: + func = filtertable[filtername] + repo.filteredrevcache[filtername] = func(repo.unfiltered()) + return repo.filteredrevcache[filtername] + +class repoview(object): + """Provide a read/write view of a repo through a filtered changelog + + This object is used to access a filtered version of a repository without + altering the original repository object itself. We can not alter the + original object for two main reasons: + - It prevents the use of a repo with multiple filters at the same time. In + particular when multiple threads are involved. + - It makes scope of the filtering harder to control. + + This object behaves very closely to the original repository. All attribute + operations are done on the original repository: + - An access to `repoview.someattr` actually returns `repo.someattr`, + - A write to `repoview.someattr` actually sets value of `repo.someattr`, + - A deletion of `repoview.someattr` actually drops `someattr` + from `repo.__dict__`. + + The only exception is the `changelog` property. It is overridden to return + a (surface) copy of `repo.changelog` with some revisions filtered. The + `filtername` attribute of the view control the revisions that need to be + filtered. (the fact the changelog is copied is an implementation detail). + + Unlike attributes, this object intercepts all method calls. This means that + all methods are run on the `repoview` object with the filtered `changelog` + property. For this purpose the simple `repoview` class must be mixed with + the actual class of the repository. This ensures that the resulting + `repoview` object have the very same methods than the repo object. This + leads to the property below. + + repoview.method() --> repo.__class__.method(repoview) + + The inheritance has to be done dynamically because `repo` can be of any + subclasses of `localrepo`. Eg: `bundlerepo` or `httprepo`. + """ + + def __init__(self, repo, filtername): + object.__setattr__(self, '_unfilteredrepo', repo) + object.__setattr__(self, 'filtername', filtername) + object.__setattr__(self, '_clcachekey', None) + object.__setattr__(self, '_clcache', None) + + # not a cacheproperty on purpose we shall implement a proper cache later + @property + def changelog(self): + """return a filtered version of the changeset + + this changelog must not be used for writing""" + # some cache may be implemented later + unfi = self._unfilteredrepo + unfichangelog = unfi.changelog + revs = filterrevs(unfi, self.filtername) + cl = self._clcache + newkey = (len(unfichangelog), unfichangelog.tip(), hash(revs)) + if cl is not None: + # we need to check curkey too for some obscure reason. + # MQ test show a corruption of the underlying repo (in _clcache) + # without change in the cachekey. + oldfilter = cl.filteredrevs + try: + cl.filterrevs = () # disable filtering for tip + curkey = (len(cl), cl.tip(), hash(oldfilter)) + finally: + cl.filteredrevs = oldfilter + if newkey != self._clcachekey or newkey != curkey: + cl = None + # could have been made None by the previous if + if cl is None: + cl = copy.copy(unfichangelog) + cl.filteredrevs = revs + object.__setattr__(self, '_clcache', cl) + object.__setattr__(self, '_clcachekey', newkey) + return cl + + def unfiltered(self): + """Return an unfiltered version of a repo""" + return self._unfilteredrepo + + def filtered(self, name): + """Return a filtered version of a repository""" + if name == self.filtername: + return self + return self.unfiltered().filtered(name) + + # everything access are forwarded to the proxied repo + def __getattr__(self, attr): + return getattr(self._unfilteredrepo, attr) + + def __setattr__(self, attr, value): + return setattr(self._unfilteredrepo, attr, value) + + def __delattr__(self, attr): + return delattr(self._unfilteredrepo, attr) + + # The `requirement` attribut is initialiazed during __init__. But + # __getattr__ won't be called as it also exists on the class. We need + # explicit forwarding to main repo here + @property + def requirements(self): + return self._unfilteredrepo.requirements +
--- a/mercurial/revlog.py Mon Jan 14 23:14:45 2013 +0900 +++ b/mercurial/revlog.py Sat Jan 19 17:24:33 2013 -0600 @@ -257,11 +257,14 @@ return iter(xrange(len(self))) def revs(self, start=0, stop=None): """iterate over all rev in this revlog (from start to stop)""" - if stop is None: - stop = len(self) + step = 1 + if stop is not None: + if start > stop: + step = -1 + stop += step else: - stop += 1 - return xrange(start, stop) + stop = len(self) + return xrange(start, stop, step) @util.propertycache def nodemap(self): @@ -338,33 +341,14 @@ return len(t) size = rawsize - def ancestors(self, revs, stoprev=0): + def ancestors(self, revs, stoprev=0, inclusive=False): """Generate the ancestors of 'revs' in reverse topological order. Does not generate revs lower than stoprev. - Yield a sequence of revision numbers starting with the parents - of each revision in revs, i.e., each revision is *not* considered - an ancestor of itself. Results are in breadth-first order: - parents of each rev in revs, then parents of those, etc. Result - does not include the null revision.""" - visit = util.deque(revs) - seen = set([nullrev]) - while visit: - for parent in self.parentrevs(visit.popleft()): - if parent < stoprev: - continue - if parent not in seen: - visit.append(parent) - seen.add(parent) - yield parent + See the documentation for ancestor.lazyancestors for more details.""" - def incancestors(self, revs, stoprev=0): - """Identical to ancestors() except it also generates the - revisions, 'revs'""" - for rev in revs: - yield rev - for rev in self.ancestors(revs, stoprev): - yield rev + return ancestor.lazyancestors(self, revs, stoprev=stoprev, + inclusive=inclusive) def descendants(self, revs): """Generate the descendants of 'revs' in revision order. @@ -429,6 +413,29 @@ missing.sort() return has, [self.node(r) for r in missing] + def findmissingrevs(self, common=None, heads=None): + """Return the revision numbers of the ancestors of heads that + are not ancestors of common. + + More specifically, return a list of revision numbers corresponding to + nodes N such that every N satisfies the following constraints: + + 1. N is an ancestor of some node in 'heads' + 2. N is not an ancestor of any node in 'common' + + The list is sorted by revision number, meaning it is + topologically sorted. + + 'heads' and 'common' are both lists of revision numbers. If heads is + not supplied, uses all of the revlog's heads. If common is not + supplied, uses nullid.""" + if common is None: + common = [nullrev] + if heads is None: + heads = self.headrevs() + + return ancestor.missingancestors(heads, common, self.parentrevs) + def findmissing(self, common=None, heads=None): """Return the ancestors of heads that are not ancestors of common. @@ -444,8 +451,16 @@ 'heads' and 'common' are both lists of node IDs. If heads is not supplied, uses all of the revlog's heads. If common is not supplied, uses nullid.""" - _common, missing = self.findcommonmissing(common, heads) - return missing + if common is None: + common = [nullid] + if heads is None: + heads = self.heads() + + common = [self.rev(n) for n in common] + heads = [self.rev(n) for n in heads] + + return [self.node(r) for r in + ancestor.missingancestors(heads, common, self.parentrevs)] def nodesbetween(self, roots=None, heads=None): """Return a topological path from 'roots' to 'heads'.
--- a/mercurial/revset.py Mon Jan 14 23:14:45 2013 +0900 +++ b/mercurial/revset.py Sat Jan 19 17:24:33 2013 -0600 @@ -13,6 +13,7 @@ from i18n import _ import encoding import obsolete as obsmod +import repoview def _revancestors(repo, revs, followfirst): """Like revlog.ancestors(), but supports followfirst.""" @@ -442,6 +443,18 @@ bumped = obsmod.getrevs(repo, 'bumped') return [r for r in subset if r in bumped] +def bundle(repo, subset, x): + """``bundle()`` + Changesets in the bundle. + + Bundle must be specified by the -R option.""" + + try: + bundlerevs = repo.changelog.bundlerevs + except AttributeError: + raise util.Abort(_("no bundle provided - specify with -R")) + return [r for r in subset if r in bundlerevs] + def checkstatus(repo, subset, pat, field): m = None s = [] @@ -475,8 +488,13 @@ def _children(repo, narrow, parentset): cs = set() + if not parentset: + return cs pr = repo.changelog.parentrevs + minrev = min(parentset) for r in narrow: + if r <= minrev: + continue for p in pr(r): if p in parentset: cs.add(r) @@ -628,6 +646,15 @@ return [r for r in subset if r in dests] +def divergent(repo, subset, x): + """``divergent()`` + Final successors of changesets with an alternative set of final successors. + """ + # i18n: "divergent" is a keyword + getargs(x, 0, 0, _("divergent takes no arguments")) + divergent = obsmod.getrevs(repo, 'divergent') + return [r for r in subset if r in divergent] + def draft(repo, subset, x): """``draft()`` Changeset in draft phase.""" @@ -865,7 +892,8 @@ """ # i18n: "hidden" is a keyword getargs(x, 0, 0, _("hidden takes no arguments")) - return [r for r in subset if r in repo.hiddenrevs] + hiddenrevs = repoview.filterrevs(repo, 'visible') + return [r for r in subset if r in hiddenrevs] def keyword(repo, subset, x): """``keyword(string)`` @@ -1513,6 +1541,7 @@ "branch": branch, "branchpoint": branchpoint, "bumped": bumped, + "bundle": bundle, "children": children, "closed": closed, "contains": contains, @@ -1522,6 +1551,7 @@ "descendants": descendants, "_firstdescendants": _firstdescendants, "destination": destination, + "divergent": divergent, "draft": draft, "extinct": extinct, "extra": extra,
--- a/mercurial/scmutil.py Mon Jan 14 23:14:45 2013 +0900 +++ b/mercurial/scmutil.py Sat Jan 19 17:24:33 2013 -0600 @@ -252,9 +252,9 @@ def _setmustaudit(self, onoff): self._audit = onoff if onoff: - self.auditor = pathauditor(self.base) + self.audit = pathauditor(self.base) else: - self.auditor = util.always + self.audit = util.always mustaudit = property(_getmustaudit, _setmustaudit) @@ -276,51 +276,52 @@ r = util.checkosfilename(path) if r: raise util.Abort("%s: %r" % (r, path)) - self.auditor(path) + self.audit(path) f = self.join(path) if not text and "b" not in mode: mode += "b" # for that other OS nlink = -1 - dirname, basename = util.split(f) - # If basename is empty, then the path is malformed because it points - # to a directory. Let the posixfile() call below raise IOError. - if basename and mode not in ('r', 'rb'): - if atomictemp: - if not os.path.isdir(dirname): - util.makedirs(dirname, self.createmode) - return util.atomictempfile(f, mode, self.createmode) - try: - if 'w' in mode: - util.unlink(f) + if mode not in ('r', 'rb'): + dirname, basename = util.split(f) + # If basename is empty, then the path is malformed because it points + # to a directory. Let the posixfile() call below raise IOError. + if basename: + if atomictemp: + if not os.path.isdir(dirname): + util.makedirs(dirname, self.createmode) + return util.atomictempfile(f, mode, self.createmode) + try: + if 'w' in mode: + util.unlink(f) + nlink = 0 + else: + # nlinks() may behave differently for files on Windows + # shares if the file is open. + fd = util.posixfile(f) + nlink = util.nlinks(f) + if nlink < 1: + nlink = 2 # force mktempcopy (issue1922) + fd.close() + except (OSError, IOError), e: + if e.errno != errno.ENOENT: + raise nlink = 0 - else: - # nlinks() may behave differently for files on Windows - # shares if the file is open. - fd = util.posixfile(f) - nlink = util.nlinks(f) - if nlink < 1: - nlink = 2 # force mktempcopy (issue1922) - fd.close() - except (OSError, IOError), e: - if e.errno != errno.ENOENT: - raise - nlink = 0 - if not os.path.isdir(dirname): - util.makedirs(dirname, self.createmode) - if nlink > 0: - if self._trustnlink is None: - self._trustnlink = nlink > 1 or util.checknlink(f) - if nlink > 1 or not self._trustnlink: - util.rename(util.mktempcopy(f), f) + if not os.path.isdir(dirname): + util.makedirs(dirname, self.createmode) + if nlink > 0: + if self._trustnlink is None: + self._trustnlink = nlink > 1 or util.checknlink(f) + if nlink > 1 or not self._trustnlink: + util.rename(util.mktempcopy(f), f) fp = util.posixfile(f, mode) if nlink == 0: self._fixfilemode(f) return fp def symlink(self, src, dst): - self.auditor(dst) + self.audit(dst) linkname = self.join(dst) try: os.unlink(linkname) @@ -340,9 +341,6 @@ else: self.write(dst, src) - def audit(self, path): - self.auditor(path) - def join(self, path): if path: return os.path.join(self.base, path) @@ -381,6 +379,18 @@ filteropener = filtervfs +class readonlyvfs(abstractvfs, auditvfs): + '''Wrapper vfs preventing any writing.''' + + def __init__(self, vfs): + auditvfs.__init__(self, vfs) + + def __call__(self, path, mode='r', *args, **kw): + if mode not in ('r', 'rb'): + raise util.Abort('this vfs is read only') + return self.vfs(path, mode, *args, **kw) + + def canonpath(root, cwd, myname, auditor=None): '''return the canonical path of myname, given cwd and root''' if util.endswithsep(root): @@ -425,7 +435,7 @@ break name = dirname - raise util.Abort('%s not under root' % myname) + raise util.Abort(_("%s not under root '%s'") % (myname, root)) def walkrepos(path, followsym=False, seen_dirs=None, recurse=False): '''yield every hg repository under path, always recursively. @@ -637,13 +647,13 @@ start, end = spec.split(_revrangesep, 1) start = revfix(repo, start, 0) end = revfix(repo, end, len(repo) - 1) - step = start > end and -1 or 1 + rangeiter = repo.changelog.revs(start, end) if not seen and not l: # by far the most common case: revs = ["-1:0"] - l = range(start, end + step, step) + l = list(rangeiter) # defer syncing seen until next iteration continue - newrevs = set(xrange(start, end + step, step)) + newrevs = set(rangeiter) if seen: newrevs.difference_update(seen) seen.update(newrevs) @@ -850,15 +860,19 @@ return requirements class filecacheentry(object): - def __init__(self, path): + def __init__(self, path, stat=True): self.path = path - self.cachestat = filecacheentry.stat(self.path) + self.cachestat = None + self._cacheable = None - if self.cachestat: - self._cacheable = self.cachestat.cacheable() - else: - # None means we don't know yet - self._cacheable = None + if stat: + self.cachestat = filecacheentry.stat(self.path) + + if self.cachestat: + self._cacheable = self.cachestat.cacheable() + else: + # None means we don't know yet + self._cacheable = None def refresh(self): if self.cacheable(): @@ -933,6 +947,7 @@ def __get__(self, obj, type=None): # do we need to check if the file changed? if self.name in obj.__dict__: + assert self.name in obj._filecache, self.name return obj.__dict__[self.name] entry = obj._filecache.get(self.name) @@ -954,12 +969,19 @@ return entry.obj def __set__(self, obj, value): - if self.name in obj._filecache: - obj._filecache[self.name].obj = value # update cached copy + if self.name not in obj._filecache: + # we add an entry for the missing value because X in __dict__ + # implies X in _filecache + ce = filecacheentry(self.join(obj, self.path), False) + obj._filecache[self.name] = ce + else: + ce = obj._filecache[self.name] + + ce.obj = value # update cached copy obj.__dict__[self.name] = value # update copy returned by obj.x def __delete__(self, obj): try: del obj.__dict__[self.name] except KeyError: - raise AttributeError, self.name + raise AttributeError(self.name)
--- a/mercurial/statichttprepo.py Mon Jan 14 23:14:45 2013 +0900 +++ b/mercurial/statichttprepo.py Sat Jan 19 17:24:33 2013 -0600 @@ -134,8 +134,7 @@ self.changelog = changelog.changelog(self.sopener) self._tags = None self.nodetagscache = None - self._branchcache = None - self._branchcachetip = None + self._branchcaches = {} self.encodepats = None self.decodepats = None
--- a/mercurial/store.py Mon Jan 14 23:14:45 2013 +0900 +++ b/mercurial/store.py Sat Jan 19 17:24:33 2013 -0600 @@ -76,7 +76,7 @@ cmap = dict([(chr(x), chr(x)) for x in xrange(127)]) for x in (range(32) + range(126, 256) + winreserved): cmap[chr(x)] = "~%02x" % x - for x in range(ord("A"), ord("Z")+1) + [ord(e)]: + for x in range(ord("A"), ord("Z") + 1) + [ord(e)]: cmap[chr(x)] = e + chr(x).lower() dmap = {} for k, v in cmap.iteritems(): @@ -128,11 +128,11 @@ cmap = dict([(chr(x), chr(x)) for x in xrange(127)]) for x in (range(32) + range(126, 256) + winreserved): cmap[chr(x)] = "~%02x" % x - for x in range(ord("A"), ord("Z")+1): + for x in range(ord("A"), ord("Z") + 1): cmap[chr(x)] = chr(x).lower() return lambda s: "".join([cmap[c] for c in s]) -lowerencode = _buildlowerencodefun() +lowerencode = getattr(parsers, 'lowerencode', None) or _buildlowerencodefun() # Windows reserved names: con, prn, aux, nul, com1..com9, lpt1..lpt9 _winres3 = ('aux', 'con', 'prn', 'nul') # length 3 @@ -255,22 +255,17 @@ return res def _pathencode(path): + de = encodedir(path) if len(path) > _maxstorepathlen: - return None - ef = _encodefname(encodedir(path)).split('/') + return _hashencode(de, True) + ef = _encodefname(de).split('/') res = '/'.join(_auxencode(ef, True)) if len(res) > _maxstorepathlen: - return None + return _hashencode(de, True) return res _pathencode = getattr(parsers, 'pathencode', _pathencode) -def _dothybridencode(f): - ef = _pathencode(f) - if ef is None: - return _hashencode(encodedir(f), True) - return ef - def _plainhybridencode(f): return _hybridencode(f, False) @@ -456,7 +451,7 @@ class fncachestore(basicstore): def __init__(self, path, vfstype, dotencode): if dotencode: - encode = _dothybridencode + encode = _pathencode else: encode = _plainhybridencode self.encode = encode
--- a/mercurial/subrepo.py Mon Jan 14 23:14:45 2013 +0900 +++ b/mercurial/subrepo.py Sat Jan 19 17:24:33 2013 -0600 @@ -14,6 +14,27 @@ nullstate = ('', '', 'empty') +class SubrepoAbort(error.Abort): + """Exception class used to avoid handling a subrepo error more than once""" + def __init__(self, *args, **kw): + error.Abort.__init__(self, *args, **kw) + self.subrepo = kw.get('subrepo') + +def annotatesubrepoerror(func): + def decoratedmethod(self, *args, **kargs): + try: + res = func(self, *args, **kargs) + except SubrepoAbort, ex: + # This exception has already been handled + raise ex + except error.Abort, ex: + subrepo = subrelpath(self) + errormsg = str(ex) + ' ' + _('(in subrepo %s)') % subrepo + # avoid handling this exception by raising a SubrepoAbort exception + raise SubrepoAbort(errormsg, hint=ex.hint, subrepo=subrepo) + return res + return decoratedmethod + def state(ctx, ui): """return a state dict, mapping subrepo paths configured in .hgsub to tuple: (source from .hgsub, revision from .hgsubstate, kind @@ -126,7 +147,7 @@ r = "%s:%s:%s" % r repo.ui.debug(" subrepo %s: %s %s\n" % (s, msg, r)) - for s, l in s1.items(): + for s, l in sorted(s1.iteritems()): a = sa.get(s, nullstate) ld = l # local state with possible dirty flag for compares if wctx.sub(s).dirty(): @@ -244,8 +265,7 @@ if repo.ui.config('paths', 'default'): return repo.ui.config('paths', 'default') if abort: - raise util.Abort(_("default path for subrepository %s not found") % - reporelpath(repo)) + raise util.Abort(_("default path for subrepository not found")) def itersubrepos(ctx1, ctx2): """find subrepos in ctx1 or ctx2""" @@ -402,6 +422,7 @@ self._repo.ui.setconfig(s, k, v) self._initrepo(r, state[0], create) + @annotatesubrepoerror def _initrepo(self, parentrepo, source, create): self._repo._subparent = parentrepo self._repo._subsource = source @@ -422,10 +443,12 @@ addpathconfig('default-push', defpushpath) fp.close() + @annotatesubrepoerror def add(self, ui, match, dryrun, listsubrepos, prefix, explicitonly): return cmdutil.add(ui, self._repo, match, dryrun, listsubrepos, os.path.join(prefix, self._path), explicitonly) + @annotatesubrepoerror def status(self, rev2, **opts): try: rev1 = self._state[1] @@ -437,6 +460,7 @@ % (inst, subrelpath(self))) return [], [], [], [], [], [], [] + @annotatesubrepoerror def diff(self, ui, diffopts, node2, match, prefix, **opts): try: node1 = node.bin(self._state[1]) @@ -446,12 +470,13 @@ node2 = node.bin(node2) cmdutil.diffordiffstat(ui, self._repo, diffopts, node1, node2, match, - prefix=os.path.join(prefix, self._path), + prefix=posixpath.join(prefix, self._path), listsubrepos=True, **opts) except error.RepoLookupError, inst: self._repo.ui.warn(_('warning: error "%s" in subrepository "%s"\n') % (inst, subrelpath(self))) + @annotatesubrepoerror def archive(self, ui, archiver, prefix, match=None): self._get(self._state + ('hg',)) abstractsubrepo.archive(self, ui, archiver, prefix, match) @@ -463,6 +488,7 @@ submatch = matchmod.narrowmatcher(subpath, match) s.archive(ui, archiver, os.path.join(prefix, self._path), submatch) + @annotatesubrepoerror def dirty(self, ignoreupdate=False): r = self._state[1] if r == '' and not ignoreupdate: # no state recorded @@ -479,6 +505,7 @@ def checknested(self, path): return self._repo._checknested(self._repo.wjoin(path)) + @annotatesubrepoerror def commit(self, text, user, date): # don't bother committing in the subrepo if it's only been # updated @@ -490,6 +517,7 @@ return self._repo['.'].hex() # different version checked out return node.hex(n) + @annotatesubrepoerror def remove(self): # we can't fully delete the repository as it may contain # local-only history @@ -519,12 +547,14 @@ bookmarks.updatefromremote(self._repo.ui, self._repo, other, srcurl) + @annotatesubrepoerror def get(self, state, overwrite=False): self._get(state) source, revision, kind = state self._repo.ui.debug("getting subrepo %s\n" % self._path) hg.updaterepo(self._repo, revision, overwrite) + @annotatesubrepoerror def merge(self, state): self._get(state) cur = self._repo['.'] @@ -551,6 +581,7 @@ else: mergefunc() + @annotatesubrepoerror def push(self, opts): force = opts.get('force') newbranch = opts.get('new_branch') @@ -569,12 +600,15 @@ other = hg.peer(self._repo, {'ssh': ssh}, dsturl) return self._repo.push(other, force, newbranch=newbranch) + @annotatesubrepoerror def outgoing(self, ui, dest, opts): return hg.outgoing(ui, self._repo, _abssource(self._repo, True), opts) + @annotatesubrepoerror def incoming(self, ui, source, opts): return hg.incoming(ui, self._repo, _abssource(self._repo, False), opts) + @annotatesubrepoerror def files(self): rev = self._state[1] ctx = self._repo[rev] @@ -593,10 +627,12 @@ ctx = self._repo[None] return ctx.walk(match) + @annotatesubrepoerror def forget(self, ui, match, prefix): return cmdutil.forget(ui, self._repo, match, os.path.join(prefix, self._path), True) + @annotatesubrepoerror def revert(self, ui, substate, *pats, **opts): # reverting a subrepo is a 2 step process: # 1. if the no_backup is not set, revert all modified @@ -751,6 +787,7 @@ pass return rev + @annotatesubrepoerror def commit(self, text, user, date): # user and date are out of our hands since svn is centralized changed, extchanged, missing = self._wcchanged() @@ -778,6 +815,7 @@ self._ui.status(self._svncommand(['update', '-r', newrev])[0]) return newrev + @annotatesubrepoerror def remove(self): if self.dirty(): self._ui.warn(_('not removing repo %s because ' @@ -802,6 +840,7 @@ except OSError: pass + @annotatesubrepoerror def get(self, state, overwrite=False): if overwrite: self._svncommand(['revert', '--recursive']) @@ -822,6 +861,7 @@ raise util.Abort((status or err).splitlines()[-1]) self._ui.status(status) + @annotatesubrepoerror def merge(self, state): old = self._state[1] new = state[1] @@ -835,6 +875,7 @@ # push is a no-op for SVN return True + @annotatesubrepoerror def files(self): output = self._svncommand(['list', '--recursive', '--xml'])[0] doc = xml.dom.minidom.parseString(output) @@ -1021,6 +1062,7 @@ raise util.Abort(_("revision %s does not exist in subrepo %s\n") % (revision, self._relpath)) + @annotatesubrepoerror def dirty(self, ignoreupdate=False): if self._gitmissing(): return self._state[1] != '' @@ -1037,6 +1079,7 @@ def basestate(self): return self._gitstate() + @annotatesubrepoerror def get(self, state, overwrite=False): source, revision, kind = state if not revision: @@ -1120,6 +1163,7 @@ # a real merge would be required, just checkout the revision rawcheckout() + @annotatesubrepoerror def commit(self, text, user, date): if self._gitmissing(): raise util.Abort(_("subrepo %s is missing") % self._relpath) @@ -1137,6 +1181,7 @@ # circumstances return self._gitstate() + @annotatesubrepoerror def merge(self, state): source, revision, kind = state self._fetch(source, revision) @@ -1159,6 +1204,7 @@ else: mergefunc() + @annotatesubrepoerror def push(self, opts): force = opts.get('force') @@ -1198,6 +1244,7 @@ (self._relpath, self._state[1])) return False + @annotatesubrepoerror def remove(self): if self._gitmissing(): return @@ -1247,6 +1294,7 @@ ui.progress(_('archiving (%s)') % relpath, None) + @annotatesubrepoerror def status(self, rev2, **opts): rev1 = self._state[1] if self._gitmissing() or not rev1:
--- a/mercurial/templater.py Mon Jan 14 23:14:45 2013 +0900 +++ b/mercurial/templater.py Sat Jan 19 17:24:33 2013 -0600 @@ -8,6 +8,7 @@ from i18n import _ import sys, os, re import util, config, templatefilters, parser, error +import types # template parsing @@ -140,6 +141,10 @@ v = context._defaults.get(key, '') if util.safehasattr(v, '__call__'): return v(**mapping) + if isinstance(v, types.GeneratorType): + v = list(v) + mapping[key] = v + return v return v def buildfilter(exp, context): @@ -179,6 +184,7 @@ for i in d: if isinstance(i, dict): lm.update(i) + lm['originalnode'] = mapping.get('node') yield runtemplate(context, lm, ctmpl) else: # v is not an iterable of dicts, this happen when 'key' @@ -259,6 +265,15 @@ t = stringify(args[3][0](context, mapping, args[3][1])) yield runtemplate(context, mapping, compiletemplate(t, context)) +def label(context, mapping, args): + if len(args) != 2: + # i18n: "label" is a keyword + raise error.ParseError(_("label expects two arguments")) + + # ignore args[0] (the label string) since this is supposed to be a a no-op + t = stringify(args[1][0](context, mapping, args[1][1])) + yield runtemplate(context, mapping, compiletemplate(t, context)) + methods = { "string": lambda e, c: (runstring, e[1]), "symbol": lambda e, c: (runsymbol, e[1]), @@ -274,6 +289,7 @@ "ifeq": ifeq, "join": join, "sub": sub, + "label": label, } # template engine
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/mercurial/templates/atom/branchentry.tmpl Sat Jan 19 17:24:33 2013 -0600 @@ -0,0 +1,8 @@ + <entry> + <title>{branch|escape}</title> + <link rel="alternate" href="{urlbase}{url}rev/{node|short}"/> + <id>{urlbase}{url}#branch-{node}</id> + <updated>{date|rfc3339date}</updated> + <published>{date|rfc3339date}</published> + <content type="text"><![CDATA[{branch|strip|escape|addbreaks}]]></content> + </entry>
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/mercurial/templates/atom/branches.tmpl Sat Jan 19 17:24:33 2013 -0600 @@ -0,0 +1,11 @@ +{header} + <id>{urlbase}{url}</id> + <link rel="self" href="{urlbase}{url}atom-tags"/> + <link rel="alternate" href="{urlbase}{url}tags"/> + <title>{repo|escape}: branches</title> + <summary>{repo|escape} branch history</summary> + <author><name>Mercurial SCM</name></author> + {latestentry%feedupdated} + + {entries%branchentry} +</feed>
--- a/mercurial/templates/atom/map Mon Jan 14 23:14:45 2013 +0900 +++ b/mercurial/templates/atom/map Sat Jan 19 17:24:33 2013 -0600 @@ -10,4 +10,6 @@ tagentry = tagentry.tmpl bookmarks = bookmarks.tmpl bookmarkentry = bookmarkentry.tmpl +branches = branches.tmpl +branchentry = branchentry.tmpl error = error.tmpl
--- a/mercurial/templates/coal/map Mon Jan 14 23:14:45 2013 +0900 +++ b/mercurial/templates/coal/map Sat Jan 19 17:24:33 2013 -0600 @@ -223,3 +223,4 @@ error = ../paper/error.tmpl urlparameter = '{separator}{name}={value|urlescape}' hiddenformentry = '<input type="hidden" name="{name}" value="{value|escape}" />' +breadcrumb = '> <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}
--- 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":
--- 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):