# HG changeset patch # User Matt Mackall # Date 1354833735 21600 # Node ID c8326ffdcb4f86877b4e71ac7df213365305cd6d # Parent 8b846dbc57b6c25247e733e00f0e7766b900a3ff# Parent ebc0fa067c07808b77f060e285d0c9d8d25c6750 merge with stable diff -r ebc0fa067c07 -r c8326ffdcb4f Makefile --- a/Makefile Thu Dec 06 13:21:27 2012 -0600 +++ b/Makefile Thu Dec 06 16:42:15 2012 -0600 @@ -34,7 +34,7 @@ local: $(PYTHON) setup.py $(PURE) build_py -c -d . build_ext -i build_hgexe -i build_mo - $(PYTHON) hg version + env HGRCPATH= $(PYTHON) hg version build: $(PYTHON) setup.py $(PURE) build diff -r ebc0fa067c07 -r c8326ffdcb4f contrib/check-code.py --- a/contrib/check-code.py Thu Dec 06 13:21:27 2012 -0600 +++ b/contrib/check-code.py Thu Dec 06 16:42:15 2012 -0600 @@ -211,11 +211,11 @@ (r'\.strip\(\)\.split\(\)', "no need to strip before splitting"), (r'^\s*except\s*:', "warning: naked except clause", r'#.*re-raises'), (r':\n( )*( ){1,3}[^ ]', "must indent 4 spaces"), + (r'ui\.(status|progress|write|note|warn)\([\'\"]x', + "missing _() in ui message (use () to hide false-positives)"), ], # warnings [ - (r'ui\.(status|progress|write|note|warn)\([\'\"]x', - "warning: unwrapped ui message"), ] ] diff -r ebc0fa067c07 -r c8326ffdcb4f contrib/hgk --- a/contrib/hgk Thu Dec 06 13:21:27 2012 -0600 +++ b/contrib/hgk Thu Dec 06 16:42:15 2012 -0600 @@ -15,8 +15,43 @@ # The whole snipped is activated only under windows, mouse wheel # bindings working already under MacOSX and Linux. +if {[catch {package require Ttk}]} { + # use a shim + namespace eval ttk { + proc style args {} + + proc entry args { + eval [linsert $args 0 ::entry] -relief flat + } + } + + interp alias {} ttk::button {} button + interp alias {} ttk::frame {} frame + interp alias {} ttk::label {} label + interp alias {} ttk::scrollbar {} scrollbar + interp alias {} ttk::optionMenu {} tk_optionMenu +} else { + proc ::ttk::optionMenu {w varName firstValue args} { + upvar #0 $varName var + + if {![info exists var]} { + set var $firstValue + } + ttk::menubutton $w -textvariable $varName -menu $w.menu \ + -direction flush + menu $w.menu -tearoff 0 + $w.menu add radiobutton -label $firstValue -variable $varName + foreach i $args { + $w.menu add radiobutton -label $i -variable $varName + } + return $w.menu + } +} + if {[tk windowingsystem] eq "win32"} { +ttk::style theme use xpnative + set mw_classes [list Text Listbox Table TreeCtrl] foreach class $mw_classes { bind $class {} } @@ -72,6 +107,12 @@ bind all [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 "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 {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 diff -r ebc0fa067c07 -r c8326ffdcb4f contrib/perf.py --- a/contrib/perf.py Thu Dec 06 13:21:27 2012 -0600 +++ b/contrib/perf.py Thu Dec 06 16:42:15 2012 -0600 @@ -40,11 +40,11 @@ except Exception: timer(lambda: len(list(cmdutil.walk(repo, pats, {})))) -def perfstatus(ui, repo, *pats): +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 @@ -238,7 +238,9 @@ 'perfnodelookup': (perfnodelookup, []), 'perfparents': (perfparents, []), 'perfstartup': (perfstartup, []), - 'perfstatus': (perfstatus, []), + 'perfstatus': (perfstatus, + [('u', 'unknown', False, + 'ask status to look for unknown files')]), 'perfwalk': (perfwalk, []), 'perfmanifest': (perfmanifest, []), 'perfchangeset': (perfchangeset, []), diff -r ebc0fa067c07 -r c8326ffdcb4f hgext/convert/__init__.py --- a/hgext/convert/__init__.py Thu Dec 06 13:21:27 2012 -0600 +++ b/hgext/convert/__init__.py Thu Dec 06 16:42:15 2012 -0600 @@ -191,6 +191,10 @@ branch indicated in the regex as the second parent of the changeset. Default is ``{{mergefrombranch ([-\\w]+)}}`` + :convert.localtimezone: use local time (as determined by the TZ + environment variable) for changeset date/times. The default + is False (use UTC). + :hook.cvslog: Specify a Python function to be called at the end of gathering the CVS log. The function is passed a list with the log entries, and can modify the entries in-place, or add or @@ -231,6 +235,10 @@ :convert.svn.trunk: specify the name of the trunk branch. The default is ``trunk``. + :convert.localtimezone: use local time (as determined by the TZ + environment variable) for changeset date/times. The default + is False (use UTC). + Source history can be retrieved starting at a specific revision, instead of being integrally converted. Only single branch conversions are supported. diff -r ebc0fa067c07 -r c8326ffdcb4f hgext/convert/common.py --- a/hgext/convert/common.py Thu Dec 06 13:21:27 2012 -0600 +++ b/hgext/convert/common.py Thu Dec 06 16:42:15 2012 -0600 @@ -5,7 +5,7 @@ # This software may be used and distributed according to the terms of the # GNU General Public License version 2 or any later version. -import base64, errno, subprocess, os +import base64, errno, subprocess, os, datetime import cPickle as pickle from mercurial import util from mercurial.i18n import _ @@ -446,3 +446,10 @@ if e.errno != errno.ENOENT: raise return m + +def makedatetimestamp(t): + """Like util.makedate() but for time t instead of current time""" + delta = (datetime.datetime.utcfromtimestamp(t) - + datetime.datetime.fromtimestamp(t)) + tz = delta.days * 86400 + delta.seconds + return t, tz diff -r ebc0fa067c07 -r c8326ffdcb4f hgext/convert/cvs.py --- a/hgext/convert/cvs.py Thu Dec 06 13:21:27 2012 -0600 +++ b/hgext/convert/cvs.py Thu Dec 06 16:42:15 2012 -0600 @@ -11,6 +11,7 @@ from mercurial.i18n import _ from common import NoRepo, commit, converter_source, checktool +from common import makedatetimestamp import cvsps class convert_cvs(converter_source): @@ -70,6 +71,8 @@ cs.author = self.recode(cs.author) self.lastbranch[cs.branch] = id cs.comment = self.recode(cs.comment) + if self.ui.configbool('convert', 'localtimezone'): + cs.date = makedatetimestamp(cs.date[0]) date = util.datestr(cs.date, '%Y-%m-%d %H:%M:%S %1%2') self.tags.update(dict.fromkeys(cs.tags, id)) diff -r ebc0fa067c07 -r c8326ffdcb4f hgext/convert/cvsps.py --- a/hgext/convert/cvsps.py Thu Dec 06 13:21:27 2012 -0600 +++ b/hgext/convert/cvsps.py Thu Dec 06 16:42:15 2012 -0600 @@ -801,22 +801,22 @@ # Note: trailing spaces on several lines here are needed to have # bug-for-bug compatibility with cvsps. ui.write('---------------------\n') - ui.write('PatchSet %d \n' % cs.id) - ui.write('Date: %s\n' % util.datestr(cs.date, - '%Y/%m/%d %H:%M:%S %1%2')) - ui.write('Author: %s\n' % cs.author) - ui.write('Branch: %s\n' % (cs.branch or 'HEAD')) - ui.write('Tag%s: %s \n' % (['', 's'][len(cs.tags) > 1], - ','.join(cs.tags) or '(none)')) + ui.write(('PatchSet %d \n' % cs.id)) + ui.write(('Date: %s\n' % util.datestr(cs.date, + '%Y/%m/%d %H:%M:%S %1%2'))) + ui.write(('Author: %s\n' % cs.author)) + ui.write(('Branch: %s\n' % (cs.branch or 'HEAD'))) + ui.write(('Tag%s: %s \n' % (['', 's'][len(cs.tags) > 1], + ','.join(cs.tags) or '(none)'))) branchpoints = getattr(cs, 'branchpoints', None) if branchpoints: - ui.write('Branchpoints: %s \n' % ', '.join(branchpoints)) + ui.write(('Branchpoints: %s \n' % ', '.join(branchpoints))) if opts["parents"] and cs.parents: if len(cs.parents) > 1: - ui.write('Parents: %s\n' % - (','.join([str(p.id) for p in cs.parents]))) + ui.write(('Parents: %s\n' % + (','.join([str(p.id) for p in cs.parents])))) else: - ui.write('Parent: %d\n' % cs.parents[0].id) + ui.write(('Parent: %d\n' % cs.parents[0].id)) if opts["ancestors"]: b = cs.branch @@ -825,11 +825,11 @@ b, c = ancestors[b] r.append('%s:%d:%d' % (b or "HEAD", c, branches[b])) if r: - ui.write('Ancestors: %s\n' % (','.join(r))) + ui.write(('Ancestors: %s\n' % (','.join(r)))) - ui.write('Log:\n') + ui.write(('Log:\n')) ui.write('%s\n\n' % cs.comment) - ui.write('Members: \n') + ui.write(('Members: \n')) for f in cs.entries: fn = f.file if fn.startswith(opts["prefix"]): diff -r ebc0fa067c07 -r c8326ffdcb4f hgext/convert/git.py --- a/hgext/convert/git.py Thu Dec 06 13:21:27 2012 -0600 +++ b/hgext/convert/git.py Thu Dec 06 16:42:15 2012 -0600 @@ -6,12 +6,24 @@ # GNU General Public License version 2 or any later version. import os -from mercurial import util +from mercurial import util, config from mercurial.node import hex, nullid from mercurial.i18n import _ from common import NoRepo, commit, converter_source, checktool +class submodule(object): + def __init__(self, path, node, url): + self.path = path + self.node = node + self.url = url + + def hgsub(self): + return "%s = [git]%s" % (self.path, self.url) + + def hgsubstate(self): + return "%s %s" % (self.node, self.path) + class convert_git(converter_source): # Windows does not support GIT_DIR= construct while other systems # cannot remove environment variable. Just assume none have @@ -55,6 +67,7 @@ checktool('git', 'git') self.path = path + self.submodules = [] def getheads(self): if not self.rev: @@ -76,16 +89,57 @@ return data def getfile(self, name, rev): - data = self.catfile(rev, "blob") - mode = self.modecache[(name, rev)] + if name == '.hgsub': + data = '\n'.join([m.hgsub() for m in self.submoditer()]) + mode = '' + elif name == '.hgsubstate': + data = '\n'.join([m.hgsubstate() for m in self.submoditer()]) + mode = '' + else: + data = self.catfile(rev, "blob") + mode = self.modecache[(name, rev)] return data, mode + def submoditer(self): + null = hex(nullid) + for m in sorted(self.submodules, key=lambda p: p.path): + if m.node != null: + yield m + + def parsegitmodules(self, content): + """Parse the formatted .gitmodules file, example file format: + [submodule "sub"]\n + \tpath = sub\n + \turl = git://giturl\n + """ + self.submodules = [] + c = config.config() + # Each item in .gitmodules starts with \t that cant be parsed + c.parse('.gitmodules', content.replace('\t','')) + for sec in c.sections(): + s = c[sec] + if 'url' in s and 'path' in s: + self.submodules.append(submodule(s['path'], '', s['url'])) + + def retrievegitmodules(self, version): + modules, ret = self.gitread("git show %s:%s" % (version, '.gitmodules')) + if ret: + raise util.Abort(_('cannot read submodules config file in %s') % + version) + self.parsegitmodules(modules) + for m in self.submodules: + node, ret = self.gitread("git rev-parse %s:%s" % (version, m.path)) + if ret: + continue + m.node = node.strip() + def getchanges(self, version): self.modecache = {} fh = self.gitopen("git diff-tree -z --root -m -r %s" % version) changes = [] seen = set() entry = None + subexists = False for l in fh.read().split('\x00'): if not entry: if not l.startswith(':'): @@ -97,15 +151,24 @@ seen.add(f) entry = entry.split() h = entry[3] - if entry[1] == '160000': - raise util.Abort('git submodules are not supported!') p = (entry[1] == "100755") s = (entry[1] == "120000") - self.modecache[(f, h)] = (p and "x") or (s and "l") or "" - changes.append((f, h)) + + if f == '.gitmodules': + subexists = True + changes.append(('.hgsub', '')) + elif entry[1] == '160000' or entry[0] == ':160000': + subexists = True + else: + self.modecache[(f, h)] = (p and "x") or (s and "l") or "" + changes.append((f, h)) entry = None if fh.close(): raise util.Abort(_('cannot read changes in %s') % version) + + if subexists: + self.retrievegitmodules(version) + changes.append(('.hgsubstate', '')) return (changes, {}) def getcommit(self, version): diff -r ebc0fa067c07 -r c8326ffdcb4f hgext/convert/hg.py --- a/hgext/convert/hg.py Thu Dec 06 13:21:27 2012 -0600 +++ b/hgext/convert/hg.py Thu Dec 06 16:42:15 2012 -0600 @@ -219,9 +219,10 @@ return self.ui.status(_("updating bookmarks\n")) + destmarks = self.repo._bookmarks for bookmark in updatedbookmark: - self.repo._bookmarks[bookmark] = bin(updatedbookmark[bookmark]) - bookmarks.write(self.repo) + destmarks[bookmark] = bin(updatedbookmark[bookmark]) + destmarks.write() def hascommit(self, rev): if rev not in self.repo and self.clonebranches: diff -r ebc0fa067c07 -r c8326ffdcb4f hgext/convert/subversion.py --- a/hgext/convert/subversion.py Thu Dec 06 13:21:27 2012 -0600 +++ b/hgext/convert/subversion.py Thu Dec 06 16:42:15 2012 -0600 @@ -18,6 +18,7 @@ from common import NoRepo, MissingTool, commit, encodeargs, decodeargs from common import commandline, converter_source, converter_sink, mapfile +from common import makedatetimestamp try: from svn.core import SubversionException, Pool @@ -802,6 +803,8 @@ # ISO-8601 conformant # '2007-01-04T17:35:00.902377Z' date = util.parsedate(date[:19] + " UTC", ["%Y-%m-%dT%H:%M:%S"]) + if self.ui.configbool('convert', 'localtimezone'): + date = makedatetimestamp(date[0]) log = message and self.recode(message) or '' author = author and self.recode(author) or '' diff -r ebc0fa067c07 -r c8326ffdcb4f hgext/eol.py --- a/hgext/eol.py Thu Dec 06 13:21:27 2012 -0600 +++ b/hgext/eol.py Thu Dec 06 16:42:15 2012 -0600 @@ -307,7 +307,7 @@ eolmtime = 0 if eolmtime > cachemtime: - ui.debug("eol: detected change in .hgeol\n") + self.ui.debug("eol: detected change in .hgeol\n") wlock = None try: wlock = self.wlock() diff -r ebc0fa067c07 -r c8326ffdcb4f hgext/hgk.py --- a/hgext/hgk.py Thu Dec 06 13:21:27 2012 -0600 +++ b/hgext/hgk.py Thu Dec 06 16:42:15 2012 -0600 @@ -98,9 +98,9 @@ if ctx is None: ctx = repo[n] # use ctx.node() instead ?? - ui.write("tree %s\n" % short(ctx.changeset()[0])) + ui.write(("tree %s\n" % short(ctx.changeset()[0]))) for p in ctx.parents(): - ui.write("parent %s\n" % p) + ui.write(("parent %s\n" % p)) date = ctx.date() description = ctx.description().replace("\0", "") @@ -108,12 +108,13 @@ if lines and lines[-1].startswith('committer:'): committer = lines[-1].split(': ')[1].rstrip() else: - committer = ctx.user() + committer = "" - ui.write("author %s %s %s\n" % (ctx.user(), int(date[0]), date[1])) - ui.write("committer %s %s %s\n" % (committer, int(date[0]), date[1])) - ui.write("revision %d\n" % ctx.rev()) - ui.write("branch %s\n\n" % ctx.branch()) + ui.write(("author %s %s %s\n" % (ctx.user(), int(date[0]), date[1]))) + if committer != '': + ui.write(("committer %s %s %s\n" % (committer, int(date[0]), date[1]))) + ui.write(("revision %d\n" % ctx.rev())) + ui.write(("branch %s\n\n" % ctx.branch())) if prefix != "": ui.write("%s%s\n" % (prefix, @@ -302,7 +303,7 @@ def config(ui, repo, **opts): """print extension options""" def writeopt(name, value): - ui.write('k=%s\nv=%s\n' % (name, value)) + ui.write(('k=%s\nv=%s\n' % (name, value))) writeopt('vdiff', ui.config('hgk', 'vdiff', '')) diff -r ebc0fa067c07 -r c8326ffdcb4f hgext/histedit.py --- a/hgext/histedit.py Thu Dec 06 13:21:27 2012 -0600 +++ b/hgext/histedit.py Thu Dec 06 16:42:15 2012 -0600 @@ -144,7 +144,6 @@ import pickle import os -from mercurial import bookmarks from mercurial import cmdutil from mercurial import discovery from mercurial import error @@ -740,12 +739,13 @@ # nothing to move moves.append((bk, new[-1])) if moves: + marks = repo._bookmarks for mark, new in moves: - old = repo._bookmarks[mark] + old = marks[mark] ui.note(_('histedit: moving bookmarks %s from %s to %s\n') % (mark, node.short(old), node.short(new))) - repo._bookmarks[mark] = new - bookmarks.write(repo) + marks[mark] = new + marks.write() def cleanupnode(ui, repo, name, nodes): """strip a group of nodes from the repository diff -r ebc0fa067c07 -r c8326ffdcb4f hgext/largefiles/overrides.py --- a/hgext/largefiles/overrides.py Thu Dec 06 13:21:27 2012 -0600 +++ b/hgext/largefiles/overrides.py Thu Dec 06 16:42:15 2012 -0600 @@ -1064,6 +1064,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): diff -r ebc0fa067c07 -r c8326ffdcb4f hgext/largefiles/reposetup.py --- a/hgext/largefiles/reposetup.py Thu Dec 06 13:21:27 2012 -0600 +++ b/hgext/largefiles/reposetup.py Thu Dec 06 16:42:15 2012 -0600 @@ -14,6 +14,7 @@ from mercurial import context, error, manifest, match as match_, util from mercurial import node as node_ from mercurial.i18n import _ +from mercurial import localrepo import lfcommands import proto @@ -88,6 +89,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 diff -r ebc0fa067c07 -r c8326ffdcb4f hgext/mq.py --- a/hgext/mq.py Thu Dec 06 13:21:27 2012 -0600 +++ b/hgext/mq.py Thu Dec 06 16:42:15 2012 -0600 @@ -63,7 +63,7 @@ from mercurial.node import bin, hex, short, nullid, nullrev from mercurial.lock import release from mercurial import commands, cmdutil, hg, scmutil, util, revset -from mercurial import repair, extensions, error, phases, bookmarks +from mercurial import repair, extensions, error, phases from mercurial import patch as patchmod import os, re, errno, shutil @@ -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 @@ -1675,9 +1676,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 +3001,7 @@ revs.update(set(rsrevs)) if not revs: del marks[mark] - repo._writebookmarks(mark) + marks.write() ui.write(_("bookmark '%s' deleted\n") % mark) if not revs: @@ -3049,7 +3051,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 +3437,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 +3453,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])) @@ -3481,7 +3484,7 @@ else: qbasenode = q.applied[0].node try: - qbase = cl.rev(qbasenode) + qbase = self.unfiltered().changelog.rev(qbasenode) except error.LookupError: self.ui.warn(_('mq status file refers to unknown node %s\n') % short(qbasenode)) diff -r ebc0fa067c07 -r c8326ffdcb4f hgext/patchbomb.py --- a/hgext/patchbomb.py Thu Dec 06 13:21:27 2012 -0600 +++ b/hgext/patchbomb.py Thu Dec 06 16:42:15 2012 -0600 @@ -474,11 +474,11 @@ if opts.get('diffstat') or opts.get('confirm'): ui.write(_('\nFinal summary:\n\n')) - ui.write('From: %s\n' % sender) + ui.write(('From: %s\n' % sender)) for addr in showaddrs: ui.write('%s\n' % addr) for m, subj, ds in msgs: - ui.write('Subject: %s\n' % subj) + ui.write(('Subject: %s\n' % subj)) if ds: ui.write(ds) ui.write('\n') diff -r ebc0fa067c07 -r c8326ffdcb4f hgext/rebase.py --- a/hgext/rebase.py Thu Dec 06 13:21:27 2012 -0600 +++ b/hgext/rebase.py Thu Dec 06 16:42:15 2012 -0600 @@ -479,13 +479,14 @@ def updatebookmarks(repo, nstate, originalbookmarks, **opts): 'Move bookmarks to their correct changesets' + marks = repo._bookmarks for k, v in originalbookmarks.iteritems(): if v in nstate: if nstate[v] != nullmerge: # update the bookmarks for revs that have moved - repo._bookmarks[k] = nstate[v] + marks[k] = nstate[v] - bookmarks.write(repo) + marks.write() def storestatus(repo, originalwd, target, state, collapse, keep, keepbranches, external): @@ -655,9 +656,12 @@ # # The actual abort is handled by `defineparents` if len(root.parents()) <= 1: - # (strict) ancestors of not ancestors of - detachset = repo.revs('::%d - ::%d - %d', root, commonbase, root) + # ancestors of not ancestors of + detachset = repo.changelog.findmissingrevs([commonbase.rev()], + [root.rev()]) state.update(dict.fromkeys(detachset, nullmerge)) + # detachset can have root, and we definitely want to rebase that + state[root.rev()] = nullrev return repo['.'].rev(), dest.rev(), state def clearrebased(ui, repo, state, collapsedas=None): diff -r ebc0fa067c07 -r c8326ffdcb4f mercurial/ancestor.py --- a/mercurial/ancestor.py Thu Dec 06 13:21:27 2012 -0600 +++ b/mercurial/ancestor.py Thu Dec 06 16:42:15 2012 -0600 @@ -6,6 +6,7 @@ # GNU General Public License version 2 or any later version. import heapq +from node import nullrev def ancestor(a, b, pfunc): """ @@ -89,3 +90,166 @@ gx = x.next() except StopIteration: return None + +def missingancestors(revs, bases, pfunc): + """Return all the ancestors of revs that are not ancestors of bases. + + This may include elements from revs. + + Equivalent to the revset (::revs - ::bases). Revs are returned in + revision number order, which is a topological order. + + revs and bases should both be iterables. pfunc must return a list of + parent revs for a given revs. + + graph is a dict of child->parent adjacency lists for this graph: + o 13 + | + | o 12 + | | + | | o 11 + | | |\ + | | | | o 10 + | | | | | + | o---+ | 9 + | | | | | + o | | | | 8 + / / / / + | | o | 7 + | | | | + o---+ | 6 + / / / + | | o 5 + | |/ + | o 4 + | | + o | 3 + | | + | o 2 + |/ + o 1 + | + o 0 + >>> graph = {0: [-1], 1: [0], 2: [1], 3: [1], 4: [2], 5: [4], 6: [4], + ... 7: [4], 8: [-1], 9: [6, 7], 10: [5], 11: [3, 7], 12: [9], + ... 13: [8]} + >>> pfunc = graph.get + + Empty revs + >>> missingancestors([], [1], pfunc) + [] + >>> missingancestors([], [], pfunc) + [] + + If bases is empty, it's the same as if it were [nullrev] + >>> missingancestors([12], [], pfunc) + [0, 1, 2, 4, 6, 7, 9, 12] + + Trivial case: revs == bases + >>> missingancestors([0], [0], pfunc) + [] + >>> missingancestors([4, 5, 6], [6, 5, 4], pfunc) + [] + + With nullrev + >>> missingancestors([-1], [12], pfunc) + [] + >>> missingancestors([12], [-1], pfunc) + [0, 1, 2, 4, 6, 7, 9, 12] + + 9 is a parent of 12. 7 is a parent of 9, so an ancestor of 12. 6 is an + ancestor of 12 but not of 7. + >>> missingancestors([12], [9], pfunc) + [12] + >>> missingancestors([9], [12], pfunc) + [] + >>> missingancestors([12, 9], [7], pfunc) + [6, 9, 12] + >>> missingancestors([7, 6], [12], pfunc) + [] + + More complex cases + >>> missingancestors([10], [11, 12], pfunc) + [5, 10] + >>> missingancestors([11], [10], pfunc) + [3, 7, 11] + >>> missingancestors([11], [10, 12], pfunc) + [3, 11] + >>> missingancestors([12], [10], pfunc) + [6, 7, 9, 12] + >>> missingancestors([12], [11], pfunc) + [6, 9, 12] + >>> missingancestors([10, 11, 12], [13], pfunc) + [0, 1, 2, 3, 4, 5, 6, 7, 9, 10, 11, 12] + >>> missingancestors([13], [10, 11, 12], pfunc) + [8, 13] + """ + + revsvisit = set(revs) + basesvisit = set(bases) + if not revsvisit: + return [] + if not basesvisit: + basesvisit.add(nullrev) + start = max(max(revsvisit), max(basesvisit)) + bothvisit = revsvisit.intersection(basesvisit) + revsvisit.difference_update(bothvisit) + basesvisit.difference_update(bothvisit) + # At this point, we hold the invariants that: + # - revsvisit is the set of nodes we know are an ancestor of at least one + # of the nodes in revs + # - basesvisit is the same for bases + # - bothvisit is the set of nodes we know are ancestors of at least one of + # the nodes in revs and one of the nodes in bases + # - a node may be in none or one, but not more, of revsvisit, basesvisit + # and bothvisit at any given time + # Now we walk down in reverse topo order, adding parents of nodes already + # visited to the sets while maintaining the invariants. When a node is + # found in both revsvisit and basesvisit, it is removed from them and + # added to bothvisit instead. When revsvisit becomes empty, there are no + # more ancestors of revs that aren't also ancestors of bases, so exit. + + missing = [] + for curr in xrange(start, nullrev, -1): + if not revsvisit: + break + + if curr in bothvisit: + bothvisit.remove(curr) + # curr's parents might have made it into revsvisit or basesvisit + # through another path + for p in pfunc(curr): + revsvisit.discard(p) + basesvisit.discard(p) + bothvisit.add(p) + continue + + # curr will never be in both revsvisit and basesvisit, since if it + # were it'd have been pushed to bothvisit + if curr in revsvisit: + missing.append(curr) + thisvisit = revsvisit + othervisit = basesvisit + elif curr in basesvisit: + thisvisit = basesvisit + othervisit = revsvisit + else: + # not an ancestor of revs or bases: ignore + continue + + thisvisit.remove(curr) + for p in pfunc(curr): + if p == nullrev: + pass + elif p in othervisit or p in bothvisit: + # p is implicitly in thisvisit. This means p is or should be + # in bothvisit + revsvisit.discard(p) + basesvisit.discard(p) + bothvisit.add(p) + else: + # visit later + thisvisit.add(p) + + missing.reverse() + return missing diff -r ebc0fa067c07 -r c8326ffdcb4f mercurial/bookmarks.py --- a/mercurial/bookmarks.py Thu Dec 06 13:21:27 2012 -0600 +++ b/mercurial/bookmarks.py Thu Dec 06 16:42:15 2012 -0600 @@ -7,35 +7,75 @@ from mercurial.i18n import _ from mercurial.node import hex -from mercurial import encoding, error, util, obsolete, phases +from mercurial import encoding, error, util, obsolete import errno, os -def read(repo): - '''Parse .hg/bookmarks file and return a dictionary +class bmstore(dict): + """Storage for bookmarks. + + This object should do all bookmark reads and writes, so that it's + fairly simple to replace the storage underlying bookmarks without + having to clone the logic surrounding bookmarks. + + This particular bmstore implementation stores bookmarks as + {hash}\s{name}\n (the same format as localtags) in + .hg/bookmarks. The mapping is stored as {name: nodeid}. + + This class does NOT handle the "current" bookmark state at this + time. + """ - Bookmarks are stored as {HASH}\\s{NAME}\\n (localtags format) values - in the .hg/bookmarks file. - Read the file and return a (name=>nodeid) dictionary - ''' - bookmarks = {} - try: - for line in repo.opener('bookmarks'): - line = line.strip() - if not line: - continue - if ' ' not in line: - repo.ui.warn(_('malformed line in .hg/bookmarks: %r\n') % line) - continue - sha, refspec = line.split(' ', 1) - refspec = encoding.tolocal(refspec) + def __init__(self, repo): + dict.__init__(self) + self._repo = repo + try: + for line in repo.vfs('bookmarks'): + line = line.strip() + if not line: + continue + if ' ' not in line: + repo.ui.warn(_('malformed line in .hg/bookmarks: %r\n') + % line) + continue + sha, refspec = line.split(' ', 1) + refspec = encoding.tolocal(refspec) + try: + self[refspec] = repo.changelog.lookup(sha) + except LookupError: + pass + except IOError, inst: + if inst.errno != errno.ENOENT: + raise + + def write(self): + '''Write bookmarks + + Write the given bookmark => hash dictionary to the .hg/bookmarks file + in a format equal to those of localtags. + + We also store a backup of the previous state in undo.bookmarks that + can be copied back on rollback. + ''' + repo = self._repo + if repo._bookmarkcurrent not in self: + setcurrent(repo, None) + + wlock = repo.wlock() + try: + + file = repo.vfs('bookmarks', 'w', atomictemp=True) + for name, node in self.iteritems(): + file.write("%s %s\n" % (hex(node), encoding.fromlocal(name))) + file.close() + + # touch 00changelog.i so hgweb reloads bookmarks (no lock needed) try: - bookmarks[refspec] = repo.changelog.lookup(sha) - except LookupError: + os.utime(repo.sjoin('00changelog.i'), None) + except OSError: pass - except IOError, inst: - if inst.errno != errno.ENOENT: - raise - return bookmarks + + finally: + wlock.release() def readcurrent(repo): '''Get the current bookmark @@ -60,37 +100,6 @@ file.close() return mark -def write(repo): - '''Write bookmarks - - Write the given bookmark => hash dictionary to the .hg/bookmarks file - in a format equal to those of localtags. - - We also store a backup of the previous state in undo.bookmarks that - can be copied back on rollback. - ''' - refs = repo._bookmarks - - if repo._bookmarkcurrent not in refs: - setcurrent(repo, None) - - wlock = repo.wlock() - try: - - file = repo.opener('bookmarks', 'w', atomictemp=True) - for refspec, node in refs.iteritems(): - file.write("%s %s\n" % (hex(node), encoding.fromlocal(refspec))) - file.close() - - # touch 00changelog.i so hgweb reloads bookmarks (no lock needed) - try: - os.utime(repo.sjoin('00changelog.i'), None) - except OSError: - pass - - finally: - wlock.release() - def setcurrent(repo, mark): '''Set the name of the bookmark that we are currently on @@ -152,7 +161,7 @@ if mark != cur: del marks[mark] if update: - repo._writebookmarks(marks) + marks.write() return update def listbookmarks(repo): @@ -179,7 +188,7 @@ if new not in repo: return False marks[key] = repo[new].node() - write(repo) + marks.write() return True finally: w.release() @@ -188,16 +197,17 @@ ui.debug("checking for updated bookmarks\n") rb = remote.listkeys('bookmarks') changed = False + localmarks = repo._bookmarks for k in rb.keys(): - if k in repo._bookmarks: - nr, nl = rb[k], repo._bookmarks[k] + if k in localmarks: + nr, nl = rb[k], localmarks[k] if nr in repo: cr = repo[nr] cl = repo[nl] if cl.rev() >= cr.rev(): continue if validdest(repo, cl, cr): - repo._bookmarks[k] = cr.node() + localmarks[k] = cr.node() changed = True ui.status(_("updating bookmark %s\n") % k) else: @@ -208,7 +218,7 @@ # find a unique @ suffix for x in range(1, 100): n = '%s@%d' % (kd, x) - if n not in repo._bookmarks: + if n not in localmarks: break # try to use an @pathalias suffix # if an @pathalias already exists, we overwrite (update) it @@ -216,17 +226,17 @@ if path == u: n = '%s@%s' % (kd, p) - repo._bookmarks[n] = cr.node() + localmarks[n] = cr.node() changed = True ui.warn(_("divergent bookmark %s stored as %s\n") % (k, n)) elif rb[k] in repo: # add remote bookmarks for changes we already have - repo._bookmarks[k] = repo[rb[k]].node() + localmarks[k] = repo[rb[k]].node() changed = True ui.status(_("adding remote bookmark %s\n") % k) if changed: - write(repo) + localmarks.write() def diff(ui, dst, src): ui.status(_("searching for changed bookmarks\n")) @@ -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) diff -r ebc0fa067c07 -r c8326ffdcb4f mercurial/bundlerepo.py --- a/mercurial/bundlerepo.py Thu Dec 06 13:21:27 2012 -0600 +++ b/mercurial/bundlerepo.py Thu Dec 06 16:42:15 2012 -0600 @@ -33,6 +33,7 @@ self.basemap = {} n = len(self) chain = None + self.bundlenodes = [] while True: chunkdata = bundle.deltachunk(chain) if not chunkdata: @@ -48,6 +49,7 @@ start = bundle.tell() - size link = linkmapper(cs) + self.bundlenodes.append(node) if node in self.nodemap: # this can happen if two branches make the same change chain = node @@ -212,7 +214,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 +222,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 +231,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 diff -r ebc0fa067c07 -r c8326ffdcb4f mercurial/cmdutil.py --- a/mercurial/cmdutil.py Thu Dec 06 13:21:27 2012 -0600 +++ b/mercurial/cmdutil.py Thu Dec 06 16:42:15 2012 -0600 @@ -10,7 +10,7 @@ import os, sys, errno, re, tempfile import util, scmutil, templater, patch, error, templatekw, revlog, copies import match as matchmod -import subrepo, context, repair, bookmarks, graphmod, revset, phases, obsolete +import subrepo, context, repair, graphmod, revset, phases, obsolete import changelog import lock as lockmod @@ -1759,9 +1759,10 @@ # Move bookmarks from old parent to amend commit bms = repo.nodebookmarks(old.node()) if bms: + marks = repo._bookmarks for bm in bms: - repo._bookmarks[bm] = newid - bookmarks.write(repo) + marks[bm] = newid + marks.write() #commit the whole amend process if obsolete._enabled and newid != old.node(): # mark the new changeset as successor of the rewritten one diff -r ebc0fa067c07 -r c8326ffdcb4f mercurial/commands.py --- a/mercurial/commands.py Thu Dec 06 13:21:27 2012 -0600 +++ b/mercurial/commands.py Thu Dec 06 16:42:15 2012 -0600 @@ -549,6 +549,10 @@ hg bisect --skip hg bisect --skip 23 + - skip all revisions that do not touch directories ``foo`` or ``bar`` + + hg bisect --skip '!( file("path:foo") & file("path:bar") )' + - forget the current bisection:: hg bisect --reset @@ -821,7 +825,7 @@ if mark == repo._bookmarkcurrent: bookmarks.setcurrent(repo, None) del marks[mark] - bookmarks.write(repo) + marks.write() elif rename: if mark is None: @@ -834,7 +838,7 @@ if repo._bookmarkcurrent == rename and not inactive: bookmarks.setcurrent(repo, mark) del marks[rename] - bookmarks.write(repo) + marks.write() elif mark is not None: mark = checkformat(mark) @@ -848,7 +852,7 @@ marks[mark] = cur if not inactive and cur == marks[mark]: bookmarks.setcurrent(repo, mark) - bookmarks.write(repo) + marks.write() # Same message whether trying to deactivate the current bookmark (-i # with no NAME) or listing bookmarks @@ -924,7 +928,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, ' @@ -1322,11 +1326,12 @@ elif marks: ui.debug('moving bookmarks %r from %s to %s\n' % (marks, old.hex(), hex(node))) + newmarks = repo._bookmarks for bm in marks: - repo._bookmarks[bm] = node + newmarks[bm] = node if bm == current: bookmarks.setcurrent(repo, bm) - bookmarks.write(repo) + newmarks.write() else: e = cmdutil.commiteditor if opts.get('force_editor'): @@ -1513,7 +1518,7 @@ ui.progress(_('building'), id, unit=_('revisions'), total=total) for type, data in dagparser.parsedag(text): if type == 'n': - ui.note('node %s\n' % str(data)) + ui.note(('node %s\n' % str(data))) id, ps = data files = [] @@ -1574,10 +1579,10 @@ at = id elif type == 'l': id, name = data - ui.note('tag %s\n' % name) + ui.note(('tag %s\n' % name)) tags.append("%s %s\n" % (hex(repo.changelog.node(id)), name)) elif type == 'a': - ui.note('branch %s\n' % data) + ui.note(('branch %s\n' % data)) atbranch = data ui.progress(_('building'), id, unit=_('revisions'), total=total) tr.close() @@ -1595,7 +1600,7 @@ try: gen = changegroup.readbundle(f, bundlepath) if all: - ui.write("format: id, p1, p2, cset, delta base, len(delta)\n") + ui.write(("format: id, p1, p2, cset, delta base, len(delta)\n")) def showchunks(named): ui.write("\n%s\n" % named) @@ -1787,11 +1792,11 @@ d = util.parsedate(date, util.extendeddateformats) else: d = util.parsedate(date) - ui.write("internal: %s %s\n" % d) - ui.write("standard: %s\n" % util.datestr(d)) + ui.write(("internal: %s %s\n") % d) + ui.write(("standard: %s\n") % util.datestr(d)) if range: m = util.matchdate(range) - ui.write("match: %s\n" % m(d[0])) + ui.write(("match: %s\n") % m(d[0])) @command('debugdiscovery', [('', 'old', None, _('use old-style discovery')), @@ -1821,7 +1826,7 @@ force=True) common = set(common) if not opts.get('nonheads'): - ui.write("unpruned common: %s\n" % " ".join([short(n) + ui.write(("unpruned common: %s\n") % " ".join([short(n) for n in common])) dag = dagutil.revlogdag(repo.changelog) all = dag.ancestorset(dag.internalizeall(common)) @@ -1831,11 +1836,11 @@ common = set(common) rheads = set(hds) lheads = set(repo.heads()) - ui.write("common heads: %s\n" % " ".join([short(n) for n in common])) + ui.write(("common heads: %s\n") % " ".join([short(n) for n in common])) if lheads <= common: - ui.write("local is subset\n") + ui.write(("local is subset\n")) elif rheads <= common: - ui.write("remote is subset\n") + ui.write(("remote is subset\n")) serverlogs = opts.get('serverlog') if serverlogs: @@ -1879,9 +1884,9 @@ def debugfsinfo(ui, path = "."): """show information detected about current filesystem""" util.writefile('.debugfsinfo', '') - ui.write('exec: %s\n' % (util.checkexec(path) and 'yes' or 'no')) - ui.write('symlink: %s\n' % (util.checklink(path) and 'yes' or 'no')) - ui.write('case-sensitive: %s\n' % (util.checkcase('.debugfsinfo') + ui.write(('exec: %s\n') % (util.checkexec(path) and 'yes' or 'no')) + ui.write(('symlink: %s\n') % (util.checklink(path) and 'yes' or 'no')) + ui.write(('case-sensitive: %s\n') % (util.checkcase('.debugfsinfo') and 'yes' or 'no')) os.unlink('.debugfsinfo') @@ -1979,7 +1984,7 @@ r = filelog if not r: r = revlog.revlog(scmutil.opener(os.getcwd(), audit=False), file_) - ui.write("digraph G {\n") + ui.write(("digraph G {\n")) for i in r: node = r.node(i) pp = r.parents(node) @@ -2325,52 +2330,54 @@ def pcfmt(value, total): return (value, 100 * float(value) / total) - ui.write('format : %d\n' % format) - ui.write('flags : %s\n' % ', '.join(flags)) + ui.write(('format : %d\n') % format) + ui.write(('flags : %s\n') % ', '.join(flags)) ui.write('\n') fmt = pcfmtstr(totalsize) fmt2 = dfmtstr(totalsize) - ui.write('revisions : ' + fmt2 % numrevs) - ui.write(' merges : ' + fmt % pcfmt(nummerges, numrevs)) - ui.write(' normal : ' + fmt % pcfmt(numrevs - nummerges, numrevs)) - ui.write('revisions : ' + fmt2 % numrevs) - ui.write(' full : ' + fmt % pcfmt(numfull, numrevs)) - ui.write(' deltas : ' + fmt % pcfmt(numdeltas, numrevs)) - ui.write('revision size : ' + fmt2 % totalsize) - ui.write(' full : ' + fmt % pcfmt(fulltotal, totalsize)) - ui.write(' deltas : ' + fmt % pcfmt(deltatotal, totalsize)) + ui.write(('revisions : ') + fmt2 % numrevs) + ui.write((' merges : ') + fmt % pcfmt(nummerges, numrevs)) + ui.write((' normal : ') + fmt % pcfmt(numrevs - nummerges, numrevs)) + ui.write(('revisions : ') + fmt2 % numrevs) + ui.write((' full : ') + fmt % pcfmt(numfull, numrevs)) + ui.write((' deltas : ') + fmt % pcfmt(numdeltas, numrevs)) + ui.write(('revision size : ') + fmt2 % totalsize) + ui.write((' full : ') + fmt % pcfmt(fulltotal, totalsize)) + ui.write((' deltas : ') + fmt % pcfmt(deltatotal, totalsize)) ui.write('\n') fmt = dfmtstr(max(avgchainlen, compratio)) - ui.write('avg chain length : ' + fmt % avgchainlen) - ui.write('compression ratio : ' + fmt % compratio) + ui.write(('avg chain length : ') + fmt % avgchainlen) + ui.write(('compression ratio : ') + fmt % compratio) if format > 0: ui.write('\n') - ui.write('uncompressed data size (min/max/avg) : %d / %d / %d\n' + ui.write(('uncompressed data size (min/max/avg) : %d / %d / %d\n') % tuple(datasize)) - ui.write('full revision size (min/max/avg) : %d / %d / %d\n' + ui.write(('full revision size (min/max/avg) : %d / %d / %d\n') % tuple(fullsize)) - ui.write('delta size (min/max/avg) : %d / %d / %d\n' + ui.write(('delta size (min/max/avg) : %d / %d / %d\n') % tuple(deltasize)) if numdeltas > 0: ui.write('\n') fmt = pcfmtstr(numdeltas) fmt2 = pcfmtstr(numdeltas, 4) - ui.write('deltas against prev : ' + fmt % pcfmt(numprev, numdeltas)) + ui.write(('deltas against prev : ') + fmt % pcfmt(numprev, numdeltas)) if numprev > 0: - ui.write(' where prev = p1 : ' + fmt2 % pcfmt(nump1prev, + ui.write((' where prev = p1 : ') + fmt2 % pcfmt(nump1prev, numprev)) - ui.write(' where prev = p2 : ' + fmt2 % pcfmt(nump2prev, + ui.write((' where prev = p2 : ') + fmt2 % pcfmt(nump2prev, numprev)) - ui.write(' other : ' + fmt2 % pcfmt(numoprev, + ui.write((' other : ') + fmt2 % pcfmt(numoprev, numprev)) if gdelta: - ui.write('deltas against p1 : ' + fmt % pcfmt(nump1, numdeltas)) - ui.write('deltas against p2 : ' + fmt % pcfmt(nump2, numdeltas)) - ui.write('deltas against other : ' + fmt % pcfmt(numother, + ui.write(('deltas against p1 : ') + + fmt % pcfmt(nump1, numdeltas)) + ui.write(('deltas against p2 : ') + + fmt % pcfmt(nump2, numdeltas)) + ui.write(('deltas against other : ') + fmt % pcfmt(numother, numdeltas)) @command('debugrevspec', [], ('REVSPEC')) @@ -2448,9 +2455,9 @@ def debugsub(ui, repo, rev=None): ctx = scmutil.revsingle(repo, rev, None) for k, v in sorted(ctx.substate.items()): - ui.write('path %s\n' % k) - ui.write(' source %s\n' % v[0]) - ui.write(' revision %s\n' % v[1]) + ui.write(('path %s\n') % k) + ui.write((' source %s\n') % v[0]) + ui.write((' revision %s\n') % v[1]) @command('debugwalk', walkopts, _('[OPTION]... [FILE]...')) def debugwalk(ui, repo, *pats, **opts): @@ -4207,6 +4214,9 @@ Returns 0 on success. """ + + fm = ui.formatter('manifest', opts) + if opts.get('all'): if rev or node: raise util.Abort(_("can't specify a revision with --all")) @@ -4224,7 +4234,9 @@ finally: lock.release() for f in res: - ui.write("%s\n" % f) + fm.startitem() + fm.write("path", '%s\n', f) + fm.end() return if rev and node: @@ -4233,14 +4245,17 @@ if not node: node = rev - decor = {'l':'644 @ ', 'x':'755 * ', '':'644 '} + char = {'l': '@', 'x': '*', '': ''} + mode = {'l': '644', 'x': '755', '': '644'} ctx = scmutil.revsingle(repo, node) + mf = ctx.manifest() for f in ctx: - if ui.debugflag: - ui.write("%40s " % hex(ctx.manifest()[f])) - if ui.verbose: - ui.write(decor[ctx.flags(f)]) - ui.write("%s\n" % f) + fm.startitem() + fl = ctx[f].flags() + fm.condwrite(ui.debugflag, 'hash', '%s ', hex(mf[f])) + fm.condwrite(ui.verbose, 'mode type', '%s %1s ', mode[fl], char[fl]) + fm.write('path', '%s\n', f) + fm.end() @command('^merge', [('f', 'force', None, _('force a merge with outstanding changes')), @@ -4666,11 +4681,12 @@ # update specified bookmarks if opts.get('bookmark'): + marks = repo._bookmarks for b in opts['bookmark']: # explicit pull overrides local bookmark if any ui.status(_("importing bookmark %s\n") % b) - repo._bookmarks[b] = repo[rb[b]].node() - bookmarks.write(repo) + marks[b] = repo[rb[b]].node() + marks.write() return ret @@ -5427,17 +5443,16 @@ copy = copies.pathcopies(repo[node1], repo[node2]) fm = ui.formatter('status', opts) - format = '%s %s' + end - if opts.get('no_status'): - format = '%.0s%s' + end + fmt = '%s' + end + showchar = not opts.get('no_status') for state, char, files in changestates: if state in show: label = 'status.' + state for f in files: fm.startitem() - fm.write("status path", format, char, - repo.pathto(f, cwd), label=label) + fm.condwrite(showchar, 'status', '%s ', char, label=label) + fm.write('path', fmt, repo.pathto(f, cwd), label=label) if f in copy: fm.write("copy", ' %s' + end, repo.pathto(copy[f], cwd), label='status.copied') @@ -5743,7 +5758,7 @@ release(lock, wlock) @command('tags', [], '') -def tags(ui, repo): +def tags(ui, repo, **opts): """list repository tags This lists both regular and local tags. When the -v/--verbose @@ -5752,27 +5767,27 @@ Returns 0 on success. """ + fm = ui.formatter('tags', opts) hexfunc = ui.debugflag and hex or short tagtype = "" for t, n in reversed(repo.tagslist()): - if ui.quiet: - ui.write("%s\n" % t, label='tags.normal') - continue - hn = hexfunc(n) - r = "%5d:%s" % (repo.changelog.rev(n), hn) - rev = ui.label(r, 'log.changeset changeset.%s' % repo[n].phasestr()) - spaces = " " * (30 - encoding.colwidth(t)) - - tag = ui.label(t, 'tags.normal') - if ui.verbose: - if repo.tagtype(t) == 'local': - tagtype = " local" - tag = ui.label(t, 'tags.local') - else: - tagtype = "" - ui.write("%s%s %s%s\n" % (tag, spaces, rev, tagtype)) + label = 'tags.normal' + tagtype = '' + if repo.tagtype(t) == 'local': + label = 'tags.local' + tagtype = 'local' + + fm.startitem() + fm.write('tag', '%s', t, label=label) + fmt = " " * (30 - encoding.colwidth(t)) + ' %5d:%s' + fm.condwrite(not ui.quiet, 'rev id', fmt, + repo.changelog.rev(n), hn, label=label) + fm.condwrite(ui.verbose and tagtype, 'type', ' %s', + tagtype, label=label) + fm.plain('\n') + fm.end() @command('tip', [('p', 'patch', None, _('show patch')), diff -r ebc0fa067c07 -r c8326ffdcb4f mercurial/context.py --- a/mercurial/context.py Thu Dec 06 13:21:27 2012 -0600 +++ b/mercurial/context.py Thu Dec 06 16:42:15 2012 -0600 @@ -95,7 +95,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: @@ -352,6 +355,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.""" diff -r ebc0fa067c07 -r c8326ffdcb4f mercurial/dirstate.py --- a/mercurial/dirstate.py Thu Dec 06 13:21:27 2012 -0600 +++ b/mercurial/dirstate.py Thu Dec 06 16:42:15 2012 -0600 @@ -260,7 +260,6 @@ return copies def setbranch(self, branch): - # no repo object here, just check for reserved names self._branch = encoding.fromlocal(branch) f = self._opener('branch', 'w', atomictemp=True) try: @@ -606,7 +605,7 @@ normalize = self._normalize skipstep3 = False else: - normalize = lambda x, y, z: x + normalize = None files = sorted(match.files()) subrepos.sort() @@ -627,7 +626,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 @@ -677,7 +679,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): @@ -697,11 +702,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'] @@ -747,13 +750,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: @@ -772,15 +781,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. diff -r ebc0fa067c07 -r c8326ffdcb4f mercurial/formatter.py --- a/mercurial/formatter.py Thu Dec 06 13:21:27 2012 -0600 +++ b/mercurial/formatter.py Thu Dec 06 16:42:15 2012 -0600 @@ -31,6 +31,10 @@ '''do default text output while assigning data to item''' for k, v in zip(fields.split(), fielddata): self._item[k] = v + def condwrite(self, cond, fields, deftext, *fielddata, **opts): + '''do conditional write (primarily for plain formatter)''' + for k, v in zip(fields.split(), fielddata): + self._item[k] = v def plain(self, text, **opts): '''show raw text for non-templated mode''' pass @@ -51,6 +55,10 @@ pass def write(self, fields, deftext, *fielddata, **opts): self._ui.write(deftext % fielddata, **opts) + def condwrite(self, cond, fields, deftext, *fielddata, **opts): + '''do conditional write''' + if cond: + self._ui.write(deftext % fielddata, **opts) def plain(self, text, **opts): self._ui.write(text, **opts) def end(self): diff -r ebc0fa067c07 -r c8326ffdcb4f mercurial/help/config.txt --- a/mercurial/help/config.txt Thu Dec 06 13:21:27 2012 -0600 +++ b/mercurial/help/config.txt Thu Dec 06 16:42:15 2012 -0600 @@ -1295,6 +1295,10 @@ (DEPRECATED) Whether to allow .zip downloading of repository revisions. Default is False. This feature creates temporary files. +``archivesubrepos`` + Whether to recurse into subrepositories when archiving. Default is + False. + ``baseurl`` Base URL to use when publishing URLs in other locations, so third-party tools like email notification hooks can construct diff -r ebc0fa067c07 -r c8326ffdcb4f mercurial/hg.py --- a/mercurial/hg.py Thu Dec 06 13:21:27 2012 -0600 +++ b/mercurial/hg.py Thu Dec 06 16:42:15 2012 -0600 @@ -171,11 +171,14 @@ r = repository(ui, root) default = srcrepo.ui.config('paths', 'default') - if default: - fp = r.opener("hgrc", "w", text=True) - fp.write("[paths]\n") - fp.write("default = %s\n" % default) - fp.close() + if not default: + # set default to source for being able to clone subrepos + default = os.path.abspath(util.urllocalpath(origsource)) + fp = r.opener("hgrc", "w", text=True) + fp.write("[paths]\n") + fp.write("default = %s\n" % default) + fp.close() + r.ui.setconfig('paths', 'default', default) if update: r.ui.status(_("updating working directory\n")) @@ -391,14 +394,15 @@ destrepo = destpeer.local() if destrepo and srcpeer.capable("pushkey"): rb = srcpeer.listkeys('bookmarks') + marks = destrepo._bookmarks for k, n in rb.iteritems(): try: m = destrepo.lookup(n) - destrepo._bookmarks[k] = m + marks[k] = m except error.RepoLookupError: pass if rb: - bookmarks.write(destrepo) + marks.write() elif srcrepo and destpeer.capable("pushkey"): for k, n in srcrepo._bookmarks.iteritems(): destpeer.pushkey('bookmarks', k, '', hex(n)) diff -r ebc0fa067c07 -r c8326ffdcb4f mercurial/hgweb/webcommands.py --- a/mercurial/hgweb/webcommands.py Thu Dec 06 13:21:27 2012 -0600 +++ b/mercurial/hgweb/webcommands.py Thu Dec 06 16:42:15 2012 -0600 @@ -14,6 +14,7 @@ from common import HTTP_OK, HTTP_FORBIDDEN, HTTP_NOT_FOUND from mercurial import graphmod, patch from mercurial import help as helpmod +from mercurial import scmutil from mercurial.i18n import _ # __all__ is populated with the allowed commands. Be sure to add to it if @@ -255,6 +256,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 +277,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 +289,7 @@ node=ctx.hex(), parent=webutil.parents(ctx), child=webutil.children(ctx), + currentbaseline=basectx.hex(), changesettag=showtags, changesetbookmark=showbookmarks, changesetbranch=showbranch, @@ -569,7 +574,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", @@ -802,7 +807,11 @@ headers.append(('Content-Encoding', encoding)) req.header(headers) req.respond(HTTP_OK) - archival.archive(web.repo, req, cnode, artype, prefix=name) + + ctx = webutil.changectx(web.repo, req) + archival.archive(web.repo, req, cnode, artype, prefix=name, + matchfn=scmutil.match(ctx, []), + subrepos=web.configbool("web", "archivesubrepos")) return [] diff -r ebc0fa067c07 -r c8326ffdcb4f mercurial/hgweb/webutil.py --- a/mercurial/hgweb/webutil.py Thu Dec 06 13:21:27 2012 -0600 +++ b/mercurial/hgweb/webutil.py Thu Dec 06 16:42:15 2012 -0600 @@ -140,13 +140,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 +149,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 +194,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 +225,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 +293,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 diff -r ebc0fa067c07 -r c8326ffdcb4f mercurial/localrepo.py --- a/mercurial/localrepo.py Thu Dec 06 13:21:27 2012 -0600 +++ b/mercurial/localrepo.py Thu Dec 06 16:42:15 2012 -0600 @@ -18,11 +18,45 @@ 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 """ + 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'])) @@ -263,17 +297,20 @@ 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 + + @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,7 +332,7 @@ self.ui.warn(msg % len(list(store))) return store - @propertycache + @unfilteredpropertycache def hiddenrevs(self): """hiddenrevs: revs that should be hidden by command and tools @@ -329,7 +366,7 @@ def manifest(self): return manifest.manifest(self.sopener) - @filecache('dirstate') + @repofilecache('dirstate') def dirstate(self): warned = [0] def validate(node): @@ -385,6 +422,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 +520,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.''' @@ -604,6 +642,7 @@ return partial + @unfilteredmethod # Until we get a smarter cache management def updatebranchcache(self): tip = self.changelog.tip() if self._branchcache is not None and self._branchcachetip == tip: @@ -656,6 +695,7 @@ bt[bn] = self._branchtip(heads) return bt + @unfilteredmethod # Until we get a smarter cache management def _readbranchcache(self): partial = {} try: @@ -688,6 +728,7 @@ partial, last, lrev = {}, nullid, nullrev return partial, last, lrev + @unfilteredmethod # Until we get a smarter cache management def _writebranchcache(self, branches, tip, tiprev): try: f = self.opener("cache/branchheads", "w", atomictemp=True) @@ -699,6 +740,7 @@ except (IOError, OSError): pass + @unfilteredmethod # Until we get a smarter cache management 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 @@ -865,11 +907,11 @@ return data - @propertycache + @unfilteredpropertycache def _encodefilterpats(self): return self._loadfilter('encode') - @propertycache + @unfilteredpropertycache def _decodefilterpats(self): return self._loadfilter('decode') @@ -964,6 +1006,7 @@ finally: release(lock, wlock) + @unfilteredmethod # Until we get smarter cache management def _rollback(self, dryrun, force): ui = self.ui try: @@ -1034,16 +1077,13 @@ return 0 def invalidatecaches(self): - def delcache(name): - try: - delattr(self, name) - except AttributeError: - pass - delcache('_tagscache') + if '_tagscache' in vars(self): + # can't use delattr on proxy + del self.__dict__['_tagscache'] - self._branchcache = None # in UTF-8 - self._branchcachetip = None + self.unfiltered()._branchcache = None # in UTF-8 + self.unfiltered()._branchcachetip = None obsolete.clearobscaches(self) def invalidatedirstate(self): @@ -1055,22 +1095,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,7 +1152,7 @@ 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': @@ -1224,6 +1265,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 +1436,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. @@ -1475,6 +1518,7 @@ tr.release() lock.release() + @unfilteredmethod def destroyed(self, newheadnodes=None): '''Inform the repository that nodes have been destroyed. Intended for use by strip and rollback, so there's a common @@ -1842,6 +1886,7 @@ if not remote.canpush(): raise util.Abort(_("destination does not support push")) # get local lock as we might write phase data + unfi = self.unfiltered() locallock = self.lock() try: self.checkpush(force, revs) @@ -1852,23 +1897,23 @@ 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!") @@ -1878,14 +1923,14 @@ # 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, + discovery.checkheads(unfi, remote, outgoing, remoteheads, newbranch, bool(inc)) @@ -1938,7 +1983,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 +2006,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 +2037,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) @@ -2068,6 +2113,7 @@ return self.getlocalbundle(source, discovery.outgoing(cl, common, heads)) + @unfilteredmethod def _changegroupsubset(self, commonrevs, csets, heads, source): cl = self.changelog @@ -2179,6 +2225,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 +2319,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 diff -r ebc0fa067c07 -r c8326ffdcb4f mercurial/manifest.py --- a/mercurial/manifest.py Thu Dec 06 13:21:27 2012 -0600 +++ b/mercurial/manifest.py Thu Dec 06 16:42:15 2012 -0600 @@ -117,15 +117,23 @@ # apply the changes collected during the bisect loop to our addlist # return a delta suitable for addrevision def addlistdelta(addlist, x): - # start from the bottom up - # so changes to the offsets don't mess things up. - for start, end, content in reversed(x): + # for large addlist arrays, building a new array is cheaper + # than repeatedly modifying the existing one + currentposition = 0 + newaddlist = array.array('c') + + for start, end, content in x: + newaddlist += addlist[currentposition:start] if content: - addlist[start:end] = array.array('c', content) - else: - del addlist[start:end] - return "".join(struct.pack(">lll", start, end, len(content)) + newaddlist += array.array('c', content) + + currentposition = end + + newaddlist += addlist[currentposition:] + + deltatext = "".join(struct.pack(">lll", start, end, len(content)) + content for start, end, content in x) + return deltatext, newaddlist def checkforbidden(l): for f in l: @@ -194,7 +202,8 @@ if dstart is not None: delta.append([dstart, dend, "".join(dline)]) # apply the delta to the addlist, and get a delta for addrevision - cachedelta = (self.rev(p1), addlistdelta(addlist, delta)) + deltatext, addlist = addlistdelta(addlist, delta) + cachedelta = (self.rev(p1), deltatext) arraytext = addlist text = util.buffer(arraytext) diff -r ebc0fa067c07 -r c8326ffdcb4f mercurial/mdiff.py --- a/mercurial/mdiff.py Thu Dec 06 13:21:27 2012 -0600 +++ b/mercurial/mdiff.py Thu Dec 06 16:42:15 2012 -0600 @@ -7,7 +7,7 @@ from i18n import _ import bdiff, mpatch, util -import re, struct +import re, struct, base85, zlib def splitnewlines(text): '''like str.splitlines, but only split on newlines.''' @@ -142,20 +142,7 @@ yield s, type yield s1, '=' -def diffline(revs, a, b, opts): - parts = ['diff'] - if opts.git: - parts.append('--git') - if revs and not opts.git: - parts.append(' '.join(["-r %s" % rev for rev in revs])) - if opts.git: - parts.append('a/%s' % a) - parts.append('b/%s' % b) - else: - parts.append(a) - return ' '.join(parts) + '\n' - -def unidiff(a, ad, b, bd, fn1, fn2, r=None, opts=defaultopts): +def unidiff(a, ad, b, bd, fn1, fn2, opts=defaultopts): def datetag(date, fn=None): if not opts.git and not opts.nodates: return '\t%s\n' % date @@ -206,9 +193,6 @@ if l[ln][-1] != '\n': l[ln] += "\n\ No newline at end of file\n" - if r: - l.insert(0, diffline(r, fn1, fn2, opts)) - return "".join(l) # creates a headerless unified diff @@ -314,6 +298,41 @@ for x in yieldhunk(hunk): yield x +def b85diff(to, tn): + '''print base85-encoded binary diff''' + def fmtline(line): + l = len(line) + if l <= 26: + l = chr(ord('A') + l - 1) + else: + l = chr(l - 26 + ord('a') - 1) + return '%c%s\n' % (l, base85.b85encode(line, True)) + + def chunk(text, csize=52): + l = len(text) + i = 0 + while i < l: + yield text[i:i + csize] + i += csize + + if to is None: + to = '' + if tn is None: + tn = '' + + if to == tn: + return '' + + # TODO: deltas + ret = [] + ret.append('GIT binary patch\n') + ret.append('literal %s\n' % len(tn)) + for l in chunk(zlib.compress(tn)): + ret.append(fmtline(l)) + ret.append('\n') + + return ''.join(ret) + def patchtext(bin): pos = 0 t = [] diff -r ebc0fa067c07 -r c8326ffdcb4f mercurial/merge.py --- a/mercurial/merge.py Thu Dec 06 13:21:27 2012 -0600 +++ b/mercurial/merge.py Thu Dec 06 16:42:15 2012 -0600 @@ -448,6 +448,27 @@ return updated, merged, removed, unresolved +def calculateupdates(repo, tctx, mctx, ancestor, branchmerge, force, partial): + "Calculate the actions needed to merge mctx into tctx" + action = [] + 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) + if not force: + _checkunknown(repo, tctx, mctx) + if tctx.rev() is None: + action += _forgetremoved(tctx, mctx, branchmerge) + action += manifestmerge(repo, tctx, mctx, + ancestor, + force and not branchmerge, + partial) + return action + def recordupdates(repo, action, branchmerge): "record merge actions to the dirstate" @@ -609,19 +630,7 @@ 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) + action = calculateupdates(repo, wc, p2, pa, branchmerge, force, partial) ### apply phase if not branchmerge: # just jump to the new rev diff -r ebc0fa067c07 -r c8326ffdcb4f mercurial/obsolete.py --- a/mercurial/obsolete.py Thu Dec 06 13:21:27 2012 -0600 +++ b/mercurial/obsolete.py Thu Dec 06 16:42:15 2012 -0600 @@ -426,6 +426,7 @@ """Return the set of revision that belong to the 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: diff -r ebc0fa067c07 -r c8326ffdcb4f mercurial/osutil.c --- a/mercurial/osutil.c Thu Dec 06 13:21:27 2012 -0600 +++ b/mercurial/osutil.c Thu Dec 06 16:42:15 2012 -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__ { diff -r ebc0fa067c07 -r c8326ffdcb4f mercurial/patch.py --- a/mercurial/patch.py Thu Dec 06 13:21:27 2012 -0600 +++ b/mercurial/patch.py Thu Dec 06 16:42:15 2012 -0600 @@ -6,7 +6,7 @@ # This software may be used and distributed according to the terms of the # GNU General Public License version 2 or any later version. -import cStringIO, email.Parser, os, errno, re +import cStringIO, email.Parser, os, errno, re, posixpath import tempfile, zlib, shutil from i18n import _ @@ -1514,44 +1514,6 @@ finally: fp.close() -def b85diff(to, tn): - '''print base85-encoded binary diff''' - def gitindex(text): - if not text: - return hex(nullid) - l = len(text) - s = util.sha1('blob %d\0' % l) - s.update(text) - return s.hexdigest() - - def fmtline(line): - l = len(line) - if l <= 26: - l = chr(ord('A') + l - 1) - else: - l = chr(l - 26 + ord('a') - 1) - return '%c%s\n' % (l, base85.b85encode(line, True)) - - def chunk(text, csize=52): - l = len(text) - i = 0 - while i < l: - yield text[i:i + csize] - i += csize - - tohash = gitindex(to) - tnhash = gitindex(tn) - if tohash == tnhash: - return "" - - # TODO: deltas - ret = ['index %s..%s\nGIT binary patch\nliteral %s\n' % - (tohash, tnhash, len(tn))] - for l in chunk(zlib.compress(tn)): - ret.append(fmtline(l)) - ret.append('\n') - return ''.join(ret) - class GitDiffRequired(Exception): pass @@ -1622,9 +1584,8 @@ return [] revs = None - if not repo.ui.quiet: - hexfunc = repo.ui.debugflag and hex or short - revs = [hexfunc(node) for node in [node1, node2] if node] + hexfunc = repo.ui.debugflag and hex or short + revs = [hexfunc(node) for node in [node1, node2] if node] copy = {} if opts.git or opts.upgrade: @@ -1690,17 +1651,45 @@ '''like diff(), but yields 2-tuples of (output, label) for ui.write()''' return difflabel(diff, *args, **kw) - -def _addmodehdr(header, omode, nmode): - if omode != nmode: - header.append('old mode %s\n' % omode) - header.append('new mode %s\n' % nmode) - def trydiff(repo, revs, ctx1, ctx2, modified, added, removed, copy, getfilectx, opts, losedatafn, prefix): def join(f): - return os.path.join(prefix, f) + return posixpath.join(prefix, f) + + def addmodehdr(header, omode, nmode): + if omode != nmode: + header.append('old mode %s\n' % omode) + header.append('new mode %s\n' % nmode) + + def addindexmeta(meta, revs): + if opts.git: + i = len(revs) + if i==2: + meta.append('index %s..%s\n' % tuple(revs)) + elif i==3: + meta.append('index %s,%s..%s\n' % tuple(revs)) + + def gitindex(text): + if not text: + return hex(nullid) + l = len(text) + s = util.sha1('blob %d\0' % l) + s.update(text) + return s.hexdigest() + + def diffline(a, b, revs): + if opts.git: + line = 'diff --git a/%s b/%s\n' % (a, b) + elif not repo.ui.quiet: + if revs: + revinfo = ' '.join(["-r %s" % rev for rev in revs]) + line = 'diff %s %s\n' % (revinfo, a) + else: + line = 'diff %s\n' % a + else: + line = '' + return line date1 = util.datestr(ctx1.date()) man1 = ctx1.manifest() @@ -1733,7 +1722,7 @@ else: a = copyto[f] omode = gitmode[man1.flags(a)] - _addmodehdr(header, omode, mode) + addmodehdr(header, omode, mode) if a in removed and a not in gone: op = 'rename' gone.add(a) @@ -1779,22 +1768,24 @@ nflag = ctx2.flags(f) binary = util.binary(to) or util.binary(tn) if opts.git: - _addmodehdr(header, gitmode[oflag], gitmode[nflag]) + addmodehdr(header, gitmode[oflag], gitmode[nflag]) if binary: dodiff = 'binary' elif binary or nflag != oflag: losedatafn(f) - if opts.git: - header.insert(0, mdiff.diffline(revs, join(a), join(b), opts)) if dodiff: + if opts.git or revs: + header.insert(0, diffline(join(a), join(b), revs)) if dodiff == 'binary': - text = b85diff(to, tn) + text = mdiff.b85diff(to, tn) + if text: + addindexmeta(header, [gitindex(to), gitindex(tn)]) else: text = mdiff.unidiff(to, date1, # ctx2 date may be dynamic tn, util.datestr(ctx2.date()), - join(a), join(b), revs, opts=opts) + join(a), join(b), opts=opts) if header and (text or len(header) > 1): yield ''.join(header) if text: diff -r ebc0fa067c07 -r c8326ffdcb4f mercurial/phases.py --- a/mercurial/phases.py Thu Dec 06 13:21:27 2012 -0600 +++ b/mercurial/phases.py Thu Dec 06 16:42:15 2012 -0600 @@ -139,6 +139,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: @@ -184,6 +185,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 +230,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 @@ -251,6 +254,7 @@ # 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] @@ -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] diff -r ebc0fa067c07 -r c8326ffdcb4f mercurial/posix.py --- a/mercurial/posix.py Thu Dec 06 13:21:27 2012 -0600 +++ b/mercurial/posix.py Thu Dec 06 16:42:15 2012 -0600 @@ -352,12 +352,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 diff -r ebc0fa067c07 -r c8326ffdcb4f mercurial/repair.py --- a/mercurial/repair.py Thu Dec 06 13:21:27 2012 -0600 +++ b/mercurial/repair.py Thu Dec 06 16:42:15 2012 -0600 @@ -6,7 +6,7 @@ # This software may be used and distributed according to the terms of the # GNU General Public License version 2 or any later version. -from mercurial import changegroup, bookmarks +from mercurial import changegroup from mercurial.node import short from mercurial.i18n import _ import os @@ -56,6 +56,7 @@ return s def strip(ui, repo, nodelist, backup="all", topic='backup'): + repo = repo.unfiltered() # 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. @@ -181,7 +182,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") diff -r ebc0fa067c07 -r c8326ffdcb4f mercurial/revlog.py --- a/mercurial/revlog.py Thu Dec 06 13:21:27 2012 -0600 +++ b/mercurial/revlog.py Thu Dec 06 16:42:15 2012 -0600 @@ -257,11 +257,14 @@ return iter(xrange(len(self))) def revs(self, start=0, stop=None): """iterate over all rev in this revlog (from start to stop)""" - if stop is None: - stop = len(self) + step = 1 + if stop is not None: + if start > stop: + step = -1 + stop += step else: - stop += 1 - return xrange(start, stop) + stop = len(self) + return xrange(start, stop, step) @util.propertycache def nodemap(self): @@ -429,6 +432,29 @@ missing.sort() return has, [self.node(r) for r in missing] + def findmissingrevs(self, common=None, heads=None): + """Return the revision numbers of the ancestors of heads that + are not ancestors of common. + + More specifically, return a list of revision numbers corresponding to + nodes N such that every N satisfies the following constraints: + + 1. N is an ancestor of some node in 'heads' + 2. N is not an ancestor of any node in 'common' + + The list is sorted by revision number, meaning it is + topologically sorted. + + 'heads' and 'common' are both lists of revision numbers. If heads is + not supplied, uses all of the revlog's heads. If common is not + supplied, uses nullid.""" + if common is None: + common = [nullrev] + if heads is None: + heads = self.headrevs() + + return ancestor.missingancestors(heads, common, self.parentrevs) + def findmissing(self, common=None, heads=None): """Return the ancestors of heads that are not ancestors of common. @@ -444,8 +470,16 @@ 'heads' and 'common' are both lists of node IDs. If heads is not supplied, uses all of the revlog's heads. If common is not supplied, uses nullid.""" - _common, missing = self.findcommonmissing(common, heads) - return missing + if common is None: + common = [nullid] + if heads is None: + heads = self.heads() + + common = [self.rev(n) for n in common] + heads = [self.rev(n) for n in heads] + + return [self.node(r) for r in + ancestor.missingancestors(heads, common, self.parentrevs)] def nodesbetween(self, roots=None, heads=None): """Return a topological path from 'roots' to 'heads'. diff -r ebc0fa067c07 -r c8326ffdcb4f mercurial/revset.py --- a/mercurial/revset.py Thu Dec 06 13:21:27 2012 -0600 +++ b/mercurial/revset.py Thu Dec 06 16:42:15 2012 -0600 @@ -442,6 +442,19 @@ bumped = obsmod.getrevs(repo, 'bumped') return [r for r in subset if r in bumped] +def bundle(repo, subset, x): + """``bundle()`` + Changesets in the bundle. + + Bundle must be specified by the -R option.""" + + try: + bundlenodes = repo.changelog.bundlenodes + except AttributeError: + raise util.Abort(_("no bundle provided - specify with -R")) + revs = set(repo[n].rev() for n in bundlenodes) + return [r for r in subset if r in revs] + def checkstatus(repo, subset, pat, field): m = None s = [] @@ -1513,6 +1526,7 @@ "branch": branch, "branchpoint": branchpoint, "bumped": bumped, + "bundle": bundle, "children": children, "closed": closed, "contains": contains, diff -r ebc0fa067c07 -r c8326ffdcb4f mercurial/scmutil.py --- a/mercurial/scmutil.py Thu Dec 06 13:21:27 2012 -0600 +++ b/mercurial/scmutil.py Thu Dec 06 16:42:15 2012 -0600 @@ -279,37 +279,38 @@ mode += "b" # for that other OS nlink = -1 - dirname, basename = util.split(f) - # If basename is empty, then the path is malformed because it points - # to a directory. Let the posixfile() call below raise IOError. - if basename and mode not in ('r', 'rb'): - if atomictemp: - if not os.path.isdir(dirname): - util.makedirs(dirname, self.createmode) - return util.atomictempfile(f, mode, self.createmode) - try: - if 'w' in mode: - util.unlink(f) + if mode not in ('r', 'rb'): + dirname, basename = util.split(f) + # If basename is empty, then the path is malformed because it points + # to a directory. Let the posixfile() call below raise IOError. + if basename: + if atomictemp: + if not os.path.isdir(dirname): + util.makedirs(dirname, self.createmode) + return util.atomictempfile(f, mode, self.createmode) + try: + if 'w' in mode: + util.unlink(f) + nlink = 0 + else: + # nlinks() may behave differently for files on Windows + # shares if the file is open. + fd = util.posixfile(f) + nlink = util.nlinks(f) + if nlink < 1: + nlink = 2 # force mktempcopy (issue1922) + fd.close() + except (OSError, IOError), e: + if e.errno != errno.ENOENT: + raise nlink = 0 - else: - # nlinks() may behave differently for files on Windows - # shares if the file is open. - fd = util.posixfile(f) - nlink = util.nlinks(f) - if nlink < 1: - nlink = 2 # force mktempcopy (issue1922) - fd.close() - except (OSError, IOError), e: - if e.errno != errno.ENOENT: - raise - nlink = 0 - if not os.path.isdir(dirname): - util.makedirs(dirname, self.createmode) - if nlink > 0: - if self._trustnlink is None: - self._trustnlink = nlink > 1 or util.checknlink(f) - if nlink > 1 or not self._trustnlink: - util.rename(util.mktempcopy(f), f) + if not os.path.isdir(dirname): + util.makedirs(dirname, self.createmode) + if nlink > 0: + if self._trustnlink is None: + self._trustnlink = nlink > 1 or util.checknlink(f) + if nlink > 1 or not self._trustnlink: + util.rename(util.mktempcopy(f), f) fp = util.posixfile(f, mode) if nlink == 0: self._fixfilemode(f) @@ -633,13 +634,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) diff -r ebc0fa067c07 -r c8326ffdcb4f mercurial/subrepo.py --- a/mercurial/subrepo.py Thu Dec 06 13:21:27 2012 -0600 +++ b/mercurial/subrepo.py Thu Dec 06 16:42:15 2012 -0600 @@ -446,7 +446,7 @@ 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') diff -r ebc0fa067c07 -r c8326ffdcb4f mercurial/templater.py --- a/mercurial/templater.py Thu Dec 06 13:21:27 2012 -0600 +++ b/mercurial/templater.py Thu Dec 06 16:42:15 2012 -0600 @@ -8,6 +8,7 @@ from i18n import _ import sys, os, re import util, config, templatefilters, parser, error +import types # template parsing @@ -140,6 +141,10 @@ v = context._defaults.get(key, '') if util.safehasattr(v, '__call__'): return v(**mapping) + if isinstance(v, types.GeneratorType): + v = list(v) + mapping[key] = v + return v return v def buildfilter(exp, context): @@ -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' diff -r ebc0fa067c07 -r c8326ffdcb4f mercurial/templates/paper/changeset.tmpl --- a/mercurial/templates/paper/changeset.tmpl Thu Dec 06 13:21:27 2012 -0600 +++ b/mercurial/templates/paper/changeset.tmpl Thu Dec 06 16:42:15 2012 -0600 @@ -74,6 +74,14 @@ + + change baseline + {parent%changesetbaseline} + + + current baseline + {currentbaseline|short} +
diff -r ebc0fa067c07 -r c8326ffdcb4f mercurial/templates/paper/map --- a/mercurial/templates/paper/map Thu Dec 06 13:21:27 2012 -0600 +++ b/mercurial/templates/paper/map Thu Dec 06 16:42:15 2012 -0600 @@ -101,6 +101,8 @@ changesetparent = '{node|short} ' +changesetbaseline = '{node|short} ' + filerevparent = '{rename%filerename}{node|short} ' filerevchild = '{node|short} ' diff -r ebc0fa067c07 -r c8326ffdcb4f mercurial/util.py --- a/mercurial/util.py Thu Dec 06 13:21:27 2012 -0600 +++ b/mercurial/util.py Thu Dec 06 16:42:15 2012 -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, diff -r ebc0fa067c07 -r c8326ffdcb4f mercurial/verify.py --- a/mercurial/verify.py Thu Dec 06 13:21:27 2012 -0600 +++ b/mercurial/verify.py Thu Dec 06 16:42:15 2012 -0600 @@ -25,6 +25,7 @@ return f def _verify(repo): + repo = repo.unfiltered() mflinkrevs = {} filelinkrevs = {} filenodes = {} diff -r ebc0fa067c07 -r c8326ffdcb4f mercurial/windows.py --- a/mercurial/windows.py Thu Dec 06 13:21:27 2012 -0600 +++ b/mercurial/windows.py Thu Dec 06 16:42:15 2012 -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) 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 diff -r ebc0fa067c07 -r c8326ffdcb4f setup.py --- a/setup.py Thu Dec 06 13:21:27 2012 -0600 +++ b/setup.py Thu Dec 06 16:42:15 2012 -0600 @@ -151,6 +151,8 @@ if not e.startswith(b('Not trusting file')) \ and not e.startswith(b('warning: Not importing'))] if err: + print >> sys.stderr, "stderr from '%s':" % (' '.join(cmd)) + print >> sys.stderr, '\n'.join([' ' + e for e in err]) return '' return out diff -r ebc0fa067c07 -r c8326ffdcb4f tests/autodiff.py --- a/tests/autodiff.py Thu Dec 06 13:21:27 2012 -0600 +++ b/tests/autodiff.py Thu Dec 06 16:42:15 2012 -0600 @@ -35,7 +35,7 @@ for chunk in it: ui.write(chunk) for fn in sorted(brokenfiles): - ui.write('data lost for: %s\n' % fn) + ui.write(('data lost for: %s\n' % fn)) cmdtable = { "autodiff": diff -r ebc0fa067c07 -r c8326ffdcb4f tests/run-tests.py --- a/tests/run-tests.py Thu Dec 06 13:21:27 2012 -0600 +++ b/tests/run-tests.py Thu Dec 06 16:42:15 2012 -0600 @@ -55,6 +55,7 @@ import re import threading import killdaemons as killmod +import cPickle as pickle processlock = threading.Lock() @@ -162,6 +163,8 @@ parser.add_option("-p", "--port", type="int", help="port on which servers should listen" " (default: $%s or %d)" % defaults['port']) + parser.add_option("--compiler", type="string", + help="compiler to build with") parser.add_option("--pure", action="store_true", help="use pure Python code instead of C extensions") parser.add_option("-R", "--restart", action="store_true", @@ -175,6 +178,8 @@ parser.add_option("-t", "--timeout", type="int", help="kill errant tests after TIMEOUT seconds" " (default: $%s or %d)" % defaults['timeout']) + parser.add_option("--time", action="store_true", + help="time how long each test takes") parser.add_option("--tmpdir", type="string", help="run tests in the given temporary directory" " (implies --keep-tmpdir)") @@ -263,6 +268,10 @@ sys.stderr.write( 'warning: --timeout option ignored with --debug\n') options.timeout = 0 + if options.time: + sys.stderr.write( + 'warning: --time option ignored with --debug\n') + options.time = False if options.py3k_warnings: if sys.version_info[:2] < (2, 6) or sys.version_info[:2] >= (3, 0): parser.error('--py3k-warnings can only be used on Python 2.6+') @@ -364,6 +373,9 @@ def installhg(options): vlog("# Performing temporary installation of HG") installerrs = os.path.join("tests", "install.err") + compiler = '' + if options.compiler: + compiler = '--compiler ' + options.compiler pure = options.pure and "--pure" or "" # Run installer in hg root @@ -377,12 +389,14 @@ # least on Windows for now, deal with .pydistutils.cfg bugs # when they happen. nohome = '' - cmd = ('%s setup.py %s clean --all' - ' build --build-base="%s"' - ' install --force --prefix="%s" --install-lib="%s"' - ' --install-scripts="%s" %s >%s 2>&1' - % (sys.executable, pure, os.path.join(HGTMP, "build"), - INST, PYTHONDIR, BINDIR, nohome, installerrs)) + cmd = ('%(exe)s setup.py %(pure)s clean --all' + ' build %(compiler)s --build-base="%(base)s"' + ' install --force --prefix="%(prefix)s" --install-lib="%(libdir)s"' + ' --install-scripts="%(bindir)s" %(nohome)s >%(logfile)s 2>&1' + % dict(exe=sys.executable, pure=pure, compiler=compiler, + base=os.path.join(HGTMP, "build"), + prefix=INST, libdir=PYTHONDIR, bindir=BINDIR, + nohome=nohome, logfile=installerrs)) vlog("# Running", cmd) if os.system(cmd) == 0: if not options.verbose: @@ -447,6 +461,14 @@ fn = os.path.join(INST, '..', '.coverage') os.environ['COVERAGE_FILE'] = fn +def outputtimes(options): + vlog('# Producing time report') + times.sort(key=lambda t: (t[1], t[0]), reverse=True) + cols = '%7.3f %s' + print '\n%-7s %s' % ('Time', 'Test') + for test, timetaken in times: + print cols % (timetaken, test) + def outputcoverage(options): vlog('# Producing coverage report') @@ -891,7 +913,12 @@ replacements.append((re.escape(testtmp), '$TESTTMP')) os.mkdir(testtmp) + if options.time: + starttime = time.time() ret, out = runner(testpath, testtmp, options, replacements) + if options.time: + endtime = time.time() + times.append((test, endtime - starttime)) vlog("# Ret was:", ret) mark = '.' @@ -1056,29 +1083,30 @@ childopts += ['--tmpdir', childtmp] cmdline = [PYTHON, sys.argv[0]] + opts + childopts + job vlog(' '.join(cmdline)) - fps[os.spawnvp(os.P_NOWAIT, cmdline[0], cmdline)] = os.fdopen(rfd, 'r') + fps[os.spawnvp(os.P_NOWAIT, cmdline[0], cmdline)] = os.fdopen(rfd, 'rb') os.close(wfd) signal.signal(signal.SIGINT, signal.SIG_IGN) failures = 0 - tested, skipped, failed = 0, 0, 0 + passed, skipped, failed = 0, 0, 0 skips = [] fails = [] while fps: pid, status = os.wait() fp = fps.pop(pid) - l = fp.read().splitlines() try: - test, skip, fail = map(int, l[:3]) - except ValueError: - test, skip, fail = 0, 0, 0 - split = -fail or len(l) - for s in l[3:split]: - skips.append(s.split(" ", 1)) - for s in l[split:]: - fails.append(s.split(" ", 1)) - tested += test - skipped += skip - failed += fail + childresults = pickle.load(fp) + except pickle.UnpicklingError: + pass + else: + passed += len(childresults['p']) + skipped += len(childresults['s']) + failed += len(childresults['f']) + skips.extend(childresults['s']) + fails.extend(childresults['f']) + if options.time: + childtimes = pickle.load(fp) + times.extend(childtimes) + vlog('pid %d exited, status %d' % (pid, status)) failures |= status print @@ -1093,17 +1121,20 @@ _checkhglib("Tested") print "# Ran %d tests, %d skipped, %d failed." % ( - tested, skipped, failed) + passed + failed, skipped, failed) + if options.time: + outputtimes(options) if options.anycoverage: outputcoverage(options) sys.exit(failures != 0) results = dict(p=[], f=[], s=[], i=[]) resultslock = threading.Lock() +times = [] iolock = threading.Lock() -def runqueue(options, tests, results): +def runqueue(options, tests): for test in tests: ret = runone(options, test) if options.first and ret is not None and not ret: @@ -1129,7 +1160,7 @@ print "running all tests" tests = orig - runqueue(options, tests, results) + runqueue(options, tests) failed = len(results['f']) tested = len(results['p']) + failed @@ -1137,12 +1168,10 @@ ignored = len(results['i']) if options.child: - fp = os.fdopen(options.child, 'w') - fp.write('%d\n%d\n%d\n' % (tested, skipped, failed)) - for s in results['s']: - fp.write("%s %s\n" % s) - for s in results['f']: - fp.write("%s %s\n" % s) + fp = os.fdopen(options.child, 'wb') + pickle.dump(results, fp, pickle.HIGHEST_PROTOCOL) + if options.time: + pickle.dump(times, fp, pickle.HIGHEST_PROTOCOL) fp.close() else: print @@ -1153,6 +1182,8 @@ _checkhglib("Tested") print "# Ran %d tests, %d skipped, %d failed." % ( tested, skipped + ignored, failed) + if options.time: + outputtimes(options) if options.anycoverage: outputcoverage(options) @@ -1170,9 +1201,9 @@ checktools() - if len(args) == 0: - args = os.listdir(".") - args.sort() + if len(args) == 0: + args = os.listdir(".") + args.sort() tests = args diff -r ebc0fa067c07 -r c8326ffdcb4f tests/test-bundle.t --- a/tests/test-bundle.t Thu Dec 06 13:21:27 2012 -0600 +++ b/tests/test-bundle.t Thu Dec 06 16:42:15 2012 -0600 @@ -444,6 +444,33 @@ added 1 changesets with 1 changes to 1 files 1 files updated, 0 files merged, 0 files removed, 0 files unresolved +View full contents of the bundle + $ hg -R test bundle --base null -r 3 ../partial.hg + 4 changesets found + $ cd test + $ hg -R ../../partial.hg log -r "bundle()" + changeset: 0:f9ee2f85a263 + user: test + date: Thu Jan 01 00:00:00 1970 +0000 + summary: 0.0 + + changeset: 1:34c2bf6b0626 + user: test + date: Thu Jan 01 00:00:00 1970 +0000 + summary: 0.1 + + changeset: 2:e38ba6f5b7e0 + user: test + date: Thu Jan 01 00:00:00 1970 +0000 + summary: 0.2 + + changeset: 3:eebf5a27f8ca + user: test + date: Thu Jan 01 00:00:00 1970 +0000 + summary: 0.3 + + $ cd .. + test for 540d1059c802 test for 540d1059c802 diff -r ebc0fa067c07 -r c8326ffdcb4f tests/test-check-code-hg.t --- a/tests/test-check-code-hg.t Thu Dec 06 13:21:27 2012 -0600 +++ b/tests/test-check-code-hg.t Thu Dec 06 16:42:15 2012 -0600 @@ -5,163 +5,7 @@ > echo "skipped: not a Mercurial working dir" >&2 > exit 80 > fi - $ hg manifest | xargs "$check_code" || echo 'FAILURE IS NOT AN OPTION!!!' - $ hg manifest | xargs "$check_code" --warnings --nolineno --per-file=0 || true - hgext/convert/cvsps.py:0: - > ui.write('Ancestors: %s\n' % (','.join(r))) - warning: unwrapped ui message - hgext/convert/cvsps.py:0: - > ui.write('Parent: %d\n' % cs.parents[0].id) - warning: unwrapped ui message - hgext/convert/cvsps.py:0: - > ui.write('Parents: %s\n' % - warning: unwrapped ui message - hgext/convert/cvsps.py:0: - > ui.write('Branchpoints: %s \n' % ', '.join(branchpoints)) - warning: unwrapped ui message - hgext/convert/cvsps.py:0: - > ui.write('Author: %s\n' % cs.author) - warning: unwrapped ui message - hgext/convert/cvsps.py:0: - > ui.write('Branch: %s\n' % (cs.branch or 'HEAD')) - warning: unwrapped ui message - hgext/convert/cvsps.py:0: - > ui.write('Date: %s\n' % util.datestr(cs.date, - warning: unwrapped ui message - hgext/convert/cvsps.py:0: - > ui.write('Log:\n') - warning: unwrapped ui message - hgext/convert/cvsps.py:0: - > ui.write('Members: \n') - warning: unwrapped ui message - hgext/convert/cvsps.py:0: - > ui.write('PatchSet %d \n' % cs.id) - warning: unwrapped ui message - hgext/convert/cvsps.py:0: - > ui.write('Tag%s: %s \n' % (['', 's'][len(cs.tags) > 1], - warning: unwrapped ui message - hgext/hgk.py:0: - > ui.write("parent %s\n" % p) - warning: unwrapped ui message - hgext/hgk.py:0: - > ui.write('k=%s\nv=%s\n' % (name, value)) - warning: unwrapped ui message - hgext/hgk.py:0: - > ui.write("author %s %s %s\n" % (ctx.user(), int(date[0]), date[1])) - warning: unwrapped ui message - hgext/hgk.py:0: - > ui.write("branch %s\n\n" % ctx.branch()) - warning: unwrapped ui message - hgext/hgk.py:0: - > ui.write("committer %s %s %s\n" % (committer, int(date[0]), date[1])) - warning: unwrapped ui message - hgext/hgk.py:0: - > ui.write("revision %d\n" % ctx.rev()) - warning: unwrapped ui message - hgext/hgk.py:0: - > ui.write("tree %s\n" % short(ctx.changeset()[0])) - warning: unwrapped ui message - hgext/patchbomb.py:0: - > ui.write('Subject: %s\n' % subj) - warning: unwrapped ui message - hgext/patchbomb.py:0: - > ui.write('From: %s\n' % sender) - warning: unwrapped ui message - mercurial/commands.py:0: - > ui.note('branch %s\n' % data) - warning: unwrapped ui message - mercurial/commands.py:0: - > ui.note('node %s\n' % str(data)) - warning: unwrapped ui message - mercurial/commands.py:0: - > ui.note('tag %s\n' % name) - warning: unwrapped ui message - mercurial/commands.py:0: - > ui.write("unpruned common: %s\n" % " ".join([short(n) - warning: unwrapped ui message - mercurial/commands.py:0: - > ui.write("format: id, p1, p2, cset, delta base, len(delta)\n") - warning: unwrapped ui message - mercurial/commands.py:0: - > ui.write("local is subset\n") - warning: unwrapped ui message - mercurial/commands.py:0: - > ui.write("remote is subset\n") - warning: unwrapped ui message - mercurial/commands.py:0: - > ui.write('deltas against other : ' + fmt % pcfmt(numother, - warning: unwrapped ui message - mercurial/commands.py:0: - > ui.write('deltas against p1 : ' + fmt % pcfmt(nump1, numdeltas)) - warning: unwrapped ui message - mercurial/commands.py:0: - > ui.write('deltas against p2 : ' + fmt % pcfmt(nump2, numdeltas)) - warning: unwrapped ui message - mercurial/commands.py:0: - > ui.write("common heads: %s\n" % " ".join([short(n) for n in common])) - warning: unwrapped ui message - mercurial/commands.py:0: - > ui.write("match: %s\n" % m(d[0])) - warning: unwrapped ui message - mercurial/commands.py:0: - > ui.write('deltas against prev : ' + fmt % pcfmt(numprev, numdeltas)) - warning: unwrapped ui message - mercurial/commands.py:0: - > ui.write('path %s\n' % k) - warning: unwrapped ui message - mercurial/commands.py:0: - > ui.write('uncompressed data size (min/max/avg) : %d / %d / %d\n' - warning: unwrapped ui message - mercurial/commands.py:0: - > ui.write("digraph G {\n") - warning: unwrapped ui message - mercurial/commands.py:0: - > ui.write("internal: %s %s\n" % d) - warning: unwrapped ui message - mercurial/commands.py:0: - > ui.write("standard: %s\n" % util.datestr(d)) - warning: unwrapped ui message - mercurial/commands.py:0: - > ui.write('avg chain length : ' + fmt % avgchainlen) - warning: unwrapped ui message - mercurial/commands.py:0: - > ui.write('case-sensitive: %s\n' % (util.checkcase('.debugfsinfo') - warning: unwrapped ui message - mercurial/commands.py:0: - > ui.write('compression ratio : ' + fmt % compratio) - warning: unwrapped ui message - mercurial/commands.py:0: - > ui.write('delta size (min/max/avg) : %d / %d / %d\n' - warning: unwrapped ui message - mercurial/commands.py:0: - > ui.write('exec: %s\n' % (util.checkexec(path) and 'yes' or 'no')) - warning: unwrapped ui message - mercurial/commands.py:0: - > ui.write('flags : %s\n' % ', '.join(flags)) - warning: unwrapped ui message - mercurial/commands.py:0: - > ui.write('format : %d\n' % format) - warning: unwrapped ui message - mercurial/commands.py:0: - > ui.write('full revision size (min/max/avg) : %d / %d / %d\n' - warning: unwrapped ui message - mercurial/commands.py:0: - > ui.write('revision size : ' + fmt2 % totalsize) - warning: unwrapped ui message - mercurial/commands.py:0: - > ui.write('revisions : ' + fmt2 % numrevs) - warning: unwrapped ui message - warning: unwrapped ui message - mercurial/commands.py:0: - > ui.write('symlink: %s\n' % (util.checklink(path) and 'yes' or 'no')) - warning: unwrapped ui message - tests/autodiff.py:0: - > ui.write('data lost for: %s\n' % fn) - warning: unwrapped ui message - tests/test-ui-color.py:0: - > testui.warn('warning\n') - warning: unwrapped ui message - tests/test-ui-color.py:0: - > testui.write('buffered\n') - warning: unwrapped ui message +New errors are not allowed. Warnings are strongly discouraged. + + $ hg manifest | xargs "$check_code" --warnings --nolineno --per-file=0 diff -r ebc0fa067c07 -r c8326ffdcb4f tests/test-convert-cvs.t --- a/tests/test-convert-cvs.t Thu Dec 06 13:21:27 2012 -0600 +++ b/tests/test-convert-cvs.t Thu Dec 06 16:42:15 2012 -0600 @@ -69,9 +69,16 @@ $TESTTMP/cvsrepo/src/b/c,v <-- *c (glob) $ cd .. -convert fresh repo +convert fresh repo and also check localtimezone option + +NOTE: This doesn't check all time zones -- it merely determines that +the configuration option is taking effect. - $ hg convert src src-hg +An arbitrary (U.S.) time zone is used here. TZ=US/Hawaii is selected +since it does not use DST (unlike other U.S. time zones) and is always +a fixed difference from UTC. + + $ TZ=US/Hawaii hg convert --config convert.localtimezone=True src src-hg initializing destination src-hg repository connecting to $TESTTMP/cvsrepo scanning source... @@ -161,7 +168,7 @@ convert again - $ hg convert src src-hg + $ TZ=US/Hawaii hg convert --config convert.localtimezone=True src src-hg connecting to $TESTTMP/cvsrepo scanning source... collecting CVS rlog @@ -221,7 +228,7 @@ convert again - $ hg convert src src-hg + $ TZ=US/Hawaii hg convert --config convert.localtimezone=True src src-hg connecting to $TESTTMP/cvsrepo scanning source... collecting CVS rlog @@ -239,7 +246,7 @@ convert again with --filemap - $ hg convert --filemap filemap src src-filemap + $ TZ=US/Hawaii hg convert --config convert.localtimezone=True --filemap filemap src src-filemap connecting to $TESTTMP/cvsrepo scanning source... collecting CVS rlog @@ -286,7 +293,7 @@ convert again - $ hg convert --config convert.cvsps.fuzz=2 src src-hg + $ TZ=US/Hawaii hg convert --config convert.cvsps.fuzz=2 --config convert.localtimezone=True src src-hg connecting to $TESTTMP/cvsrepo scanning source... collecting CVS rlog @@ -300,25 +307,25 @@ 2 funny 1 fuzzy 0 fuzzy - $ hg -R src-hg glog --template '{rev} ({branches}) {desc} files: {files}\n' - o 8 (branch) fuzzy files: b/c + $ hg -R src-hg glog --template '{rev} ({branches}) {desc} date: {date|date} files: {files}\n' + o 8 (branch) fuzzy date: * -1000 files: b/c (glob) | - o 7 (branch) fuzzy files: a + o 7 (branch) fuzzy date: * -1000 files: a (glob) | o 6 (branch) funny | ---------------------------- - | log message files: a - o 5 (branch) ci2 files: b/c + | log message date: * -1000 files: a (glob) + o 5 (branch) ci2 date: * -1000 files: b/c (glob) - o 4 () ci1 files: a b/c + o 4 () ci1 date: * -1000 files: a b/c (glob) | - o 3 () update tags files: .hgtags + o 3 () update tags date: * +0000 files: .hgtags (glob) | - o 2 () ci0 files: b/c + o 2 () ci0 date: * -1000 files: b/c (glob) | - | o 1 (INITIAL) import files: + | o 1 (INITIAL) import date: * -1000 files: (glob) |/ - o 0 () Initial revision files: a b/c + o 0 () Initial revision date: * -1000 files: a b/c (glob) testing debugcvsps diff -r ebc0fa067c07 -r c8326ffdcb4f tests/test-convert-git.t --- a/tests/test-convert-git.t Thu Dec 06 13:21:27 2012 -0600 +++ b/tests/test-convert-git.t Thu Dec 06 16:42:15 2012 -0600 @@ -298,3 +298,50 @@ $ hg convert git-repo4 git-repo4-broken-hg 2>&1 | \ > grep 'abort:' | sed 's/abort:.*/abort:/g' abort: + +test sub modules + + $ mkdir git-repo5 + $ cd git-repo5 + $ git init-db >/dev/null 2>/dev/null + $ echo 'sub' >> foo + $ git add foo + $ commit -a -m 'addfoo' + $ BASE=${PWD} + $ cd .. + $ mkdir git-repo6 + $ cd git-repo6 + $ git init-db >/dev/null 2>/dev/null + $ git submodule add ${BASE} >/dev/null 2>/dev/null + $ commit -a -m 'addsubmodule' >/dev/null 2>/dev/null + $ cd .. + +convert sub modules + $ hg convert git-repo6 git-repo6-hg + initializing destination git-repo6-hg repository + scanning source... + sorting... + converting... + 0 addsubmodule + updating bookmarks + $ hg -R git-repo6-hg log -v + changeset: 0:* (glob) + bookmark: master + tag: tip + user: nottest + date: Mon Jan 01 00:00:23 2007 +0000 + files: .hgsub .hgsubstate + description: + addsubmodule + + committer: test + + + + $ cd git-repo6-hg + $ hg up >/dev/null 2>/dev/null + $ cat .hgsubstate + * git-repo5 (glob) + $ cd git-repo5 + $ cat foo + sub diff -r ebc0fa067c07 -r c8326ffdcb4f tests/test-convert-svn-source.t --- a/tests/test-convert-svn-source.t Thu Dec 06 13:21:27 2012 -0600 +++ b/tests/test-convert-svn-source.t Thu Dec 06 16:42:15 2012 -0600 @@ -63,9 +63,16 @@ Committed revision 5. $ cd .. -Convert to hg once +Convert to hg once and also test localtimezone option + +NOTE: This doesn't check all time zones -- it merely determines that +the configuration option is taking effect. - $ hg convert "$SVNREPOURL/proj%20B" B-hg +An arbitrary (U.S.) time zone is used here. TZ=US/Hawaii is selected +since it does not use DST (unlike other U.S. time zones) and is always +a fixed difference from UTC. + + $ TZ=US/Hawaii hg convert --config convert.localtimezone=True "$SVNREPOURL/proj%20B" B-hg initializing destination B-hg repository scanning source... sorting... @@ -109,7 +116,7 @@ Test incremental conversion - $ hg convert "$SVNREPOURL/proj%20B" B-hg + $ TZ=US/Hawaii hg convert --config convert.localtimezone=True "$SVNREPOURL/proj%20B" B-hg scanning source... sorting... converting... @@ -118,22 +125,22 @@ updating tags $ cd B-hg - $ hg glog --template '{rev} {desc|firstline} files: {files}\n' - o 7 update tags files: .hgtags + $ hg glog --template '{rev} {desc|firstline} date: {date|date} files: {files}\n' + o 7 update tags date: * +0000 files: .hgtags (glob) | - o 6 work in progress files: letter2.txt + o 6 work in progress date: * -1000 files: letter2.txt (glob) | - o 5 second letter files: letter .txt letter2.txt + o 5 second letter date: * -1000 files: letter .txt letter2.txt (glob) | - o 4 update tags files: .hgtags + o 4 update tags date: * +0000 files: .hgtags (glob) | - o 3 nice day files: letter .txt + o 3 nice day date: * -1000 files: letter .txt (glob) | - o 2 world files: letter .txt + o 2 world date: * -1000 files: letter .txt (glob) | - o 1 hello files: letter .txt + o 1 hello date: * -1000 files: letter .txt (glob) | - o 0 init projB files: + o 0 init projB date: * -1000 files: (glob) $ hg tags -q tip diff -r ebc0fa067c07 -r c8326ffdcb4f tests/test-convert.t --- a/tests/test-convert.t Thu Dec 06 13:21:27 2012 -0600 +++ b/tests/test-convert.t Thu Dec 06 16:42:15 2012 -0600 @@ -172,6 +172,10 @@ will add the most recent revision on the branch indicated in the regex as the second parent of the changeset. Default is "{{mergefrombranch ([-\w]+)}}" + convert.localtimezone + use local time (as determined by the TZ environment + variable) for changeset date/times. The default is False + (use UTC). hook.cvslog Specify a Python function to be called at the end of gathering the CVS log. The function is passed a list with the log entries, and can modify the entries in-place, or add @@ -211,6 +215,10 @@ convert.svn.trunk specify the name of the trunk branch. The default is "trunk". + convert.localtimezone + use local time (as determined by the TZ environment + variable) for changeset date/times. The default is False + (use UTC). Source history can be retrieved starting at a specific revision, instead of being integrally converted. Only single branch conversions are diff -r ebc0fa067c07 -r c8326ffdcb4f tests/test-eolfilename.t --- a/tests/test-eolfilename.t Thu Dec 06 13:21:27 2012 -0600 +++ b/tests/test-eolfilename.t Thu Dec 06 16:42:15 2012 -0600 @@ -68,9 +68,9 @@ $ touch "$A" $ touch "$B" $ hg status --color=always - \x1b[0;35;1;4m? foo\x1b[0m (esc) + \x1b[0;35;1;4m? \x1b[0m\x1b[0;35;1;4mfoo\x1b[0m (esc) \x1b[0;35;1;4mbar\x1b[0m (esc) - \x1b[0;35;1;4m? foo\x1b[0m (esc) + \x1b[0;35;1;4m? \x1b[0m\x1b[0;35;1;4mfoo\x1b[0m (esc) \x1b[0;35;1;4mbar.baz\x1b[0m (esc) $ cd .. diff -r ebc0fa067c07 -r c8326ffdcb4f tests/test-hgk.t --- a/tests/test-hgk.t Thu Dec 06 13:21:27 2012 -0600 +++ b/tests/test-hgk.t Thu Dec 06 16:42:15 2012 -0600 @@ -11,7 +11,6 @@ tree a0c8bcbbb45c parent 000000000000 author test 0 0 - committer test 0 0 revision 0 branch default diff -r ebc0fa067c07 -r c8326ffdcb4f tests/test-hgweb-commands.t --- a/tests/test-hgweb-commands.t Thu Dec 06 13:21:27 2012 -0600 +++ b/tests/test-hgweb-commands.t Thu Dec 06 16:42:15 2012 -0600 @@ -441,6 +441,14 @@
+ + change baseline + + + + current baseline + 000000000000 +
diff -r ebc0fa067c07 -r c8326ffdcb4f tests/test-hgweb-diffs.t --- a/tests/test-hgweb-diffs.t Thu Dec 06 13:21:27 2012 -0600 +++ b/tests/test-hgweb-diffs.t Thu Dec 06 16:42:15 2012 -0600 @@ -139,6 +139,14 @@
+ + change baseline + + + + current baseline + 000000000000 +
@@ -400,6 +408,14 @@
+ + change baseline + + + + current baseline + 000000000000 +
diff -r ebc0fa067c07 -r c8326ffdcb4f tests/test-hgweb-removed.t --- a/tests/test-hgweb-removed.t Thu Dec 06 13:21:27 2012 -0600 +++ b/tests/test-hgweb-removed.t Thu Dec 06 16:42:15 2012 -0600 @@ -112,6 +112,14 @@
+ + change baseline + cb9a9f314b8b + + + current baseline + cb9a9f314b8b +
diff -r ebc0fa067c07 -r c8326ffdcb4f tests/test-mq-qrefresh.t --- a/tests/test-mq-qrefresh.t Thu Dec 06 13:21:27 2012 -0600 +++ b/tests/test-mq-qrefresh.t Thu Dec 06 16:42:15 2012 -0600 @@ -209,6 +209,7 @@ $ hg add orphanchild $ hg qrefresh nonexistentfilename # clear patch nonexistentfilename: * (glob) + $ hg diff -c qtip $ hg qrefresh --short 1/base $ hg qrefresh --short 2/base diff -r ebc0fa067c07 -r c8326ffdcb4f tests/test-mq.t --- a/tests/test-mq.t Thu Dec 06 13:21:27 2012 -0600 +++ b/tests/test-mq.t Thu Dec 06 16:42:15 2012 -0600 @@ -198,11 +198,11 @@ status --mq with color (issue2096) $ hg status --mq --config extensions.color= --config color.mode=ansi --color=always - \x1b[0;32;1mA .hgignore\x1b[0m (esc) - \x1b[0;32;1mA A\x1b[0m (esc) - \x1b[0;32;1mA B\x1b[0m (esc) - \x1b[0;32;1mA series\x1b[0m (esc) - \x1b[0;35;1;4m? flaf\x1b[0m (esc) + \x1b[0;32;1mA \x1b[0m\x1b[0;32;1m.hgignore\x1b[0m (esc) + \x1b[0;32;1mA \x1b[0m\x1b[0;32;1mA\x1b[0m (esc) + \x1b[0;32;1mA \x1b[0m\x1b[0;32;1mB\x1b[0m (esc) + \x1b[0;32;1mA \x1b[0m\x1b[0;32;1mseries\x1b[0m (esc) + \x1b[0;35;1;4m? \x1b[0m\x1b[0;35;1;4mflaf\x1b[0m (esc) try the --mq option on a command provided by an extension diff -r ebc0fa067c07 -r c8326ffdcb4f tests/test-obsolete.t --- a/tests/test-obsolete.t Thu Dec 06 13:21:27 2012 -0600 +++ b/tests/test-obsolete.t Thu Dec 06 16:42:15 2012 -0600 @@ -173,6 +173,13 @@ And that we can't push bumped changeset + $ hg push ../tmpa -r 0 --force #(make repo) + pushing to ../tmpa + searching for changes + 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 diff -r ebc0fa067c07 -r c8326ffdcb4f tests/test-pathencode.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tests/test-pathencode.py Thu Dec 06 16:42:15 2012 -0600 @@ -0,0 +1,193 @@ +# This is a randomized test that generates different pathnames every +# time it is invoked, and tests the encoding of those pathnames. +# +# It uses a simple probabilistic model to generate valid pathnames +# that have proven likely to expose bugs and divergent behaviour in +# different encoding implementations. + +from mercurial import parsers +from mercurial import store +import binascii, itertools, math, os, random, sys, time +import collections + +if sys.version_info[:2] < (2, 6): + sys.exit(0) + +def hybridencode(path): + return store._hybridencode(path, True) + +validchars = set(map(chr, range(0, 256))) +alphanum = range(ord('A'), ord('Z')) + +for c in '\0/': + validchars.remove(c) + +winreserved = ('aux con prn nul'.split() + + ['com%d' % i for i in xrange(1, 10)] + + ['lpt%d' % i for i in xrange(1, 10)]) + +def casecombinations(names): + '''Build all case-diddled combinations of names.''' + + combos = set() + + for r in names: + for i in xrange(len(r) + 1): + for c in itertools.combinations(xrange(len(r)), i): + d = r + for j in c: + d = ''.join((d[:j], d[j].upper(), d[j + 1:])) + combos.add(d) + return sorted(combos) + +def buildprobtable(fp, cmd='hg manifest tip'): + '''Construct and print a table of probabilities for path name + components. The numbers are percentages.''' + + counts = collections.defaultdict(lambda: 0) + for line in os.popen(cmd).read().splitlines(): + if line[-2:] in ('.i', '.d'): + line = line[:-2] + if line.startswith('data/'): + line = line[5:] + for c in line: + counts[c] += 1 + for c in '\r/\n': + counts.pop(c, None) + t = sum(counts.itervalues()) / 100.0 + fp.write('probtable = (') + for i, (k, v) in enumerate(sorted(counts.iteritems(), key=lambda x: x[1], + reverse=True)): + if (i % 5) == 0: + fp.write('\n ') + vt = v / t + if vt < 0.0005: + break + fp.write('(%r, %.03f), ' % (k, vt)) + fp.write('\n )\n') + +# A table of character frequencies (as percentages), gleaned by +# looking at filelog names from a real-world, very large repo. + +probtable = ( + ('t', 9.828), ('e', 9.042), ('s', 8.011), ('a', 6.801), ('i', 6.618), + ('g', 5.053), ('r', 5.030), ('o', 4.887), ('p', 4.363), ('n', 4.258), + ('l', 3.830), ('h', 3.693), ('_', 3.659), ('.', 3.377), ('m', 3.194), + ('u', 2.364), ('d', 2.296), ('c', 2.163), ('b', 1.739), ('f', 1.625), + ('6', 0.666), ('j', 0.610), ('y', 0.554), ('x', 0.487), ('w', 0.477), + ('k', 0.476), ('v', 0.473), ('3', 0.336), ('1', 0.335), ('2', 0.326), + ('4', 0.310), ('5', 0.305), ('9', 0.302), ('8', 0.300), ('7', 0.299), + ('q', 0.298), ('0', 0.250), ('z', 0.223), ('-', 0.118), ('C', 0.095), + ('T', 0.087), ('F', 0.085), ('B', 0.077), ('S', 0.076), ('P', 0.076), + ('L', 0.059), ('A', 0.058), ('N', 0.051), ('D', 0.049), ('M', 0.046), + ('E', 0.039), ('I', 0.035), ('R', 0.035), ('G', 0.028), ('U', 0.026), + ('W', 0.025), ('O', 0.017), ('V', 0.015), ('H', 0.013), ('Q', 0.011), + ('J', 0.007), ('K', 0.005), ('+', 0.004), ('X', 0.003), ('Y', 0.001), + ) + +for c, _ in probtable: + validchars.remove(c) +validchars = list(validchars) + +def pickfrom(rng, table): + c = 0 + r = rng.random() * sum(i[1] for i in table) + for i, p in table: + c += p + if c >= r: + return i + +reservedcombos = casecombinations(winreserved) + +# The first component of a name following a slash. + +firsttable = ( + (lambda rng: pickfrom(rng, probtable), 90), + (lambda rng: rng.choice(validchars), 5), + (lambda rng: rng.choice(reservedcombos), 5), + ) + +# Components of a name following the first. + +resttable = firsttable[:-1] + +# Special suffixes. + +internalsuffixcombos = casecombinations('.hg .i .d'.split()) + +# The last component of a path, before a slash or at the end of a name. + +lasttable = resttable + ( + (lambda rng: '', 95), + (lambda rng: rng.choice(internalsuffixcombos), 5), + ) + +def makepart(rng, k): + '''Construct a part of a pathname, without slashes.''' + + p = pickfrom(rng, firsttable)(rng) + l = len(p) + ps = [p] + while l <= k: + p = pickfrom(rng, resttable)(rng) + l += len(p) + ps.append(p) + ps.append(pickfrom(rng, lasttable)(rng)) + return ''.join(ps) + +def makepath(rng, j, k): + '''Construct a complete pathname.''' + + return ('data/' + '/'.join(makepart(rng, k) for _ in xrange(j)) + + rng.choice(['.d', '.i'])) + +def genpath(rng, count): + '''Generate random pathnames with gradually increasing lengths.''' + + mink, maxk = 1, 4096 + def steps(): + x, k = 0, mink + for i in xrange(count): + yield mink + int(round(math.sqrt((maxk - mink) * float(i) / count))) + for k in steps(): + x = rng.randint(1, k) + y = rng.randint(1, k) + yield makepath(rng, x, y) + +def runtests(rng, seed, count): + nerrs = 0 + for p in genpath(rng, count): + hybridencode(p) + return nerrs + +def main(): + import getopt + + # Empirically observed to take about a second to run + count = 100 + seed = None + opts, args = getopt.getopt(sys.argv[1:], 'c:s:', + ['build', 'count=', 'seed=']) + for o, a in opts: + if o in ('-c', '--count'): + count = int(a) + elif o in ('-s', '--seed'): + seed = long(a) + elif o == '--build': + buildprobtable(sys.stdout, + 'find .hg/store/data -type f && ' + 'cat .hg/store/fncache 2>/dev/null') + sys.exit(0) + + if seed is None: + try: + seed = long(binascii.hexlify(os.urandom(16)), 16) + except AttributeError: + seed = long(time.time() * 1000) + + rng = random.Random(seed) + if runtests(rng, seed, count): + sys.exit(1) + +if __name__ == '__main__': + main() diff -r ebc0fa067c07 -r c8326ffdcb4f tests/test-status-color.t --- a/tests/test-status-color.t Thu Dec 06 13:21:27 2012 -0600 +++ b/tests/test-status-color.t Thu Dec 06 16:42:15 2012 -0600 @@ -15,100 +15,100 @@ hg status in repo root: $ hg status --color=always - \x1b[0;35;1;4m? a/1/in_a_1\x1b[0m (esc) - \x1b[0;35;1;4m? a/in_a\x1b[0m (esc) - \x1b[0;35;1;4m? b/1/in_b_1\x1b[0m (esc) - \x1b[0;35;1;4m? b/2/in_b_2\x1b[0m (esc) - \x1b[0;35;1;4m? b/in_b\x1b[0m (esc) - \x1b[0;35;1;4m? in_root\x1b[0m (esc) + \x1b[0;35;1;4m? \x1b[0m\x1b[0;35;1;4ma/1/in_a_1\x1b[0m (esc) + \x1b[0;35;1;4m? \x1b[0m\x1b[0;35;1;4ma/in_a\x1b[0m (esc) + \x1b[0;35;1;4m? \x1b[0m\x1b[0;35;1;4mb/1/in_b_1\x1b[0m (esc) + \x1b[0;35;1;4m? \x1b[0m\x1b[0;35;1;4mb/2/in_b_2\x1b[0m (esc) + \x1b[0;35;1;4m? \x1b[0m\x1b[0;35;1;4mb/in_b\x1b[0m (esc) + \x1b[0;35;1;4m? \x1b[0m\x1b[0;35;1;4min_root\x1b[0m (esc) hg status . in repo root: $ hg status --color=always . - \x1b[0;35;1;4m? a/1/in_a_1\x1b[0m (esc) - \x1b[0;35;1;4m? a/in_a\x1b[0m (esc) - \x1b[0;35;1;4m? b/1/in_b_1\x1b[0m (esc) - \x1b[0;35;1;4m? b/2/in_b_2\x1b[0m (esc) - \x1b[0;35;1;4m? b/in_b\x1b[0m (esc) - \x1b[0;35;1;4m? in_root\x1b[0m (esc) + \x1b[0;35;1;4m? \x1b[0m\x1b[0;35;1;4ma/1/in_a_1\x1b[0m (esc) + \x1b[0;35;1;4m? \x1b[0m\x1b[0;35;1;4ma/in_a\x1b[0m (esc) + \x1b[0;35;1;4m? \x1b[0m\x1b[0;35;1;4mb/1/in_b_1\x1b[0m (esc) + \x1b[0;35;1;4m? \x1b[0m\x1b[0;35;1;4mb/2/in_b_2\x1b[0m (esc) + \x1b[0;35;1;4m? \x1b[0m\x1b[0;35;1;4mb/in_b\x1b[0m (esc) + \x1b[0;35;1;4m? \x1b[0m\x1b[0;35;1;4min_root\x1b[0m (esc) $ hg status --color=always --cwd a - \x1b[0;35;1;4m? a/1/in_a_1\x1b[0m (esc) - \x1b[0;35;1;4m? a/in_a\x1b[0m (esc) - \x1b[0;35;1;4m? b/1/in_b_1\x1b[0m (esc) - \x1b[0;35;1;4m? b/2/in_b_2\x1b[0m (esc) - \x1b[0;35;1;4m? b/in_b\x1b[0m (esc) - \x1b[0;35;1;4m? in_root\x1b[0m (esc) + \x1b[0;35;1;4m? \x1b[0m\x1b[0;35;1;4ma/1/in_a_1\x1b[0m (esc) + \x1b[0;35;1;4m? \x1b[0m\x1b[0;35;1;4ma/in_a\x1b[0m (esc) + \x1b[0;35;1;4m? \x1b[0m\x1b[0;35;1;4mb/1/in_b_1\x1b[0m (esc) + \x1b[0;35;1;4m? \x1b[0m\x1b[0;35;1;4mb/2/in_b_2\x1b[0m (esc) + \x1b[0;35;1;4m? \x1b[0m\x1b[0;35;1;4mb/in_b\x1b[0m (esc) + \x1b[0;35;1;4m? \x1b[0m\x1b[0;35;1;4min_root\x1b[0m (esc) $ hg status --color=always --cwd a . - \x1b[0;35;1;4m? 1/in_a_1\x1b[0m (esc) - \x1b[0;35;1;4m? in_a\x1b[0m (esc) + \x1b[0;35;1;4m? \x1b[0m\x1b[0;35;1;4m1/in_a_1\x1b[0m (esc) + \x1b[0;35;1;4m? \x1b[0m\x1b[0;35;1;4min_a\x1b[0m (esc) $ hg status --color=always --cwd a .. - \x1b[0;35;1;4m? 1/in_a_1\x1b[0m (esc) - \x1b[0;35;1;4m? in_a\x1b[0m (esc) - \x1b[0;35;1;4m? ../b/1/in_b_1\x1b[0m (esc) - \x1b[0;35;1;4m? ../b/2/in_b_2\x1b[0m (esc) - \x1b[0;35;1;4m? ../b/in_b\x1b[0m (esc) - \x1b[0;35;1;4m? ../in_root\x1b[0m (esc) + \x1b[0;35;1;4m? \x1b[0m\x1b[0;35;1;4m1/in_a_1\x1b[0m (esc) + \x1b[0;35;1;4m? \x1b[0m\x1b[0;35;1;4min_a\x1b[0m (esc) + \x1b[0;35;1;4m? \x1b[0m\x1b[0;35;1;4m../b/1/in_b_1\x1b[0m (esc) + \x1b[0;35;1;4m? \x1b[0m\x1b[0;35;1;4m../b/2/in_b_2\x1b[0m (esc) + \x1b[0;35;1;4m? \x1b[0m\x1b[0;35;1;4m../b/in_b\x1b[0m (esc) + \x1b[0;35;1;4m? \x1b[0m\x1b[0;35;1;4m../in_root\x1b[0m (esc) $ hg status --color=always --cwd b - \x1b[0;35;1;4m? a/1/in_a_1\x1b[0m (esc) - \x1b[0;35;1;4m? a/in_a\x1b[0m (esc) - \x1b[0;35;1;4m? b/1/in_b_1\x1b[0m (esc) - \x1b[0;35;1;4m? b/2/in_b_2\x1b[0m (esc) - \x1b[0;35;1;4m? b/in_b\x1b[0m (esc) - \x1b[0;35;1;4m? in_root\x1b[0m (esc) + \x1b[0;35;1;4m? \x1b[0m\x1b[0;35;1;4ma/1/in_a_1\x1b[0m (esc) + \x1b[0;35;1;4m? \x1b[0m\x1b[0;35;1;4ma/in_a\x1b[0m (esc) + \x1b[0;35;1;4m? \x1b[0m\x1b[0;35;1;4mb/1/in_b_1\x1b[0m (esc) + \x1b[0;35;1;4m? \x1b[0m\x1b[0;35;1;4mb/2/in_b_2\x1b[0m (esc) + \x1b[0;35;1;4m? \x1b[0m\x1b[0;35;1;4mb/in_b\x1b[0m (esc) + \x1b[0;35;1;4m? \x1b[0m\x1b[0;35;1;4min_root\x1b[0m (esc) $ hg status --color=always --cwd b . - \x1b[0;35;1;4m? 1/in_b_1\x1b[0m (esc) - \x1b[0;35;1;4m? 2/in_b_2\x1b[0m (esc) - \x1b[0;35;1;4m? in_b\x1b[0m (esc) + \x1b[0;35;1;4m? \x1b[0m\x1b[0;35;1;4m1/in_b_1\x1b[0m (esc) + \x1b[0;35;1;4m? \x1b[0m\x1b[0;35;1;4m2/in_b_2\x1b[0m (esc) + \x1b[0;35;1;4m? \x1b[0m\x1b[0;35;1;4min_b\x1b[0m (esc) $ hg status --color=always --cwd b .. - \x1b[0;35;1;4m? ../a/1/in_a_1\x1b[0m (esc) - \x1b[0;35;1;4m? ../a/in_a\x1b[0m (esc) - \x1b[0;35;1;4m? 1/in_b_1\x1b[0m (esc) - \x1b[0;35;1;4m? 2/in_b_2\x1b[0m (esc) - \x1b[0;35;1;4m? in_b\x1b[0m (esc) - \x1b[0;35;1;4m? ../in_root\x1b[0m (esc) + \x1b[0;35;1;4m? \x1b[0m\x1b[0;35;1;4m../a/1/in_a_1\x1b[0m (esc) + \x1b[0;35;1;4m? \x1b[0m\x1b[0;35;1;4m../a/in_a\x1b[0m (esc) + \x1b[0;35;1;4m? \x1b[0m\x1b[0;35;1;4m1/in_b_1\x1b[0m (esc) + \x1b[0;35;1;4m? \x1b[0m\x1b[0;35;1;4m2/in_b_2\x1b[0m (esc) + \x1b[0;35;1;4m? \x1b[0m\x1b[0;35;1;4min_b\x1b[0m (esc) + \x1b[0;35;1;4m? \x1b[0m\x1b[0;35;1;4m../in_root\x1b[0m (esc) $ hg status --color=always --cwd a/1 - \x1b[0;35;1;4m? a/1/in_a_1\x1b[0m (esc) - \x1b[0;35;1;4m? a/in_a\x1b[0m (esc) - \x1b[0;35;1;4m? b/1/in_b_1\x1b[0m (esc) - \x1b[0;35;1;4m? b/2/in_b_2\x1b[0m (esc) - \x1b[0;35;1;4m? b/in_b\x1b[0m (esc) - \x1b[0;35;1;4m? in_root\x1b[0m (esc) + \x1b[0;35;1;4m? \x1b[0m\x1b[0;35;1;4ma/1/in_a_1\x1b[0m (esc) + \x1b[0;35;1;4m? \x1b[0m\x1b[0;35;1;4ma/in_a\x1b[0m (esc) + \x1b[0;35;1;4m? \x1b[0m\x1b[0;35;1;4mb/1/in_b_1\x1b[0m (esc) + \x1b[0;35;1;4m? \x1b[0m\x1b[0;35;1;4mb/2/in_b_2\x1b[0m (esc) + \x1b[0;35;1;4m? \x1b[0m\x1b[0;35;1;4mb/in_b\x1b[0m (esc) + \x1b[0;35;1;4m? \x1b[0m\x1b[0;35;1;4min_root\x1b[0m (esc) $ hg status --color=always --cwd a/1 . - \x1b[0;35;1;4m? in_a_1\x1b[0m (esc) + \x1b[0;35;1;4m? \x1b[0m\x1b[0;35;1;4min_a_1\x1b[0m (esc) $ hg status --color=always --cwd a/1 .. - \x1b[0;35;1;4m? in_a_1\x1b[0m (esc) - \x1b[0;35;1;4m? ../in_a\x1b[0m (esc) + \x1b[0;35;1;4m? \x1b[0m\x1b[0;35;1;4min_a_1\x1b[0m (esc) + \x1b[0;35;1;4m? \x1b[0m\x1b[0;35;1;4m../in_a\x1b[0m (esc) $ hg status --color=always --cwd b/1 - \x1b[0;35;1;4m? a/1/in_a_1\x1b[0m (esc) - \x1b[0;35;1;4m? a/in_a\x1b[0m (esc) - \x1b[0;35;1;4m? b/1/in_b_1\x1b[0m (esc) - \x1b[0;35;1;4m? b/2/in_b_2\x1b[0m (esc) - \x1b[0;35;1;4m? b/in_b\x1b[0m (esc) - \x1b[0;35;1;4m? in_root\x1b[0m (esc) + \x1b[0;35;1;4m? \x1b[0m\x1b[0;35;1;4ma/1/in_a_1\x1b[0m (esc) + \x1b[0;35;1;4m? \x1b[0m\x1b[0;35;1;4ma/in_a\x1b[0m (esc) + \x1b[0;35;1;4m? \x1b[0m\x1b[0;35;1;4mb/1/in_b_1\x1b[0m (esc) + \x1b[0;35;1;4m? \x1b[0m\x1b[0;35;1;4mb/2/in_b_2\x1b[0m (esc) + \x1b[0;35;1;4m? \x1b[0m\x1b[0;35;1;4mb/in_b\x1b[0m (esc) + \x1b[0;35;1;4m? \x1b[0m\x1b[0;35;1;4min_root\x1b[0m (esc) $ hg status --color=always --cwd b/1 . - \x1b[0;35;1;4m? in_b_1\x1b[0m (esc) + \x1b[0;35;1;4m? \x1b[0m\x1b[0;35;1;4min_b_1\x1b[0m (esc) $ hg status --color=always --cwd b/1 .. - \x1b[0;35;1;4m? in_b_1\x1b[0m (esc) - \x1b[0;35;1;4m? ../2/in_b_2\x1b[0m (esc) - \x1b[0;35;1;4m? ../in_b\x1b[0m (esc) + \x1b[0;35;1;4m? \x1b[0m\x1b[0;35;1;4min_b_1\x1b[0m (esc) + \x1b[0;35;1;4m? \x1b[0m\x1b[0;35;1;4m../2/in_b_2\x1b[0m (esc) + \x1b[0;35;1;4m? \x1b[0m\x1b[0;35;1;4m../in_b\x1b[0m (esc) $ hg status --color=always --cwd b/2 - \x1b[0;35;1;4m? a/1/in_a_1\x1b[0m (esc) - \x1b[0;35;1;4m? a/in_a\x1b[0m (esc) - \x1b[0;35;1;4m? b/1/in_b_1\x1b[0m (esc) - \x1b[0;35;1;4m? b/2/in_b_2\x1b[0m (esc) - \x1b[0;35;1;4m? b/in_b\x1b[0m (esc) - \x1b[0;35;1;4m? in_root\x1b[0m (esc) + \x1b[0;35;1;4m? \x1b[0m\x1b[0;35;1;4ma/1/in_a_1\x1b[0m (esc) + \x1b[0;35;1;4m? \x1b[0m\x1b[0;35;1;4ma/in_a\x1b[0m (esc) + \x1b[0;35;1;4m? \x1b[0m\x1b[0;35;1;4mb/1/in_b_1\x1b[0m (esc) + \x1b[0;35;1;4m? \x1b[0m\x1b[0;35;1;4mb/2/in_b_2\x1b[0m (esc) + \x1b[0;35;1;4m? \x1b[0m\x1b[0;35;1;4mb/in_b\x1b[0m (esc) + \x1b[0;35;1;4m? \x1b[0m\x1b[0;35;1;4min_root\x1b[0m (esc) $ hg status --color=always --cwd b/2 . - \x1b[0;35;1;4m? in_b_2\x1b[0m (esc) + \x1b[0;35;1;4m? \x1b[0m\x1b[0;35;1;4min_b_2\x1b[0m (esc) $ hg status --color=always --cwd b/2 .. - \x1b[0;35;1;4m? ../1/in_b_1\x1b[0m (esc) - \x1b[0;35;1;4m? in_b_2\x1b[0m (esc) - \x1b[0;35;1;4m? ../in_b\x1b[0m (esc) + \x1b[0;35;1;4m? \x1b[0m\x1b[0;35;1;4m../1/in_b_1\x1b[0m (esc) + \x1b[0;35;1;4m? \x1b[0m\x1b[0;35;1;4min_b_2\x1b[0m (esc) + \x1b[0;35;1;4m? \x1b[0m\x1b[0;35;1;4m../in_b\x1b[0m (esc) $ cd .. $ hg init repo2 @@ -128,59 +128,59 @@ hg status: $ hg status --color=always - \x1b[0;32;1mA added\x1b[0m (esc) - \x1b[0;31;1mR removed\x1b[0m (esc) - \x1b[0;36;1;4m! deleted\x1b[0m (esc) - \x1b[0;35;1;4m? unknown\x1b[0m (esc) + \x1b[0;32;1mA \x1b[0m\x1b[0;32;1madded\x1b[0m (esc) + \x1b[0;31;1mR \x1b[0m\x1b[0;31;1mremoved\x1b[0m (esc) + \x1b[0;36;1;4m! \x1b[0m\x1b[0;36;1;4mdeleted\x1b[0m (esc) + \x1b[0;35;1;4m? \x1b[0m\x1b[0;35;1;4munknown\x1b[0m (esc) hg status modified added removed deleted unknown never-existed ignored: $ hg status --color=always modified added removed deleted unknown never-existed ignored never-existed: * (glob) - \x1b[0;32;1mA added\x1b[0m (esc) - \x1b[0;31;1mR removed\x1b[0m (esc) - \x1b[0;36;1;4m! deleted\x1b[0m (esc) - \x1b[0;35;1;4m? unknown\x1b[0m (esc) + \x1b[0;32;1mA \x1b[0m\x1b[0;32;1madded\x1b[0m (esc) + \x1b[0;31;1mR \x1b[0m\x1b[0;31;1mremoved\x1b[0m (esc) + \x1b[0;36;1;4m! \x1b[0m\x1b[0;36;1;4mdeleted\x1b[0m (esc) + \x1b[0;35;1;4m? \x1b[0m\x1b[0;35;1;4munknown\x1b[0m (esc) $ hg copy modified copied hg status -C: $ hg status --color=always -C - \x1b[0;32;1mA added\x1b[0m (esc) - \x1b[0;32;1mA copied\x1b[0m (esc) + \x1b[0;32;1mA \x1b[0m\x1b[0;32;1madded\x1b[0m (esc) + \x1b[0;32;1mA \x1b[0m\x1b[0;32;1mcopied\x1b[0m (esc) \x1b[0;0m modified\x1b[0m (esc) - \x1b[0;31;1mR removed\x1b[0m (esc) - \x1b[0;36;1;4m! deleted\x1b[0m (esc) - \x1b[0;35;1;4m? unknown\x1b[0m (esc) + \x1b[0;31;1mR \x1b[0m\x1b[0;31;1mremoved\x1b[0m (esc) + \x1b[0;36;1;4m! \x1b[0m\x1b[0;36;1;4mdeleted\x1b[0m (esc) + \x1b[0;35;1;4m? \x1b[0m\x1b[0;35;1;4munknown\x1b[0m (esc) hg status -A: $ hg status --color=always -A - \x1b[0;32;1mA added\x1b[0m (esc) - \x1b[0;32;1mA copied\x1b[0m (esc) + \x1b[0;32;1mA \x1b[0m\x1b[0;32;1madded\x1b[0m (esc) + \x1b[0;32;1mA \x1b[0m\x1b[0;32;1mcopied\x1b[0m (esc) \x1b[0;0m modified\x1b[0m (esc) - \x1b[0;31;1mR removed\x1b[0m (esc) - \x1b[0;36;1;4m! deleted\x1b[0m (esc) - \x1b[0;35;1;4m? unknown\x1b[0m (esc) - \x1b[0;30;1mI ignored\x1b[0m (esc) - \x1b[0;0mC .hgignore\x1b[0m (esc) - \x1b[0;0mC modified\x1b[0m (esc) + \x1b[0;31;1mR \x1b[0m\x1b[0;31;1mremoved\x1b[0m (esc) + \x1b[0;36;1;4m! \x1b[0m\x1b[0;36;1;4mdeleted\x1b[0m (esc) + \x1b[0;35;1;4m? \x1b[0m\x1b[0;35;1;4munknown\x1b[0m (esc) + \x1b[0;30;1mI \x1b[0m\x1b[0;30;1mignored\x1b[0m (esc) + \x1b[0;0mC \x1b[0m\x1b[0;0m.hgignore\x1b[0m (esc) + \x1b[0;0mC \x1b[0m\x1b[0;0mmodified\x1b[0m (esc) hg status -A (with terminfo color): $ mkdir "$TESTTMP/terminfo" $ TERMINFO="$TESTTMP/terminfo" tic "$TESTDIR/hgterm.ti" $ TERM=hgterm TERMINFO="$TESTTMP/terminfo" hg status --config color.mode=terminfo --color=always -A - \x1b[30m\x1b[32m\x1b[1mA added\x1b[30m (esc) - \x1b[30m\x1b[32m\x1b[1mA copied\x1b[30m (esc) + \x1b[30m\x1b[32m\x1b[1mA \x1b[30m\x1b[30m\x1b[32m\x1b[1madded\x1b[30m (esc) + \x1b[30m\x1b[32m\x1b[1mA \x1b[30m\x1b[30m\x1b[32m\x1b[1mcopied\x1b[30m (esc) \x1b[30m\x1b[30m modified\x1b[30m (esc) - \x1b[30m\x1b[31m\x1b[1mR removed\x1b[30m (esc) - \x1b[30m\x1b[36m\x1b[1m\x1b[4m! deleted\x1b[30m (esc) - \x1b[30m\x1b[35m\x1b[1m\x1b[4m? unknown\x1b[30m (esc) - \x1b[30m\x1b[30m\x1b[1mI ignored\x1b[30m (esc) - \x1b[30m\x1b[30mC .hgignore\x1b[30m (esc) - \x1b[30m\x1b[30mC modified\x1b[30m (esc) + \x1b[30m\x1b[31m\x1b[1mR \x1b[30m\x1b[30m\x1b[31m\x1b[1mremoved\x1b[30m (esc) + \x1b[30m\x1b[36m\x1b[1m\x1b[4m! \x1b[30m\x1b[30m\x1b[36m\x1b[1m\x1b[4mdeleted\x1b[30m (esc) + \x1b[30m\x1b[35m\x1b[1m\x1b[4m? \x1b[30m\x1b[30m\x1b[35m\x1b[1m\x1b[4munknown\x1b[30m (esc) + \x1b[30m\x1b[30m\x1b[1mI \x1b[30m\x1b[30m\x1b[30m\x1b[1mignored\x1b[30m (esc) + \x1b[30m\x1b[30mC \x1b[30m\x1b[30m\x1b[30m.hgignore\x1b[30m (esc) + \x1b[30m\x1b[30mC \x1b[30m\x1b[30m\x1b[30mmodified\x1b[30m (esc) $ echo "^ignoreddir$" > .hgignore @@ -194,7 +194,7 @@ hg status -i ignoreddir/file: $ hg status --color=always -i ignoreddir/file - \x1b[0;30;1mI ignoreddir/file\x1b[0m (esc) + \x1b[0;30;1mI \x1b[0m\x1b[0;30;1mignoreddir/file\x1b[0m (esc) $ cd .. check 'status -q' and some combinations @@ -220,11 +220,11 @@ $ hg --config color.status.modified=periwinkle status --color=always ignoring unknown color/effect 'periwinkle' (configured in color.status.modified) M modified - \x1b[0;32;1mA added\x1b[0m (esc) - \x1b[0;32;1mA copied\x1b[0m (esc) - \x1b[0;31;1mR removed\x1b[0m (esc) - \x1b[0;36;1;4m! deleted\x1b[0m (esc) - \x1b[0;35;1;4m? unknown\x1b[0m (esc) + \x1b[0;32;1mA \x1b[0m\x1b[0;32;1madded\x1b[0m (esc) + \x1b[0;32;1mA \x1b[0m\x1b[0;32;1mcopied\x1b[0m (esc) + \x1b[0;31;1mR \x1b[0m\x1b[0;31;1mremoved\x1b[0m (esc) + \x1b[0;36;1;4m! \x1b[0m\x1b[0;36;1;4mdeleted\x1b[0m (esc) + \x1b[0;35;1;4m? \x1b[0m\x1b[0;35;1;4munknown\x1b[0m (esc) Run status with 2 different flags. Check if result is the same or different. diff -r ebc0fa067c07 -r c8326ffdcb4f tests/test-subrepo.t --- a/tests/test-subrepo.t Thu Dec 06 13:21:27 2012 -0600 +++ b/tests/test-subrepo.t Thu Dec 06 16:42:15 2012 -0600 @@ -718,6 +718,14 @@ committing subrepository subrepo-2 $ hg st subrepo-2/file +Check that share works with subrepo + $ hg --config extensions.share= share . ../shared + updating working directory + cloning subrepo subrepo-2 from $TESTTMP/subrepo-status/subrepo-2 + 2 files updated, 0 files merged, 0 files removed, 0 files unresolved + $ test -f ../shared/subrepo-1/.hg/sharedpath + [1] + Check hg update --clean $ cd $TESTTMP/t $ rm -r t/t.orig diff -r ebc0fa067c07 -r c8326ffdcb4f tests/test-ui-color.py --- a/tests/test-ui-color.py Thu Dec 06 13:21:27 2012 -0600 +++ b/tests/test-ui-color.py Thu Dec 06 16:42:15 2012 -0600 @@ -5,8 +5,8 @@ # ensure errors aren't buffered testui = color.colorui() testui.pushbuffer() -testui.write('buffered\n') -testui.warn('warning\n') +testui.write(('buffered\n')) +testui.warn(('warning\n')) testui.write_err('error\n') print repr(testui.popbuffer())