Mercurial > hg
changeset 19067:292cd385856d stable 2.6-rc
merge default int stable for 2.6 code freeze
author | Matt Mackall <mpm@selenic.com> |
---|---|
date | Thu, 18 Apr 2013 23:46:26 -0500 |
parents | e9662c24f946 (current diff) 2cad301a7f06 (diff) |
children | 8618d31d532b |
files | tests/test-atomictempfile.py.out tests/test-debugcomplete.t tests/test-histedit-revspec.t tests/test-interhg.t |
diffstat | 273 files changed, 8689 insertions(+), 3006 deletions(-) [+] |
line wrap: on
line diff
--- a/Makefile Thu Apr 04 16:28:19 2013 -0500 +++ b/Makefile Thu Apr 18 23:46:26 2013 -0500 @@ -94,6 +94,9 @@ test-%: cd tests && $(PYTHON) run-tests.py $(TESTFLAGS) $@ +check-code: + hg manifest | xargs python contrib/check-code.py + update-pot: i18n/hg.pot i18n/hg.pot: $(PYFILES) $(DOCFILES)
--- a/contrib/bash_completion Thu Apr 04 16:28:19 2013 -0500 +++ b/contrib/bash_completion Thu Apr 18 23:46:26 2013 -0500 @@ -1,4 +1,4 @@ -# bash completion for the Mercurial distributed SCM +# bash completion for the Mercurial distributed SCM -*- sh -*- # Docs: # @@ -80,26 +80,20 @@ done } -_hg_status() +_hg_debugpathcomplete() { - local files="$(_hg_cmd status -n$1 .)" + local files="$(_hg_cmd debugpathcomplete $1 "$cur")" local IFS=$'\n' compopt -o filenames 2>/dev/null COMPREPLY=(${COMPREPLY[@]:-} $(compgen -W '$files' -- "$cur")) } -_hg_tags() +_hg_status() { - local tags="$(_hg_cmd tags -q)" + local files="$(_hg_cmd status -n$1 "glob:$cur**")" local IFS=$'\n' - COMPREPLY=(${COMPREPLY[@]:-} $(compgen -W '$tags' -- "$cur")) -} - -_hg_branches() -{ - local branches="$(_hg_cmd branches -q)" - local IFS=$'\n' - COMPREPLY=(${COMPREPLY[@]:-} $(compgen -W '$branches' -- "$cur")) + compopt -o filenames 2>/dev/null + COMPREPLY=(${COMPREPLY[@]:-} $(compgen -W '$files' -- "$cur")) } _hg_bookmarks() @@ -111,9 +105,9 @@ _hg_labels() { - _hg_tags - _hg_branches - _hg_bookmarks + local labels="$(_hg_cmd debuglabelcomplete "$cur")" + local IFS=$'\n' + COMPREPLY=(${COMPREPLY[@]:-} $(compgen -W '$labels' -- "$cur")) } # this is "kind of" ugly... @@ -235,7 +229,7 @@ fi _hg_labels ;; - manifest|update) + manifest|update|up|checkout|co) _hg_labels ;; pull|push|outgoing|incoming) @@ -251,20 +245,20 @@ merge) _hg_labels ;; - commit|record) + commit|ci|record) _hg_status "mar" ;; - remove) - _hg_status "d" + remove|rm) + _hg_debugpathcomplete -n ;; forget) - _hg_status "a" + _hg_debugpathcomplete -fa ;; diff) _hg_status "mar" ;; revert) - _hg_status "mard" + _hg_debugpathcomplete ;; clone) local count=$(_hg_count_non_option) @@ -294,13 +288,6 @@ # Completion for commands provided by extensions # bookmarks -_hg_bookmarks() -{ - local bookmarks="$(_hg_cmd bookmarks --quiet )" - local IFS=$'\n' - COMPREPLY=(${COMPREPLY[@]:-} $(compgen -W '$bookmarks' -- "$cur")) -} - _hg_cmd_bookmarks() { if [[ "$prev" = @(-d|--delete|-m|--rename) ]]; then
--- a/contrib/check-code.py Thu Apr 04 16:28:19 2013 -0500 +++ b/contrib/check-code.py Thu Apr 18 23:46:26 2013 -0500 @@ -19,7 +19,8 @@ def reppython(m): comment = m.group('comment') if comment: - return "#" * len(comment) + l = len(comment.rstrip()) + return "#" * l + comment[l:] return repquote(m) def repcomment(m): @@ -80,6 +81,7 @@ (r'^diff.*-\w*N', "don't use 'diff -N'"), (r'\$PWD|\${PWD}', "don't use $PWD, use `pwd`"), (r'^([^"\'\n]|("[^"\n]*")|(\'[^\'\n]*\'))*\^', "^ must be quoted"), + (r'kill (`|\$\()', "don't use kill, use killdaemons.py") ] ] @@ -88,6 +90,7 @@ (r"<<(\S+)((.|\n)*?\n\1)", rephere), ] +winglobmsg = "use (glob) to match Windows paths too" uprefix = r"^ \$ " utestpats = [ [ @@ -100,11 +103,16 @@ "explicit exit code checks unnecessary"), (uprefix + r'set -e', "don't use set -e"), (uprefix + r'\s', "don't indent commands, use > for continued lines"), - (r'^ saved backup bundle to \$TESTTMP.*\.hg$', - "use (glob) to match Windows paths too"), + (r'^ saved backup bundle to \$TESTTMP.*\.hg$', winglobmsg), + (r'^ changeset .* references (corrupted|missing) \$TESTTMP/.*[^)]$', + winglobmsg), + (r'^ pulling from \$TESTTMP/.*[^)]$', winglobmsg, '\$TESTTMP/unix-repo$'), ], # warnings - [] + [ + (r'^ [^*?/\n]* \(glob\)$', + "warning: glob match with no glob character (?*/)"), + ] ] for i in [0, 1]: @@ -212,10 +220,11 @@ (r'(?i)descendent', "the proper spelling is descendAnt"), (r'\.debug\(\_', "don't mark debug messages for translation"), (r'\.strip\(\)\.split\(\)', "no need to strip before splitting"), - (r'^\s*except\s*:', "warning: naked except clause", r'#.*re-raises'), + (r'^\s*except\s*:', "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)"), + (r'release\(.*wlock, .*lock\)', "wrong lock release order"), ], # warnings [ @@ -229,6 +238,15 @@ (?P=quote))""", reppython), ] +txtfilters = [] + +txtpats = [ + [ + ('\s$', 'trailing whitespace'), + ], + [] +] + cpats = [ [ (r'//', "don't use //-style comments"), @@ -284,6 +302,7 @@ inrevlogpats), ('layering violation ui in util', r'mercurial/util\.py', pyfilters, inutilpats), + ('txt', r'.*\.txt$', txtfilters, txtpats), ] class norepeatlogger(object):
--- a/contrib/hgk Thu Apr 04 16:28:19 2013 -0500 +++ b/contrib/hgk Thu Apr 18 23:46:26 2013 -0500 @@ -30,6 +30,8 @@ interp alias {} ttk::label {} label interp alias {} ttk::scrollbar {} scrollbar interp alias {} ttk::optionMenu {} tk_optionMenu + + proc updatepalette {} {} } else { proc ::ttk::optionMenu {w varName firstValue args} { upvar #0 $varName var @@ -46,6 +48,11 @@ } return $w.menu } + proc updatepalette {} { + catch { + tk_setPalette background [ttk::style lookup client -background] + } + } } if {[tk windowingsystem] eq "win32"} { @@ -120,6 +127,7 @@ } +updatepalette # Unify right mouse button handling. # See "mouse buttons on macintosh" thread on comp.lang.tcl @@ -139,6 +147,18 @@ } } +proc popupify {w} { + wm resizable $w 0 0 + wm withdraw $w + update + set x [expr {([winfo screenwidth .]-[winfo reqwidth $w])/2}] + set y [expr {([winfo screenheight .]-[winfo reqheight $w])/2}] + wm geometry $w +$x+$y + wm transient $w . + wm deiconify $w + wm resizable $w 1 1 +} + proc getcommits {rargs} { global commits commfd phase canv mainfont env global startmsecs nextupdate ncmupdate @@ -380,11 +400,11 @@ } } if {$audate != {}} { - set audate [clock format $audate -format "%Y-%m-%d %H:%M:%S"] + set audate [clock format $audate] } if {$comdate != {}} { set cdate($id) $comdate - set comdate [clock format $comdate -format "%Y-%m-%d %H:%M:%S"] + set comdate [clock format $comdate] } set commitinfo($id) [list $headline $auname $audate \ $comname $comdate $comment $rev $branch $bookmark] @@ -436,16 +456,13 @@ exit 2 } } - regsub -all "\r\n" $tags "\n" tags - - set lines [split $tags "\n"] - foreach f $lines { - regexp {(\S+)$} $f full - regsub {\s+(\S+)$} $f "" direct - set sha [split $full ':'] - set tag [lindex $sha 1] - lappend tagids($direct) $tag - lappend idtags($tag) $direct + + foreach {tag rev} $tags { + # we use foreach as Tcl8.4 doesn't support lassign + foreach {- id} [split $rev :] { + lappend tagids($tag) $id + lappend idtags($id) $tag + } } set status [catch {exec $env(HG) --config ui.report_untrusted=false heads} heads] @@ -455,9 +472,8 @@ exit 2 } } - regsub -all "\r\n" $heads "\n" heads - - set lines [split $heads "\n"] + + set lines [split $heads \r\n] foreach f $lines { set match "" regexp {changeset:\s+(\S+):(\S+)$} $f match id sha @@ -529,6 +545,7 @@ ttk::button $w.ok -text OK -command "destroy $w" pack $w.ok -side bottom -fill x bind $w <Visibility> "grab $w; focus $w" + popupify $w tkwait window $w } @@ -545,7 +562,7 @@ if {[info exists posx]} { wm geometry . +$posx+$posy } - + menu .bar .bar add cascade -label "File" -menu .bar.file menu .bar.file @@ -642,7 +659,7 @@ findtype Exact IgnCase Regexp] set findloc "All fields" ttk::optionMenu .ctop.top.bar.findloc findloc "All fields" Headline \ - Comments Author Committer Files Pickaxe + Comments Author Files Pickaxe pack .ctop.top.bar.findloc -side right pack .ctop.top.bar.findtype -side right # for making sure type==Exact whenever loc==Pickaxe @@ -949,6 +966,7 @@ pack $w.m -side top -fill x -padx 20 -pady 20 ttk::button $w.ok -text Close -command "destroy $w" pack $w.ok -side bottom + popupify $w } set aunextcolor 0 @@ -1929,7 +1947,7 @@ set oldsel $selectedline } set didsel 0 - set fldtypes {Headline Author Date Committer CDate Comment} + set fldtypes {Headline Author Date CDate Comment} for {set l 0} {$l < $numcommits} {incr l} { set id $lineid($l) set info $commitinfo($id) @@ -2471,12 +2489,12 @@ $ctext mark set fmark.0 0.0 $ctext mark gravity fmark.0 left set info $commitinfo($id) - $ctext insert end "Revision: [lindex $info 6]\n" + $ctext insert end "Changeset: [lindex $info 6]\n" if {[llength [lindex $info 7]] > 0} { $ctext insert end "Branch: [lindex $info 7]\n" } - $ctext insert end "Author: [lindex $info 1] [lindex $info 2]\n" - $ctext insert end "Committer: [lindex $info 3] [lindex $info 4]\n" + $ctext insert end "User: [lindex $info 1]\n" + $ctext insert end "Date: [lindex $info 2]\n" if {[info exists idbookmarks($id)]} { $ctext insert end "Bookmarks:" foreach bookmark $idbookmarks($id) { @@ -3601,7 +3619,7 @@ $ctext tag bind link0 <1> [list selbyid $id] set info $commitinfo($id) $ctext insert end "\n\t[lindex $info 0]\n" - $ctext insert end "\tAuthor:\t[lindex $info 1]\n" + $ctext insert end "\tUser:\t[lindex $info 1]\n" $ctext insert end "\tDate:\t[lindex $info 2]\n" if {[info exists children($id)]} { $ctext insert end "\nChildren:" @@ -3613,7 +3631,7 @@ $ctext insert end $child [list link link$i] $ctext tag bind link$i <1> [list selbyid $child] $ctext insert end "\n\t[lindex $info 0]" - $ctext insert end "\n\tAuthor:\t[lindex $info 1]" + $ctext insert end "\n\tUser:\t[lindex $info 1]" $ctext insert end "\n\tDate:\t[lindex $info 2]\n" } } @@ -3720,13 +3738,11 @@ set patchtop $top catch {destroy $top} toplevel $top - ttk::label $top.title -text "Generate patch" - grid $top.title - -pady 10 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 + grid $top.from $top.fromsha1 -sticky w -pady {10 0} ttk::entry $top.fromhead -width 60 $top.fromhead insert 0 $oldhead $top.fromhead conf -state readonly @@ -3755,6 +3771,8 @@ grid columnconfigure $top.buts 1 -weight 1 -uniform a grid $top.buts - -pady 10 -sticky ew focus $top.fname + popupify $top + wm title $top "Generate a patch" } proc mkpatchrev {} { @@ -3800,13 +3818,11 @@ set mktagtop $top catch {destroy $top} toplevel $top - ttk::label $top.title -text "Create tag" - grid $top.title - -pady 10 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 + grid $top.id $top.sha1 -sticky w -pady {10 0} ttk::entry $top.head -width 60 $top.head insert 0 [lindex $commitinfo($rowmenuid) 0] $top.head conf -state readonly @@ -3822,6 +3838,8 @@ grid columnconfigure $top.buts 1 -weight 1 -uniform a grid $top.buts - -pady 10 -sticky ew focus $top.tag + popupify $top + wm title $top "Create a tag" } proc domktag {} { @@ -3880,13 +3898,11 @@ set wrcomtop $top catch {destroy $top} toplevel $top - ttk::label $top.title -text "Write commit to file" - grid $top.title - -pady 10 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 + grid $top.id $top.sha1 -sticky w -pady {10 0} ttk::entry $top.head -width 60 $top.head insert 0 [lindex $commitinfo($rowmenuid) 0] $top.head conf -state readonly @@ -3906,6 +3922,8 @@ grid columnconfigure $top.buts 1 -weight 1 -uniform a grid $top.buts - -pady 10 -sticky ew focus $top.fname + popupify $top + wm title $top "Write commit to a file" } proc wrcomgo {} {
--- a/contrib/mergetools.hgrc Thu Apr 04 16:28:19 2013 -0500 +++ b/contrib/mergetools.hgrc Thu Apr 18 23:46:26 2013 -0500 @@ -25,7 +25,8 @@ gpyfm.gui=True meld.gui=True -meld.args=--label='local' $local --label='base' $base --label='other' $other +meld.args=--label='local' $local --label='merged' $base --label='other' $other -o $output +meld.check=changed meld.diffargs=-a --label='$plabel1' $parent --label='$clabel' $child tkdiff.args=$local $other -a $base -o $output
--- a/contrib/perf.py Thu Apr 04 16:28:19 2013 -0500 +++ b/contrib/perf.py Thu Apr 18 23:46:26 2013 -0500 @@ -2,7 +2,7 @@ '''helper extension to measure performance''' from mercurial import cmdutil, scmutil, util, match, commands, obsolete -from mercurial import repoview, branchmap +from mercurial import repoview, branchmap, merge, copies import time, os, sys cmdtable = {} @@ -54,6 +54,15 @@ # False)))) timer(lambda: sum(map(len, repo.status(**opts)))) +@command('perfaddremove') +def perfaddremove(ui, repo): + try: + oldquiet = repo.ui.quiet + repo.ui.quiet = True + timer(lambda: scmutil.addremove(repo, dry_run=True)) + finally: + repo.ui.quiet = oldquiet + def clearcaches(cl): # behave somewhat consistently across internal API changes if util.safehasattr(cl, 'clearcaches'): @@ -99,6 +108,15 @@ rev in s timer(d) +@command('perfdirs') +def perfdirs(ui, repo): + dirstate = repo.dirstate + 'a' in dirstate + def d(): + dirstate.dirs() + del dirstate._dirs + timer(d) + @command('perfdirstate') def perfdirstate(ui, repo): "a" in repo.dirstate @@ -124,6 +142,30 @@ ds.write() timer(d) +@command('perfmergecalculate', + [('r', 'rev', '.', 'rev to merge against')]) +def perfmergecalculate(ui, repo, rev): + wctx = repo[None] + rctx = scmutil.revsingle(repo, rev, rev) + ancestor = wctx.ancestor(rctx) + # we don't want working dir files to be stat'd in the benchmark, so prime + # that cache + wctx.dirty() + def d(): + # acceptremote is True because we don't want prompts in the middle of + # our benchmark + merge.calculateupdates(repo, wctx, rctx, ancestor, False, False, False, + acceptremote=True) + timer(d) + +@command('perfpathcopies', [], "REV REV") +def perfpathcopies(ui, repo, rev1, rev2): + ctx1 = scmutil.revsingle(repo, rev1, rev1) + ctx2 = scmutil.revsingle(repo, rev2, rev2) + def d(): + copies.pathcopies(ctx1, ctx2) + timer(d) + @command('perfmanifest') def perfmanifest(ui, repo): def d(): @@ -268,7 +310,7 @@ def perfrevset(ui, repo, expr, clear=False): """benchmark the execution time of a revset - Use the --clean option if need to evaluate the impact of build volative + Use the --clean option if need to evaluate the impact of build volatile revisions set cache on the revset execution. Volatile cache hold filtered and obsolete related cache.""" def d(): @@ -361,6 +403,3 @@ finally: branchmap.read = oldread branchmap.branchcache.write = oldwrite - - -
--- a/contrib/pylintrc Thu Apr 04 16:28:19 2013 -0500 +++ b/contrib/pylintrc Thu Apr 18 23:46:26 2013 -0500 @@ -1,11 +1,11 @@ # lint Python modules using external checkers. -# +# # This is the main checker controlling the other ones and the reports # generation. It is itself both a raw checker and an astng checker in order # to: # * handle message activation / deactivation at the module level # * handle some basic but necessary stats'data (number of classes, methods...) -# +# [MASTER] # Specify a configuration file. @@ -95,7 +95,7 @@ # try to find bugs in the code using type inference -# +# [TYPECHECK] # Tells whether missing members accessed in mixin class should be ignored. A @@ -120,7 +120,7 @@ # * undefined variables # * redefinition of variable from builtins or from an outer scope # * use of variable before assignment -# +# [VARIABLES] # Tells whether we should check for unused import in __init__ files. @@ -143,7 +143,7 @@ # * dangerous default values as arguments # * redefinition of function / method / class # * uses of the global statement -# +# [BASIC] # Required attributes for module, separated by a comma @@ -197,7 +197,7 @@ # * relative / wildcard imports # * cyclic imports # * uses of deprecated modules -# +# [IMPORTS] # Deprecated modules which should not be used, separated by a comma @@ -219,7 +219,7 @@ # checks for sign of poor/misdesign: # * number of methods, attributes, local variables... # * size, complexity of functions, methods -# +# [DESIGN] # Maximum number of arguments for function / method @@ -257,7 +257,7 @@ # * attributes not defined in the __init__ method # * supported interfaces implementation # * unreachable code -# +# [CLASSES] # List of interface methods to ignore, separated by a comma. This is used for @@ -273,7 +273,7 @@ # * strict indentation # * line length # * use of <> instead of != -# +# [FORMAT] # Maximum number of characters on a single line. @@ -290,7 +290,7 @@ # checks for: # * warning notes in the code like FIXME, XXX # * PEP 263: source code with non ascii character but no encoding declaration -# +# [MISCELLANEOUS] # List of note tags to take in consideration, separated by a comma. @@ -300,7 +300,7 @@ # checks for similarities and duplicated code. This computation may be # memory / CPU intensive, so you should disable it if you experiments some # problems. -# +# [SIMILARITIES] # Minimum lines number of a similarity.
--- a/contrib/simplemerge Thu Apr 04 16:28:19 2013 -0500 +++ b/contrib/simplemerge Thu Apr 18 23:46:26 2013 -0500 @@ -44,7 +44,7 @@ try: for fp in (sys.stdin, sys.stdout, sys.stderr): util.setbinary(fp) - + opts = {} try: args = fancyopts.fancyopts(sys.argv[1:], options, opts)
--- a/contrib/synthrepo.py Thu Apr 04 16:28:19 2013 -0500 +++ b/contrib/synthrepo.py Thu Apr 18 23:46:26 2013 -0500 @@ -35,7 +35,7 @@ - Symlinks and binary files are ignored ''' -import bisect, collections, json, os, random, time +import bisect, collections, json, os, random, time, sys from mercurial import cmdutil, context, patch, scmutil, url, util, hg from mercurial.i18n import _ from mercurial.node import nullrev, nullid
--- a/contrib/undumprevlog Thu Apr 04 16:28:19 2013 -0500 +++ b/contrib/undumprevlog Thu Apr 18 23:46:26 2013 -0500 @@ -11,7 +11,7 @@ opener = scmutil.opener('.', False) tr = transaction.transaction(sys.stderr.write, opener, "undump.journal") -while 1: +while True: l = sys.stdin.readline() if not l: break
--- a/contrib/vim/hgcommand.vim Thu Apr 04 16:28:19 2013 -0500 +++ b/contrib/vim/hgcommand.vim Thu Apr 18 23:46:26 2013 -0500 @@ -10,7 +10,7 @@ " Bob Hiestand <bob.hiestand@gmail.com> for the fabulous " cvscommand.vim from which this script was directly created by " means of sed commands and minor tweaks. -" Note: +" Note: " For Vim7 the use of Bob Hiestand's vcscommand.vim " <http://www.vim.org/scripts/script.php?script_id=90> " in conjunction with Vladmir Marek's Hg backend
--- a/contrib/win32/win32-build.txt Thu Apr 04 16:28:19 2013 -0500 +++ b/contrib/win32/win32-build.txt Thu Apr 18 23:46:26 2013 -0500 @@ -24,7 +24,7 @@ http://www.microsoft.com/downloads/details.aspx?FamilyID=9b2da534-3e03-4391-8a4d-074b9f2bc1bf for 64-bit: http://www.microsoft.com/downloads/details.aspx?familyid=bd2a6171-e2d6-4230-b809-9a8d7548c1b6 - + The py2exe distutils extension http://sourceforge.net/projects/py2exe/ @@ -94,7 +94,7 @@ amd64_Microsoft.VC90.CRT_(...)_9.0.21022.8(...).manifest for x64), copy it in the dist directory and rename it to Microsoft.VC90.CRT.manifest. -Before building the installer, you have to build Mercurial HTML documentation +Before building the installer, you have to build Mercurial HTML documentation (or fix mercurial.iss to not reference the doc directory): cd doc
--- a/contrib/wix/guids.wxi Thu Apr 04 16:28:19 2013 -0500 +++ b/contrib/wix/guids.wxi Thu Apr 18 23:46:26 2013 -0500 @@ -19,7 +19,7 @@ <!-- help.wxs --> <?define helpFolder.guid = {9FA957DB-6DFE-44f2-AD03-293B2791CF17} ?> - + <!-- i18n.wxs --> <?define i18nFolder.guid = {1BF8026D-CF7C-4174-AEE6-D6B7BF119248} ?>
--- a/contrib/wix/i18n.wxs Thu Apr 04 16:28:19 2013 -0500 +++ b/contrib/wix/i18n.wxs Thu Apr 18 23:46:26 2013 -0500 @@ -4,7 +4,7 @@ <?include guids.wxi ?> <?include defines.wxi ?> - <?define hg_po_langs = + <?define hg_po_langs = da;de;el;fr;it;ja;pt_BR;ro;ru;sv;zh_CN;zh_TW ?> @@ -14,8 +14,8 @@ <Component Id="i18nFolder" Guid="$(var.i18nFolder.guid)" Win64='$(var.IsX64)'> <File Name="hggettext" KeyPath="yes" /> <?foreach LANG in $(var.hg_po_langs) ?> - <File Id="hg.$(var.LANG).po" - Name="$(var.LANG).po" + <File Id="hg.$(var.LANG).po" + Name="$(var.LANG).po" /> <?endforeach?> </Component>
--- a/contrib/wix/mercurial.wxs Thu Apr 04 16:28:19 2013 -0500 +++ b/contrib/wix/mercurial.wxs Thu Apr 18 23:46:26 2013 -0500 @@ -16,7 +16,7 @@ <?endif?> <Product Id='*' - Name='Mercurial $(var.Version) ($(var.Platform))' + Name='Mercurial $(var.Version) ($(var.Platform))' UpgradeCode='$(var.ProductUpgradeCode)' Language='1033' Codepage='1252' Version='$(var.Version)' Manufacturer='Matt Mackall and others'>
--- a/contrib/zsh_completion Thu Apr 04 16:28:19 2013 -0500 +++ b/contrib/zsh_completion Thu Apr 18 23:46:26 2013 -0500 @@ -163,21 +163,10 @@ } _hg_labels() { - _hg_tags "$@" - _hg_bookmarks "$@" - _hg_branches "$@" + labels=("${(f)$(_hg_cmd debuglabelcomplete)}") + (( $#labels )) && _describe -t labels 'labels' labels } -_hg_tags() { - typeset -a tags - local tag rev - - _hg_cmd tags | while read tag - do - tags+=(${tag/ #[0-9]#:*}) - done - (( $#tags )) && _describe -t tags 'tags' tags -} _hg_bookmarks() { typeset -a bookmark bookmarks @@ -508,7 +497,7 @@ '(--user -u)'{-u+,--user}'[record user as commiter]:user:' \ '(--rev -r)'{-r+,--rev}'[revision]:revision:_hg_labels' \ '(--message -m)'{-m+,--message}'[use <text> as commit message]:text:' \ - '(--logfile -l)'{-l+,--logfile}'[read commit message from <file>]:log file:_files -g \*.txt' + '(--logfile -l)'{-l+,--logfile}'[read commit message from <file>]:log file:_files' } _hg_cmd_bisect() { @@ -576,7 +565,7 @@ _arguments -s -w : $_hg_global_opts $_hg_pat_opts $_hg_subrepos_opts \ '(--addremove -A)'{-A,--addremove}'[mark new/missing files as added/removed before committing]' \ '(--message -m)'{-m+,--message}'[use <text> as commit message]:text:' \ - '(--logfile -l)'{-l+,--logfile}'[read commit message from <file>]:log file:_files -g \*.txt' \ + '(--logfile -l)'{-l+,--logfile}'[read commit message from <file>]:log file:_files' \ '(--date -d)'{-d+,--date}'[record datecode as commit date]:date code:' \ '(--user -u)'{-u+,--user}'[record user as commiter]:user:' \ '--amend[amend the parent of the working dir]' \ @@ -940,7 +929,7 @@ _hg_cmd_view() { _arguments -s -w : $_hg_global_opts \ '(--limit -l)'{-l+,--limit}'[limit number of changes displayed]:' \ - ':revision range:_hg_tags' + ':revision range:_hg_labels' } # MQ
--- a/doc/gendoc.py Thu Apr 04 16:28:19 2013 -0500 +++ b/doc/gendoc.py Thu Apr 18 23:46:26 2013 -0500 @@ -5,6 +5,7 @@ sys.path.append(os.path.join('..', 'mercurial', 'pure')) from mercurial import demandimport; demandimport.enable() from mercurial import encoding +from mercurial import minirst from mercurial.commands import table, globalopts from mercurial.i18n import _ from mercurial.help import helptable @@ -63,28 +64,15 @@ return d -def section(ui, s): - ui.write("%s\n%s\n\n" % (s, "\"" * encoding.colwidth(s))) - -def subsection(ui, s): - ui.write("%s\n%s\n\n" % (s, '=' * encoding.colwidth(s))) - -def subsubsection(ui, s): - ui.write("%s\n%s\n\n" % (s, "-" * encoding.colwidth(s))) - -def subsubsubsection(ui, s): - ui.write("%s\n%s\n\n" % (s, "." * encoding.colwidth(s))) - - def show_doc(ui): # print options - section(ui, _("Options")) + ui.write(minirst.section(_("Options"))) for optstr, desc in get_opts(globalopts): ui.write("%s\n %s\n\n" % (optstr, desc)) # print cmds - section(ui, _("Commands")) - commandprinter(ui, table, subsection) + ui.write(minirst.section(_("Commands"))) + commandprinter(ui, table, minirst.subsection) # print topics for names, sec, doc in helptable: @@ -95,13 +83,13 @@ for name in names: ui.write(".. _%s:\n" % name) ui.write("\n") - section(ui, sec) + ui.write(minirst.section(sec)) if util.safehasattr(doc, '__call__'): doc = doc() ui.write(doc) ui.write("\n") - section(ui, _("Extensions")) + ui.write(minirst.section(_("Extensions"))) ui.write(_("This section contains help for extensions that are " "distributed together with Mercurial. Help for other " "extensions is available in the help system.")) @@ -113,12 +101,12 @@ for extensionname in sorted(allextensionnames()): mod = extensions.load(None, extensionname, None) - subsection(ui, extensionname) + ui.write(minirst.subsection(extensionname)) ui.write("%s\n\n" % mod.__doc__) cmdtable = getattr(mod, 'cmdtable', None) if cmdtable: - subsubsection(ui, _('Commands')) - commandprinter(ui, cmdtable, subsubsubsection) + ui.write(minirst.subsubsection(_('Commands'))) + commandprinter(ui, cmdtable, minirst.subsubsubsection) def commandprinter(ui, cmdtable, sectionfunc): h = {} @@ -133,7 +121,7 @@ if f.startswith("debug"): continue d = get_cmd(h[f], cmdtable) - sectionfunc(ui, d['cmd']) + ui.write(sectionfunc(d['cmd'])) # synopsis ui.write("::\n\n") synopsislines = d['synopsis'].splitlines()
--- a/doc/hg.1.txt Thu Apr 04 16:28:19 2013 -0500 +++ b/doc/hg.1.txt Thu Apr 18 23:46:26 2013 -0500 @@ -84,7 +84,7 @@ This file can be used to define local tags which are not shared among repositories. The file format is the same as for ``.hgtags``, but it is encoded using the local system encoding. - + Some commands (e.g. revert) produce backup files ending in ``.orig``, if the ``.orig`` file already exists and is not tracked by Mercurial, it will be overwritten.
--- a/doc/style.css Thu Apr 04 16:28:19 2013 -0500 +++ b/doc/style.css Thu Apr 18 23:46:26 2013 -0500 @@ -303,7 +303,7 @@ div.contents.local { -moz-column-width: 10em; -moz-column-gap: 1em; - + -webkit-column-width: 10em; -webkit-column-gap: 1em; }
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/hgext/blackbox.py Thu Apr 18 23:46:26 2013 -0500 @@ -0,0 +1,157 @@ +# blackbox.py - log repository events to a file for post-mortem debugging +# +# Copyright 2010 Nicolas Dumazet +# Copyright 2013 Facebook, Inc. +# +# This software may be used and distributed according to the terms of the +# GNU General Public License version 2 or any later version. + +"""log repository events to a blackbox for debugging + +Logs event information to .hg/blackbox.log to help debug and diagnose problems. +The events that get logged can be configured via the blackbox.track config key. +Examples: + + [blackbox] + track = * + + [blackbox] + track = command, commandfinish, commandexception, exthook, pythonhook + + [blackbox] + track = incoming + + [blackbox] + # limit the size of a log file + maxsize = 1.5 MB + # rotate up to N log files when the current one gets too big + maxfiles = 3 + +""" + +from mercurial import util, cmdutil +from mercurial.i18n import _ +import errno, os, re + +cmdtable = {} +command = cmdutil.command(cmdtable) +testedwith = 'internal' +lastblackbox = None + +def wrapui(ui): + class blackboxui(ui.__class__): + @util.propertycache + def track(self): + return self.configlist('blackbox', 'track', ['*']) + + def _openlogfile(self): + def rotate(oldpath, newpath): + try: + os.unlink(newpath) + except OSError, err: + if err.errno != errno.ENOENT: + self.debug("warning: cannot remove '%s': %s\n" % + (newpath, err.strerror)) + try: + if newpath: + os.rename(oldpath, newpath) + except OSError, err: + if err.errno != errno.ENOENT: + self.debug("warning: cannot rename '%s' to '%s': %s\n" % + (newpath, oldpath, err.strerror)) + + fp = self._bbopener('blackbox.log', 'a') + maxsize = self.configbytes('blackbox', 'maxsize', 1048576) + if maxsize > 0: + st = os.fstat(fp.fileno()) + if st.st_size >= maxsize: + path = fp.name + fp.close() + maxfiles = self.configint('blackbox', 'maxfiles', 7) + for i in xrange(maxfiles - 1, 1, -1): + rotate(oldpath='%s.%d' % (path, i - 1), + newpath='%s.%d' % (path, i)) + rotate(oldpath=path, + newpath=maxfiles > 0 and path + '.1') + fp = self._bbopener('blackbox.log', 'a') + return fp + + def log(self, event, *msg, **opts): + global lastblackbox + super(blackboxui, self).log(event, *msg, **opts) + + if not '*' in self.track and not event in self.track: + return + + if util.safehasattr(self, '_blackbox'): + blackbox = self._blackbox + elif util.safehasattr(self, '_bbopener'): + try: + self._blackbox = self._openlogfile() + except (IOError, OSError), err: + self.debug('warning: cannot write to blackbox.log: %s\n' % + err.strerror) + del self._bbopener + self._blackbox = None + blackbox = self._blackbox + else: + # certain ui instances exist outside the context of + # a repo, so just default to the last blackbox that + # was seen. + blackbox = lastblackbox + + if blackbox: + date = util.datestr(None, '%Y/%m/%d %H:%M:%S') + user = util.getuser() + formattedmsg = msg[0] % msg[1:] + try: + blackbox.write('%s %s> %s' % (date, user, formattedmsg)) + except IOError, err: + self.debug('warning: cannot write to blackbox.log: %s\n' % + err.strerror) + lastblackbox = blackbox + + def setrepo(self, repo): + self._bbopener = repo.opener + + ui.__class__ = blackboxui + +def uisetup(ui): + wrapui(ui) + +def reposetup(ui, repo): + # During 'hg pull' a httppeer repo is created to represent the remote repo. + # It doesn't have a .hg directory to put a blackbox in, so we don't do + # the blackbox setup for it. + if not repo.local(): + return + + ui.setrepo(repo) + +@command('^blackbox', + [('l', 'limit', 10, _('the number of events to show')), + ], + _('hg blackbox [OPTION]...')) +def blackbox(ui, repo, *revs, **opts): + '''view the recent repository events + ''' + + if not os.path.exists(repo.join('blackbox.log')): + return + + limit = opts.get('limit') + blackbox = repo.opener('blackbox.log', 'r') + lines = blackbox.read().split('\n') + + count = 0 + output = [] + for line in reversed(lines): + if count >= limit: + break + + # count the commands by matching lines like: 2013/01/23 19:13:36 root> + if re.match('^\d{4}/\d{2}/\d{2} \d{2}:\d{2}:\d{2} .*> .*', line): + count += 1 + output.append(line) + + ui.status('\n'.join(reversed(output)))
--- a/hgext/color.py Thu Apr 04 16:28:19 2013 -0500 +++ b/hgext/color.py Thu Apr 18 23:46:26 2013 -0500 @@ -103,7 +103,7 @@ import os from mercurial import commands, dispatch, extensions, ui as uimod, util -from mercurial import templater +from mercurial import templater, error from mercurial.i18n import _ testedwith = 'internal' @@ -379,16 +379,15 @@ return repo.ui.label(thing, label) def uisetup(ui): - global _terminfo_params if ui.plain(): return + if not issubclass(ui.__class__, colorui): + colorui.__bases__ = (ui.__class__,) + ui.__class__ = colorui def colorcmd(orig, ui_, opts, cmd, cmdfunc): mode = _modesetup(ui_, opts) if mode: colorui._colormode = mode - if not issubclass(ui_.__class__, colorui): - colorui.__bases__ = (ui_.__class__,) - ui_.__class__ = colorui extstyles() configstyles(ui_) return orig(ui_, opts, cmd, cmdfunc)
--- a/hgext/convert/__init__.py Thu Apr 04 16:28:19 2013 -0500 +++ b/hgext/convert/__init__.py Thu Apr 18 23:46:26 2013 -0500 @@ -61,6 +61,10 @@ --sourcesort try to preserve source revisions order, only supported by Mercurial sources. + --closesort try to move closed revisions as close as possible + to parent branches, only supported by Mercurial + sources. + If ``REVMAP`` isn't given, it will be put in a default location (``<dest>/.hg/shamap`` by default). The ``REVMAP`` is a simple text file that maps each source commit ID to the destination ID @@ -318,7 +322,8 @@ _('change branch names while converting'), _('FILE')), ('', 'branchsort', None, _('try to sort changesets by branches')), ('', 'datesort', None, _('try to sort changesets by date')), - ('', 'sourcesort', None, _('preserve source changesets order'))], + ('', 'sourcesort', None, _('preserve source changesets order')), + ('', 'closesort', None, _('try to reorder closed revisions'))], _('hg convert [OPTION]... SOURCE [DEST [REVMAP]]')), "debugsvnlog": (debugsvnlog,
--- a/hgext/convert/common.py Thu Apr 04 16:28:19 2013 -0500 +++ b/hgext/convert/common.py Thu Apr 18 23:46:26 2013 -0500 @@ -145,6 +145,11 @@ """ return False + def hasnativeclose(self): + """Return true if this source has ability to close branch. + """ + return False + def lookuprev(self, rev): """If rev is a meaningful revision reference in source, return the referenced identifier in the same format used by getcommit().
--- a/hgext/convert/convcmd.py Thu Apr 04 16:28:19 2013 -0500 +++ b/hgext/convert/convcmd.py Thu Apr 18 23:46:26 2013 -0500 @@ -227,6 +227,14 @@ return sorted(nodes, key=keyfn)[0] return picknext + def makeclosesorter(): + """Close order sort.""" + keyfn = lambda n: ('close' not in self.commitcache[n].extra, + self.commitcache[n].sortkey) + def picknext(nodes): + return sorted(nodes, key=keyfn)[0] + return picknext + def makedatesorter(): """Sort revisions by date.""" dates = {} @@ -246,6 +254,8 @@ picknext = makedatesorter() elif sortmode == 'sourcesort': picknext = makesourcesorter() + elif sortmode == 'closesort': + picknext = makeclosesorter() else: raise util.Abort(_('unknown sort mode: %s') % sortmode) @@ -446,13 +456,15 @@ shutil.rmtree(path, True) raise - sortmodes = ('branchsort', 'datesort', 'sourcesort') + sortmodes = ('branchsort', 'datesort', 'sourcesort', 'closesort') sortmode = [m for m in sortmodes if opts.get(m)] if len(sortmode) > 1: raise util.Abort(_('more than one sort mode specified')) sortmode = sortmode and sortmode[0] or defaultsort if sortmode == 'sourcesort' and not srcc.hasnativeorder(): raise util.Abort(_('--sourcesort is not supported by this data source')) + if sortmode == 'closesort' and not srcc.hasnativeclose(): + raise util.Abort(_('--closesort is not supported by this data source')) fmap = opts.get('filemap') if fmap:
--- a/hgext/convert/cvsps.py Thu Apr 04 16:28:19 2013 -0500 +++ b/hgext/convert/cvsps.py Thu Apr 18 23:46:26 2013 -0500 @@ -508,9 +508,15 @@ ui.status(_('creating changesets\n')) + # try to order commitids by date + mindate = {} + for e in log: + if e.commitid: + mindate[e.commitid] = min(e.date, mindate.get(e.commitid)) + # Merge changesets - log.sort(key=lambda x: (x.commitid, x.comment, x.author, x.branch, x.date, - x.branchpoints)) + log.sort(key=lambda x: (mindate.get(x.commitid), x.commitid, x.comment, + x.author, x.branch, x.date, x.branchpoints)) changesets = [] files = set()
--- a/hgext/convert/git.py Thu Apr 04 16:28:19 2013 -0500 +++ b/hgext/convert/git.py Thu Apr 18 23:46:26 2013 -0500 @@ -6,6 +6,7 @@ # GNU General Public License version 2 or any later version. import os +import subprocess from mercurial import util, config from mercurial.node import hex, nullid from mercurial.i18n import _ @@ -29,13 +30,15 @@ # cannot remove environment variable. Just assume none have # both issues. if util.safehasattr(os, 'unsetenv'): - def gitopen(self, s, noerr=False): + def gitopen(self, s, err=None): prevgitdir = os.environ.get('GIT_DIR') os.environ['GIT_DIR'] = self.path try: - if noerr: + if err == subprocess.PIPE: (stdin, stdout, stderr) = util.popen3(s) return stdout + elif err == subprocess.STDOUT: + return self.popen_with_stderr(s) else: return util.popen(s, 'rb') finally: @@ -44,13 +47,25 @@ else: os.environ['GIT_DIR'] = prevgitdir else: - def gitopen(self, s, noerr=False): - if noerr: + def gitopen(self, s, err=None): + if err == subprocess.PIPE: (sin, so, se) = util.popen3('GIT_DIR=%s %s' % (self.path, s)) return so + elif err == subprocess.STDOUT: + return self.popen_with_stderr(s) else: return util.popen('GIT_DIR=%s %s' % (self.path, s), 'rb') + def popen_with_stderr(self, s): + p = subprocess.Popen(s, shell=True, bufsize=-1, + close_fds=util.closefds, + stdin=subprocess.PIPE, + stdout=subprocess.PIPE, + stderr=subprocess.STDOUT, + universal_newlines=False, + env=None) + return p.stdout + def gitread(self, s): fh = self.gitopen(s) data = fh.read() @@ -209,12 +224,15 @@ def gettags(self): tags = {} alltags = {} - fh = self.gitopen('git ls-remote --tags "%s"' % self.path) + fh = self.gitopen('git ls-remote --tags "%s"' % self.path, + err=subprocess.STDOUT) prefix = 'refs/tags/' # Build complete list of tags, both annotated and bare ones for line in fh: line = line.strip() + if line.startswith("error:") or line.startswith("fatal:"): + raise util.Abort(_('cannot read tags from %s') % self.path) node, tag = line.split(None, 1) if not tag.startswith(prefix): continue @@ -266,7 +284,7 @@ # Origin heads for reftype in gitcmd: try: - fh = self.gitopen(gitcmd[reftype], noerr=True) + fh = self.gitopen(gitcmd[reftype], err=subprocess.PIPE) for line in fh: line = line.strip() rev, name = line.split(None, 1)
--- a/hgext/convert/hg.py Thu Apr 04 16:28:19 2013 -0500 +++ b/hgext/convert/hg.py Thu Apr 18 23:46:26 2013 -0500 @@ -386,6 +386,9 @@ def hasnativeorder(self): return True + def hasnativeclose(self): + return True + def lookuprev(self, rev): try: return hex(self.repo.lookup(rev))
--- a/hgext/factotum.py Thu Apr 04 16:28:19 2013 -0500 +++ b/hgext/factotum.py Thu Apr 18 23:46:26 2013 -0500 @@ -47,8 +47,8 @@ from mercurial.i18n import _ from mercurial.url import passwordmgr -from mercurial import httpconnection, urllib2, util -import os +from mercurial import httpconnection, util +import os, urllib2 ERRMAX = 128
--- a/hgext/hgk.py Thu Apr 04 16:28:19 2013 -0500 +++ b/hgext/hgk.py Thu Apr 18 23:46:26 2013 -0500 @@ -114,7 +114,8 @@ 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())) + ui.write(("branch %s\n" % ctx.branch())) + ui.write(("phase %s\n\n" % ctx.phasestr())) if prefix != "": ui.write("%s%s\n" % (prefix,
--- a/hgext/histedit.py Thu Apr 04 16:28:19 2013 -0500 +++ b/hgext/histedit.py Thu Apr 18 23:46:26 2013 -0500 @@ -143,6 +143,7 @@ except ImportError: import pickle import os +import sys from mercurial import cmdutil from mercurial import discovery @@ -179,7 +180,7 @@ def commitfuncfor(repo, src): """Build a commit function for the replacement of <src> - This function ensure we apply the same treatement to all changesets. + This function ensure we apply the same treatment to all changesets. - Add a 'histedit_source' entry in extra. @@ -301,8 +302,8 @@ hg.update(repo, ctx.node()) stats = applychanges(ui, repo, oldctx, opts) if stats and stats[3] > 0: - raise util.Abort(_('Fix up the change and run ' - 'hg histedit --continue')) + raise error.InterventionRequired(_('Fix up the change and run ' + 'hg histedit --continue')) # drop the second merge parent commit = commitfuncfor(repo, oldctx) n = commit(text=oldctx.description(), user=oldctx.user(), @@ -319,17 +320,17 @@ oldctx = repo[ha] hg.update(repo, ctx.node()) applychanges(ui, repo, oldctx, opts) - raise util.Abort(_('Make changes as needed, you may commit or record as ' - 'needed now.\nWhen you are finished, run hg' - ' histedit --continue to resume.')) + raise error.InterventionRequired( + _('Make changes as needed, you may commit or record as needed now.\n' + 'When you are finished, run hg histedit --continue to resume.')) def fold(ui, repo, ctx, ha, opts): oldctx = repo[ha] hg.update(repo, ctx.node()) stats = applychanges(ui, repo, oldctx, opts) if stats and stats[3] > 0: - raise util.Abort(_('Fix up the change and run ' - 'hg histedit --continue')) + raise error.InterventionRequired( + _('Fix up the change and run hg histedit --continue')) n = repo.commit(text='fold-temp-revision %s' % ha, user=oldctx.user(), date=oldctx.date(), extra=oldctx.extra()) if n is None: @@ -390,8 +391,8 @@ hg.update(repo, ctx.node()) stats = applychanges(ui, repo, oldctx, opts) if stats and stats[3] > 0: - raise util.Abort(_('Fix up the change and run ' - 'hg histedit --continue')) + raise error.InterventionRequired( + _('Fix up the change and run hg histedit --continue')) message = oldctx.description() + '\n' message = ui.edit(message, ui.username()) commit = commitfuncfor(repo, oldctx) @@ -403,6 +404,29 @@ # We didn't make an edit, so just indicate no replaced nodes return newctx, [] +def findoutgoing(ui, repo, remote=None, force=False, opts={}): + """utility function to find the first outgoing changeset + + Used by initialisation code""" + dest = ui.expandpath(remote or 'default-push', remote or 'default') + dest, revs = hg.parseurl(dest, None)[:2] + ui.status(_('comparing with %s\n') % util.hidepassword(dest)) + + revs, checkout = hg.addbranchrevs(repo, repo, revs, None) + other = hg.peer(repo, opts, dest) + + if revs: + revs = [repo.lookup(rev) for rev in revs] + + # hexlify nodes from outgoing, because we're going to parse + # parent[0] using revsingle below, and if the binary hash + # contains special revset characters like ":" the revset + # parser can choke. + outgoing = discovery.findcommonoutgoing(repo, other, revs, force=force) + if not outgoing.missing: + raise util.Abort(_('no outgoing ancestors')) + return outgoing.missing[0] + actiontable = {'p': pick, 'pick': pick, 'e': edit, @@ -427,7 +451,7 @@ _('force outgoing even for unrelated repositories')), ('r', 'rev', [], _('first revision to be edited'))], _("[PARENT]")) -def histedit(ui, repo, *parent, **opts): +def histedit(ui, repo, *freeargs, **opts): """interactively edit changeset history """ # TODO only abort if we try and histedit mq patches, not just @@ -436,45 +460,48 @@ if mq and mq.applied: raise util.Abort(_('source has mq patches applied')) - parent = list(parent) + opts.get('rev', []) - if opts.get('outgoing'): - if len(parent) > 1: - raise util.Abort( - _('only one repo argument allowed with --outgoing')) - elif parent: - parent = parent[0] - - dest = ui.expandpath(parent or 'default-push', parent or 'default') - dest, revs = hg.parseurl(dest, None)[:2] - ui.status(_('comparing with %s\n') % util.hidepassword(dest)) - - revs, checkout = hg.addbranchrevs(repo, repo, revs, None) - other = hg.peer(repo, opts, dest) + # basic argument incompatibility processing + outg = opts.get('outgoing') + cont = opts.get('continue') + abort = opts.get('abort') + force = opts.get('force') + rules = opts.get('commands', '') + revs = opts.get('rev', []) + goal = 'new' # This invocation goal, in new, continue, abort + if force and not outg: + raise util.Abort(_('--force only allowed with --outgoing')) + if cont: + if util.any((outg, abort, revs, freeargs, rules)): + raise util.Abort(_('no arguments allowed with --continue')) + goal = 'continue' + elif abort: + if util.any((outg, revs, freeargs, rules)): + raise util.Abort(_('no arguments allowed with --abort')) + goal = 'abort' + else: + if os.path.exists(os.path.join(repo.path, 'histedit-state')): + raise util.Abort(_('history edit already in progress, try ' + '--continue or --abort')) + if outg: + if revs: + raise util.Abort(_('no revisions allowed with --outgoing')) + if len(freeargs) > 1: + raise util.Abort( + _('only one repo argument allowed with --outgoing')) + else: + revs.extend(freeargs) + if len(revs) != 1: + raise util.Abort( + _('histedit requires exactly one parent revision')) - if revs: - revs = [repo.lookup(rev) for rev in revs] - # hexlify nodes from outgoing, because we're going to parse - # parent[0] using revsingle below, and if the binary hash - # contains special revset characters like ":" the revset - # parser can choke. - parent = [node.hex(n) for n in discovery.findcommonoutgoing( - repo, other, [], force=opts.get('force')).missing[0:1]] - else: - if opts.get('force'): - raise util.Abort(_('--force only allowed with --outgoing')) - - if opts.get('continue', False): - if len(parent) != 0: - raise util.Abort(_('no arguments allowed with --continue')) + if goal == 'continue': (parentctxnode, rules, keep, topmost, replacements) = readstate(repo) currentparent, wantnull = repo.dirstate.parents() parentctx = repo[parentctxnode] parentctx, repl = bootstrapcontinue(ui, repo, parentctx, rules, opts) replacements.extend(repl) - elif opts.get('abort', False): - if len(parent) != 0: - raise util.Abort(_('no arguments allowed with --abort')) + elif goal == 'abort': (parentctxnode, rules, keep, topmost, replacements) = readstate(repo) mapping, tmpnodes, leafs, _ntm = processreplacement(repo, replacements) ui.debug('restore wc to old parent %s\n' % node.short(topmost)) @@ -485,28 +512,29 @@ return else: cmdutil.bailifchanged(repo) - if os.path.exists(os.path.join(repo.path, 'histedit-state')): - raise util.Abort(_('history edit already in progress, try ' - '--continue or --abort')) topmost, empty = repo.dirstate.parents() - - if len(parent) != 1: - raise util.Abort(_('histedit requires exactly one parent revision')) - parent = scmutil.revsingle(repo, parent[0]).node() + if outg: + if freeargs: + remote = freeargs[0] + else: + remote = None + root = findoutgoing(ui, repo, remote, force, opts) + else: + root = revs[0] + root = scmutil.revsingle(repo, root).node() keep = opts.get('keep', False) - revs = between(repo, parent, topmost, keep) + revs = between(repo, root, topmost, keep) if not revs: - ui.warn(_('nothing to edit\n')) - return 1 + raise util.Abort(_('%s is not an ancestor of working directory') % + node.short(root)) ctxs = [repo[r] for r in revs] - rules = opts.get('commands', '') if not rules: rules = '\n'.join([makedesc(c) for c in ctxs]) rules += '\n\n' - rules += editcomment % (node.short(parent), node.short(topmost)) + rules += editcomment % (node.short(root), node.short(topmost)) rules = ui.edit(rules, ui.username()) # Save edit rules in .hg/histedit-last-edit.txt in case # the user needs to ask for help after something @@ -515,14 +543,17 @@ f.write(rules) f.close() else: - f = open(rules) + if rules == '-': + f = sys.stdin + else: + f = open(rules) rules = f.read() f.close() rules = [l for l in (r.strip() for r in rules.splitlines()) if l and not l[0] == '#'] rules = verifyrules(rules, repo, ctxs) - parentctx = repo[parent].parents()[0] + parentctx = repo[root].parents()[0] keep = opts.get('keep', False) replacements = [] @@ -580,14 +611,15 @@ # note: does not take non linear new change in account (but previous # implementation didn't used them anyway (issue3655) newchildren = [c.node() for c in repo.set('(%d::.)', parentctx)] - if not newchildren: - # `parentctxnode` should match but no result. This means that - # currentnode is not a descendant from parentctxnode. - msg = _('working directory parent is not a descendant of %s') - hint = _('update to %s or descendant and run "hg histedit ' - '--continue" again') % parentctx - raise util.Abort(msg % parentctx, hint=hint) - newchildren.pop(0) # remove parentctxnode + if parentctx.node() != node.nullid: + if not newchildren: + # `parentctxnode` should match but no result. This means that + # currentnode is not a descendant from parentctxnode. + msg = _('%s is not an ancestor of working directory') + hint = _('update to %s or descendant and run "hg histedit ' + '--continue" again') % parentctx + raise util.Abort(msg % parentctx, hint=hint) + newchildren.pop(0) # remove parentctxnode # Commit dirty working directory if necessary new = None m, a, r, d = repo.status()[:4] @@ -617,16 +649,22 @@ replacements.append((ctx.node(), tuple(newchildren))) if action in ('f', 'fold'): - # finalize fold operation if applicable - if new is None: - new = newchildren[-1] + if newchildren: + # finalize fold operation if applicable + if new is None: + new = newchildren[-1] + else: + newchildren.pop() # remove new from internal changes + parentctx, repl = finishfold(ui, repo, parentctx, ctx, new, opts, + newchildren) + replacements.extend(repl) else: - newchildren.pop() # remove new from internal changes - parentctx, repl = finishfold(ui, repo, parentctx, ctx, new, opts, - newchildren) - replacements.extend(repl) + # newchildren is empty if the fold did not result in any commit + # this happen when all folded change are discarded during the + # merge. + replacements.append((ctx.node(), (parentctx.node(),))) elif newchildren: - # otherwize update "parentctx" before proceding to further operation + # otherwise update "parentctx" before proceeding to further operation parentctx = repo[newchildren[-1]] return parentctx, replacements @@ -678,25 +716,30 @@ or a rule on a changeset outside of the user-given range. """ parsed = [] - if len(rules) != len(ctxs): - raise util.Abort(_('must specify a rule for each changeset once')) + expected = set(str(c) for c in ctxs) + seen = set() for r in rules: if ' ' not in r: raise util.Abort(_('malformed line "%s"') % r) action, rest = r.split(' ', 1) - if ' ' in rest.strip(): - ha, rest = rest.split(' ', 1) - else: - ha = r.strip() + ha = rest.strip().split(' ', 1)[0] try: - if repo[ha] not in ctxs: - raise util.Abort( - _('may not use changesets other than the ones listed')) + ha = str(repo[ha]) # ensure its a short hash except error.RepoError: raise util.Abort(_('unknown changeset %s listed') % ha) + if ha not in expected: + raise util.Abort( + _('may not use changesets other than the ones listed')) + if ha in seen: + raise util.Abort(_('duplicated command for changeset %s') % ha) + seen.add(ha) if action not in actiontable: raise util.Abort(_('unknown action "%s"') % action) parsed.append([action, ha]) + missing = sorted(expected - seen) # sort to stabilize output + if missing: + raise util.Abort(_('missing rules for changeset %s') % missing[0], + hint=_('do you want to use the drop action?')) return parsed def processreplacement(repo, replacements):
--- a/hgext/keyword.py Thu Apr 04 16:28:19 2013 -0500 +++ b/hgext/keyword.py Thu Apr 18 23:46:26 2013 -0500 @@ -384,7 +384,7 @@ fn = 'demo.txt' tmpdir = tempfile.mkdtemp('', 'kwdemo.') ui.note(_('creating temporary repository at %s\n') % tmpdir) - repo = localrepo.localrepository(ui, tmpdir, True) + repo = localrepo.localrepository(repo.baseui, tmpdir, True) ui.setconfig('keyword', fn, '') svn = ui.configbool('keywordset', 'svn') # explicitly set keywordset for demo output
--- a/hgext/largefiles/__init__.py Thu Apr 04 16:28:19 2013 -0500 +++ b/hgext/largefiles/__init__.py Thu Apr 18 23:46:26 2013 -0500 @@ -41,11 +41,30 @@ enabled for this to work. When you pull a changeset that affects largefiles from a remote -repository, Mercurial behaves as normal. However, when you update to -such a revision, any largefiles needed by that revision are downloaded -and cached (if they have never been downloaded before). This means -that network access may be required to update to changesets you have -not previously updated to. +repository, the largefiles for the changeset will by default not be +pulled down. However, when you update to such a revision, any +largefiles needed by that revision are downloaded and cached (if +they have never been downloaded before). One way to pull largefiles +when pulling is thus to use --update, which will update your working +copy to the latest pulled revision (and thereby downloading any new +largefiles). + +If you want to pull largefiles you don't need for update yet, then +you can use pull with the `--lfrev` option or the :hg:`lfpull` command. + +If you know you are pulling from a non-default location and want do +download all the largefiles that corresponds to the new changesets at +the same time, then you can pull with `--lfrev "pulled()"`. + +If you just want to ensure that you will have the largefiles needed to +merge or rebase with new heads that you are pulling, then you can pull +with `--lfrev "head(pulled())"` flag to pre-emptively download any largefiles +that are new in the heads you are pulling. + +Keep in mind that network access may now be required to update to +changesets that you have not previously updated to. The nature of the +largefiles extension means that updating is no longer guaranteed to +be a local-only operation. If you already have large files tracked by Mercurial without the largefiles extension, you will need to convert your repository in
--- a/hgext/largefiles/basestore.py Thu Apr 04 16:28:19 2013 -0500 +++ b/hgext/largefiles/basestore.py Thu Apr 18 23:46:26 2013 -0500 @@ -8,7 +8,6 @@ '''base class for store implementations and store-related utility code''' -import binascii import re from mercurial import util, node, hg @@ -39,11 +38,12 @@ self.url = url def put(self, source, hash): - '''Put source file into the store under <filename>/<hash>.''' + '''Put source file into the store so it can be retrieved by hash.''' raise NotImplementedError('abstract method') def exists(self, hashes): - '''Check to see if the store contains the given hashes.''' + '''Check to see if the store contains the given hashes. Given an + iterable of hashes it returns a mapping from hash to bool.''' raise NotImplementedError('abstract method') def get(self, files): @@ -62,22 +62,29 @@ util.makedirs(lfutil.storepath(self.repo, '')) at = 0 + available = self.exists(set(hash for (_filename, hash) in files)) for filename, hash in files: ui.progress(_('getting largefiles'), at, unit='lfile', total=len(files)) at += 1 ui.note(_('getting %s:%s\n') % (filename, hash)) + if not available.get(hash): + ui.warn(_('%s: largefile %s not available from %s\n') + % (filename, hash, self.url)) + missing.append(filename) + continue + storefilename = lfutil.storepath(self.repo, hash) tmpfile = util.atomictempfile(storefilename + '.tmp', createmode=self.repo.store.createmode) try: - hhash = binascii.hexlify(self._getfile(tmpfile, filename, hash)) + hhash = self._getfile(tmpfile, filename, hash) except StoreError, err: ui.warn(err.longmessage()) hhash = "" - tmpfile.close() # has probably already been closed! + tmpfile.close() if hhash != hash: if hhash != "": @@ -98,10 +105,10 @@ '''Verify the existence (and, optionally, contents) of every big file revision referenced by every changeset in revs. Return 0 if all is well, non-zero on any errors.''' - write = self.ui.write failed = False - write(_('searching %d changesets for largefiles\n') % len(revs)) + self.ui.status(_('searching %d changesets for largefiles\n') % + len(revs)) verified = set() # set of (filename, filenode) tuples for rev in revs: @@ -115,24 +122,30 @@ numrevs = len(verified) numlfiles = len(set([fname for (fname, fnode) in verified])) if contents: - write(_('verified contents of %d revisions of %d largefiles\n') - % (numrevs, numlfiles)) + self.ui.status( + _('verified contents of %d revisions of %d largefiles\n') + % (numrevs, numlfiles)) else: - write(_('verified existence of %d revisions of %d largefiles\n') - % (numrevs, numlfiles)) - + self.ui.status( + _('verified existence of %d revisions of %d largefiles\n') + % (numrevs, numlfiles)) return int(failed) def _getfile(self, tmpfile, filename, hash): '''Fetch one revision of one file from the store and write it to tmpfile. Compute the hash of the file on-the-fly as it - downloads and return the binary hash. Close tmpfile. Raise + downloads and return the hash. Close tmpfile. Raise StoreError if unable to download the file (e.g. it does not exist in the store).''' raise NotImplementedError('abstract method') def _verifyfile(self, cctx, cset, contents, standin, verified): '''Perform the actual verification of a file in the store. + 'cset' is only used in warnings. + 'contents' controls verification of content hash. + 'standin' is the standin path of the largefile to verify. + 'verified' is maintained as a set of already verified files. + Returns _true_ if it is a standin and any problems are found! ''' raise NotImplementedError('abstract method')
--- a/hgext/largefiles/lfcommands.py Thu Apr 04 16:28:19 2013 -0500 +++ b/hgext/largefiles/lfcommands.py Thu Apr 18 23:46:26 2013 -0500 @@ -368,9 +368,9 @@ ui.progress(_('uploading largefiles'), None) def verifylfiles(ui, repo, all=False, contents=False): - '''Verify that every big file revision in the current changeset + '''Verify that every largefile revision in the current changeset exists in the central store. With --contents, also verify that - the contents of each big file revision are correct (SHA-1 hash + the contents of each local largefile file revision are correct (SHA-1 hash matches the revision ID). With --all, check every changeset in this repository.''' if all: @@ -530,22 +530,40 @@ lfdirstate.drop(lfile) return ret -def catlfile(repo, lfile, rev, filename): - hash = lfutil.readstandin(repo, lfile, rev) - if not lfutil.inusercache(repo.ui, hash): - store = basestore._openstore(repo) - success, missing = store.get([(lfile, hash)]) - if len(success) != 1: - raise util.Abort( - _('largefile %s is not in cache and could not be downloaded') - % lfile) - path = lfutil.usercachepath(repo.ui, hash) - fpout = cmdutil.makefileobj(repo, filename) - fpin = open(path, "rb") - fpout.write(fpin.read()) - fpout.close() - fpin.close() - return 0 +def lfpull(ui, repo, source="default", **opts): + """pull largefiles for the specified revisions from the specified source + + Pull largefiles that are referenced from local changesets but missing + locally, pulling from a remote repository to the local cache. + + If SOURCE is omitted, the 'default' path will be used. + See :hg:`help urls` for more information. + + .. container:: verbose + + Some examples: + + - pull largefiles for all branch heads:: + + hg lfpull -r "head() and not closed()" + + - pull largefiles on the default branch:: + + hg lfpull -r "branch(default)" + """ + repo.lfpullsource = source + + revs = opts.get('rev', []) + if not revs: + raise util.Abort(_('no revisions specified')) + revs = scmutil.revrange(repo, revs) + + numcached = 0 + for rev in revs: + ui.note(_('pulling largefiles for revision %s\n') % rev) + (cached, missing) = cachelfiles(ui, repo, rev) + numcached += len(cached) + ui.status(_("%d largefiles cached\n") % numcached) # -- hg commands declarations ------------------------------------------------ @@ -559,6 +577,11 @@ _('convert from a largefiles repo to a normal repo')), ], _('hg lfconvert SOURCE DEST [FILE ...]')), + 'lfpull': (lfpull, + [('r', 'rev', [], _('pull largefiles for these revisions')) + ] + commands.remoteopts, + _('-r REV... [-e CMD] [--remotecmd CMD] [SOURCE]') + ), } commands.inferrepo += " lfconvert"
--- a/hgext/largefiles/lfutil.py Thu Apr 04 16:28:19 2013 -0500 +++ b/hgext/largefiles/lfutil.py Thu Apr 18 23:46:26 2013 -0500 @@ -9,7 +9,6 @@ '''largefiles utility code: must not import other modules in this package.''' import os -import errno import platform import shutil import stat @@ -39,6 +38,7 @@ return lfsize def link(src, dest): + util.makedirs(os.path.dirname(dest)) try: util.oslink(src, dest) except OSError: @@ -86,7 +86,6 @@ elif inusercache(repo.ui, hash): repo.ui.note(_('found %s in system cache\n') % hash) path = storepath(repo, hash) - util.makedirs(os.path.dirname(path)) link(usercachepath(repo.ui, hash), path) return path return None @@ -127,14 +126,7 @@ matcher = getstandinmatcher(repo) for standin in repo.dirstate.walk(matcher, [], False, False): lfile = splitstandin(standin) - hash = readstandin(repo, lfile) lfdirstate.normallookup(lfile) - try: - if hash == hashfile(repo.wjoin(lfile)): - lfdirstate.normal(lfile) - except OSError, err: - if err.errno != errno.ENOENT: - raise return lfdirstate def lfdirstatestatus(lfdirstate, repo, rev): @@ -203,10 +195,10 @@ def copytostoreabsolute(repo, file, hash): - util.makedirs(os.path.dirname(storepath(repo, hash))) if inusercache(repo.ui, hash): link(usercachepath(repo.ui, hash), storepath(repo, hash)) elif not getattr(repo, "_isconverting", False): + util.makedirs(os.path.dirname(storepath(repo, hash))) dst = util.atomictempfile(storepath(repo, hash), createmode=repo.store.createmode) for chunk in util.filechunkiter(open(file, 'rb')): @@ -217,7 +209,6 @@ def linktousercache(repo, hash): path = usercachepath(repo.ui, hash) if path: - util.makedirs(os.path.dirname(path)) link(storepath(repo, hash), path) def getstandinmatcher(repo, pats=[], opts={}): @@ -290,19 +281,12 @@ def copyandhash(instream, outfile): '''Read bytes from instream (iterable) and write them to outfile, - computing the SHA-1 hash of the data along the way. Close outfile - when done and return the binary hash.''' + computing the SHA-1 hash of the data along the way. Return the hash.''' hasher = util.sha1('') for data in instream: hasher.update(data) outfile.write(data) - - # Blecch: closing a file that somebody else opened is rude and - # wrong. But it's so darn convenient and practical! After all, - # outfile was opened just to copy and hash. - outfile.close() - - return hasher.digest() + return hasher.hexdigest() def hashrepofile(repo, file): return hashfile(repo.wjoin(file)) @@ -312,36 +296,11 @@ return '' hasher = util.sha1('') fd = open(file, 'rb') - for data in blockstream(fd): + for data in util.filechunkiter(fd, 128 * 1024): hasher.update(data) fd.close() return hasher.hexdigest() -class limitreader(object): - def __init__(self, f, limit): - self.f = f - self.limit = limit - - def read(self, length): - if self.limit == 0: - return '' - length = length > self.limit and self.limit or length - self.limit -= length - return self.f.read(length) - - def close(self): - pass - -def blockstream(infile, blocksize=128 * 1024): - """Generator that yields blocks of data from infile and closes infile.""" - while True: - data = infile.read(blocksize) - if not data: - break - yield data - # same blecch as copyandhash() above - infile.close() - def writehash(hash, filename, executable): util.makedirs(os.path.dirname(filename)) util.writefile(filename, hash + '\n') @@ -398,14 +357,6 @@ def __init__(self, storetypes): self.storetypes = storetypes -def getcurrentheads(repo): - branches = repo.branchmap() - heads = [] - for branch in branches: - newheads = repo.branchheads(branch) - heads = heads + newheads - return heads - def getstandinsstate(repo): standins = [] matcher = getstandinmatcher(repo)
--- a/hgext/largefiles/localstore.py Thu Apr 04 16:28:19 2013 -0500 +++ b/hgext/largefiles/localstore.py Thu Apr 18 23:46:26 2013 -0500 @@ -8,9 +8,6 @@ '''store class for local filesystem''' -import os - -from mercurial import util from mercurial.i18n import _ import lfutil @@ -26,11 +23,9 @@ super(localstore, self).__init__(ui, repo, self.remote.url()) def put(self, source, hash): - util.makedirs(os.path.dirname(lfutil.storepath(self.remote, hash))) if lfutil.instore(self.remote, hash): return - lfutil.link(lfutil.storepath(self.repo, hash), - lfutil.storepath(self.remote, hash)) + lfutil.link(source, lfutil.storepath(self.remote, hash)) def exists(self, hashes): retval = {} @@ -40,11 +35,8 @@ def _getfile(self, tmpfile, filename, hash): - if lfutil.instore(self.remote, hash): - path = lfutil.storepath(self.remote, hash) - elif lfutil.inusercache(self.ui, hash): - path = lfutil.usercachepath(self.ui, hash) - else: + path = lfutil.findfile(self.remote, hash) + if not path: raise basestore.StoreError(filename, hash, self.url, _("can't get file locally")) fd = open(path, 'rb') @@ -63,23 +55,19 @@ return False expecthash = fctx.data()[0:40] + storepath = lfutil.storepath(self.remote, expecthash) verified.add(key) if not lfutil.instore(self.remote, expecthash): self.ui.warn( - _('changeset %s: %s missing\n' - ' (looked for hash %s)\n') - % (cset, filename, expecthash)) + _('changeset %s: %s references missing %s\n') + % (cset, filename, storepath)) return True # failed if contents: - storepath = lfutil.storepath(self.remote, expecthash) actualhash = lfutil.hashfile(storepath) if actualhash != expecthash: self.ui.warn( - _('changeset %s: %s: contents differ\n' - ' (%s:\n' - ' expected hash %s,\n' - ' but got %s)\n') - % (cset, filename, storepath, expecthash, actualhash)) + _('changeset %s: %s references corrupted %s\n') + % (cset, filename, storepath)) return True # failed return False
--- a/hgext/largefiles/overrides.py Thu Apr 04 16:28:19 2013 -0500 +++ b/hgext/largefiles/overrides.py Thu Apr 18 23:46:26 2013 -0500 @@ -19,6 +19,7 @@ import lfutil import lfcommands +import basestore # -- Utility functions: commonly/repeatedly needed functionality --------------- @@ -34,6 +35,7 @@ manifest) m._files = filter(notlfile, m._files) m._fmap = set(m._files) + m._always = False origmatchfn = m.matchfn m.matchfn = lambda f: notlfile(f) and origmatchfn(f) or None return m @@ -251,6 +253,7 @@ standins = [lfutil.standin(f) for f in m._files] m._files.extend(standins) m._fmap = set(m._files) + m._always = False origmatchfn = m.matchfn def lfmatchfn(f): lf = lfutil.splitstandin(f) @@ -274,7 +277,7 @@ contents = opts.pop('lfc', False) result = orig(ui, repo, *pats, **opts) - if large: + if large or all or contents: result = result or lfcommands.verifylfiles(ui, repo, all, contents) return result @@ -360,29 +363,35 @@ # Finally, the merge.applyupdates function will then take care of # writing the files into the working copy and lfcommands.updatelfiles # will update the largefiles. -def overridemanifestmerge(origfn, repo, p1, p2, pa, overwrite, partial): - actions = origfn(repo, p1, p2, pa, overwrite, partial) +def overridemanifestmerge(origfn, repo, p1, p2, pa, branchmerge, force, + partial, acceptremote=False): + overwrite = force and not branchmerge + actions = origfn(repo, p1, p2, pa, branchmerge, force, partial, + acceptremote) processed = [] for action in actions: if overwrite: processed.append(action) continue - f, m = action[:2] + f, m, args, msg = action choices = (_('&Largefile'), _('&Normal file')) - if m == "g" and lfutil.splitstandin(f) in p1 and f in p2: + + splitstandin = lfutil.splitstandin(f) + if (m == "g" and splitstandin is not None and + splitstandin in p1 and f in p2): # Case 1: normal file in the working copy, largefile in # the second parent - lfile = lfutil.splitstandin(f) + lfile = splitstandin standin = f msg = _('%s has been turned into a largefile\n' 'use (l)argefile or keep as (n)ormal file?') % lfile if repo.ui.promptchoice(msg, choices, 0) == 0: - processed.append((lfile, "r")) - processed.append((standin, "g", p2.flags(standin))) + processed.append((lfile, "r", None, msg)) + processed.append((standin, "g", (p2.flags(standin),), msg)) else: - processed.append((standin, "r")) + processed.append((standin, "r", None, msg)) elif m == "g" and lfutil.standin(f) in p1 and f in p2: # Case 2: largefile in the working copy, normal file in # the second parent @@ -391,10 +400,10 @@ msg = _('%s has been turned into a normal file\n' 'keep as (l)argefile or use (n)ormal file?') % lfile if repo.ui.promptchoice(msg, choices, 0) == 0: - processed.append((lfile, "r")) + processed.append((lfile, "r", None, msg)) else: - processed.append((standin, "r")) - processed.append((lfile, "g", p2.flags(lfile))) + processed.append((standin, "r", None, msg)) + processed.append((lfile, "g", (p2.flags(lfile),), msg)) else: processed.append(action) @@ -513,6 +522,7 @@ lfile = lambda f: lfutil.standin(f) in manifest m._files = [lfutil.standin(f) for f in m._files if lfile(f)] m._fmap = set(m._files) + m._always = False origmatchfn = m.matchfn m.matchfn = lambda f: (lfutil.isstandin(f) and (f in manifest) and @@ -619,6 +629,7 @@ m._files = [tostandin(f) for f in m._files] m._files = [f for f in m._files if f is not None] m._fmap = set(m._files) + m._always = False origmatchfn = m.matchfn def matchfn(f): if lfutil.isstandin(f): @@ -693,6 +704,9 @@ # working copy def overridepull(orig, ui, repo, source=None, **opts): revsprepull = len(repo) + if not source: + source = 'default' + repo.lfpullsource = source if opts.get('rebase', False): repo._isrebasing = True try: @@ -706,9 +720,6 @@ def _dummy(*args, **kwargs): pass commands.postincoming = _dummy - if not source: - source = 'default' - repo.lfpullsource = source try: result = commands.pull(ui, repo, source, **opts) finally: @@ -719,31 +730,50 @@ finally: repo._isrebasing = False else: - if not source: - source = 'default' - repo.lfpullsource = source - oldheads = lfutil.getcurrentheads(repo) result = orig(ui, repo, source, **opts) - # If we do not have the new largefiles for any new heads we pulled, we - # will run into a problem later if we try to merge or rebase with one of - # these heads, so cache the largefiles now directly into the system - # cache. - ui.status(_("caching new largefiles\n")) + revspostpull = len(repo) + lfrevs = opts.get('lfrev', []) + if opts.get('all_largefiles'): + lfrevs.append('pulled()') + if lfrevs and revspostpull > revsprepull: numcached = 0 - heads = lfutil.getcurrentheads(repo) - newheads = set(heads).difference(set(oldheads)) - for head in newheads: - (cached, missing) = lfcommands.cachelfiles(ui, repo, head) - numcached += len(cached) + repo.firstpulled = revsprepull # for pulled() revset expression + try: + for rev in scmutil.revrange(repo, lfrevs): + ui.note(_('pulling largefiles for revision %s\n') % rev) + (cached, missing) = lfcommands.cachelfiles(ui, repo, rev) + numcached += len(cached) + finally: + del repo.firstpulled ui.status(_("%d largefiles cached\n") % numcached) - if opts.get('all_largefiles'): - revspostpull = len(repo) - revs = [] - for rev in xrange(revsprepull, revspostpull): - revs.append(repo[rev].rev()) - lfcommands.downloadlfiles(ui, repo, revs) return result +def pulledrevsetsymbol(repo, subset, x): + """``pulled()`` + Changesets that just has been pulled. + + Only available with largefiles from pull --lfrev expressions. + + .. container:: verbose + + Some examples: + + - pull largefiles for all new changesets:: + + hg pull -lfrev "pulled()" + + - pull largefiles for all new branch heads:: + + hg pull -lfrev "head(pulled()) and not closed()" + + """ + + try: + firstpulled = repo.firstpulled + except AttributeError: + raise util.Abort(_("pulled() only available in --lfrev")) + return [r for r in subset if r >= firstpulled] + def overrideclone(orig, ui, source, dest=None, **opts): d = dest if d is None: @@ -762,17 +792,6 @@ sourcerepo, destrepo = result repo = destrepo.local() - # The .hglf directory must exist for the standin matcher to match - # anything (which listlfiles uses for each rev), and .hg/largefiles is - # assumed to exist by the code that caches the downloaded file. These - # directories exist if clone updated to any rev. (If the repo does not - # have largefiles, download never gets to the point of needing - # .hg/largefiles, and the standin matcher won't match anything anyway.) - if 'largefiles' in repo.requirements: - if opts.get('noupdate'): - util.makedirs(repo.wjoin(lfutil.shortname)) - util.makedirs(repo.join(lfutil.longname)) - # Caching is implicitly limited to 'rev' option, since the dest repo was # truncated at that point. The user may expect a download count with # this option, so attempt whether or not this is a largefile repo. @@ -1153,13 +1172,37 @@ notbad.add(lf) return origmatchfn(lf) m.matchfn = lfmatchfn - m.bad = lambda f, msg: f not in notbad + origbadfn = m.bad + def lfbadfn(f, msg): + if not f in notbad: + return origbadfn(f, msg) + m.bad = lfbadfn for f in ctx.walk(m): + fp = cmdutil.makefileobj(repo, opts.get('output'), ctx.node(), + pathname=f) lf = lfutil.splitstandin(f) if lf is None: - err = orig(ui, repo, f, **opts) + # duplicating unreachable code from commands.cat + data = ctx[f].data() + if opts.get('decode'): + data = repo.wwritedata(f, data) + fp.write(data) else: - err = lfcommands.catlfile(repo, lf, ctx.rev(), opts.get('output')) + hash = lfutil.readstandin(repo, lf, ctx.rev()) + if not lfutil.inusercache(repo.ui, hash): + store = basestore._openstore(repo) + success, missing = store.get([(lf, hash)]) + if len(success) != 1: + raise util.Abort( + _('largefile %s is not in cache and could not be ' + 'downloaded') % lf) + path = lfutil.usercachepath(repo.ui, hash) + fpin = open(path, "rb") + for chunk in util.filechunkiter(fpin, 128 * 1024): + fp.write(chunk) + fpin.close() + fp.close() + err = 0 return err def mercurialsinkbefore(orig, sink):
--- a/hgext/largefiles/proto.py Thu Apr 04 16:28:19 2013 -0500 +++ b/hgext/largefiles/proto.py Thu Apr 18 23:46:26 2013 -0500 @@ -16,6 +16,11 @@ '\n\nPlease enable it in your Mercurial config ' 'file.\n') +# these will all be replaced by largefiles.uisetup +capabilitiesorig = None +ssholdcallstream = None +httpoldcallstream = None + def putlfile(repo, proto, sha): '''Put a largefile into a repository's local store and into the user cache.''' @@ -58,7 +63,7 @@ # ssh proto does for string responses. def generator(): yield '%d\n' % length - for chunk in f: + for chunk in util.filechunkiter(f): yield chunk return wireproto.streamres(generator()) @@ -109,6 +114,7 @@ _('putlfile failed (unexpected response):'), ret) def getlfile(self, sha): + """returns an iterable with the chunks of the file with sha sha""" stream = self._callstream("getlfile", sha=sha) length = stream.readline() try: @@ -116,7 +122,17 @@ except ValueError: self._abort(error.ResponseError(_("unexpected response:"), length)) - return (length, stream) + + # SSH streams will block if reading more than length + for chunk in util.filechunkiter(stream, 128 * 1024, length): + yield chunk + # HTTP streams must hit the end to process the last empty + # chunk of Chunked-Encoding so the connection can be reused. + if issubclass(self.__class__, httppeer.httppeer): + chunk = stream.read(1) + if chunk: + self._abort(error.ResponseError(_("unexpected response:"), + chunk)) @batchable def statlfile(self, sha):
--- a/hgext/largefiles/remotestore.py Thu Apr 04 16:28:19 2013 -0500 +++ b/hgext/largefiles/remotestore.py Thu Apr 18 23:46:26 2013 -0500 @@ -29,7 +29,7 @@ _('remotestore: put %s to remote store %s') % (source, self.url)) def exists(self, hashes): - return self._verify(hashes) + return dict((h, s == 0) for (h, s) in self._stat(hashes).iteritems()) def sendfile(self, filename, hash): self.ui.debug('remotestore: sendfile(%s, %s)\n' % (filename, hash)) @@ -47,18 +47,8 @@ fd.close() def _getfile(self, tmpfile, filename, hash): - # quit if the largefile isn't there - stat = self._stat([hash])[hash] - if stat == 1: - raise util.Abort(_('remotestore: largefile %s is invalid') % hash) - elif stat == 2: - raise util.Abort(_('remotestore: largefile %s is missing') % hash) - elif stat != 0: - raise RuntimeError('error getting file: unexpected response from ' - 'statlfile (%r)' % stat) - try: - length, infile = self._get(hash) + chunks = self._get(hash) except urllib2.HTTPError, e: # 401s get converted to util.Aborts; everything else is fine being # turned into a StoreError @@ -71,13 +61,7 @@ except IOError, e: raise basestore.StoreError(filename, hash, self.url, str(e)) - # Mercurial does not close its SSH connections after writing a stream - if length is not None: - infile = lfutil.limitreader(infile, length) - return lfutil.copyandhash(lfutil.blockstream(infile), tmpfile) - - def _verify(self, hashes): - return dict((h, s == 0) for (h, s) in self._stat(hashes).iteritems()) + return lfutil.copyandhash(chunks, tmpfile) def _verifyfile(self, cctx, cset, contents, standin, verified): filename = lfutil.splitstandin(standin)
--- a/hgext/largefiles/reposetup.py Thu Apr 04 16:28:19 2013 -0500 +++ b/hgext/largefiles/reposetup.py Thu Apr 18 23:46:26 2013 -0500 @@ -27,10 +27,11 @@ if not repo.local(): return proto.wirereposetup(ui, repo) + origclass = localrepo.localrepository + repoclass = repo.__class__ for name in ('status', 'commitctx', 'commit', 'push'): - method = getattr(repo, name) - if (isinstance(method, types.FunctionType) and - method.func_name == 'wrap'): + if (getattr(origclass, name) != getattr(repoclass, name) or + isinstance(getattr(repo, name), types.FunctionType)): ui.warn(_('largefiles: repo method %r appears to have already been' ' wrapped by another extension: ' 'largefiles may behave incorrectly\n') @@ -125,127 +126,146 @@ if match is None: match = match_.always(self.root, self.getcwd()) - # First check if there were files specified on the - # command line. If there were, and none of them were - # largefiles, we should just bail here and let super - # handle it -- thus gaining a big performance boost. - lfdirstate = lfutil.openlfdirstate(ui, self) - if match.files() and not match.anypats(): - for f in lfdirstate: - if match(f): - break - else: - return super(lfilesrepo, self).status(node1, node2, - match, listignored, listclean, - listunknown, listsubrepos) + wlock = None + try: + try: + # updating the dirstate is optional + # so we don't wait on the lock + wlock = self.wlock(False) + except error.LockError: + pass - # Create a copy of match that matches standins instead - # of largefiles. - def tostandins(files): - if not working: - return files - newfiles = [] - dirstate = self.dirstate - for f in files: - sf = lfutil.standin(f) - if sf in dirstate: - newfiles.append(sf) - elif sf in dirstate.dirs(): - # Directory entries could be regular or - # standin, check both - newfiles.extend((f, sf)) + # First check if there were files specified on the + # command line. If there were, and none of them were + # largefiles, we should just bail here and let super + # handle it -- thus gaining a big performance boost. + lfdirstate = lfutil.openlfdirstate(ui, self) + if match.files() and not match.anypats(): + for f in lfdirstate: + if match(f): + break else: - newfiles.append(f) - return newfiles - - m = copy.copy(match) - m._files = tostandins(m._files) + return super(lfilesrepo, self).status(node1, node2, + match, listignored, listclean, + listunknown, listsubrepos) - result = super(lfilesrepo, self).status(node1, node2, m, - ignored, clean, unknown, listsubrepos) - if working: + # Create a copy of match that matches standins instead + # of largefiles. + def tostandins(files): + if not working: + return files + newfiles = [] + dirstate = self.dirstate + for f in files: + sf = lfutil.standin(f) + if sf in dirstate: + newfiles.append(sf) + elif sf in dirstate.dirs(): + # Directory entries could be regular or + # standin, check both + newfiles.extend((f, sf)) + else: + newfiles.append(f) + return newfiles - def sfindirstate(f): - sf = lfutil.standin(f) - dirstate = self.dirstate - return sf in dirstate or sf in dirstate.dirs() + m = copy.copy(match) + m._files = tostandins(m._files) - match._files = [f for f in match._files - if sfindirstate(f)] - # Don't waste time getting the ignored and unknown - # files from lfdirstate - s = lfdirstate.status(match, [], False, - listclean, False) - (unsure, modified, added, removed, missing, _unknown, - _ignored, clean) = s - if parentworking: - for lfile in unsure: - standin = lfutil.standin(lfile) - if standin not in ctx1: - # from second parent - modified.append(lfile) - elif ctx1[standin].data().strip() \ - != lfutil.hashfile(self.wjoin(lfile)): - modified.append(lfile) - else: - clean.append(lfile) - lfdirstate.normal(lfile) - else: - tocheck = unsure + modified + added + clean - modified, added, clean = [], [], [] + result = super(lfilesrepo, self).status(node1, node2, m, + ignored, clean, unknown, listsubrepos) + if working: + + def sfindirstate(f): + sf = lfutil.standin(f) + dirstate = self.dirstate + return sf in dirstate or sf in dirstate.dirs() - for lfile in tocheck: - standin = lfutil.standin(lfile) - if inctx(standin, ctx1): - if ctx1[standin].data().strip() != \ - lfutil.hashfile(self.wjoin(lfile)): + match._files = [f for f in match._files + if sfindirstate(f)] + # Don't waste time getting the ignored and unknown + # files from lfdirstate + s = lfdirstate.status(match, [], False, + listclean, False) + (unsure, modified, added, removed, missing, _unknown, + _ignored, clean) = s + if parentworking: + for lfile in unsure: + standin = lfutil.standin(lfile) + if standin not in ctx1: + # from second parent + modified.append(lfile) + elif ctx1[standin].data().strip() \ + != lfutil.hashfile(self.wjoin(lfile)): modified.append(lfile) else: clean.append(lfile) - else: - added.append(lfile) + lfdirstate.normal(lfile) + else: + tocheck = unsure + modified + added + clean + modified, added, clean = [], [], [] - # Standins no longer found in lfdirstate has been removed - for standin in ctx1.manifest(): - if not lfutil.isstandin(standin): - continue - lfile = lfutil.splitstandin(standin) - if not match(lfile): - continue - if lfile not in lfdirstate: - removed.append(lfile) + for lfile in tocheck: + standin = lfutil.standin(lfile) + if inctx(standin, ctx1): + if ctx1[standin].data().strip() != \ + lfutil.hashfile(self.wjoin(lfile)): + modified.append(lfile) + else: + clean.append(lfile) + else: + added.append(lfile) - # Filter result lists - result = list(result) + # Standins no longer found in lfdirstate has been + # removed + for standin in ctx1.manifest(): + if not lfutil.isstandin(standin): + continue + lfile = lfutil.splitstandin(standin) + if not match(lfile): + continue + if lfile not in lfdirstate: + removed.append(lfile) + + # Filter result lists + result = list(result) - # Largefiles are not really removed when they're - # still in the normal dirstate. Likewise, normal - # files are not really removed if they are still in - # lfdirstate. This happens in merges where files - # change type. - removed = [f for f in removed if f not in self.dirstate] - result[2] = [f for f in result[2] if f not in lfdirstate] + # Largefiles are not really removed when they're + # still in the normal dirstate. Likewise, normal + # files are not really removed if they are still in + # lfdirstate. This happens in merges where files + # change type. + removed = [f for f in removed + if f not in self.dirstate] + result[2] = [f for f in result[2] + if f not in lfdirstate] - lfiles = set(lfdirstate._map) - # Unknown files - result[4] = set(result[4]).difference(lfiles) - # Ignored files - result[5] = set(result[5]).difference(lfiles) - # combine normal files and largefiles - normals = [[fn for fn in filelist - if not lfutil.isstandin(fn)] - for filelist in result] - lfiles = (modified, added, removed, missing, [], [], clean) - result = [sorted(list1 + list2) - for (list1, list2) in zip(normals, lfiles)] - else: - def toname(f): - if lfutil.isstandin(f): - return lfutil.splitstandin(f) - return f - result = [[toname(f) for f in items] for items in result] + lfiles = set(lfdirstate._map) + # Unknown files + result[4] = set(result[4]).difference(lfiles) + # Ignored files + result[5] = set(result[5]).difference(lfiles) + # combine normal files and largefiles + normals = [[fn for fn in filelist + if not lfutil.isstandin(fn)] + for filelist in result] + lfiles = (modified, added, removed, missing, [], [], + clean) + result = [sorted(list1 + list2) + for (list1, list2) in zip(normals, lfiles)] + else: + def toname(f): + if lfutil.isstandin(f): + return lfutil.splitstandin(f) + return f + result = [[toname(f) for f in items] + for items in result] - lfdirstate.write() + if wlock: + lfdirstate.write() + + finally: + if wlock: + wlock.release() if not listunknown: result[4] = [] @@ -446,7 +466,7 @@ the largefiles. So we do the following: For directories that only have largefiles as matches, - we explicitly add the largefiles to the matchlist and remove + we explicitly add the largefiles to the match list and remove the directory. In other cases, we leave the match list unmodified. '''
--- a/hgext/largefiles/uisetup.py Thu Apr 04 16:28:19 2013 -0500 +++ b/hgext/largefiles/uisetup.py Thu Apr 18 23:46:26 2013 -0500 @@ -9,7 +9,7 @@ '''setup for largefiles extension: uisetup''' from mercurial import archival, cmdutil, commands, extensions, filemerge, hg, \ - httppeer, localrepo, merge, scmutil, sshpeer, wireproto + httppeer, localrepo, merge, scmutil, sshpeer, wireproto, revset from mercurial.i18n import _ from mercurial.hgweb import hgweb_mod, webcommands from mercurial.subrepo import hgsubrepo @@ -52,11 +52,12 @@ entry = extensions.wrapcommand(commands.table, 'verify', overrides.overrideverify) - verifyopt = [('', 'large', None, _('verify largefiles')), + verifyopt = [('', 'large', None, + _('verify that all largefiles in current revision exists')), ('', 'lfa', None, - _('verify all revisions of largefiles not just current')), + _('verify largefiles in all revisions, not just current')), ('', 'lfc', None, - _('verify largefile contents not just existence'))] + _('verify local largefile contents, not just existence'))] entry[1].extend(verifyopt) entry = extensions.wrapcommand(commands.table, 'debugstate', @@ -78,8 +79,12 @@ entry = extensions.wrapcommand(commands.table, 'pull', overrides.overridepull) pullopt = [('', 'all-largefiles', None, - _('download all pulled versions of largefiles'))] + _('download all pulled versions of largefiles (DEPRECATED)')), + ('', 'lfrev', [], + _('download largefiles for these revisions'), _('REV'))] entry[1].extend(pullopt) + revset.symbols['pulled'] = overrides.pulledrevsetsymbol + entry = extensions.wrapcommand(commands.table, 'clone', overrides.overrideclone) cloneopt = [('', 'all-largefiles', None,
--- a/hgext/largefiles/wirestore.py Thu Apr 04 16:28:19 2013 -0500 +++ b/hgext/largefiles/wirestore.py Thu Apr 18 23:46:26 2013 -0500 @@ -26,8 +26,9 @@ return self.remote.getlfile(hash) def _stat(self, hashes): - '''For each hash, return 2 if the largefile is missing, 1 if it has a - mismatched checksum, or 0 if it is in good condition''' + '''For each hash, return 0 if it is available, other values if not. + It is usually 2 if the largefile is missing, but might be 1 the server + has a corrupted copy.''' batch = self.remote.batch() futures = {} for hash in hashes:
--- a/hgext/mq.py Thu Apr 04 16:28:19 2013 -0500 +++ b/hgext/mq.py Thu Apr 18 23:46:26 2013 -0500 @@ -282,7 +282,7 @@ if phase is not None: backup = repo.ui.backupconfig('phases', 'new-commit') # Marking the repository as committing an mq patch can be used - # to optimize operations like _branchtags(). + # to optimize operations like branchtags(). repo._committingpatch = True try: if phase is not None: @@ -297,7 +297,7 @@ pass class queue(object): - def __init__(self, ui, path, patchdir=None): + def __init__(self, ui, baseui, path, patchdir=None): self.basepath = path try: fh = open(os.path.join(path, 'patches.queue')) @@ -312,6 +312,7 @@ self.path = patchdir or curpath self.opener = scmutil.opener(self.path) self.ui = ui + self.baseui = baseui self.applieddirty = False self.seriesdirty = False self.added = [] @@ -1571,7 +1572,7 @@ r = list(dd) a = list(aa) - # create 'match' that includes the files to be recommited. + # create 'match' that includes the files to be recommitted. # apply matchfn via repo.status to ensure correct case handling. cm, ca, cr, cd = repo.status(patchparent, match=matchfn)[:4] allmatches = set(cm + ca + cr + cd) @@ -1774,9 +1775,7 @@ return True def qrepo(self, create=False): - ui = self.ui.copy() - ui.setconfig('paths', 'default', '', overlay=False) - ui.setconfig('paths', 'default-push', '', overlay=False) + ui = self.baseui.copy() if create or os.path.isdir(self.join(".hg")): return hg.repository(ui, path=self.path, create=create) @@ -2761,7 +2760,7 @@ if not newpath: ui.warn(_("no saved queues found, please use -n\n")) return 1 - mergeq = queue(ui, repo.path, newpath) + mergeq = queue(ui, repo.baseui, repo.path, newpath) ui.warn(_("merging with queue at: %s\n") % mergeq.path) ret = q.push(repo, patch, force=opts.get('force'), list=opts.get('list'), mergeq=mergeq, all=opts.get('all'), move=opts.get('move'), @@ -2795,7 +2794,7 @@ opts = fixkeepchangesopts(ui, opts) localupdate = True if opts.get('name'): - q = queue(ui, repo.path, repo.join(opts.get('name'))) + q = queue(ui, repo.baseui, repo.path, repo.join(opts.get('name'))) ui.warn(_('using patch queue: %s\n') % q.path) localupdate = False else: @@ -3037,7 +3036,22 @@ wlock = repo.wlock() try: urev = repo.mq.qparents(repo, revs[0]) - repo.dirstate.rebuild(urev, repo[urev].manifest()) + uctx = repo[urev] + + # only reset the dirstate for files that would actually change + # between the working context and uctx + descendantrevs = repo.revs("%s::." % uctx.rev()) + changedfiles = [] + for rev in descendantrevs: + # blindly reset the files, regardless of what actually changed + changedfiles.extend(repo[rev].files()) + + # reset files that only changed in the dirstate too + dirstate = repo.dirstate + dirchanges = [f for f in dirstate if dirstate[f] != 'n'] + changedfiles.extend(dirchanges) + + repo.dirstate.rebuild(urev, uctx.manifest(), changedfiles) repo.dirstate.write() update = False finally: @@ -3398,7 +3412,7 @@ class mqrepo(repo.__class__): @util.propertycache def mq(self): - return queue(self.ui, self.path) + return queue(self.ui, self.baseui, self.path) def abortifwdirpatched(self, errmsg, force=False): if self.mq.applied and not force: @@ -3454,6 +3468,12 @@ % short(mqtags[-1][0])) return result + # do not add fake tags for filtered revisions + included = self.changelog.hasnode + mqtags = [mqt for mqt in mqtags if included(mqt[0])] + if not mqtags: + return result + mqtags.append((mqtags[-1][0], 'qtip')) mqtags.append((mqtags[0][0], 'qbase')) mqtags.append((self.changelog.parents(mqtags[0][0])[0], 'qparent'))
--- a/hgext/pager.py Thu Apr 04 16:28:19 2013 -0500 +++ b/hgext/pager.py Thu Apr 18 23:46:26 2013 -0500 @@ -47,7 +47,7 @@ normal behavior. ''' -import atexit, sys, os, signal, subprocess +import atexit, sys, os, signal, subprocess, errno, shlex from mercurial import commands, dispatch, util, extensions from mercurial.i18n import _ @@ -94,6 +94,8 @@ @atexit.register def killpager(): + if util.safehasattr(signal, "SIGINT"): + signal.signal(signal.SIGINT, signal.SIG_IGN) pager.stdin.close() os.dup2(stdout, sys.stdout.fileno()) os.dup2(stderr, sys.stderr.fileno())
--- a/hgext/patchbomb.py Thu Apr 04 16:28:19 2013 -0500 +++ b/hgext/patchbomb.py Thu Apr 18 23:46:26 2013 -0500 @@ -540,7 +540,13 @@ fp.close() else: if not sendmail: - sendmail = mail.connect(ui, mbox=mbox) + verifycert = ui.config('smtp', 'verifycert') + if opts.get('insecure'): + ui.setconfig('smtp', 'verifycert', 'loose') + try: + sendmail = mail.connect(ui, mbox=mbox) + finally: + ui.setconfig('smtp', 'verifycert', verifycert) ui.status(_('sending '), subj, ' ...\n') ui.progress(_('sending'), i, item=subj, total=len(msgs)) if not mbox:
--- a/hgext/rebase.py Thu Apr 04 16:28:19 2013 -0500 +++ b/hgext/rebase.py Thu Apr 18 23:46:26 2013 -0500 @@ -15,7 +15,7 @@ ''' from mercurial import hg, util, repair, merge, cmdutil, commands, bookmarks -from mercurial import extensions, patch, scmutil, phases, obsolete +from mercurial import extensions, patch, scmutil, phases, obsolete, error from mercurial.commands import templateopts from mercurial.node import nullrev from mercurial.lock import release @@ -209,10 +209,6 @@ _("can't remove original changesets with" " unrebased descendants"), hint=_('use --keep to keep original changesets')) - elif not keepf and not repo[root].mutable(): - raise util.Abort(_("can't rebase immutable changeset %s") - % repo[root], - hint=_('see hg help phases for details')) else: result = buildstate(repo, dest, rebaseset, collapsef) @@ -220,6 +216,10 @@ # Empty state built, nothing to rebase ui.status(_('nothing to rebase\n')) return 1 + elif not keepf and not repo[root].mutable(): + raise util.Abort(_("can't rebase immutable changeset %s") + % repo[root], + hint=_('see hg help phases for details')) else: originalwd, target, state = result if collapsef: @@ -269,8 +269,9 @@ ui.setconfig('ui', 'forcemerge', opts.get('tool', '')) stats = rebasenode(repo, rev, p1, state, collapsef) if stats and stats[3] > 0: - raise util.Abort(_('unresolved conflicts (see hg ' - 'resolve, then hg rebase --continue)')) + raise error.InterventionRequired( + _('unresolved conflicts (see hg ' + 'resolve, then hg rebase --continue)')) finally: ui.setconfig('ui', 'forcemerge', '') cmdutil.duplicatecopies(repo, rev, target) @@ -699,8 +700,8 @@ # If we have multiple roots, we may have "hole" in the rebase set. # Rebase roots that descend from those "hole" should not be detached as # other root are. We use the special `revignored` to inform rebase that - # the revision should be ignored but that `defineparent` should search - # a rebase destination that make sense regarding rebaset topology. + # the revision should be ignored but that `defineparents` should search + # a rebase destination that make sense regarding rebased topology. rebasedomain = set(repo.revs('%ld::%ld', rebaseset, rebaseset)) for ignored in set(rebasedomain) - set(rebaseset): state[ignored] = revignored
--- a/hgext/record.py Thu Apr 04 16:28:19 2013 -0500 +++ b/hgext/record.py Thu Apr 18 23:46:26 2013 -0500 @@ -76,7 +76,7 @@ if m: yield 'range', m.groups() else: - raise patch.PatchError('unknown patch content: %r' % line) + yield 'other', line class header(object): """patch header @@ -228,6 +228,9 @@ self.headers.append(h) self.header = h + def addother(self, line): + pass # 'other' lines are ignored + def finished(self): self.addcontext([]) return self.headers @@ -239,12 +242,14 @@ 'range': addrange}, 'context': {'file': newfile, 'hunk': addhunk, - 'range': addrange}, + 'range': addrange, + 'other': addother}, 'hunk': {'context': addcontext, 'file': newfile, 'range': addrange}, 'range': {'context': addcontext, 'hunk': addhunk}, + 'other': {'other': addother}, } p = parser() @@ -531,7 +536,11 @@ fp.seek(0) # 1. filter patch, so we have intending-to apply subset of it - chunks = filterpatch(ui, parsepatch(fp)) + try: + chunks = filterpatch(ui, parsepatch(fp)) + except patch.PatchError, err: + raise util.Abort(_('error parsing patch: %s') % err) + del fp contenders = set()
--- a/hgext/relink.py Thu Apr 04 16:28:19 2013 -0500 +++ b/hgext/relink.py Thu Apr 18 23:46:26 2013 -0500 @@ -41,7 +41,7 @@ if (not util.safehasattr(util, 'samefile') or not util.safehasattr(util, 'samedevice')): raise util.Abort(_('hardlinks are not supported on this system')) - src = hg.repository(ui, ui.expandpath(origin or 'default-relink', + src = hg.repository(repo.baseui, ui.expandpath(origin or 'default-relink', origin or 'default')) ui.status(_('relinking %s to %s\n') % (src.store.path, repo.store.path)) if repo.root == src.root:
--- a/hgext/schemes.py Thu Apr 04 16:28:19 2013 -0500 +++ b/hgext/schemes.py Thu Apr 18 23:46:26 2013 -0500 @@ -62,7 +62,10 @@ def instance(self, ui, url, create): # Should this use the util.url class, or is manual parsing better? - url = url.split('://', 1)[1] + try: + url = url.split('://', 1)[1] + except IndexError: + raise util.Abort(_("no '://' in scheme url '%s'") % url) parts = url.split('/', self.parts) if len(parts) > self.parts: tail = parts[-1]
--- a/hgext/share.py Thu Apr 04 16:28:19 2013 -0500 +++ b/hgext/share.py Thu Apr 18 23:46:26 2013 -0500 @@ -59,7 +59,7 @@ lock and lock.release() # update store, spath, sopener and sjoin of repo - repo.__init__(ui, repo.root) + repo.__init__(repo.baseui, repo.root) cmdtable = { "share":
--- a/hgext/transplant.py Thu Apr 04 16:28:19 2013 -0500 +++ b/hgext/transplant.py Thu Apr 18 23:46:26 2013 -0500 @@ -7,7 +7,8 @@ '''command to transplant changesets from another branch -This extension allows you to transplant patches from another branch. +This extension allows you to transplant changes to another parent revision, +possibly in another repository. The transplant is done using 'diff' patches. Transplanted patches are recorded in .hg/transplant/transplants, as a map from a changeset hash to its hash in the source repository. @@ -294,10 +295,10 @@ return n - def resume(self, repo, source, opts=None): + def resume(self, repo, source, opts): '''recover last transaction and apply remaining changesets''' if os.path.exists(os.path.join(self.path, 'journal')): - n, node = self.recover(repo) + n, node = self.recover(repo, source, opts) self.ui.status(_('%s transplanted as %s\n') % (short(node), short(n))) seriespath = os.path.join(self.path, 'series') @@ -312,7 +313,7 @@ self.apply(repo, source, revmap, merges, opts) - def recover(self, repo): + def recover(self, repo, source, opts): '''commit working directory using journal metadata''' node, user, date, message, parents = self.readlog() merge = False @@ -492,10 +493,9 @@ return (transplants, merges) @command('transplant', - [('s', 'source', '', _('pull patches from REPO'), _('REPO')), - ('b', 'branch', [], - _('pull patches from branch BRANCH'), _('BRANCH')), - ('a', 'all', None, _('pull all changesets up to BRANCH')), + [('s', 'source', '', _('transplant changesets from REPO'), _('REPO')), + ('b', 'branch', [], _('use this source changeset as head'), _('REV')), + ('a', 'all', None, _('pull all changesets up to the --branch revisions')), ('p', 'prune', [], _('skip over REV'), _('REV')), ('m', 'merge', [], _('merge at REV'), _('REV')), ('', 'parent', '', @@ -503,7 +503,7 @@ ('e', 'edit', False, _('invoke editor on commit messages')), ('', 'log', None, _('append transplant info to log message')), ('c', 'continue', None, _('continue last transplant session ' - 'after repair')), + 'after fixing conflicts')), ('', 'filter', '', _('filter changesets through command'), _('CMD'))], _('hg transplant [-s REPO] [-b BRANCH [-a]] [-p REV] ' @@ -513,9 +513,13 @@ Selected changesets will be applied on top of the current working directory with the log of the original changeset. The changesets - are copied and will thus appear twice in the history. Use the - rebase extension instead if you want to move a whole branch of - unpublished changesets. + are copied and will thus appear twice in the history with different + identities. + + Consider using the graft command if everything is inside the same + repository - it will use merges and will usually give a better result. + Use the rebase extension if the changesets are unpublished and you want + to move them instead of copying them. If --log is specified, log messages will have a comment appended of the form:: @@ -526,16 +530,19 @@ Its argument will be invoked with the current changelog message as $1 and the patch as $2. - If --source/-s is specified, selects changesets from the named - repository. If --branch/-b is specified, selects changesets from - the branch holding the named revision, up to that revision. If - --all/-a is specified, all changesets on the branch will be - transplanted, otherwise you will be prompted to select the - changesets you want. + --source/-s specifies another repository to use for selecting changesets, + just as if it temporarily had been pulled. + If --branch/-b is specified, these revisions will be used as + heads when deciding which changsets to transplant, just as if only + these revisions had been pulled. + If --all/-a is specified, all the revisions up to the heads specified + with --branch will be transplanted. - :hg:`transplant --branch REV --all` will transplant the - selected branch (up to the named revision) onto your current - working directory. + Example: + + - transplant all changes up to REV on top of your current revision:: + + hg transplant --branch REV --all You can optionally mark selected transplanted changesets as merge changesets. You will not be prompted to transplant any ancestors @@ -557,13 +564,16 @@ if match(node): yield node - def transplantwalk(repo, root, branches, match=util.always): - if not branches: - branches = repo.heads() + def transplantwalk(repo, dest, heads, match=util.always): + '''Yield all nodes that are ancestors of a head but not ancestors + of dest. + If no heads are specified, the heads of repo will be used.''' + if not heads: + heads = repo.heads() ancestors = [] - for branch in branches: - ancestors.append(repo.changelog.ancestor(root, branch)) - for node in repo.changelog.nodesbetween(ancestors, branches)[0]: + for head in heads: + ancestors.append(repo.changelog.ancestor(dest, head)) + for node in repo.changelog.nodesbetween(ancestors, heads)[0]: if match(node): yield node @@ -571,11 +581,11 @@ if opts.get('continue'): if opts.get('branch') or opts.get('all') or opts.get('merge'): raise util.Abort(_('--continue is incompatible with ' - 'branch, all or merge')) + '--branch, --all and --merge')) return if not (opts.get('source') or revs or opts.get('merge') or opts.get('branch')): - raise util.Abort(_('no source URL, branch tag or revision ' + raise util.Abort(_('no source URL, branch revision or revision ' 'list provided')) if opts.get('all'): if not opts.get('branch'): @@ -608,12 +618,12 @@ sourcerepo = opts.get('source') if sourcerepo: peer = hg.peer(repo, opts, ui.expandpath(sourcerepo)) - branches = map(peer.lookup, opts.get('branch', ())) + heads = map(peer.lookup, opts.get('branch', ())) source, csets, cleanupfn = bundlerepo.getremotechanges(ui, repo, peer, - onlyheads=branches, force=True) + onlyheads=heads, force=True) else: source = repo - branches = map(source.lookup, opts.get('branch', ())) + heads = map(source.lookup, opts.get('branch', ())) cleanupfn = None try: @@ -623,8 +633,8 @@ tf = tp.transplantfilter(repo, source, p1) if opts.get('prune'): - prune = [source.lookup(r) - for r in scmutil.revrange(source, opts.get('prune'))] + prune = set(source.lookup(r) + for r in scmutil.revrange(source, opts.get('prune'))) matchfn = lambda x: tf(x) and x not in prune else: matchfn = tf @@ -637,7 +647,7 @@ if source != repo: alltransplants = incwalk(source, csets, match=matchfn) else: - alltransplants = transplantwalk(source, p1, branches, + alltransplants = transplantwalk(source, p1, heads, match=matchfn) if opts.get('all'): revs = alltransplants
--- a/i18n/el.po Thu Apr 04 16:28:19 2013 -0500 +++ b/i18n/el.po Thu Apr 18 23:46:26 2013 -0500 @@ -1,8 +1,8 @@ # Greek translations for Mercurial # Ελληνική μετάφραση των μηνυμάτων του Mercurial -# +# # Copyright (C) 2009 Matt Mackall και άλλοι -# +# msgid "" msgstr "" "Project-Id-Version: Mercurial\n"
--- a/i18n/fr.po Thu Apr 04 16:28:19 2013 -0500 +++ b/i18n/fr.po Thu Apr 18 23:46:26 2013 -0500 @@ -1,17 +1,17 @@ # French translations for Mercurial # Traductions françaises de Mercurial # Copyright (C) 2009 Matt Mackall <mpm@selenic.com> and others -# +# # Quelques règles : # - dans l'aide d'une commande, la première ligne descriptive # commence par un verbe au présent sans majuscule # - dans l'aide d'une commande, la description des options # utilise un verbe à l'infinitif -# +# # Note : la terminologie ci-dessous est loin d'être complète, figée ou # parfaite. À compléter et à améliorer, particulièrement les # termes comportant un point d'interrogation... -# +# # Dictionnaire de termes courants : # - to apply a patch appliquer un patch # - a branch une branche @@ -46,7 +46,7 @@ # untracked non suivi, non géré, pas sous contrôle du dépôt, # hors révision ? # - the working directory le répertoire de travail -# +# # Termes très courants repris de l'anglais - à utiliser sans guillemets # pour ne pas alourdir inutilement la traduction : # - a diff un diff ? (ou également un patch ? synonymes...) @@ -54,7 +54,7 @@ # - a patch un patch # - a tag un tag # - to tag taguer -# +# # Termes anglais avec une signification très spécifique, difficile à # paraphraser sans alourdir ou perdre le sens - à utiliser avec guillemets : # - a bundle un \"bundle\" @@ -62,7 +62,7 @@ # - a changeset un \"changeset\" # - a changegroup un \"changegroup\" # - the tip la révision \"tip\" -# +# # Termes dont le classement / la traduction sont à déterminer : # - a commit un commit, un \"commit\" # - to commit \"committer\" ? (beuark, même dit tous les jours) @@ -73,7 +73,7 @@ # - to push propager ? (utilisé par svn pour commit) # publier ? pousser ?? envoyer ?? # - the series file (mq) ? -# +# # Notes : # - (cédric) je verrais bien l'ajout d'une rubrique générale dans l'aide # (par exemple 'hg help glossary') librement remplissable par chaque équipe @@ -81,7 +81,7 @@ # qui vont être rencontrés dans mercurial - et en particulier permettrait # de faire le lien avec des termes franglisants parfois utilisés # (par ex. fusionner = "merger", etc.) ... ? -# +# msgid "" msgstr "" "Project-Id-Version: Mercurial\n"
--- a/i18n/it.po Thu Apr 04 16:28:19 2013 -0500 +++ b/i18n/it.po Thu Apr 18 23:46:26 2013 -0500 @@ -6,7 +6,7 @@ "Project-Id-Version: Mercurial\n" "Report-Msgid-Bugs-To: <mercurial-devel@selenic.com>\n" "POT-Creation-Date: 2011-03-22 22:04+0100\n" -"PO-Revision-Date: 2011-03-15 17:05+0100\n" +"PO-Revision-Date: 2013-04-05 14:47+0100\n" "Last-Translator: Stefano Tortarolo <stefano.tortarolo@gmail.com>\n" "Language-Team: Italian <Italian>\n" "Language: it\n" @@ -14,6 +14,7 @@ "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=(n != 1);\n" +"X-Generator: Poedit 1.5.5\n" #, python-format msgid " (default: %s)" @@ -1862,13 +1863,13 @@ "used.\n" msgstr "" -#, fuzzy, python-format +#, python-format msgid "%s should not have CRLF line endings" -msgstr " %s in %s non dovrebbe avere %s fine riga" - -#, fuzzy, python-format +msgstr "%s non dovrebbe avere fine riga CRLF" + +#, python-format msgid "%s should not have LF line endings" -msgstr " %s in %s non dovrebbe avere %s fine riga" +msgstr "%s non dovrebbe avere fine riga LF" #, python-format msgid "warning: ignoring .hgeol file due to parse error at %s: %s\n" @@ -5439,17 +5440,15 @@ msgid "hardlinks are not supported on this system" msgstr "hardlink non supportati su questo sistema" -#, fuzzy msgid "must specify local origin repository" -msgstr "%s non è un repository locale Mercurial" +msgstr "è necessario specificare il repository di origine locale" #, python-format msgid "relinking %s to %s\n" msgstr "sto ricollegando %s a %s\n" -#, fuzzy msgid "there is nothing to relink\n" -msgstr "non c'è nulla di cui fare il merge" +msgstr "non c'è nulla di cui fare il relink\n" #, python-format msgid "tip has %d files, estimated total number of files: %s\n" @@ -5649,7 +5648,7 @@ msgstr "nessuna opzione\n" msgid "transplant changesets from another branch" -msgstr "" +msgstr "trapianta changeset da un'altro branch" msgid "" " Selected changesets will be applied on top of the current working\n" @@ -6023,11 +6022,11 @@ msgstr "il nome '%s' è riservato" msgid "options --message and --logfile are mutually exclusive" -msgstr "" +msgstr "le opzioni --message e --logfile sono mutualmente esclusive" #, python-format msgid "can't read commit message '%s': %s" -msgstr "" +msgstr "impossibile leggere il messaggio di commit '%s': %s" msgid "limit must be a positive integer" msgstr "limit dev'essere un intero positivo " @@ -6116,7 +6115,7 @@ msgstr "(considera di usare --after)\n" msgid "child process failed to start" -msgstr "avvio fallito del processo figlio" +msgstr "fallito l'avvio del processo figlio" #, python-format msgid "changeset: %d:%s\n" @@ -6128,7 +6127,7 @@ #, python-format msgid "bookmark: %s\n" -msgstr "segnalibro: %s\n" +msgstr "segnalibro: %s\n" #, python-format msgid "tag: %s\n" @@ -6161,11 +6160,11 @@ #, python-format msgid "files: %s\n" -msgstr "file: %s\n" +msgstr "file: %s\n" #, python-format msgid "copies: %s\n" -msgstr "copie: %s\n" +msgstr "copie: %s\n" #, python-format msgid "extra: %s=%s\n" @@ -13379,13 +13378,13 @@ msgid "not removing repo %s because it has changes.\n" msgstr "non rimuovo il repository %s in quanto contiene modifiche\n" -#, fuzzy, python-format +#, python-format msgid "cloning subrepo %s\n" -msgstr "clonazione in corso del subrepository %s da %s\n" - -#, fuzzy, python-format +msgstr "clonazione in corso del subrepository %s\n" + +#, python-format msgid "pulling subrepo %s\n" -msgstr "pull in corso del subrepository %s da %s\n" +msgstr "pull in corso del subrepository %s\n" #, python-format msgid "revision %s does not exist in subrepo %s\n" @@ -13463,7 +13462,7 @@ #, python-format msgid "template file %s: %s" -msgstr "" +msgstr "file template %s: %s" msgid "cannot use transaction when it is already committed/aborted" msgstr ""
--- a/i18n/polib.py Thu Apr 04 16:28:19 2013 -0500 +++ b/i18n/polib.py Thu Apr 18 23:46:26 2013 -0500 @@ -277,7 +277,7 @@ an instance of :class:`~polib._BaseEntry`. """ return self.find(entry.msgid, by='msgid') is not None - + def __eq__(self, other): return unicode(self) == unicode(other) @@ -502,7 +502,7 @@ 7*4+entries_len*8, # start of value index 0, keystart # size and offset of hash table # Important: we don't use hash tables - ) + ) output += array.array("i", offsets).tostring() output += ids output += strs @@ -631,7 +631,7 @@ def __init__(self, *args, **kwargs): """ - Constructor, accepts all keywords arguments accepted by + Constructor, accepts all keywords arguments accepted by :class:`~polib._BaseFile` class. """ _BaseFile.__init__(self, *args, **kwargs) @@ -774,7 +774,7 @@ Returns the string representation of the entry. """ return unicode(self).encode(self.encoding) - + def __eq__(self, other): return unicode(self) == unicode(other) @@ -787,7 +787,7 @@ specialchars_count = 0 for c in ['\\', '\n', '\r', '\t', '"']: specialchars_count += field.count(c) - # comparison must take into account fieldname length + one space + # comparison must take into account fieldname length + one space # + 2 quotes (eg. msgid "<string>") flength = len(fieldname) + 3 if plural_index: @@ -890,9 +890,9 @@ filelist.append(fpath) filestr = ' '.join(filelist) if wrapwidth > 0 and len(filestr) + 3 > wrapwidth: - # textwrap split words that contain hyphen, this is not - # what we want for filenames, so the dirty hack is to - # temporally replace hyphens with a char that a file cannot + # textwrap split words that contain hyphen, this is not + # what we want for filenames, so the dirty hack is to + # temporally replace hyphens with a char that a file cannot # contain, like "*" ret += [l.replace('*', '-') for l in wrap( filestr.replace('-', '*'), @@ -1099,7 +1099,7 @@ self.add('PP', all, 'PP') self.add('CT', ['ST', 'HE', 'GC', 'OC', 'FL', 'TC', 'PC', 'PM', 'PP', 'MS', 'MX'], 'CT') - self.add('MI', ['ST', 'HE', 'GC', 'OC', 'FL', 'CT', 'TC', 'PC', + self.add('MI', ['ST', 'HE', 'GC', 'OC', 'FL', 'CT', 'TC', 'PC', 'PM', 'PP', 'MS', 'MX'], 'MI') self.add('MP', ['TC', 'GC', 'PC', 'PM', 'PP', 'MI'], 'MP') self.add('MS', ['MI', 'MP', 'TC'], 'MS') @@ -1213,7 +1213,7 @@ # since entries are added when another entry is found, we must add # the last entry here (only if there are lines) self.instance.append(self.current_entry) - # before returning the instance, check if there's metadata and if + # before returning the instance, check if there's metadata and if # so extract it in a dict firstentry = self.instance[0] if firstentry.msgid == '': # metadata found @@ -1512,7 +1512,7 @@ # close opened file self.fhandle.close() return self.instance - + def _build_entry(self, msgid, msgstr=None, msgid_plural=None, msgstr_plural=None): msgctxt_msgid = msgid.split('\x04') @@ -1551,7 +1551,7 @@ drop_whitespace option. """ def __init__(self, *args, **kwargs): - drop_whitespace = kwargs.pop('drop_whitespace', True) + drop_whitespace = kwargs.pop('drop_whitespace', True) textwrap.TextWrapper.__init__(self, *args, **kwargs) self.drop_whitespace = drop_whitespace
--- a/i18n/pt_BR.po Thu Apr 04 16:28:19 2013 -0500 +++ b/i18n/pt_BR.po Thu Apr 18 23:46:26 2013 -0500 @@ -1,13 +1,13 @@ # Brazilian Portuguese translations for Mercurial # Traduções do Mercurial para português do Brasil # Copyright (C) 2011 Matt Mackall and others -# +# # Translators: # Diego Oliveira <diego@diegooliveira.com> # Wagner Bruna <wbruna@softwareexpress.com.br> -# +# # Translation dictionary: -# +# # archive pacote # branch ramificar (v.), ramo (s.) # bundle bundle @@ -26,7 +26,7 @@ # tip tip (tag), ponta # update atualizar (v.), atualização (s.) # working directory diretório de trabalho -# +# msgid "" msgstr "" "Project-Id-Version: Mercurial\n"
--- a/i18n/ru.po Thu Apr 04 16:28:19 2013 -0500 +++ b/i18n/ru.po Thu Apr 18 23:46:26 2013 -0500 @@ -7,15 +7,15 @@ # Файловая структура # # repository — хранилище (не «репозиторий») -# subrepository — подхранилище (не «субрепозиторий») -# store — склад (= место хранения файлов в LargefilesExtension), +# subrepository — подхранилище (не «субрепозиторий») +# store — склад (= место хранения файлов в LargefilesExtension), # (не «хранилище») # directory — каталог (не «директорий», не «директория») # folder — папка # source — источник -# source file — исходный файл +# source file — исходный файл # destination — назначение -# destination file — файл назначения +# destination file — файл назначения # # # Названия основных действий @@ -33,30 +33,30 @@ # checkout — извлечь (напр. из хранилища Subversion) # resolve — уладить (конфликт) # revert — вернуть -# discard — отбросить +# discard — отбросить # -# Слова на тему «удаление» +# Слова на тему «удаление» # # clean — чистый (~ локальный файл, не содержащий изменений по сравнению с хранилищем) -# clean working copy — чистая рабочая копия (= не содержащая изменённых файлов) -# purge — зачистить (напр. удалить неиспользуемые файлы из рабочей копии), +# clean working copy — чистая рабочая копия (= не содержащая изменённых файлов) +# purge — зачистить (напр. удалить неиспользуемые файлы из рабочей копии), # (не «удалить», не «очистить») # strip — срезать (~ ревизию и всех её потомков) -# strip revision — срезать ревизию +# strip revision — срезать ревизию # delete — удалить, уничтожить (напр. стереть с диска) -# remove — изъять (из-под контроля версий, не «удалить») +# remove — изъять (из-под контроля версий, не «удалить») # # # Слова на тему «слияние» # # branch — ветка (не «ветвь», не «бранч»), гл. ветвление -# frozen branch — замороженная ветка +# frozen branch — замороженная ветка # named branch — именованная ветка # diff — различия (не «дифф») # merge — слить (сущ. «слияние») # backout — обратить изменения (~ сделанные в ранней ревизии) # rollback — откатить (~ транзакцию) -# cancel — отменить +# cancel — отменить # # Слова на тему «управление изменениями» # @@ -67,10 +67,10 @@ # patch — заплатка, патч, накладывать заплатку, патчить, пропатчить # ??? apply patch — наложить заплатку (не «применить заплатку») # ??? unapply patch — отпороть заплатку -# ??? fold patch(es) — подшить заплатки (~ к самой верхней наложенной заплатке) +# ??? fold patch(es) — подшить заплатки (~ к самой верхней наложенной заплатке) # ??? chunk, hunk — лоскут (= часть заплатки) # ??? shelf — долгий ящик (= место для откладывания изменений) ;) -# shelve — отложить изменения (в долгий ящик) +# shelve — отложить изменения (в долгий ящик) # # # Разное @@ -78,12 +78,12 @@ # amend - исправлять # extension — расширение # option — параметр (не «опция») -# options — параметры (не «настройки») +# options — параметры (не «настройки») # settings — настройки (не «установки») # hook — хук, перехватчик, (не «крючок», не «ловушка», не «уловка», не «зацепка») # hook script — хук, скрипт-перехватчик (не «скрипт ловушки») # иногда слово hook употребляется в том же смысле, что и intercepted action (например, в документации) -# — тогда его следует переводить как «перехватываемое действие/событие» или «точка/момент перехвата» +# — тогда его следует переводить как «перехватываемое действие/событие» или «точка/момент перехвата» # # alias — псевдоним # changeset — набор изменений @@ -99,7 +99,7 @@ # hash хэш # glob глоб, glob, (thg: «маска файла (glob)») # binary бинарный -# basename ? +# basename ? # import импортировать # export экспортировать # rename переименовывать @@ -152,7 +152,7 @@ # - пометить как-то строки, которые не требуют перевода или не будут переводиться, # чтобы можно легко понять, сколько еще осталось сделать. # - в какой форме д.б. глаголы сообщений о текущем действии: -# 1 л ед.ч. - загружаю обновления, +# 1 л ед.ч. - загружаю обновления, # 1 л мн.ч - загружаем обновления, -- Так! // comment by Andrei Polushin # 3 л - загружаются обновления ? # Сюда же относятся сообщения cannot do smth: не могу сделать что-то или что-то невозможно? @@ -161,7 +161,7 @@ # - bisect - можно во многих местах употреблять термин "бисекция" (употребляется в thg) # вместо неуклюжего "метод деления пополам". Это устоявшийся термин. # - в строке должно быть не более 75 символов! -# - переводить ли примеры конфигов? я оставил как есть, чтобы пользователь +# - переводить ли примеры конфигов? я оставил как есть, чтобы пользователь # привыкал к англ, т.к. все настройки Mercurial на англ # - Attention Caution !Danger! Error Hint Important Note Tip Warning! # какая разница? @@ -6693,7 +6693,7 @@ msgid "hg qpush [-f] [-l] [-a] [--move] [PATCH | INDEX]" msgstr "hg qpush [-f] [-l] [-a] [--move] [ПАТЧ | ИНДЕКС]" -# MAYBE: поместить (добавить) следующий патч в стек +# MAYBE: поместить (добавить) следующий патч в стек msgid "push the next patch onto the stack" msgstr "протолкнуть следующий патч в стек"
--- a/i18n/zh_CN.po Thu Apr 04 16:28:19 2013 -0500 +++ b/i18n/zh_CN.po Thu Apr 18 23:46:26 2013 -0500 @@ -1,25 +1,25 @@ # Chinese (simplified) translation for Mercurial # This file is distributed under the same license as Mercurial -# +# # Copyright (C) 2009 the Mercurial team # Dongsheng Song <dongsheng.song@gmail.com>, 2009 -# +# # Update with pot file: # msgmerge --update zh_CN.po hg.pot # msgfmt --statistics -c zh_CN.po -# +# # Please test your translation before commit: # python setup.py build_py -c -d . build_ext -i build_mo # LC_ALL=zh_CN.UTF-8 ./hg -# +# # Please format your translation before commit: # msgcat --width=80 --sort-by-file -o zh_CN_new.po zh_CN.po # mv -f zh_CN_new.po zh_CN.po -# +# # Please remove '#: filename:line' lines before submit to hg: # msgcat --width=80 --no-location -o zh_CN_new.po zh_CN.po # mv -f zh_CN_new.po zh_CN.po -# +# # Dictionary: # blame 追溯 # branch 分支 @@ -42,7 +42,7 @@ # versioned 受版本控制 # working copy 工作副本 # ... -# +# msgid "" msgstr "" "Project-Id-Version: Mercurial 1.3\n"
--- a/mercurial/ancestor.py Thu Apr 04 16:28:19 2013 -0500 +++ b/mercurial/ancestor.py Thu Apr 18 23:46:26 2013 -0500 @@ -8,7 +8,129 @@ import heapq, util from node import nullrev -def ancestor(a, b, pfunc): +def ancestors(pfunc, *orignodes): + """ + Returns the common ancestors of a and b that are furthest from a + root (as measured by longest path). + + pfunc must return a list of parent vertices for a given vertex. + """ + if not isinstance(orignodes, set): + orignodes = set(orignodes) + if nullrev in orignodes: + return set() + if len(orignodes) <= 1: + return orignodes + + def candidates(nodes): + allseen = (1 << len(nodes)) - 1 + seen = [0] * (max(nodes) + 1) + for i, n in enumerate(nodes): + seen[n] = 1 << i + poison = 1 << (i + 1) + + gca = set() + interesting = left = len(nodes) + nv = len(seen) - 1 + while nv >= 0 and interesting: + v = nv + nv -= 1 + if not seen[v]: + continue + sv = seen[v] + if sv < poison: + interesting -= 1 + if sv == allseen: + gca.add(v) + sv |= poison + if v in nodes: + left -= 1 + if left <= 1: + # history is linear + return set([v]) + if sv < poison: + for p in pfunc(v): + sp = seen[p] + if p == nullrev: + continue + if sp == 0: + seen[p] = sv + interesting += 1 + elif sp != sv: + seen[p] |= sv + else: + for p in pfunc(v): + if p == nullrev: + continue + sp = seen[p] + if sp and sp < poison: + interesting -= 1 + seen[p] = sv + return gca + + def deepest(nodes): + interesting = {} + count = max(nodes) + 1 + depth = [0] * count + seen = [0] * count + mapping = [] + for (i, n) in enumerate(sorted(nodes)): + depth[n] = 1 + b = 1 << i + seen[n] = b + interesting[b] = 1 + mapping.append((b, n)) + nv = count - 1 + while nv >= 0 and len(interesting) > 1: + v = nv + nv -= 1 + dv = depth[v] + if dv == 0: + continue + sv = seen[v] + for p in pfunc(v): + if p == nullrev: + continue + dp = depth[p] + nsp = sp = seen[p] + if dp <= dv: + depth[p] = dv + 1 + if sp != sv: + interesting[sv] += 1 + nsp = seen[p] = sv + if sp: + interesting[sp] -= 1 + if interesting[sp] == 0: + del interesting[sp] + elif dv == dp - 1: + nsp = sp | sv + if nsp == sp: + continue + seen[p] = nsp + interesting.setdefault(nsp, 0) + interesting[nsp] += 1 + interesting[sp] -= 1 + if interesting[sp] == 0: + del interesting[sp] + interesting[sv] -= 1 + if interesting[sv] == 0: + del interesting[sv] + + if len(interesting) != 1: + return [] + + k = 0 + for i in interesting: + k |= i + return set(n for (i, n) in mapping if k & i) + + gca = candidates(orignodes) + + if len(gca) <= 1: + return gca + return deepest(gca) + +def genericancestor(a, b, pfunc): """ Returns the common ancestor of a and b that is furthest from a root (as measured by longest path) or None if no ancestor is @@ -30,7 +152,7 @@ depth = {} while visit: vertex = visit[-1] - pl = pfunc(vertex) + pl = [p for p in pfunc(vertex) if p != nullrev] parentcache[vertex] = pl if not pl: depth[vertex] = 0
--- a/mercurial/archival.py Thu Apr 04 16:28:19 2013 -0500 +++ b/mercurial/archival.py Thu Apr 18 23:46:26 2013 -0500 @@ -13,6 +13,7 @@ import cStringIO, os, tarfile, time, zipfile import zlib, gzip import struct +import error # from unzip source code: _UNX_IFREG = 0x8000 @@ -288,20 +289,25 @@ files = [f for f in ctx.manifest().keys() if matchfn(f)] else: files = ctx.manifest().keys() - files.sort() total = len(files) - repo.ui.progress(_('archiving'), 0, unit=_('files'), total=total) - for i, f in enumerate(files): - ff = ctx.flags(f) - write(f, 'x' in ff and 0755 or 0644, 'l' in ff, ctx[f].data) - repo.ui.progress(_('archiving'), i + 1, item=f, - unit=_('files'), total=total) - repo.ui.progress(_('archiving'), None) + if total: + files.sort() + repo.ui.progress(_('archiving'), 0, unit=_('files'), total=total) + for i, f in enumerate(files): + ff = ctx.flags(f) + write(f, 'x' in ff and 0755 or 0644, 'l' in ff, ctx[f].data) + repo.ui.progress(_('archiving'), i + 1, item=f, + unit=_('files'), total=total) + repo.ui.progress(_('archiving'), None) if subrepos: for subpath in sorted(ctx.substate): sub = ctx.sub(subpath) submatch = matchmod.narrowmatcher(subpath, matchfn) - sub.archive(repo.ui, archiver, prefix, submatch) + total += sub.archive(repo.ui, archiver, prefix, submatch) + + if total == 0: + raise error.Abort(_('no files match the archive pattern')) archiver.done() + return total
--- a/mercurial/bdiff.c Thu Apr 04 16:28:19 2013 -0500 +++ b/mercurial/bdiff.c Thu Apr 18 23:46:26 2013 -0500 @@ -347,6 +347,11 @@ if (!PyArg_ParseTuple(args, "s#s#:bdiff", &sa, &la, &sb, &lb)) return NULL; + if (la > UINT_MAX || lb > UINT_MAX) { + PyErr_SetString(PyExc_ValueError, "bdiff inputs too large"); + return NULL; + } + _save = PyEval_SaveThread(); an = splitlines(sa, la, &al); bn = splitlines(sb, lb, &bl); @@ -381,18 +386,9 @@ for (h = l.next; h; h = h->next) { if (h->a1 != la || h->b1 != lb) { len = bl[h->b1].l - bl[lb].l; - -#define checkputbe32(__x, __c) \ - if (__x > UINT_MAX) { \ - PyErr_SetString(PyExc_ValueError, \ - "bdiff: value too large for putbe32"); \ - goto nomem; \ - } \ - putbe32((uint32_t)(__x), __c); - - checkputbe32(al[la].l - al->l, rb); - checkputbe32(al[h->a1].l - al->l, rb + 4); - checkputbe32(len, rb + 8); + putbe32((uint32_t)(al[la].l - al->l), rb); + putbe32((uint32_t)(al[h->a1].l - al->l), rb + 4); + putbe32((uint32_t)len, rb + 8); memcpy(rb + 12, bl[lb].l, len); rb += 12 + len; }
--- a/mercurial/bookmarks.py Thu Apr 04 16:28:19 2013 -0500 +++ b/mercurial/bookmarks.py Thu Apr 18 23:46:26 2013 -0500 @@ -221,14 +221,13 @@ finally: w.release() -def updatefromremote(ui, repo, remote, path): +def updatefromremote(ui, repo, remotemarks, path): ui.debug("checking for updated bookmarks\n") - rb = remote.listkeys('bookmarks') changed = False localmarks = repo._bookmarks - for k in sorted(rb): + for k in sorted(remotemarks): if k in localmarks: - nr, nl = rb[k], localmarks[k] + nr, nl = remotemarks[k], localmarks[k] if nr in repo: cr = repo[nr] cl = repo[nl] @@ -257,9 +256,9 @@ localmarks[n] = cr.node() changed = True ui.warn(_("divergent bookmark %s stored as %s\n") % (k, n)) - elif rb[k] in repo: + elif remotemarks[k] in repo: # add remote bookmarks for changes we already have - localmarks[k] = repo[rb[k]].node() + localmarks[k] = repo[remotemarks[k]].node() changed = True ui.status(_("adding remote bookmark %s\n") % k) @@ -293,19 +292,7 @@ # (new != nullrev has been excluded by the previous check) return True elif repo.obsstore: - # We only need this complicated logic if there is obsolescence - # XXX will probably deserve an optimised revset. - nm = repo.changelog.nodemap - validdests = set([old]) - plen = -1 - # compute the whole set of successors or descendants - while len(validdests) != plen: - plen = len(validdests) - succs = set(c.node() for c in validdests) - 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)) - return new in validdests + return new.node() in obsolete.foreground(repo, [old.node()]) else: + # still an independant clause as it is lazyer (and therefore faster) return old.descendant(new)
--- a/mercurial/branchmap.py Thu Apr 04 16:28:19 2013 -0500 +++ b/mercurial/branchmap.py Thu Apr 18 23:46:26 2013 -0500 @@ -95,7 +95,7 @@ def _hashfiltered(self, repo): """build hash of revision filtered in the current cache - Tracking tipnode and tiprev is not enough to ensure validaty of the + Tracking tipnode and tiprev is not enough to ensure validity of the cache as they do not help to distinct cache that ignored various revision bellow tiprev. @@ -114,9 +114,9 @@ return key def validfor(self, repo): - """Is the cache content valide regarding a repo + """Is the cache content valid regarding a repo - - False when cached tipnode are unknown or if we detect a strip. + - False when cached tipnode is unknown or if we detect a strip. - True when cache is up to date or a subset of current repo.""" try: return ((self.tipnode == repo.changelog.node(self.tiprev))
--- a/mercurial/bundlerepo.py Thu Apr 04 16:28:19 2013 -0500 +++ b/mercurial/bundlerepo.py Thu Apr 18 23:46:26 2013 -0500 @@ -22,18 +22,15 @@ # How it works: # To retrieve a revision, we need to know the offset of the revision in # the bundle (an unbundle object). We store this offset in the index - # (start). - # - # basemap is indexed with revisions coming from the bundle, and it - # maps to the revision that is the base of the corresponding delta. + # (start). The base of the delta is stored in the base field. # # To differentiate a rev in the bundle from a rev in the revlog, we - # check revision against basemap. + # check revision against repotiprev. opener = scmutil.readonlyvfs(opener) revlog.revlog.__init__(self, opener, indexfile) self.bundle = bundle - self.basemap = {} # mapping rev to delta base rev n = len(self) + self.repotiprev = n - 1 chain = None self.bundlerevs = set() # used by 'bundle()' revset expression while True: @@ -68,9 +65,8 @@ baserev = self.rev(deltabase) # start, size, full unc. size, base (unused), link, p1, p2, node - e = (revlog.offset_type(start, 0), size, -1, -1, link, + e = (revlog.offset_type(start, 0), size, -1, baserev, link, self.rev(p1), self.rev(p2), node) - self.basemap[n] = baserev self.index.insert(-1, e) self.nodemap[node] = n self.bundlerevs.add(n) @@ -78,22 +74,22 @@ n += 1 def _chunk(self, rev): - # Warning: in case of bundle, the diff is against self.basemap, - # not against rev - 1 + # Warning: in case of bundle, the diff is against what we stored as + # delta base, not against rev - 1 # XXX: could use some caching - if rev not in self.basemap: + if rev <= self.repotiprev: return revlog.revlog._chunk(self, rev) self.bundle.seek(self.start(rev)) return self.bundle.read(self.length(rev)) def revdiff(self, rev1, rev2): """return or calculate a delta between two revisions""" - if rev1 in self.basemap and rev2 in self.basemap: + if rev1 > self.repotiprev and rev2 > self.repotiprev: # hot path for bundle - revb = self.basemap[rev2] + revb = self.index[rev2][3] if revb == rev1: return self._chunk(rev2) - elif rev1 not in self.basemap and rev2 not in self.basemap: + elif rev1 <= self.repotiprev and rev2 <= self.repotiprev: return revlog.revlog.revdiff(self, rev1, rev2) return mdiff.textdiff(self.revision(self.node(rev1)), @@ -117,12 +113,12 @@ chain = [] iterrev = rev # reconstruct the revision if it is from a changegroup - while iterrev in self.basemap: + while iterrev > self.repotiprev: if self._cache and self._cache[1] == iterrev: text = self._cache[2] break chain.append(iterrev) - iterrev = self.basemap[iterrev] + iterrev = self.index[iterrev][3] if text is None: text = revlog.revlog.revision(self, iterrev) @@ -364,7 +360,8 @@ bundle = None if not localrepo: # use the created uncompressed bundlerepo - localrepo = bundlerepo = bundlerepository(ui, repo.root, fname) + localrepo = bundlerepo = bundlerepository(repo.baseui, repo.root, + fname) # this repo contains local and other now, so filter out local again common = repo.heads() if localrepo:
--- a/mercurial/byterange.py Thu Apr 04 16:28:19 2013 -0500 +++ b/mercurial/byterange.py Thu Apr 18 23:46:26 2013 -0500 @@ -238,7 +238,6 @@ unquote, addclosehook, addinfourl import ftplib import socket -import sys import mimetypes import email @@ -320,7 +319,7 @@ headers = email.message_from_string(headers) return addinfourl(fp, headers, req.get_full_url()) except ftplib.all_errors, msg: - raise IOError('ftp error', msg), sys.exc_info()[2] + raise IOError('ftp error', msg) def connect_ftp(self, user, passwd, host, port, dirs): fw = ftpwrapper(user, passwd, host, port, dirs) @@ -350,7 +349,7 @@ try: self.ftp.nlst(file) except ftplib.error_perm, reason: - raise IOError('ftp error', reason), sys.exc_info()[2] + raise IOError('ftp error', reason) # Restore the transfer mode! self.ftp.voidcmd(cmd) # Try to retrieve as a file @@ -364,7 +363,7 @@ fp = RangeableFileObject(fp, (rest,'')) return (fp, retrlen) elif not str(reason).startswith('550'): - raise IOError('ftp error', reason), sys.exc_info()[2] + raise IOError('ftp error', reason) if not conn: # Set transfer mode to ASCII! self.ftp.voidcmd('TYPE A')
--- a/mercurial/changelog.py Thu Apr 04 16:28:19 2013 -0500 +++ b/mercurial/changelog.py Thu Apr 18 23:46:26 2013 -0500 @@ -183,7 +183,7 @@ """filtered version of revlog.rev""" r = super(changelog, self).rev(node) if r in self.filteredrevs: - raise error.LookupError(node, self.indexfile, _('no node')) + raise error.LookupError(hex(node), self.indexfile, _('no node')) return r def node(self, rev):
--- a/mercurial/cmdutil.py Thu Apr 04 16:28:19 2013 -0500 +++ b/mercurial/cmdutil.py Thu Apr 18 23:46:26 2013 -0500 @@ -12,6 +12,7 @@ import match as matchmod import subrepo, context, repair, graphmod, revset, phases, obsolete import changelog +import bookmarks import lock as lockmod def parsealiases(cmd): @@ -169,7 +170,8 @@ inst.args[0]) def makefileobj(repo, pat, node=None, desc=None, total=None, - seqno=None, revwidth=None, mode='wb', pathname=None): + seqno=None, revwidth=None, mode='wb', modemap={}, + pathname=None): writable = mode not in ('r', 'rb') @@ -195,9 +197,11 @@ return pat if util.safehasattr(pat, 'read') and 'r' in mode: return pat - return open(makefilename(repo, pat, node, desc, total, seqno, revwidth, - pathname), - mode) + fn = makefilename(repo, pat, node, desc, total, seqno, revwidth, pathname) + mode = modemap.get(fn, mode) + if mode == 'wb': + modemap[fn] = 'ab' + return open(fn, mode) def openrevlog(repo, cmd, file_, opts): """opens the changelog, manifest, a filelog or a given revlog""" @@ -538,6 +542,7 @@ total = len(revs) revwidth = max([len(str(rev)) for rev in revs]) + filemode = {} def single(rev, seqno, fp): ctx = repo[rev] @@ -553,7 +558,8 @@ desc_lines = ctx.description().rstrip().split('\n') desc = desc_lines[0] #Commit always has a first line. fp = makefileobj(repo, template, node, desc=desc, total=total, - seqno=seqno, revwidth=revwidth, mode='ab') + seqno=seqno, revwidth=revwidth, mode='wb', + modemap=filemode) if fp != template: shouldclose = True if fp and fp != sys.stdout and util.safehasattr(fp, 'name'): @@ -569,6 +575,7 @@ write("# HG changeset patch\n") write("# User %s\n" % ctx.user()) write("# Date %d %d\n" % ctx.date()) + write("# %s\n" % util.datestr(ctx.date())) if branch and branch != 'default': write("# Branch %s\n" % branch) write("# Node ID %s\n" % hex(node)) @@ -1015,8 +1022,6 @@ follow = opts.get('follow') or opts.get('follow_first') - if not len(repo): - return [] if opts.get('rev'): revs = scmutil.revrange(repo, opts.get('rev')) elif follow: @@ -1203,6 +1208,13 @@ if ff.match(x): wanted.discard(x) + # Choose a small initial window if we will probably only visit a + # few commits. + limit = loglimit(opts) + windowsize = 8 + if limit: + windowsize = min(limit, windowsize) + # Now that wanted is correctly initialized, we can iterate over the # revision range, yielding only revisions in wanted. def iterate(): @@ -1214,7 +1226,7 @@ def want(rev): return rev in wanted - for i, window in increasingwindows(0, len(revs)): + for i, window in increasingwindows(0, len(revs), windowsize): nrevs = [rev for rev in revs[i:i + window] if want(rev)] for rev in sorted(nrevs): fns = fncache.get(rev) @@ -1576,10 +1588,13 @@ forgot.extend(forget) return bad, forgot -def duplicatecopies(repo, rev, p1): - "Reproduce copies found in the source revision in the dirstate for grafts" - for dst, src in copies.pathcopies(repo[p1], repo[rev]).iteritems(): - repo.dirstate.copy(src, dst) +def duplicatecopies(repo, rev, fromrev): + '''reproduce copies from fromrev to rev in the dirstate''' + for dst, src in copies.pathcopies(repo[fromrev], repo[rev]).iteritems(): + # copies.pathcopies returns backward renames, so dst might not + # actually be in the dirstate + if repo.dirstate[dst] in "nma": + repo.dirstate.copy(src, dst) def commit(ui, repo, commitfunc, pats, opts): '''commit the specified files or all outstanding changes''' @@ -1643,7 +1658,13 @@ # Also update it from the intermediate commit or from the wctx extra.update(ctx.extra()) - files = set(old.files()) + if len(old.parents()) > 1: + # ctx.files() isn't reliable for merges, so fall back to the + # slower repo.status() method + files = set([fn for st in repo.status(base, old)[:3] + for fn in st]) + else: + files = set(old.files()) # Second, we use either the commit we just did, or if there were no # changes the parent of the working directory as the version of the @@ -1708,7 +1729,7 @@ extra['amend_source'] = old.hex() new = context.memctx(repo, - parents=[base.node(), nullid], + parents=[base.node(), old.p2().node()], text=message, files=files, filectxfn=filectxfn, @@ -1769,7 +1790,7 @@ finally: if newid is None: repo.dirstate.invalidate() - lockmod.release(wlock, lock) + lockmod.release(lock, wlock) return newid def commiteditor(repo, ctx, subs): @@ -1793,6 +1814,8 @@ edittext.append(_("HG: branch merge")) if ctx.branch(): edittext.append(_("HG: branch '%s'") % ctx.branch()) + if bookmarks.iscurrent(repo): + edittext.append(_("HG: bookmark '%s'") % repo._bookmarkcurrent) edittext.extend([_("HG: subrepo %s") % s for s in subs]) edittext.extend([_("HG: added %s") % f for f in added]) edittext.extend([_("HG: changed %s") % f for f in modified]) @@ -1812,6 +1835,52 @@ return text +def commitstatus(repo, node, branch, bheads=None, opts={}): + ctx = repo[node] + parents = ctx.parents() + + if (not opts.get('amend') and bheads and node not in bheads and not + [x for x in parents if x.node() in bheads and x.branch() == branch]): + repo.ui.status(_('created new head\n')) + # The message is not printed for initial roots. For the other + # changesets, it is printed in the following situations: + # + # Par column: for the 2 parents with ... + # N: null or no parent + # B: parent is on another named branch + # C: parent is a regular non head changeset + # H: parent was a branch head of the current branch + # Msg column: whether we print "created new head" message + # In the following, it is assumed that there already exists some + # initial branch heads of the current branch, otherwise nothing is + # printed anyway. + # + # Par Msg Comment + # N N y additional topo root + # + # B N y additional branch root + # C N y additional topo head + # H N n usual case + # + # B B y weird additional branch root + # C B y branch merge + # H B n merge with named branch + # + # C C y additional head from merge + # C H n merge with a head + # + # H H n head merge: head count decreases + + if not opts.get('close_branch'): + for r in parents: + if r.closesbranch() and r.branch() == branch: + repo.ui.status(_('reopening closed branch head %d\n') % r) + + if repo.ui.debugflag: + repo.ui.write(_('committed changeset %d:%s\n') % (int(ctx), ctx.hex())) + elif repo.ui.verbose: + repo.ui.write(_('committed changeset %d:%s\n') % (int(ctx), ctx)) + def revert(ui, repo, ctx, parents, *pats, **opts): parent, p2 = parents node = ctx.node()
--- a/mercurial/commands.py Thu Apr 04 16:28:19 2013 -0500 +++ b/mercurial/commands.py Thu Apr 18 23:46:26 2013 -0500 @@ -7,9 +7,9 @@ from node import hex, bin, nullid, nullrev, short from lock import release -from i18n import _, gettext +from i18n import _ import os, re, difflib, time, tempfile, errno -import hg, scmutil, util, revlog, extensions, copies, error, bookmarks +import hg, scmutil, util, revlog, copies, error, bookmarks import patch, help, encoding, templatekw, discovery import archival, changegroup, cmdutil, hbisect import sshserver, hgweb, hgweb.server, commandserver @@ -456,14 +456,11 @@ wlock = repo.wlock() try: branch = repo.dirstate.branch() + bheads = repo.branchheads(branch) hg.clean(repo, node, show_stats=False) repo.dirstate.setbranch(branch) - revert_opts = opts.copy() - revert_opts['date'] = None - revert_opts['all'] = True - revert_opts['rev'] = hex(parent) - revert_opts['no_backup'] = None - revert(ui, repo, **revert_opts) + rctx = scmutil.revsingle(repo, hex(parent)) + cmdutil.revert(ui, repo, rctx, repo.dirstate.parents()) if not opts.get('merge') and op1 != node: try: ui.setconfig('ui', 'forcemerge', opts.get('tool', '')) @@ -471,13 +468,18 @@ finally: ui.setconfig('ui', 'forcemerge', '') - commit_opts = opts.copy() - commit_opts['addremove'] = False - if not commit_opts['message'] and not commit_opts['logfile']: + e = cmdutil.commiteditor + if not opts['message'] and not opts['logfile']: # we don't translate commit messages - commit_opts['message'] = "Backed out changeset %s" % short(node) - commit_opts['force_editor'] = True - commit(ui, repo, **commit_opts) + opts['message'] = "Backed out changeset %s" % short(node) + e = cmdutil.commitforceeditor + + def commitfunc(ui, repo, message, match, opts): + return repo.commit(message, opts.get('user'), opts.get('date'), + match, editor=e) + newnode = cmdutil.commit(ui, repo, commitfunc, [], opts) + cmdutil.commitstatus(repo, newnode, branch, bheads) + def nice(node): return '%d:%s' % (repo.changelog.rev(node), short(node)) ui.status(_('changeset %s backs out changeset %s\n') % @@ -806,8 +808,18 @@ scmutil.checknewlabel(repo, mark, 'bookmark') return mark - def checkconflict(repo, mark, force=False): + def checkconflict(repo, mark, force=False, target=None): if mark in marks and not force: + if target: + if marks[mark] == target and target == cur: + # re-activating a bookmark + return + anc = repo.changelog.ancestors([repo[target].rev()]) + bmctx = repo[marks[mark]] + if bmctx.rev() in anc: + ui.status(_("moving bookmark '%s' forward from %s\n") % + (mark, short(bmctx.node()))) + return raise util.Abort(_("bookmark '%s' already exists " "(use -f to force)") % mark) if ((mark in repo.branchmap() or mark == repo.dirstate.branch()) @@ -850,13 +862,15 @@ if inactive and mark == repo._bookmarkcurrent: bookmarks.setcurrent(repo, None) return - checkconflict(repo, mark, force) + tgt = cur if rev: - marks[mark] = scmutil.revsingle(repo, rev).node() - else: - marks[mark] = cur + tgt = scmutil.revsingle(repo, rev).node() + checkconflict(repo, mark, force, tgt) + marks[mark] = tgt if not inactive and cur == marks[mark]: bookmarks.setcurrent(repo, mark) + elif cur != tgt and mark == repo._bookmarkcurrent: + bookmarks.setcurrent(repo, None) marks.write() # Same message whether trying to deactivate the current bookmark (-i @@ -1285,10 +1299,6 @@ extra = {} if opts.get('close_branch'): - if repo['.'].node() not in repo.branchheads(): - # The topo heads set is included in the branch heads set of the - # current branch, so it's sufficient to test branchheads - raise util.Abort(_('can only close branch heads')) extra['close'] = 1 branch = repo[None].branch() @@ -1301,8 +1311,6 @@ old = repo['.'] if old.phase() == phases.public: raise util.Abort(_('cannot amend public changesets')) - if len(old.parents()) > 1: - raise util.Abort(_('cannot amend merge changesets')) if len(repo[None].parents()) > 1: raise util.Abort(_('cannot amend while merging')) if (not obsolete._enabled) and old.children(): @@ -1361,50 +1369,7 @@ ui.status(_("nothing changed\n")) return 1 - ctx = repo[node] - parents = ctx.parents() - - if (not opts.get('amend') and bheads and node not in bheads and not - [x for x in parents if x.node() in bheads and x.branch() == branch]): - ui.status(_('created new head\n')) - # The message is not printed for initial roots. For the other - # changesets, it is printed in the following situations: - # - # Par column: for the 2 parents with ... - # N: null or no parent - # B: parent is on another named branch - # C: parent is a regular non head changeset - # H: parent was a branch head of the current branch - # Msg column: whether we print "created new head" message - # In the following, it is assumed that there already exists some - # initial branch heads of the current branch, otherwise nothing is - # printed anyway. - # - # Par Msg Comment - # N N y additional topo root - # - # B N y additional branch root - # C N y additional topo head - # H N n usual case - # - # B B y weird additional branch root - # C B y branch merge - # H B n merge with named branch - # - # C C y additional head from merge - # C H n merge with a head - # - # H H n head merge: head count decreases - - if not opts.get('close_branch'): - for r in parents: - if r.closesbranch() and r.branch() == branch: - ui.status(_('reopening closed branch head %d\n') % r) - - if ui.debugflag: - ui.write(_('committed changeset %d:%s\n') % (int(ctx), ctx.hex())) - elif ui.verbose: - ui.write(_('committed changeset %d:%s\n') % (int(ctx), ctx)) + cmdutil.commitstatus(repo, node, branch, bheads, opts) @command('copy|cp', [('A', 'after', None, _('record a copy that has already occurred')), @@ -2101,6 +2066,26 @@ flags = repo.known([bin(s) for s in ids]) ui.write("%s\n" % ("".join([f and "1" or "0" for f in flags]))) +@command('debuglabelcomplete', [], _('LABEL...')) +def debuglabelcomplete(ui, repo, *args): + '''complete "labels" - tags, open branch names, bookmark names''' + + labels = set() + labels.update(t[0] for t in repo.tagslist()) + labels.update(repo._bookmarks.keys()) + for heads in repo.branchmap().itervalues(): + for h in heads: + ctx = repo[h] + if not ctx.closesbranch(): + labels.add(ctx.branch()) + completions = set() + if not args: + args = [''] + for a in args: + completions.update(l for l in labels if l.startswith(a)) + ui.write('\n'.join(sorted(completions))) + ui.write('\n') + @command('debugobsolete', [('', 'flags', 0, _('markers flag')), ] + commitopts2, @@ -2150,6 +2135,75 @@ sorted(m.metadata().items())))) ui.write('\n') +@command('debugpathcomplete', + [('f', 'full', None, _('complete an entire path')), + ('n', 'normal', None, _('show only normal files')), + ('a', 'added', None, _('show only added files')), + ('r', 'removed', None, _('show only removed files'))], + _('FILESPEC...')) +def debugpathcomplete(ui, repo, *specs, **opts): + '''complete part or all of a tracked path + + This command supports shells that offer path name completion. It + currently completes only files already known to the dirstate. + + Completion extends only to the next path segment unless + --full is specified, in which case entire paths are used.''' + + def complete(path, acceptable): + dirstate = repo.dirstate + spec = os.path.normpath(os.path.join(os.getcwd(), path)) + rootdir = repo.root + os.sep + if spec != repo.root and not spec.startswith(rootdir): + return [], [] + if os.path.isdir(spec): + spec += '/' + spec = spec[len(rootdir):] + fixpaths = os.sep != '/' + if fixpaths: + spec = spec.replace(os.sep, '/') + speclen = len(spec) + fullpaths = opts['full'] + files, dirs = set(), set() + adddir, addfile = dirs.add, files.add + for f, st in dirstate.iteritems(): + if f.startswith(spec) and st[0] in acceptable: + if fixpaths: + f = f.replace('/', os.sep) + if fullpaths: + addfile(f) + continue + s = f.find(os.sep, speclen) + if s >= 0: + adddir(f[:s + 1]) + else: + addfile(f) + return files, dirs + + acceptable = '' + if opts['normal']: + acceptable += 'nm' + if opts['added']: + acceptable += 'a' + if opts['removed']: + acceptable += 'r' + cwd = repo.getcwd() + if not specs: + specs = ['.'] + + files, dirs = set(), set() + for spec in specs: + f, d = complete(spec, acceptable or 'nmar') + files.update(f) + dirs.update(d) + if not files and len(dirs) == 1: + # force the shell to consider a completion that matches one + # directory and zero files to be ambiguous + dirs.add(iter(dirs).next() + '.') + files.update(dirs) + ui.write('\n'.join(repo.pathto(p, cwd) for p in sorted(files))) + ui.write('\n') + @command('debugpushkey', [], _('REPO NAMESPACE [KEY OLD NEW]')) def debugpushkey(ui, repopath, namespace, *keyinfo, **opts): '''access the pushkey key/value protocol @@ -2192,11 +2246,21 @@ (abs(pa._depth - pb._depth), pvec._hamming(pa._vec, pb._vec), pa.distance(pb), rel)) -@command('debugrebuildstate', +@command('debugrebuilddirstate|debugrebuildstate', [('r', 'rev', '', _('revision to rebuild to'), _('REV'))], - _('[-r REV] [REV]')) -def debugrebuildstate(ui, repo, rev="tip"): - """rebuild the dirstate as it would look like for the given revision""" + _('[-r REV]')) +def debugrebuilddirstate(ui, repo, rev): + """rebuild the dirstate as it would look like for the given revision + + If no revision is specified the first current parent will be used. + + The dirstate will be set to the files of the given revision. + The actual working directory content or existing dirstate + information such as adds or removes is not considered. + + One use of this command is to make the next :hg:`status` invocation + check the actual file content. + """ ctx = scmutil.revsingle(repo, rev) wlock = repo.wlock() try: @@ -2430,7 +2494,7 @@ finally: wlock.release() -@command('debugstate', +@command('debugdirstate|debugstate', [('', 'nodates', None, _('do not display the saved mtime')), ('', 'datesort', None, _('sort by saved mtime'))], _('[OPTION]...')) @@ -2655,11 +2719,12 @@ ('', 'switch-parent', None, _('diff against the second parent')), ('r', 'rev', [], _('revisions to export'), _('REV')), ] + diffopts, - _('[OPTION]... [-o OUTFILESPEC] [-r] REV...')) + _('[OPTION]... [-o OUTFILESPEC] [-r] [REV]...')) def export(ui, repo, *changesets, **opts): """dump the header and diffs for one or more changesets Print the changeset header and diffs for one or more revisions. + If no revision is given, the parent of the working directory is used. The information shown in the changeset header is: author, date, branch name (if non-default), changeset hash, parent(s) and commit @@ -2715,6 +2780,8 @@ Returns 0 on success. """ changesets += tuple(opts.get('rev', [])) + if not changesets: + changesets = ['.'] revs = scmutil.revrange(repo, changesets) if not revs: raise util.Abort(_("export requires at least one changeset")) @@ -2861,9 +2928,13 @@ return -1 # check for ancestors of dest branch - for rev in repo.revs('::. and %ld', revs): - ui.warn(_('skipping ancestor revision %s\n') % rev) - revs.remove(rev) + crev = repo['.'].rev() + ancestors = repo.changelog.ancestors([crev], inclusive=True) + # don't mutate while iterating, create a copy + for rev in list(revs): + if rev in ancestors: + ui.warn(_('skipping ancestor revision %s\n') % rev) + revs.remove(rev) if not revs: return -1 @@ -2877,7 +2948,9 @@ # check ancestors for earlier grafts ui.debug('scanning for duplicate grafts\n') - for ctx in repo.set("::. - ::%ld", revs): + + for rev in repo.changelog.findmissingrevs(revs, [crev]): + ctx = repo[rev] n = ctx.extra().get('source') if n in ids: r = repo[n].rev() @@ -2891,7 +2964,7 @@ elif ctx.hex() in ids: r = ids[ctx.hex()] ui.warn(_('skipping already grafted revision %s ' - '(was grafted from %d)\n') % (r, ctx.rev())) + '(was grafted from %d)\n') % (r, rev)) revs.remove(r) if not revs: return -1 @@ -3001,7 +3074,7 @@ if opts.get('ignore_case'): reflags |= re.I try: - regexp = re.compile(pattern, reflags) + regexp = util.compilere(pattern, reflags) except re.error, inst: ui.warn(_("grep: invalid match pattern: %s\n") % inst) return 1 @@ -3255,7 +3328,7 @@ ('k', 'keyword', '', _('show topics matching keyword')), ], _('[-ec] [TOPIC]')) -def help_(ui, name=None, unknowncmd=False, full=True, **opts): +def help_(ui, name=None, **opts): """show help for a given topic or a help overview With no arguments, print a list of commands with short help messages. @@ -3268,291 +3341,9 @@ textwidth = min(ui.termwidth(), 80) - 2 - def helpcmd(name): - try: - aliases, entry = cmdutil.findcmd(name, table, strict=unknowncmd) - except error.AmbiguousCommand, inst: - # py3k fix: except vars can't be used outside the scope of the - # except block, nor can be used inside a lambda. python issue4617 - prefix = inst.args[0] - select = lambda c: c.lstrip('^').startswith(prefix) - rst = helplist(select) - return rst - - rst = [] - - # check if it's an invalid alias and display its error if it is - if getattr(entry[0], 'badalias', False): - if not unknowncmd: - ui.pushbuffer() - entry[0](ui) - rst.append(ui.popbuffer()) - return rst - - # synopsis - if len(entry) > 2: - if entry[2].startswith('hg'): - rst.append("%s\n" % entry[2]) - else: - rst.append('hg %s %s\n' % (aliases[0], entry[2])) - else: - rst.append('hg %s\n' % aliases[0]) - # aliases - if full and not ui.quiet and len(aliases) > 1: - rst.append(_("\naliases: %s\n") % ', '.join(aliases[1:])) - rst.append('\n') - - # description - doc = gettext(entry[0].__doc__) - if not doc: - doc = _("(no help text available)") - if util.safehasattr(entry[0], 'definition'): # aliased command - if entry[0].definition.startswith('!'): # shell alias - doc = _('shell alias for::\n\n %s') % entry[0].definition[1:] - else: - doc = _('alias for: hg %s\n\n%s') % (entry[0].definition, doc) - doc = doc.splitlines(True) - if ui.quiet or not full: - rst.append(doc[0]) - else: - rst.extend(doc) - rst.append('\n') - - # check if this command shadows a non-trivial (multi-line) - # extension help text - try: - mod = extensions.find(name) - doc = gettext(mod.__doc__) or '' - if '\n' in doc.strip(): - msg = _('use "hg help -e %s" to show help for ' - 'the %s extension') % (name, name) - rst.append('\n%s\n' % msg) - except KeyError: - pass - - # options - if not ui.quiet and entry[1]: - rst.append('\n%s\n\n' % _("options:")) - rst.append(help.optrst(entry[1], ui.verbose)) - - if ui.verbose: - rst.append('\n%s\n\n' % _("global options:")) - rst.append(help.optrst(globalopts, ui.verbose)) - - if not ui.verbose: - if not full: - rst.append(_('\nuse "hg help %s" to show the full help text\n') - % name) - elif not ui.quiet: - omitted = _('use "hg -v help %s" to show more complete' - ' help and the global options') % name - notomitted = _('use "hg -v help %s" to show' - ' the global options') % name - help.indicateomitted(rst, omitted, notomitted) - - return rst - - - def helplist(select=None): - # list of commands - if name == "shortlist": - header = _('basic commands:\n\n') - else: - header = _('list of commands:\n\n') - - h = {} - cmds = {} - for c, e in table.iteritems(): - f = c.split("|", 1)[0] - if select and not select(f): - continue - if (not select and name != 'shortlist' and - e[0].__module__ != __name__): - continue - if name == "shortlist" and not f.startswith("^"): - continue - f = f.lstrip("^") - if not ui.debugflag and f.startswith("debug"): - continue - doc = e[0].__doc__ - if doc and 'DEPRECATED' in doc and not ui.verbose: - continue - doc = gettext(doc) - if not doc: - doc = _("(no help text available)") - h[f] = doc.splitlines()[0].rstrip() - cmds[f] = c.lstrip("^") - - rst = [] - if not h: - if not ui.quiet: - rst.append(_('no commands defined\n')) - return rst - - if not ui.quiet: - rst.append(header) - fns = sorted(h) - for f in fns: - if ui.verbose: - commands = cmds[f].replace("|",", ") - rst.append(" :%s: %s\n" % (commands, h[f])) - else: - rst.append(' :%s: %s\n' % (f, h[f])) - - if not name: - exts = help.listexts(_('enabled extensions:'), extensions.enabled()) - if exts: - rst.append('\n') - rst.extend(exts) - - rst.append(_("\nadditional help topics:\n\n")) - topics = [] - for names, header, doc in help.helptable: - topics.append((names[0], header)) - for t, desc in topics: - rst.append(" :%s: %s\n" % (t, desc)) - - optlist = [] - if not ui.quiet: - if ui.verbose: - optlist.append((_("global options:"), globalopts)) - if name == 'shortlist': - optlist.append((_('use "hg help" for the full list ' - 'of commands'), ())) - else: - if name == 'shortlist': - msg = _('use "hg help" for the full list of commands ' - 'or "hg -v" for details') - elif name and not full: - msg = _('use "hg help %s" to show the full help ' - 'text') % name - else: - msg = _('use "hg -v help%s" to show builtin aliases and ' - 'global options') % (name and " " + name or "") - optlist.append((msg, ())) - - if optlist: - for title, options in optlist: - rst.append('\n%s\n' % title) - if options: - rst.append('\n%s\n' % help.optrst(options, ui.verbose)) - return rst - - def helptopic(name): - for names, header, doc in help.helptable: - if name in names: - break - else: - raise error.UnknownCommand(name) - - rst = ["%s\n\n" % header] - # description - if not doc: - rst.append(" %s\n" % _("(no help text available)")) - if util.safehasattr(doc, '__call__'): - rst += [" %s\n" % l for l in doc().splitlines()] - - if not ui.verbose: - omitted = (_('use "hg help -v %s" to show more complete help') % - name) - help.indicateomitted(rst, omitted) - - try: - cmdutil.findcmd(name, table) - rst.append(_('\nuse "hg help -c %s" to see help for ' - 'the %s command\n') % (name, name)) - except error.UnknownCommand: - pass - return rst - - def helpext(name): - try: - mod = extensions.find(name) - doc = gettext(mod.__doc__) or _('no help text available') - except KeyError: - mod = None - doc = extensions.disabledext(name) - if not doc: - raise error.UnknownCommand(name) - - if '\n' not in doc: - head, tail = doc, "" - else: - head, tail = doc.split('\n', 1) - rst = [_('%s extension - %s\n\n') % (name.split('.')[-1], head)] - if tail: - rst.extend(tail.splitlines(True)) - rst.append('\n') - - if not ui.verbose: - omitted = (_('use "hg help -v %s" to show more complete help') % - name) - help.indicateomitted(rst, omitted) - - if mod: - try: - ct = mod.cmdtable - except AttributeError: - ct = {} - modcmds = set([c.split('|', 1)[0] for c in ct]) - rst.extend(helplist(modcmds.__contains__)) - else: - rst.append(_('use "hg help extensions" for information on enabling ' - 'extensions\n')) - return rst - - def helpextcmd(name): - cmd, ext, mod = extensions.disabledcmd(ui, name, - ui.configbool('ui', 'strict')) - doc = gettext(mod.__doc__).splitlines()[0] - - rst = help.listexts(_("'%s' is provided by the following " - "extension:") % cmd, {ext: doc}, indent=4) - rst.append('\n') - rst.append(_('use "hg help extensions" for information on enabling ' - 'extensions\n')) - return rst - - - rst = [] - kw = opts.get('keyword') - if kw: - matches = help.topicmatch(kw) - for t, title in (('topics', _('Topics')), - ('commands', _('Commands')), - ('extensions', _('Extensions')), - ('extensioncommands', _('Extension Commands'))): - if matches[t]: - rst.append('%s:\n\n' % title) - rst.extend(minirst.maketable(sorted(matches[t]), 1)) - rst.append('\n') - elif name and name != 'shortlist': - i = None - if unknowncmd: - queries = (helpextcmd,) - elif opts.get('extension'): - queries = (helpext,) - elif opts.get('command'): - queries = (helpcmd,) - else: - queries = (helptopic, helpcmd, helpext, helpextcmd) - for f in queries: - try: - rst = f(name) - i = None - break - except error.UnknownCommand, inst: - i = inst - if i: - raise i - else: - # program name - if not ui.quiet: - rst = [_("Mercurial Distributed SCM\n"), '\n'] - rst.extend(helplist()) - keep = ui.verbose and ['verbose'] or [] - text = ''.join(rst) + text = help.help_(ui, name, **opts) + formatted, pruned = minirst.format(text, textwidth, keep=keep) if 'verbose' in pruned: keep.append('omitted') @@ -3812,11 +3603,6 @@ wlock = lock = tr = None msgs = [] - def checkexact(repo, n, nodeid): - if opts.get('exact') and hex(n) != nodeid: - repo.rollback() - raise util.Abort(_('patch is damaged or loses information')) - def tryone(ui, hunk, parents): tmpname, message, user, date, branch, nodeid, p1, p2 = \ patch.extract(ui, hunk) @@ -3888,7 +3674,6 @@ n = repo.commit(message, opts.get('user') or user, opts.get('date') or date, match=m, editor=editor) - checkexact(repo, n, nodeid) else: if opts.get('exact') or opts.get('import_branch'): branch = branch or 'default' @@ -3910,9 +3695,10 @@ editor=cmdutil.commiteditor) repo.savecommitmessage(memctx.description()) n = memctx.commit() - checkexact(repo, n, nodeid) finally: store.close() + if opts.get('exact') and hex(n) != nodeid: + raise util.Abort(_('patch is damaged or loses information')) if n: # i18n: refers to a short changeset id msg = _('created %s') % short(n) @@ -4257,10 +4043,10 @@ displayer.show(ctx, copies=copies, matchfn=revmatchfn) for ctx in cmdutil.walkchangerevs(repo, matchfn, opts, prep): + if displayer.flush(ctx.rev()): + count += 1 if count == limit: break - if displayer.flush(ctx.rev()): - count += 1 displayer.close() @command('manifest', @@ -4723,14 +4509,15 @@ ui.status(_('pulling from %s\n') % util.hidepassword(source)) revs, checkout = hg.addbranchrevs(repo, other, branches, opts.get('rev')) + remotebookmarks = other.listkeys('bookmarks') + if opts.get('bookmark'): if not revs: revs = [] - rb = other.listkeys('bookmarks') for b in opts['bookmark']: - if b not in rb: + if b not in remotebookmarks: raise util.Abort(_('remote bookmark %s not found!') % b) - revs.append(rb[b]) + revs.append(remotebookmarks[b]) if revs: try: @@ -4741,7 +4528,7 @@ raise util.Abort(err) modheads = repo.pull(other, heads=revs, force=opts.get('force')) - bookmarks.updatefromremote(ui, repo, other, source) + bookmarks.updatefromremote(ui, repo, remotebookmarks, source) if checkout: checkout = str(repo.changelog.rev(other.lookup(checkout))) repo._subtoppath = source @@ -4757,7 +4544,7 @@ for b in opts['bookmark']: # explicit pull overrides local bookmark if any ui.status(_("importing bookmark %s\n") % b) - marks[b] = repo[rb[b]].node() + marks[b] = repo[remotebookmarks[b]].node() marks.write() return ret @@ -5318,9 +5105,9 @@ if not repo: raise error.RepoError(_("there is no Mercurial repository" " here (.hg not found)")) - o = repo.root - - app = hgweb.hgweb(o, baseui=ui) + o = repo + + app = hgweb.hgweb(o, baseui=baseui) class service(object): def init(self): @@ -5577,12 +5364,11 @@ # i18n: column positioning for "hg summary" ui.write(_('bookmarks:'), label='log.bookmark') if current is not None: - try: - marks.remove(current) + if current in marks: ui.write(' *' + current, label='bookmarks.current') - except ValueError: - # current bookmark not in parent ctx marks - pass + marks.remove(current) + else: + ui.write(' [%s]' % current, label='bookmarks.current') for m in marks: ui.write(' ' + m, label='log.bookmark') ui.write('\n', label='log.bookmark') @@ -5674,25 +5460,32 @@ if opts.get('remote'): t = [] source, branches = hg.parseurl(ui.expandpath('default')) + sbranch = branches[0] other = hg.peer(repo, {}, source) revs, checkout = hg.addbranchrevs(repo, other, branches, opts.get('rev')) + if revs: + revs = [other.lookup(rev) for rev in revs] ui.debug('comparing with %s\n' % util.hidepassword(source)) repo.ui.pushbuffer() - commoninc = discovery.findcommonincoming(repo, other) + commoninc = discovery.findcommonincoming(repo, other, heads=revs) _common, incoming, _rheads = commoninc repo.ui.popbuffer() if incoming: t.append(_('1 or more incoming')) dest, branches = hg.parseurl(ui.expandpath('default-push', 'default')) + dbranch = branches[0] revs, checkout = hg.addbranchrevs(repo, repo, branches, None) if source != dest: other = hg.peer(repo, {}, dest) + ui.debug('comparing with %s\n' % util.hidepassword(dest)) + if (source != dest or (sbranch is not None and sbranch != dbranch)): commoninc = None - ui.debug('comparing with %s\n' % util.hidepassword(dest)) + if revs: + revs = [repo.lookup(rev) for rev in revs] repo.ui.pushbuffer() - outgoing = discovery.findcommonoutgoing(repo, other, + outgoing = discovery.findcommonoutgoing(repo, other, onlyheads=revs, commoninc=commoninc) repo.ui.popbuffer() o = outgoing.missing @@ -5818,7 +5611,7 @@ # don't allow tagging the null rev if (not opts.get('remove') and scmutil.revsingle(repo, rev_).rev() == nullrev): - raise util.Abort(_("null revision specified")) + raise util.Abort(_("cannot tag null revision")) repo.tag(names, r, message, opts.get('local'), opts.get('user'), date) finally:
--- a/mercurial/context.py Thu Apr 04 16:28:19 2013 -0500 +++ b/mercurial/context.py Thu Apr 18 23:46:26 2013 -0500 @@ -374,16 +374,7 @@ @propertycache def _dirs(self): - dirs = set() - for f in self._manifest: - pos = f.rfind('/') - while pos != -1: - f = f[:pos] - if f in dirs: - break # dirs already contains this and above - dirs.add(f) - pos = f.rfind('/') - return dirs + return scmutil.dirs(self._manifest) def dirs(self): return self._dirs @@ -426,12 +417,12 @@ # repository is filtered this may lead to `filectx` trying to build # `changectx` for filtered revision. In such case we fallback to # creating `changectx` on the unfiltered version of the reposition. - # This fallback should not be an issue because`changectx` from - # `filectx` are not used in complexe operation that care about + # This fallback should not be an issue because `changectx` from + # `filectx` are not used in complex operations that care about # filtering. # # This fallback is a cheap and dirty fix that prevent several - # crash. It does not ensure the behavior is correct. However the + # crashes. It does not ensure the behavior is correct. However the # behavior was not correct before filtering either and "incorrect # behavior" is seen as better as "crash" # @@ -698,7 +689,8 @@ needed = {base: 1} while visit: f = visit[-1] - if f not in pcache: + pcached = f in pcache + if not pcached: pcache[f] = parents(f) ready = True @@ -707,14 +699,21 @@ if p not in hist: ready = False visit.append(p) + if not pcached: needed[p] = needed.get(p, 0) + 1 if ready: visit.pop() - curr = decorate(f.data(), f) + reusable = f in hist + if reusable: + curr = hist[f] + else: + curr = decorate(f.data(), f) for p in pl: - curr = pair(hist[p], curr) + if not reusable: + curr = pair(hist[p], curr) if needed[p] == 1: del hist[p] + del needed[p] else: needed[p] -= 1 @@ -765,7 +764,7 @@ return pl a, b = (self._path, self._filenode), (fc2._path, fc2._filenode) - v = ancestor.ancestor(a, b, parents) + v = ancestor.genericancestor(a, b, parents) if v: f, n = v return filectx(self._repo, f, fileid=n, filelog=flcache[f]) @@ -1138,8 +1137,24 @@ finally: wlock.release() + def markcommitted(self, node): + """Perform post-commit cleanup necessary after committing this ctx + + Specifically, this updates backing stores this working context + wraps to reflect the fact that the changes reflected by this + workingctx have been committed. For example, it marks + modified and added files as normal in the dirstate. + + """ + + for f in self.modified() + self.added(): + self._repo.dirstate.normal(f) + for f in self.removed(): + self._repo.dirstate.drop(f) + self._repo.dirstate.setparents(node) + def dirs(self): - return set(self._repo.dirstate.dirs()) + return self._repo.dirstate.dirs() class workingfilectx(filectx): """A workingfilectx object makes access to data related to a particular
--- a/mercurial/copies.py Thu Apr 04 16:28:19 2013 -0500 +++ b/mercurial/copies.py Thu Apr 18 23:46:26 2013 -0500 @@ -133,11 +133,13 @@ # we currently don't try to find where old files went, too expensive # this means we can miss a case like 'hg rm b; hg cp a b' cm = {} - for f in b: - if f not in a: - ofctx = _tracefile(b[f], a) - if ofctx: - cm[f] = ofctx.path() + missing = set(b.manifest().iterkeys()) + missing.difference_update(a.manifest().iterkeys()) + + for f in missing: + ofctx = _tracefile(b[f], a) + if ofctx: + cm[f] = ofctx.path() # combine copies from dirstate if necessary if w is not None: @@ -333,8 +335,8 @@ # generate a directory move map d1, d2 = c1.dirs(), c2.dirs() - d1.add('') - d2.add('') + d1.addpath('/') + d2.addpath('/') invalid = set() dirmove = {}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/mercurial/dicthelpers.py Thu Apr 18 23:46:26 2013 -0500 @@ -0,0 +1,55 @@ +# dicthelpers.py - helper routines for Python dicts +# +# Copyright 2013 Facebook +# +# This software may be used and distributed according to the terms of the +# GNU General Public License version 2 or any later version. + +def diff(d1, d2, default=None): + '''Return all key-value pairs that are different between d1 and d2. + + This includes keys that are present in one dict but not the other, and + keys whose values are different. The return value is a dict with values + being pairs of values from d1 and d2 respectively, and missing values + treated as default, so if a value is missing from one dict and the same as + default in the other, it will not be returned.''' + res = {} + if d1 is d2: + # same dict, so diff is empty + return res + + for k1, v1 in d1.iteritems(): + v2 = d2.get(k1, default) + if v1 != v2: + res[k1] = (v1, v2) + + for k2 in d2: + if k2 not in d1: + v2 = d2[k2] + if v2 != default: + res[k2] = (default, v2) + + return res + +def join(d1, d2, default=None): + '''Return all key-value pairs from both d1 and d2. + + This is akin to an outer join in relational algebra. The return value is a + dict with values being pairs of values from d1 and d2 respectively, and + missing values represented as default.''' + res = {} + + for k1, v1 in d1.iteritems(): + if k1 in d2: + res[k1] = (v1, d2[k1]) + else: + res[k1] = (v1, default) + + if d1 is d2: + return res + + for k2 in d2: + if k2 not in d1: + res[k2] = (default, d2[k2]) + + return res
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/mercurial/dirs.c Thu Apr 18 23:46:26 2013 -0500 @@ -0,0 +1,305 @@ +/* + dirs.c - dynamic directory diddling for dirstates + + Copyright 2013 Facebook + + This software may be used and distributed according to the terms of + the GNU General Public License, incorporated herein by reference. +*/ + +#define PY_SSIZE_T_CLEAN +#include <Python.h> +#include "util.h" + +/* + * This is a multiset of directory names, built from the files that + * appear in a dirstate or manifest. + * + * A few implementation notes: + * + * We modify Python integers for refcounting, but those integers are + * never visible to Python code. + * + * We mutate strings in-place, but leave them immutable once they can + * be seen by Python code. + */ +typedef struct { + PyObject_HEAD + PyObject *dict; +} dirsObject; + +static inline Py_ssize_t _finddir(PyObject *path, Py_ssize_t pos) +{ + const char *s = PyString_AS_STRING(path); + + while (pos != -1) { + if (s[pos] == '/') + break; + pos -= 1; + } + + return pos; +} + +static int _addpath(PyObject *dirs, PyObject *path) +{ + const char *cpath = PyString_AS_STRING(path); + Py_ssize_t pos = PyString_GET_SIZE(path); + PyObject *key = NULL; + int ret = -1; + + while ((pos = _finddir(path, pos - 1)) != -1) { + PyObject *val; + + /* It's likely that every prefix already has an entry + in our dict. Try to avoid allocating and + deallocating a string for each prefix we check. */ + if (key != NULL) + ((PyStringObject *)key)->ob_shash = -1; + else { + /* Force Python to not reuse a small shared string. */ + key = PyString_FromStringAndSize(cpath, + pos < 2 ? 2 : pos); + if (key == NULL) + goto bail; + } + PyString_GET_SIZE(key) = pos; + PyString_AS_STRING(key)[pos] = '\0'; + + val = PyDict_GetItem(dirs, key); + if (val != NULL) { + PyInt_AS_LONG(val) += 1; + continue; + } + + /* Force Python to not reuse a small shared int. */ + val = PyInt_FromLong(0x1eadbeef); + + if (val == NULL) + goto bail; + + PyInt_AS_LONG(val) = 1; + ret = PyDict_SetItem(dirs, key, val); + Py_DECREF(val); + if (ret == -1) + goto bail; + Py_CLEAR(key); + } + ret = 0; + +bail: + Py_XDECREF(key); + + return ret; +} + +static int _delpath(PyObject *dirs, PyObject *path) +{ + Py_ssize_t pos = PyString_GET_SIZE(path); + PyObject *key = NULL; + int ret = -1; + + while ((pos = _finddir(path, pos - 1)) != -1) { + PyObject *val; + + key = PyString_FromStringAndSize(PyString_AS_STRING(path), pos); + + if (key == NULL) + goto bail; + + val = PyDict_GetItem(dirs, key); + if (val == NULL) { + PyErr_SetString(PyExc_ValueError, + "expected a value, found none"); + goto bail; + } + + if (--PyInt_AS_LONG(val) <= 0 && + PyDict_DelItem(dirs, key) == -1) + goto bail; + Py_CLEAR(key); + } + ret = 0; + +bail: + Py_XDECREF(key); + + return ret; +} + +static int dirs_fromdict(PyObject *dirs, PyObject *source, char skipchar) +{ + PyObject *key, *value; + Py_ssize_t pos = 0; + + while (PyDict_Next(source, &pos, &key, &value)) { + if (!PyString_Check(key)) { + PyErr_SetString(PyExc_TypeError, "expected string key"); + return -1; + } + if (skipchar) { + PyObject *st; + + if (!PyTuple_Check(value) || + PyTuple_GET_SIZE(value) == 0) { + PyErr_SetString(PyExc_TypeError, + "expected non-empty tuple"); + return -1; + } + + st = PyTuple_GET_ITEM(value, 0); + + if (!PyString_Check(st) || PyString_GET_SIZE(st) == 0) { + PyErr_SetString(PyExc_TypeError, + "expected non-empty string " + "at tuple index 0"); + return -1; + } + + if (PyString_AS_STRING(st)[0] == skipchar) + continue; + } + + if (_addpath(dirs, key) == -1) + return -1; + } + + return 0; +} + +static int dirs_fromiter(PyObject *dirs, PyObject *source) +{ + PyObject *iter, *item = NULL; + int ret; + + iter = PyObject_GetIter(source); + if (iter == NULL) + return -1; + + while ((item = PyIter_Next(iter)) != NULL) { + if (!PyString_Check(item)) { + PyErr_SetString(PyExc_TypeError, "expected string"); + break; + } + + if (_addpath(dirs, item) == -1) + break; + Py_CLEAR(item); + } + + ret = PyErr_Occurred() ? -1 : 0; + Py_XDECREF(item); + return ret; +} + +/* + * Calculate a refcounted set of directory names for the files in a + * dirstate. + */ +static int dirs_init(dirsObject *self, PyObject *args) +{ + PyObject *dirs = NULL, *source = NULL; + char skipchar = 0; + int ret = -1; + + self->dict = NULL; + + if (!PyArg_ParseTuple(args, "|Oc:__init__", &source, &skipchar)) + return -1; + + dirs = PyDict_New(); + + if (dirs == NULL) + return -1; + + if (source == NULL) + ret = 0; + else if (PyDict_Check(source)) + ret = dirs_fromdict(dirs, source, skipchar); + else if (skipchar) + PyErr_SetString(PyExc_ValueError, + "skip character is only supported " + "with a dict source"); + else + ret = dirs_fromiter(dirs, source); + + if (ret == -1) + Py_XDECREF(dirs); + else + self->dict = dirs; + + return ret; +} + +PyObject *dirs_addpath(dirsObject *self, PyObject *args) +{ + PyObject *path; + + if (!PyArg_ParseTuple(args, "O!:addpath", &PyString_Type, &path)) + return NULL; + + if (_addpath(self->dict, path) == -1) + return NULL; + + Py_RETURN_NONE; +} + +static PyObject *dirs_delpath(dirsObject *self, PyObject *args) +{ + PyObject *path; + + if (!PyArg_ParseTuple(args, "O!:delpath", &PyString_Type, &path)) + return NULL; + + if (_delpath(self->dict, path) == -1) + return NULL; + + Py_RETURN_NONE; +} + +static int dirs_contains(dirsObject *self, PyObject *value) +{ + return PyString_Check(value) ? PyDict_Contains(self->dict, value) : 0; +} + +static void dirs_dealloc(dirsObject *self) +{ + Py_XDECREF(self->dict); + PyObject_Del(self); +} + +static PyObject *dirs_iter(dirsObject *self) +{ + return PyObject_GetIter(self->dict); +} + +static PySequenceMethods dirs_sequence_methods; + +static PyMethodDef dirs_methods[] = { + {"addpath", (PyCFunction)dirs_addpath, METH_VARARGS, "add a path"}, + {"delpath", (PyCFunction)dirs_delpath, METH_VARARGS, "remove a path"}, + {NULL} /* Sentinel */ +}; + +static PyTypeObject dirsType = { PyObject_HEAD_INIT(NULL) }; + +void dirs_module_init(PyObject *mod) +{ + dirs_sequence_methods.sq_contains = (objobjproc)dirs_contains; + dirsType.tp_name = "parsers.dirs"; + dirsType.tp_new = PyType_GenericNew; + dirsType.tp_basicsize = sizeof(dirsObject); + dirsType.tp_dealloc = (destructor)dirs_dealloc; + dirsType.tp_as_sequence = &dirs_sequence_methods; + dirsType.tp_flags = Py_TPFLAGS_DEFAULT; + dirsType.tp_doc = "dirs"; + dirsType.tp_iter = (getiterfunc)dirs_iter; + dirsType.tp_methods = dirs_methods; + dirsType.tp_init = (initproc)dirs_init; + + if (PyType_Ready(&dirsType) < 0) + return; + Py_INCREF(&dirsType); + + PyModule_AddObject(mod, "dirs", (PyObject *)&dirsType); +}
--- a/mercurial/dirstate.py Thu Apr 04 16:28:19 2013 -0500 +++ b/mercurial/dirstate.py Thu Apr 18 23:46:26 2013 -0500 @@ -9,10 +9,8 @@ from node import nullid from i18n import _ import scmutil, util, ignore, osutil, parsers, encoding -import struct, os, stat, errno -import cStringIO +import os, stat, errno, gc -_format = ">cllll" propertycache = util.propertycache filecache = scmutil.filecache _rangemask = 0x7fffffff @@ -27,26 +25,6 @@ def join(self, obj, fname): return obj._join(fname) -def _finddirs(path): - pos = path.rfind('/') - while pos != -1: - yield path[:pos] - pos = path.rfind('/', 0, pos) - -def _incdirs(dirs, path): - for base in _finddirs(path): - if base in dirs: - dirs[base] += 1 - return - dirs[base] = 1 - -def _decdirs(dirs, path): - for base in _finddirs(path): - if dirs[base] > 1: - dirs[base] -= 1 - return - del dirs[base] - class dirstate(object): def __init__(self, opener, ui, root, validate): @@ -115,11 +93,7 @@ @propertycache def _dirs(self): - dirs = {} - for f, s in self._map.iteritems(): - if s[0] != 'r': - _incdirs(dirs, f) - return dirs + return scmutil.dirs(self._map, 'r') def dirs(self): return self._dirs @@ -156,11 +130,14 @@ def flagfunc(self, buildfallback): if self._checklink and self._checkexec: def f(x): - p = self._join(x) - if os.path.islink(p): - return 'l' - if util.isexec(p): - return 'x' + try: + st = os.lstat(self._join(x)) + if util.statislink(st): + return 'l' + if util.statisexec(st): + return 'x' + except OSError: + pass return '' return f @@ -225,6 +202,9 @@ for x in sorted(self._map): yield x + def iteritems(self): + return self._map.iteritems() + def parents(self): return [self._validate(p) for p in self._pl] @@ -287,7 +267,23 @@ if not st: return - p = parsers.parse_dirstate(self._map, self._copymap, st) + # Python's garbage collector triggers a GC each time a certain number + # of container objects (the number being defined by + # gc.get_threshold()) are allocated. parse_dirstate creates a tuple + # for each file in the dirstate. The C version then immediately marks + # them as not to be tracked by the collector. However, this has no + # effect on when GCs are triggered, only on what objects the GC looks + # into. This means that O(number of files) GCs are unavoidable. + # Depending on when in the process's lifetime the dirstate is parsed, + # this can get very expensive. As a workaround, disable GC while + # parsing the dirstate. + gcenabled = gc.isenabled() + gc.disable() + try: + p = parsers.parse_dirstate(self._map, self._copymap, st) + finally: + if gcenabled: + gc.enable() if not self._dirtypl: self._pl = p @@ -317,7 +313,7 @@ def _droppath(self, f): if self[f] not in "?r" and "_dirs" in self.__dict__: - _decdirs(self._dirs, f) + self._dirs.delpath(f) def _addpath(self, f, state, mode, size, mtime): oldstate = self[f] @@ -326,14 +322,14 @@ if f in self._dirs: raise util.Abort(_('directory %r already in dirstate') % f) # shadows - for d in _finddirs(f): + for d in scmutil.finddirs(f): if d in self._dirs: break if d in self._map and self[d] != 'r': raise util.Abort( _('file %r in dirstate clashes with %r') % (d, f)) if oldstate in "?r" and "_dirs" in self.__dict__: - _incdirs(self._dirs, f) + self._dirs.addpath(f) self._dirty = True self._map[f] = (state, mode, size, mtime) @@ -484,13 +480,18 @@ self._lastnormaltime = 0 self._dirty = True - def rebuild(self, parent, files): + def rebuild(self, parent, allfiles, changedfiles=None): + changedfiles = changedfiles or allfiles + oldmap = self._map self.clear() - for f in files: - if 'x' in files.flags(f): - self._map[f] = ('n', 0777, -1, 0) + for f in allfiles: + if f not in changedfiles: + self._map[f] = oldmap[f] else: - self._map[f] = ('n', 0666, -1, 0) + if 'x' in allfiles.flags(f): + self._map[f] = ('n', 0777, -1, 0) + else: + self._map[f] = ('n', 0666, -1, 0) self._pl = (parent, nullid) self._dirty = True @@ -508,45 +509,14 @@ # use the modification time of the newly created temporary file as the # filesystem's notion of 'now' now = util.fstat(st).st_mtime - copymap = self._copymap - try: - finish(parsers.pack_dirstate(self._map, copymap, self._pl, now)) - return - except AttributeError: - pass - - now = int(now) - cs = cStringIO.StringIO() - pack = struct.pack - write = cs.write - write("".join(self._pl)) - for f, e in self._map.iteritems(): - if e[0] == 'n' and e[3] == now: - # The file was last modified "simultaneously" with the current - # write to dirstate (i.e. within the same second for file- - # systems with a granularity of 1 sec). This commonly happens - # for at least a couple of files on 'update'. - # The user could change the file without changing its size - # within the same second. Invalidate the file's stat data in - # dirstate, forcing future 'status' calls to compare the - # contents of the file. This prevents mistakenly treating such - # files as clean. - e = (e[0], 0, -1, -1) # mark entry as 'unset' - self._map[f] = e - - if f in copymap: - f = "%s\0%s" % (f, copymap[f]) - e = pack(_format, e[0], e[1], e[2], e[3], len(f)) - write(e) - write(f) - finish(cs.getvalue()) + finish(parsers.pack_dirstate(self._map, self._copymap, self._pl, now)) def _dirignore(self, f): if f == '.': return False if self._ignore(f): return True - for p in _finddirs(f): + for p in scmutil.finddirs(f): if self._ignore(p): return True return False @@ -589,6 +559,7 @@ dirignore = util.always matchfn = match.matchfn + matchalways = match.always() badfn = match.bad dmap = self._map normpath = util.normpath @@ -696,26 +667,53 @@ if not ignore(nf): match.dir(nf) wadd(nf) - if nf in dmap and matchfn(nf): + if nf in dmap and (matchalways or matchfn(nf)): results[nf] = None elif kind == regkind or kind == lnkkind: if nf in dmap: - if matchfn(nf): + if matchalways or matchfn(nf): results[nf] = st - elif matchfn(nf) and not ignore(nf): + elif (matchalways or matchfn(nf)) and not ignore(nf): results[nf] = st - elif nf in dmap and matchfn(nf): + elif nf in dmap and (matchalways or matchfn(nf)): results[nf] = None + for s in subrepos: + del results[s] + del results['.hg'] + # 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)]) - 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'] + if not results and matchalways: + visit = dmap.keys() + else: + visit = [f for f in dmap if f not in results and matchfn(f)] + visit.sort() + + if unknown: + # unknown == True means we walked the full directory tree above. + # So if a file is not seen it was either a) not matching matchfn + # b) ignored, c) missing, or d) under a symlink directory. + audit_path = scmutil.pathauditor(self._root) + + for nf in iter(visit): + # Report ignored items in the dmap as long as they are not + # under a symlink directory. + if ignore(nf) and audit_path.check(nf): + try: + results[nf] = lstat(join(nf)) + except OSError: + # file doesn't exist + results[nf] = None + else: + # It's either missing or under a symlink directory + results[nf] = None + else: + # We may not have walked the full directory tree above, + # so stat everything we missed. + nf = iter(visit).next + for st in util.statfiles([join(i) for i in visit]): + results[nf()] = st return results def status(self, match, subrepos, ignored, clean, unknown):
--- a/mercurial/dispatch.py Thu Apr 04 16:28:19 2013 -0500 +++ b/mercurial/dispatch.py Thu Apr 18 23:46:26 2013 -0500 @@ -151,6 +151,9 @@ commands.help_(ui, inst.args[0], unknowncmd=True) except error.UnknownCommand: commands.help_(ui, 'shortlist') + except error.InterventionRequired, inst: + ui.warn("%s\n" % inst) + return 1 except util.Abort, inst: ui.warn(_("abort: %s\n") % inst) if inst.hint: @@ -247,6 +250,7 @@ (_("** Mercurial Distributed SCM (version %s)\n") % myver) + (_("** Extensions loaded: %s\n") % ", ".join([x[0] for x in extensions.extensions()]))) + ui.log("commandexception", "%s\n%s\n", warning, traceback.format_exc()) ui.warn(warning) raise @@ -333,7 +337,7 @@ self.cmdname = cmd = args.pop(0) args = map(util.expandpath, args) - for invalidarg in ("--cwd", "-R", "--repository", "--repo"): + for invalidarg in ("--cwd", "-R", "--repository", "--repo", "--config"): if _earlygetopt([invalidarg], args): def fn(ui, *args): ui.warn(_("error in definition for alias '%s': %s may only " @@ -507,10 +511,8 @@ def runcommand(lui, repo, cmd, fullargs, ui, options, d, cmdpats, cmdoptions): # run pre-hook, and abort if it fails - ret = hook.hook(lui, repo, "pre-%s" % cmd, False, args=" ".join(fullargs), - pats=cmdpats, opts=cmdoptions) - if ret: - return ret + hook.hook(lui, repo, "pre-%s" % cmd, True, args=" ".join(fullargs), + pats=cmdpats, opts=cmdoptions) ret = _runcommand(ui, options, cmd, d) # run post-hook, passing command result hook.hook(lui, repo, "post-%s" % cmd, False, args=" ".join(fullargs), @@ -736,18 +738,25 @@ ui.warn(_("warning: --repository ignored\n")) msg = ' '.join(' ' in a and repr(a) or a for a in fullargs) - ui.log("command", msg + "\n") + ui.log("command", '%s\n', msg) d = lambda: util.checksignature(func)(ui, *args, **cmdoptions) + starttime = time.time() + ret = None try: - return runcommand(lui, repo, cmd, fullargs, ui, options, d, - cmdpats, cmdoptions) + ret = runcommand(lui, repo, cmd, fullargs, ui, options, d, + cmdpats, cmdoptions) + return ret finally: + duration = time.time() - starttime + ui.log("commandfinish", "%s exited %s after %0.2f seconds\n", + cmd, ret, duration) if repo and repo != req.repo: repo.close() def lsprofile(ui, func, fp): format = ui.config('profiling', 'format', default='text') field = ui.config('profiling', 'sort', default='inlinetime') + limit = ui.configint('profiling', 'limit', default=30) climit = ui.configint('profiling', 'nested', default=5) if format not in ['text', 'kcachegrind']: @@ -776,7 +785,7 @@ # format == 'text' stats = lsprof.Stats(p.getstats()) stats.sort(field) - stats.pprint(limit=30, file=fp, climit=climit) + stats.pprint(limit=limit, file=fp, climit=climit) def statprofile(ui, func, fp): try:
--- a/mercurial/error.py Thu Apr 04 16:28:19 2013 -0500 +++ b/mercurial/error.py Thu Apr 18 23:46:26 2013 -0500 @@ -33,6 +33,9 @@ class CommandError(Exception): """Exception raised on errors in parsing the command line.""" +class InterventionRequired(Exception): + """Exception raised when a command requires human intervention.""" + class Abort(Exception): """Raised if a command needs to print an error and exit.""" def __init__(self, *args, **kw):
--- a/mercurial/extensions.py Thu Apr 04 16:28:19 2013 -0500 +++ b/mercurial/extensions.py Thu Apr 18 23:46:26 2013 -0500 @@ -11,7 +11,7 @@ _extensions = {} _order = [] -_ignore = ['hbisect', 'bookmarks', 'parentrevspec'] +_ignore = ['hbisect', 'bookmarks', 'parentrevspec', 'interhg'] def extensions(): for name in _order: @@ -50,7 +50,6 @@ raise def load(ui, name, path): - # unused ui argument kept for backwards compatibility if name.startswith('hgext.') or name.startswith('hgext/'): shortname = name[6:] else:
--- a/mercurial/fileset.py Thu Apr 04 16:28:19 2013 -0500 +++ b/mercurial/fileset.py Thu Apr 18 23:46:26 2013 -0500 @@ -353,6 +353,29 @@ return s +def eol(mctx, x): + """``eol(style)`` + File contains newlines of the given style (dos, unix, mac). Binary + files are excluded, files with mixed line endings match multiple + styles. + """ + + # i18n: "encoding" is a keyword + enc = getstring(x, _("encoding requires an encoding name")) + + s = [] + for f in mctx.existing(): + d = mctx.ctx[f].data() + if util.binary(d): + continue + if (enc == 'dos' or enc == 'win') and '\r\n' in d: + s.append(f) + elif enc == 'unix' and re.search('(?<!\r)\n', d): + s.append(f) + elif enc == 'mac' and re.search('\r(?!\n)', d): + s.append(f) + return s + def copied(mctx, x): """``copied()`` File that is recorded as being copied. @@ -395,6 +418,7 @@ 'copied': copied, 'deleted': deleted, 'encoding': encoding, + 'eol': eol, 'exec': exec_, 'grep': grep, 'ignored': ignored,
--- a/mercurial/help.py Thu Apr 04 16:28:19 2013 -0500 +++ b/mercurial/help.py Thu Apr 18 23:46:26 2013 -0500 @@ -6,9 +6,10 @@ # GNU General Public License version 2 or any later version. from i18n import gettext, _ -import itertools, sys, os +import itertools, sys, os, error import extensions, revset, fileset, templatekw, templatefilters, filemerge import encoding, util, minirst +import cmdutil def listexts(header, exts, indent=1): '''return a text listing of the given extensions''' @@ -206,3 +207,298 @@ addtopicsymbols('revsets', '.. predicatesmarker', revset.symbols) addtopicsymbols('templates', '.. keywordsmarker', templatekw.dockeywords) addtopicsymbols('templates', '.. filtersmarker', templatefilters.filters) + +def help_(ui, name, unknowncmd=False, full=True, **opts): + ''' + Generate the help for 'name' as unformatted restructured text. If + 'name' is None, describe the commands available. + ''' + + import commands # avoid cycle + + def helpcmd(name): + try: + aliases, entry = cmdutil.findcmd(name, commands.table, + strict=unknowncmd) + except error.AmbiguousCommand, inst: + # py3k fix: except vars can't be used outside the scope of the + # except block, nor can be used inside a lambda. python issue4617 + prefix = inst.args[0] + select = lambda c: c.lstrip('^').startswith(prefix) + rst = helplist(select) + return rst + + rst = [] + + # check if it's an invalid alias and display its error if it is + if getattr(entry[0], 'badalias', False): + if not unknowncmd: + ui.pushbuffer() + entry[0](ui) + rst.append(ui.popbuffer()) + return rst + + # synopsis + if len(entry) > 2: + if entry[2].startswith('hg'): + rst.append("%s\n" % entry[2]) + else: + rst.append('hg %s %s\n' % (aliases[0], entry[2])) + else: + rst.append('hg %s\n' % aliases[0]) + # aliases + if full and not ui.quiet and len(aliases) > 1: + rst.append(_("\naliases: %s\n") % ', '.join(aliases[1:])) + rst.append('\n') + + # description + doc = gettext(entry[0].__doc__) + if not doc: + doc = _("(no help text available)") + if util.safehasattr(entry[0], 'definition'): # aliased command + if entry[0].definition.startswith('!'): # shell alias + doc = _('shell alias for::\n\n %s') % entry[0].definition[1:] + else: + doc = _('alias for: hg %s\n\n%s') % (entry[0].definition, doc) + doc = doc.splitlines(True) + if ui.quiet or not full: + rst.append(doc[0]) + else: + rst.extend(doc) + rst.append('\n') + + # check if this command shadows a non-trivial (multi-line) + # extension help text + try: + mod = extensions.find(name) + doc = gettext(mod.__doc__) or '' + if '\n' in doc.strip(): + msg = _('use "hg help -e %s" to show help for ' + 'the %s extension') % (name, name) + rst.append('\n%s\n' % msg) + except KeyError: + pass + + # options + if not ui.quiet and entry[1]: + rst.append('\n%s\n\n' % _("options:")) + rst.append(optrst(entry[1], ui.verbose)) + + if ui.verbose: + rst.append('\n%s\n\n' % _("global options:")) + rst.append(optrst(commands.globalopts, ui.verbose)) + + if not ui.verbose: + if not full: + rst.append(_('\nuse "hg help %s" to show the full help text\n') + % name) + elif not ui.quiet: + omitted = _('use "hg -v help %s" to show more complete' + ' help and the global options') % name + notomitted = _('use "hg -v help %s" to show' + ' the global options') % name + indicateomitted(rst, omitted, notomitted) + + return rst + + + def helplist(select=None): + # list of commands + if name == "shortlist": + header = _('basic commands:\n\n') + else: + header = _('list of commands:\n\n') + + h = {} + cmds = {} + for c, e in commands.table.iteritems(): + f = c.split("|", 1)[0] + if select and not select(f): + continue + if (not select and name != 'shortlist' and + e[0].__module__ != commands.__name__): + continue + if name == "shortlist" and not f.startswith("^"): + continue + f = f.lstrip("^") + if not ui.debugflag and f.startswith("debug"): + continue + doc = e[0].__doc__ + if doc and 'DEPRECATED' in doc and not ui.verbose: + continue + doc = gettext(doc) + if not doc: + doc = _("(no help text available)") + h[f] = doc.splitlines()[0].rstrip() + cmds[f] = c.lstrip("^") + + rst = [] + if not h: + if not ui.quiet: + rst.append(_('no commands defined\n')) + return rst + + if not ui.quiet: + rst.append(header) + fns = sorted(h) + for f in fns: + if ui.verbose: + commacmds = cmds[f].replace("|",", ") + rst.append(" :%s: %s\n" % (commacmds, h[f])) + else: + rst.append(' :%s: %s\n' % (f, h[f])) + + if not name: + exts = listexts(_('enabled extensions:'), extensions.enabled()) + if exts: + rst.append('\n') + rst.extend(exts) + + rst.append(_("\nadditional help topics:\n\n")) + topics = [] + for names, header, doc in helptable: + topics.append((names[0], header)) + for t, desc in topics: + rst.append(" :%s: %s\n" % (t, desc)) + + optlist = [] + if not ui.quiet: + if ui.verbose: + optlist.append((_("global options:"), commands.globalopts)) + if name == 'shortlist': + optlist.append((_('use "hg help" for the full list ' + 'of commands'), ())) + else: + if name == 'shortlist': + msg = _('use "hg help" for the full list of commands ' + 'or "hg -v" for details') + elif name and not full: + msg = _('use "hg help %s" to show the full help ' + 'text') % name + else: + msg = _('use "hg -v help%s" to show builtin aliases and ' + 'global options') % (name and " " + name or "") + optlist.append((msg, ())) + + if optlist: + for title, options in optlist: + rst.append('\n%s\n' % title) + if options: + rst.append('\n%s\n' % optrst(options, ui.verbose)) + return rst + + def helptopic(name): + for names, header, doc in helptable: + if name in names: + break + else: + raise error.UnknownCommand(name) + + rst = [minirst.section(header)] + + # description + if not doc: + rst.append(" %s\n" % _("(no help text available)")) + if util.safehasattr(doc, '__call__'): + rst += [" %s\n" % l for l in doc().splitlines()] + + if not ui.verbose: + omitted = (_('use "hg help -v %s" to show more complete help') % + name) + indicateomitted(rst, omitted) + + try: + cmdutil.findcmd(name, commands.table) + rst.append(_('\nuse "hg help -c %s" to see help for ' + 'the %s command\n') % (name, name)) + except error.UnknownCommand: + pass + return rst + + def helpext(name): + try: + mod = extensions.find(name) + doc = gettext(mod.__doc__) or _('no help text available') + except KeyError: + mod = None + doc = extensions.disabledext(name) + if not doc: + raise error.UnknownCommand(name) + + if '\n' not in doc: + head, tail = doc, "" + else: + head, tail = doc.split('\n', 1) + rst = [_('%s extension - %s\n\n') % (name.split('.')[-1], head)] + if tail: + rst.extend(tail.splitlines(True)) + rst.append('\n') + + if not ui.verbose: + omitted = (_('use "hg help -v %s" to show more complete help') % + name) + indicateomitted(rst, omitted) + + if mod: + try: + ct = mod.cmdtable + except AttributeError: + ct = {} + modcmds = set([c.split('|', 1)[0] for c in ct]) + rst.extend(helplist(modcmds.__contains__)) + else: + rst.append(_('use "hg help extensions" for information on enabling ' + 'extensions\n')) + return rst + + def helpextcmd(name): + cmd, ext, mod = extensions.disabledcmd(ui, name, + ui.configbool('ui', 'strict')) + doc = gettext(mod.__doc__).splitlines()[0] + + rst = listexts(_("'%s' is provided by the following " + "extension:") % cmd, {ext: doc}, indent=4) + rst.append('\n') + rst.append(_('use "hg help extensions" for information on enabling ' + 'extensions\n')) + return rst + + + rst = [] + kw = opts.get('keyword') + if kw: + matches = topicmatch(kw) + for t, title in (('topics', _('Topics')), + ('commands', _('Commands')), + ('extensions', _('Extensions')), + ('extensioncommands', _('Extension Commands'))): + if matches[t]: + rst.append('%s:\n\n' % title) + rst.extend(minirst.maketable(sorted(matches[t]), 1)) + rst.append('\n') + elif name and name != 'shortlist': + i = None + if unknowncmd: + queries = (helpextcmd,) + elif opts.get('extension'): + queries = (helpext,) + elif opts.get('command'): + queries = (helpcmd,) + else: + queries = (helptopic, helpcmd, helpext, helpextcmd) + for f in queries: + try: + rst = f(name) + i = None + break + except error.UnknownCommand, inst: + i = inst + if i: + raise i + else: + # program name + if not ui.quiet: + rst = [_("Mercurial Distributed SCM\n"), '\n'] + rst.extend(helplist()) + + return ''.join(rst)
--- a/mercurial/help/config.txt Thu Apr 04 16:28:19 2013 -0500 +++ b/mercurial/help/config.txt Thu Apr 18 23:46:26 2013 -0500 @@ -745,7 +745,7 @@ For example:: [hostfingerprints] - hg.intevation.org = 38:76:52:7c:87:26:9a:8f:4a:f8:d3:de:08:45:3b:ea:d6:4b:ee:cc + hg.intevation.org = 44:ed:af:1f:97:11:b6:01:7a:48:45:fc:10:3c:b7:f9:d4:89:2a:9d This feature is only supported when using Python 2.6 or later. @@ -996,10 +996,13 @@ ``inlinetime``. Default: inlinetime. +``limit`` + Number of lines to show. Specific to the ``ls`` instrumenting profiler. + Default: 30. + ``nested`` - Show at most this number of lines of drill-down info in a tree structure - after each main entry. This can help explain the difference between Total - and Inline. + Show at most this number of lines of drill-down info after each main entry. + This can help explain the difference between Total and Inline. Specific to the ``ls`` instrumenting profiler. Default: 5. @@ -1043,12 +1046,23 @@ Host name of mail server, e.g. "mail.example.com". ``port`` - Optional. Port to connect to on mail server. Default: 25. + Optional. Port to connect to on mail server. Default: 465 (if + ``tls`` is smtps) or 25 (otherwise). ``tls`` Optional. Method to enable TLS when connecting to mail server: starttls, smtps or none. Default: none. +``verifycert`` + Optional. Verification for the certificate of mail server, when + ``tls`` is starttls or smtps. "strict", "loose" or False. For + "strict" or "loose", the certificate is verified as same as the + verification for HTTPS connections (see ``[hostfingerprints]`` and + ``[web] cacerts`` also). For "strict", sending email is also + aborted, if there is no configuration for mail server in + ``[hostfingerprints]`` and ``[web] cacerts``. --insecure for + :hg:`email` overwrites this as "loose". Default: "strict". + ``username`` Optional. User name for authenticating with the SMTP server. Default: none. @@ -1459,3 +1473,48 @@ ``templates`` Where to find the HTML templates. Default is install path. + +``websub`` +---------- + +Web substitution filter definition. You can use this section to +define a set of regular expression substitution patterns which +let you automatically modify the hgweb server output. + +The default hgweb templates only apply these substitution patterns +on the revision description fields. You can apply them anywhere +you want when you create your own templates by adding calls to the +"websub" filter (usually after calling the "escape" filter). + +This can be used, for example, to convert issue references to links +to your issue tracker, or to convert "markdown-like" syntax into +HTML (see the examples below). + +Each entry in this section names a substitution filter. +The value of each entry defines the substitution expression itself. +The websub expressions follow the old interhg extension syntax, +which in turn imitates the Unix sed replacement syntax:: + + patternname = s/SEARCH_REGEX/REPLACE_EXPRESSION/[i] + +You can use any separator other than "/". The final "i" is optional +and indicates that the search must be case insensitive. + +Examples:: + + [websub] + issues = s|issue(\d+)|<a href="http://bts.example.org/issue\1">issue\1</a>|i + italic = s/\b_(\S+)_\b/<i>\1<\/i>/ + bold = s/\*\b(\S+)\b\*/<b>\1<\/b>/ + +``worker`` +---------- + +Parallel master/worker configuration. We currently perform working +directory updates in parallel on Unix-like systems, which greatly +helps performance. + +``numcpus`` + Number of CPUs to use for parallel operations. Default is 4 or the + number of CPUs on the system, whichever is larger. A zero or + negative value is treated as ``use the default``.
--- a/mercurial/help/dates.txt Thu Apr 04 16:28:19 2013 -0500 +++ b/mercurial/help/dates.txt Thu Apr 18 23:46:26 2013 -0500 @@ -18,6 +18,9 @@ - ``12-6`` - ``12/6`` - ``12/6/6`` (Dec 6 2006) +- ``today`` (midnight) +- ``yesterday`` (midnight) +- ``now`` - right now Lastly, there is Mercurial's internal format:
--- a/mercurial/help/filesets.txt Thu Apr 04 16:28:19 2013 -0500 +++ b/mercurial/help/filesets.txt Thu Apr 18 23:46:26 2013 -0500 @@ -1,5 +1,5 @@ Mercurial supports a functional language for selecting a set of -files. +files. Like other file patterns, this pattern type is indicated by a prefix, 'set:'. The language supports a number of predicates which are joined
--- a/mercurial/help/patterns.txt Thu Apr 04 16:28:19 2013 -0500 +++ b/mercurial/help/patterns.txt Thu Apr 18 23:46:26 2013 -0500 @@ -7,7 +7,7 @@ Alternate pattern notations must be specified explicitly. .. note:: - Patterns specified in ``.hgignore`` are not rooted. + Patterns specified in ``.hgignore`` are not rooted. Please see :hg:`help hgignore` for details. To use a plain path name without any pattern matching, start it with
--- a/mercurial/help/phases.txt Thu Apr 04 16:28:19 2013 -0500 +++ b/mercurial/help/phases.txt Thu Apr 18 23:46:26 2013 -0500 @@ -79,6 +79,6 @@ - resynchronize draft changesets relative to a remote repository:: - hg phase -fd 'outgoing(URL)' + hg phase -fd 'outgoing(URL)' See :hg:`help phase` for more information on manually manipulating phases.
--- a/mercurial/help/templates.txt Thu Apr 04 16:28:19 2013 -0500 +++ b/mercurial/help/templates.txt Thu Apr 18 23:46:26 2013 -0500 @@ -44,19 +44,23 @@ In addition to filters, there are some basic built-in functions: +- date(date[, fmt]) + +- fill(text[, width]) + +- get(dict, key) + - if(expr, then[, else]) - ifeq(expr, expr, then[, else]) -- sub(pat, repl, expr) - - join(list, sep) - label(label, expr) -- date(date[, fmt]) +- sub(pat, repl, expr) -- fill(text[, width]) +- rstdoc(text, style) Also, for any expression that returns a list, there is a list operator:
--- a/mercurial/hg.py Thu Apr 04 16:28:19 2013 -0500 +++ b/mercurial/hg.py Thu Apr 18 23:46:26 2013 -0500 @@ -9,8 +9,8 @@ from i18n import _ from lock import release from node import hex, nullid -import localrepo, bundlerepo, httppeer, sshpeer, statichttprepo, bookmarks -import lock, util, extensions, error, node, scmutil, phases, url +import localrepo, bundlerepo, unionrepo, httppeer, sshpeer, statichttprepo +import bookmarks, lock, util, extensions, error, node, scmutil, phases, url import cmdutil, discovery import merge as mergemod import verify as verifymod @@ -64,6 +64,7 @@ schemes = { 'bundle': bundlerepo, + 'union': unionrepo, 'file': _local, 'http': httppeer, 'https': httppeer,
--- a/mercurial/hgweb/common.py Thu Apr 04 16:28:19 2013 -0500 +++ b/mercurial/hgweb/common.py Thu Apr 18 23:46:26 2013 -0500 @@ -18,6 +18,15 @@ HTTP_SERVER_ERROR = 500 +def ismember(ui, username, userlist): + """Check if username is a member of userlist. + + If userlist has a single '*' member, all users are considered members. + Can be overriden by extensions to provide more complex authorization + schemes. + """ + return userlist == ['*'] or username in userlist + def checkauthz(hgweb, req, op): '''Check permission for operation based on request data (including authentication info). Return if op allowed, else raise an ErrorResponse @@ -26,12 +35,11 @@ user = req.env.get('REMOTE_USER') deny_read = hgweb.configlist('web', 'deny_read') - if deny_read and (not user or deny_read == ['*'] or user in deny_read): + if deny_read and (not user or ismember(hgweb.repo.ui, user, deny_read)): raise ErrorResponse(HTTP_UNAUTHORIZED, 'read not authorized') allow_read = hgweb.configlist('web', 'allow_read') - result = (not allow_read) or (allow_read == ['*']) - if not (result or user in allow_read): + if allow_read and (not ismember(hgweb.repo.ui, user, allow_read)): raise ErrorResponse(HTTP_UNAUTHORIZED, 'read not authorized') if op == 'pull' and not hgweb.allowpull: @@ -51,12 +59,11 @@ raise ErrorResponse(HTTP_FORBIDDEN, 'ssl required') deny = hgweb.configlist('web', 'deny_push') - if deny and (not user or deny == ['*'] or user in deny): + if deny and (not user or ismember(hgweb.repo.ui, user, deny)): raise ErrorResponse(HTTP_UNAUTHORIZED, 'push not authorized') allow = hgweb.configlist('web', 'allow_push') - result = allow and (allow == ['*'] or user in allow) - if not result: + if not (allow and ismember(hgweb.repo.ui, user, allow)): raise ErrorResponse(HTTP_UNAUTHORIZED, 'push not authorized') # Hooks for hgweb permission checks; extensions can add hooks here. @@ -129,7 +136,7 @@ for part in parts: if (part in ('', os.curdir, os.pardir) or os.sep in part or os.altsep is not None and os.altsep in part): - return "" + return fpath = os.path.join(*parts) if isinstance(directory, str): directory = [directory] @@ -144,7 +151,6 @@ data = fp.read() fp.close() req.respond(HTTP_OK, ct, body=data) - return "" except TypeError: raise ErrorResponse(HTTP_SERVER_ERROR, 'illegal filename') except OSError, err:
--- a/mercurial/hgweb/hgweb_mod.py Thu Apr 04 16:28:19 2013 -0500 +++ b/mercurial/hgweb/hgweb_mod.py Thu Apr 18 23:46:26 2013 -0500 @@ -8,11 +8,13 @@ import os from mercurial import ui, hg, hook, error, encoding, templater, util, repoview +from mercurial.templatefilters import websub +from mercurial.i18n import _ from common import get_stat, ErrorResponse, permhooks, caching from common import HTTP_OK, HTTP_NOT_MODIFIED, HTTP_BAD_REQUEST from common import HTTP_NOT_FOUND, HTTP_SERVER_ERROR from request import wsgirequest -import webcommands, protocol, webutil +import webcommands, protocol, webutil, re perms = { 'changegroup': 'pull', @@ -63,7 +65,9 @@ self.repo = self._getview(self.repo) self.repo.ui.setconfig('ui', 'report_untrusted', 'off') + self.repo.baseui.setconfig('ui', 'report_untrusted', 'off') self.repo.ui.setconfig('ui', 'nontty', 'true') + self.repo.baseui.setconfig('ui', 'nontty', 'true') hook.redirect(True) self.mtime = -1 self.size = -1 @@ -73,6 +77,7 @@ # a repo owner may set web.templates in .hg/hgrc to get any file # readable by the user running the CGI script self.templatepath = self.config('web', 'templates') + self.websubtable = self.loadwebsub() # The CGI scripts are often run by a user different from the repo owner. # Trust the settings from the .hg/hgrc files by default. @@ -98,16 +103,14 @@ return repo.filtered('served') def refresh(self, request=None): - if request: - self.repo.ui.environ = request.env st = get_stat(self.repo.spath) # compare changelog size in addition to mtime to catch # rollbacks made less than a second ago if st.st_mtime != self.mtime or st.st_size != self.size: self.mtime = st.st_mtime self.size = st.st_size - self.repo = hg.repository(self.repo.ui, self.repo.root) - self.repo = self._getview(self.repo) + r = hg.repository(self.repo.baseui, self.repo.root) + self.repo = self._getview(r) self.maxchanges = int(self.config("web", "maxchanges", 10)) self.stripecount = int(self.config("web", "stripes", 1)) self.maxshortchanges = int(self.config("web", "maxshortchanges", @@ -116,6 +119,8 @@ self.allowpull = self.configbool("web", "allowpull", True) encoding.encoding = self.config("web", "encoding", encoding.encoding) + if request: + self.repo.ui.environ = request.env def run(self): if not os.environ.get('GATEWAY_INTERFACE', '').startswith("CGI/1."): @@ -259,6 +264,47 @@ return [''] return tmpl('error', error=inst.message) + def loadwebsub(self): + websubtable = [] + websubdefs = self.repo.ui.configitems('websub') + # we must maintain interhg backwards compatibility + websubdefs += self.repo.ui.configitems('interhg') + for key, pattern in websubdefs: + # grab the delimiter from the character after the "s" + unesc = pattern[1] + delim = re.escape(unesc) + + # identify portions of the pattern, taking care to avoid escaped + # delimiters. the replace format and flags are optional, but + # delimiters are required. + match = re.match( + r'^s%s(.+)(?:(?<=\\\\)|(?<!\\))%s(.*)%s([ilmsux])*$' + % (delim, delim, delim), pattern) + if not match: + self.repo.ui.warn(_("websub: invalid pattern for %s: %s\n") + % (key, pattern)) + continue + + # we need to unescape the delimiter for regexp and format + delim_re = re.compile(r'(?<!\\)\\%s' % delim) + regexp = delim_re.sub(unesc, match.group(1)) + format = delim_re.sub(unesc, match.group(2)) + + # the pattern allows for 6 regexp flags, so set them if necessary + flagin = match.group(3) + flags = 0 + if flagin: + for flag in flagin.upper(): + flags |= re.__dict__[flag] + + try: + regexp = re.compile(regexp, flags) + websubtable.append((regexp, format)) + except re.error: + self.repo.ui.warn(_("websub: invalid regexp for %s: %s\n") + % (key, regexp)) + return websubtable + def templater(self, req): # determine scheme, port and server name @@ -312,9 +358,13 @@ or req.env.get('REPO_NAME') or req.url.strip('/') or self.repo.root) + def websubfilter(text): + return websub(text, self.websubtable) + # create the templater tmpl = templater.templater(mapfile, + filters={"websub": websubfilter}, defaults={"url": req.url, "logourl": logourl, "logoimg": logoimg,
--- a/mercurial/hgweb/hgwebdir_mod.py Thu Apr 04 16:28:19 2013 -0500 +++ b/mercurial/hgweb/hgwebdir_mod.py Thu Apr 18 23:46:26 2013 -0500 @@ -10,7 +10,7 @@ from mercurial.i18n import _ from mercurial import ui, hg, scmutil, util, templater from mercurial import error, encoding -from common import ErrorResponse, get_mtime, staticfile, paritygen, \ +from common import ErrorResponse, get_mtime, staticfile, paritygen, ismember, \ get_contact, HTTP_OK, HTTP_NOT_FOUND, HTTP_SERVER_ERROR from hgweb_mod import hgweb, makebreadcrumb from request import wsgirequest @@ -164,12 +164,12 @@ user = req.env.get('REMOTE_USER') deny_read = ui.configlist('web', 'deny_read', untrusted=True) - if deny_read and (not user or deny_read == ['*'] or user in deny_read): + if deny_read and (not user or ismember(ui, user, deny_read)): return False allow_read = ui.configlist('web', 'allow_read', untrusted=True) # by default, allow reading if no allow_read option has been set - if (not allow_read) or (allow_read == ['*']) or (user in allow_read): + if (not allow_read) or ismember(ui, user, allow_read): return True return False @@ -197,7 +197,8 @@ if isinstance(tp, str): tp = [tp] static = [os.path.join(p, 'static') for p in tp] - return (staticfile(static, fname, req),) + staticfile(static, fname, req) + return [] # top-level index elif not virtual:
--- a/mercurial/hgweb/webcommands.py Thu Apr 04 16:28:19 2013 -0500 +++ b/mercurial/hgweb/webcommands.py Thu Apr 18 23:46:26 2013 -0500 @@ -89,6 +89,7 @@ author=fctx.user(), date=fctx.date(), desc=fctx.description(), + extra=fctx.extra(), branch=webutil.nodebranchnodefault(fctx), parent=webutil.parents(fctx), child=webutil.children(fctx), @@ -162,6 +163,7 @@ child=webutil.children(ctx), changelogtag=showtags, desc=ctx.description(), + extra=ctx.extra(), date=ctx.date(), files=files, rev=ctx.rev(), @@ -216,6 +218,7 @@ "child": webutil.children(ctx, i + 1), "changelogtag": showtags, "desc": ctx.description(), + "extra": ctx.extra(), "date": ctx.date(), "files": files, "rev": i, @@ -299,6 +302,7 @@ changesetbranch=showbranch, author=ctx.user(), desc=ctx.description(), + extra=ctx.extra(), date=ctx.date(), files=files, diffsummary=lambda **x: webutil.diffsummary(diffstatgen), @@ -531,6 +535,7 @@ parity=parity.next(), author=ctx.user(), desc=ctx.description(), + extra=ctx.extra(), date=ctx.date(), rev=i, node=hn, @@ -590,6 +595,7 @@ rev=ctx.rev(), date=ctx.date(), desc=ctx.description(), + extra=ctx.extra(), author=ctx.user(), rename=rename, branch=webutil.nodebranchnodefault(ctx), @@ -651,6 +657,7 @@ rev=ctx.rev(), date=ctx.date(), desc=ctx.description(), + extra=ctx.extra(), author=ctx.user(), rename=rename, branch=webutil.nodebranchnodefault(ctx), @@ -689,6 +696,7 @@ "rev": f.rev(), "author": f.user(), "desc": f.description(), + "extra": f.extra(), "file": f.path(), "targetline": targetline, "line": l, @@ -705,6 +713,7 @@ author=fctx.user(), date=fctx.date(), desc=fctx.description(), + extra=fctx.extra(), rename=webutil.renamelink(fctx), branch=webutil.nodebranchnodefault(fctx), parent=webutil.parents(fctx), @@ -770,6 +779,7 @@ "parent": webutil.parents(iterfctx), "child": webutil.children(iterfctx), "desc": iterfctx.description(), + "extra": iterfctx.extra(), "tags": webutil.nodetagsdict(repo, iterfctx.node()), "bookmarks": webutil.nodebookmarksdict( repo, iterfctx.node()), @@ -806,6 +816,20 @@ if cnode == key or key == 'tip': arch_version = short(cnode) name = "%s-%s" % (reponame, arch_version) + + ctx = webutil.changectx(web.repo, req) + pats = [] + matchfn = None + file = req.form.get('file', None) + if file: + pats = ['path:' + file[0]] + matchfn = scmutil.match(ctx, pats, default='path') + if pats: + files = [f for f in ctx.manifest().keys() if matchfn(f)] + if not files: + raise ErrorResponse(HTTP_NOT_FOUND, + 'file(s) not found: %s' % file[0]) + mimetype, artype, extension, encoding = web.archive_specs[type_] headers = [ ('Content-Disposition', 'attachment; filename=%s%s' % (name, extension)) @@ -815,9 +839,8 @@ req.headers.extend(headers) req.respond(HTTP_OK, mimetype) - ctx = webutil.changectx(web.repo, req) archival.archive(web.repo, req, cnode, artype, prefix=name, - matchfn=scmutil.match(ctx, []), + matchfn=matchfn, subrepos=web.configbool("web", "archivesubrepos")) return [] @@ -832,7 +855,8 @@ if isinstance(tp, str): tp = [tp] static = [os.path.join(p, 'static') for p in tp] - return [staticfile(static, fname, req)] + staticfile(static, fname, req) + return [] def graph(web, req, tmpl): @@ -982,11 +1006,9 @@ othercommands=othercommands, title='Index') u = webutil.wsgiui() - u.pushbuffer() u.verbose = True try: - commands.help_(u, topicname) + doc = helpmod.help_(u, topicname) except error.UnknownCommand: raise ErrorResponse(HTTP_NOT_FOUND) - doc = u.popbuffer() return tmpl('help', topic=topicname, doc=doc)
--- a/mercurial/hook.py Thu Apr 04 16:28:19 2013 -0500 +++ b/mercurial/hook.py Thu Apr 18 23:46:26 2013 -0500 @@ -6,7 +6,7 @@ # GNU General Public License version 2 or any later version. from i18n import _ -import os, sys +import os, sys, time, types import extensions, util, demandimport def _pythonhook(ui, repo, name, hname, funcname, args, throw): @@ -20,6 +20,8 @@ be run as hooks without wrappers to convert return values.''' ui.note(_("calling hook %s: %s\n") % (hname, funcname)) + starttime = time.time() + obj = funcname if not util.safehasattr(obj, '__call__'): d = funcname.rfind('.') @@ -92,6 +94,12 @@ return True finally: sys.stdout, sys.stderr, sys.stdin = old + duration = time.time() - starttime + readablefunc = funcname + if isinstance(funcname, types.FunctionType): + readablefunc = funcname.__module__ + "." + funcname.__name__ + ui.log('pythonhook', 'pythonhook-%s: %s finished in %0.2f seconds\n', + name, readablefunc, duration) if r: if throw: raise util.Abort(_('%s hook failed') % hname) @@ -101,6 +109,7 @@ def _exthook(ui, repo, name, cmd, args, throw): ui.note(_("running hook %s: %s\n") % (name, cmd)) + starttime = time.time() env = {} for k, v in args.iteritems(): if util.safehasattr(v, '__call__'): @@ -121,6 +130,10 @@ r = util.system(cmd, environ=env, cwd=cwd, out=ui) else: r = util.system(cmd, environ=env, cwd=cwd, out=ui.fout) + + duration = time.time() - starttime + ui.log('exthook', 'exthook-%s: %s finished in %0.2f seconds\n', + name, cmd, duration) if r: desc, r = util.explainexit(r) if throw:
--- a/mercurial/httpclient/__init__.py Thu Apr 04 16:28:19 2013 -0500 +++ b/mercurial/httpclient/__init__.py Thu Apr 18 23:46:26 2013 -0500 @@ -125,24 +125,16 @@ This may block until either a line ending is found or the response is complete. """ - # TODO: move this into the reader interface where it can be - # smarter (and probably avoid copies) - bytes = [] - while not bytes: - try: - bytes = [self._reader.read(1)] - except _readers.ReadNotReady: - self._select() - while bytes[-1] != '\n' and not self.complete(): + blocks = [] + while True: + self._reader.readto('\n', blocks) + + if blocks and blocks[-1][-1] == '\n' or self.complete(): + break + self._select() - bytes.append(self._reader.read(1)) - if bytes[-1] != '\n': - next = self._reader.read(1) - while next and next != '\n': - bytes.append(next) - next = self._reader.read(1) - bytes.append(next) - return ''.join(bytes) + + return ''.join(blocks) def read(self, length=None): # if length is None, unbounded read
--- a/mercurial/httpclient/_readers.py Thu Apr 04 16:28:19 2013 -0500 +++ b/mercurial/httpclient/_readers.py Thu Apr 18 23:46:26 2013 -0500 @@ -57,10 +57,21 @@ def __init__(self): self._finished = False self._done_chunks = [] + self.available_data = 0 - @property - def available_data(self): - return sum(map(len, self._done_chunks)) + def addchunk(self, data): + self._done_chunks.append(data) + self.available_data += len(data) + + def pushchunk(self, data): + self._done_chunks.insert(0, data) + self.available_data += len(data) + + def popchunk(self): + b = self._done_chunks.pop(0) + self.available_data -= len(b) + + return b def done(self): return self._finished @@ -68,23 +79,46 @@ def read(self, amt): if self.available_data < amt and not self._finished: raise ReadNotReady() - need = [amt] - def pred(s): - needed = need[0] > 0 - need[0] -= len(s) - return needed - blocks = list(itertools.takewhile(pred, self._done_chunks)) - self._done_chunks = self._done_chunks[len(blocks):] - over_read = sum(map(len, blocks)) - amt - if over_read > 0 and blocks: - logger.debug('need to reinsert %d data into done chunks', over_read) - last = blocks[-1] - blocks[-1], reinsert = last[:-over_read], last[-over_read:] - self._done_chunks.insert(0, reinsert) + blocks = [] + need = amt + while self._done_chunks: + b = self.popchunk() + if len(b) > need: + nb = b[:need] + self.pushchunk(b[need:]) + b = nb + blocks.append(b) + need -= len(b) + if need == 0: + break result = ''.join(blocks) assert len(result) == amt or (self._finished and len(result) < amt) + return result + def readto(self, delimstr, blocks = None): + """return available data chunks up to the first one in which delimstr + occurs. No data will be returned after delimstr -- the chunk in which + it occurs will be split and the remainder pushed back onto the available + data queue. If blocks is supplied chunks will be added to blocks, otherwise + a new list will be allocated. + """ + if blocks is None: + blocks = [] + + while self._done_chunks: + b = self.popchunk() + i = b.find(delimstr) + len(delimstr) + if i: + if i < len(b): + self.pushchunk(b[i:]) + blocks.append(b[:i]) + break + else: + blocks.append(b) + + return blocks + def _load(self, data): # pragma: no cover """Subclasses must implement this. @@ -121,7 +155,7 @@ assert not self._finished, ( 'tried to add data (%r) to a closed reader!' % data) logger.debug('%s read an additional %d data', self.name, len(data)) - self._done_chunks.append(data) + self.addchunk(data) class CloseIsEndReader(AbstractSimpleReader): @@ -190,6 +224,6 @@ self._finished = True logger.debug('closing chunked reader due to chunk of length 0') return - self._done_chunks.append(data[block_start:block_start + amt]) + self.addchunk(data[block_start:block_start + amt]) position = block_start + amt + len(self._eol) # no-check-code
--- a/mercurial/httppeer.py Thu Apr 04 16:28:19 2013 -0500 +++ b/mercurial/httppeer.py Thu Apr 18 23:46:26 2013 -0500 @@ -145,13 +145,14 @@ raise error.OutOfBandError(resp.read()) # accept old "text/plain" and "application/hg-changegroup" for now if not (proto.startswith('application/mercurial-') or - proto.startswith('text/plain') or + (proto.startswith('text/plain') + and not resp.headers.get('content-length')) or proto.startswith('application/hg-changegroup')): self.ui.debug("requested URL: '%s'\n" % util.hidepassword(cu)) raise error.RepoError( _("'%s' does not appear to be an hg repository:\n" "---%%<--- (%s)\n%s\n---%%<---\n") - % (safeurl, proto or 'no content-type', resp.read())) + % (safeurl, proto or 'no content-type', resp.read(1024))) if proto.startswith('application/mercurial-'): try:
--- a/mercurial/localrepo.py Thu Apr 04 16:28:19 2013 -0500 +++ b/mercurial/localrepo.py Thu Apr 18 23:46:26 2013 -0500 @@ -8,7 +8,7 @@ from i18n import _ import peer, changegroup, subrepo, discovery, pushkey, obsolete, repoview import changelog, dirstate, filelog, manifest, context, bookmarks, phases -import lock, transaction, store, encoding, base85 +import lock, transaction, store, encoding import scmutil, util, extensions, hook, error, revset import match as matchmod import merge as mergemod @@ -49,7 +49,7 @@ def hasunfilteredcache(repo, name): - """check if an repo and a unfilteredproperty cached value for <name>""" + """check if a repo has an unfilteredpropertycache value for <name>""" return name in vars(repo.unfiltered()) def unfilteredmethod(orig): @@ -153,7 +153,7 @@ return self.requirements[:] def __init__(self, baseui, path=None, create=False): - self.wvfs = scmutil.vfs(path, expand=True) + self.wvfs = scmutil.vfs(path, expandpath=True, realpath=True) self.wopener = self.wvfs self.root = self.wvfs.base self.path = self.wvfs.join(".hg") @@ -209,8 +209,10 @@ self.sharedpath = self.path try: - s = os.path.realpath(self.opener.read("sharedpath").rstrip('\n')) - if not os.path.exists(s): + vfs = scmutil.vfs(self.vfs.read("sharedpath").rstrip('\n'), + realpath=True) + s = vfs.base + if not vfs.exists(): raise error.RepoError( _('.hg/sharedpath points to nonexistent directory %s') % s) self.sharedpath = s @@ -310,13 +312,13 @@ def unfiltered(self): """Return unfiltered version of the repository - Intended to be ovewritten by filtered repo.""" + Intended to be overwritten by filtered repo.""" return self def filtered(self, name): """Return a filtered version of a repository""" # build a new class with the mixin and the current class - # (possibily subclass of the repo) + # (possibly subclass of the repo) class proxycls(repoview.repoview, self.unfiltered().__class__): pass return proxycls(self, name) @@ -733,7 +735,7 @@ return self.wopener(f, mode) def _link(self, f): - return os.path.islink(self.wjoin(f)) + return self.wvfs.islink(f) def _loadfilter(self, filter): if filter not in self.filterpats: @@ -781,7 +783,7 @@ def wread(self, filename): if self._link(filename): - data = os.readlink(self.wjoin(filename)) + data = self.wvfs.readlink(filename) else: data = self.wopener.read(filename) return self._filter(self._encodefilterpats, filename, data) @@ -793,7 +795,7 @@ else: self.wopener.write(filename, data) if 'x' in flags: - util.setflags(self.wjoin(filename), False, True) + self.wvfs.setflags(filename, False, True) def wwritedata(self, filename, data): return self._filter(self._decodefilterpats, filename, data) @@ -804,12 +806,12 @@ return tr.nest() # abort here if the journal already exists - if os.path.exists(self.sjoin("journal")): + if self.svfs.exists("journal"): raise error.RepoError( _("abandoned transaction found - run hg recover")) self._writejournal(desc) - renames = [(x, undoname(x)) for x in self._journalfiles()] + renames = [(vfs, x, undoname(x)) for vfs, x in self._journalfiles()] tr = transaction.transaction(self.ui.warn, self.sopener, self.sjoin("journal"), @@ -819,13 +821,15 @@ return tr def _journalfiles(self): - return (self.sjoin('journal'), self.join('journal.dirstate'), - self.join('journal.branch'), self.join('journal.desc'), - self.join('journal.bookmarks'), - self.sjoin('journal.phaseroots')) + return ((self.svfs, 'journal'), + (self.vfs, 'journal.dirstate'), + (self.vfs, 'journal.branch'), + (self.vfs, 'journal.desc'), + (self.vfs, 'journal.bookmarks'), + (self.svfs, 'journal.phaseroots')) def undofiles(self): - return [undoname(x) for x in self._journalfiles()] + return [vfs.join(undoname(x)) for vfs, x in self._journalfiles()] def _writejournal(self, desc): self.opener.write("journal.dirstate", @@ -842,7 +846,7 @@ def recover(self): lock = self.lock() try: - if os.path.exists(self.sjoin("journal")): + if self.svfs.exists("journal"): self.ui.status(_("rolling back interrupted transaction\n")) transaction.rollback(self.sopener, self.sjoin("journal"), self.ui.warn) @@ -859,7 +863,7 @@ try: wlock = self.wlock() lock = self.lock() - if os.path.exists(self.sjoin("undo")): + if self.svfs.exists("undo"): return self._rollback(dryrun, force) else: self.ui.warn(_("no rollback information available\n")) @@ -901,18 +905,16 @@ parents = self.dirstate.parents() self.destroying() transaction.rollback(self.sopener, self.sjoin('undo'), ui.warn) - if os.path.exists(self.join('undo.bookmarks')): - util.rename(self.join('undo.bookmarks'), - self.join('bookmarks')) - if os.path.exists(self.sjoin('undo.phaseroots')): - util.rename(self.sjoin('undo.phaseroots'), - self.sjoin('phaseroots')) + if self.vfs.exists('undo.bookmarks'): + self.vfs.rename('undo.bookmarks', 'bookmarks') + if self.svfs.exists('undo.phaseroots'): + self.svfs.rename('undo.phaseroots', 'phaseroots') self.invalidate() parentgone = (parents[0] not in self.changelog.nodemap or parents[1] not in self.changelog.nodemap) if parentgone: - util.rename(self.join('undo.dirstate'), self.join('dirstate')) + self.vfs.rename('undo.dirstate', 'dirstate') try: branch = self.opener.read('undo.branch') self.dirstate.setbranch(encoding.tolocal(branch)) @@ -966,7 +968,7 @@ delattr(self.unfiltered(), 'dirstate') def invalidate(self): - unfiltered = self.unfiltered() # all filecaches are stored on unfiltered + unfiltered = self.unfiltered() # all file caches are stored unfiltered for k in self._filecache: # dirstate is invalidated separately in invalidatedirstate() if k == 'dirstate': @@ -1234,12 +1236,14 @@ elif f not in self.dirstate: fail(f, _("file not tracked!")) + cctx = context.workingctx(self, text, user, date, extra, changes) + if (not force and not extra.get("close") and not merge - and not (changes[0] or changes[1] or changes[2]) + and not cctx.files() and wctx.branch() == wctx.p1().branch()): return None - if merge and changes[3]: + if merge and cctx.deleted(): raise util.Abort(_("cannot commit merge with missing files")) ms = mergemod.mergestate(self) @@ -1248,7 +1252,6 @@ raise util.Abort(_("unresolved merge conflicts " "(see hg help resolve)")) - cctx = context.workingctx(self, text, user, date, extra, changes) if editor: cctx._text = editor(self, cctx, subs) edited = (text != cctx._text) @@ -1282,11 +1285,7 @@ # update bookmarks, dirstate and mergestate bookmarks.update(self, [p1, p2], ret) - for f in changes[0] + changes[1]: - self.dirstate.normal(f) - for f in changes[2]: - self.dirstate.drop(f) - self.dirstate.setparents(ret) + cctx.markcommitted(ret) ms.reset() finally: wlock.release() @@ -1401,12 +1400,6 @@ '''Inform the repository that nodes have been destroyed. Intended for use by strip and rollback, so there's a common place for anything that has to be done after destroying history. - - If you know the branchheadcache was uptodate before nodes were removed - and you also know the set of candidate new heads that may have resulted - from the destruction, you can set newheadnodes. This will enable the - code to update the branchheads cache, rather than having future code - decide it's invalid and regenerating it from scratch. ''' # When one tries to: # 1) destroy nodes thus calling this method (e.g. strip) @@ -1420,7 +1413,7 @@ self._phasecache.write() # update the 'served' branch cache to help read only server process - # Thanks to branchcach collaboration this is done from the nearest + # Thanks to branchcache collaboration this is done from the nearest # filtered subset and it is expected to be fast. branchmap.updatecache(self.filtered('served')) @@ -1544,12 +1537,12 @@ modified, added, clean = [], [], [] withflags = mf1.withflags() | mf2.withflags() - for fn in mf2: + for fn, mf2node in mf2.iteritems(): if fn in mf1: if (fn not in deleted and ((fn in withflags and mf1.flags(fn) != mf2.flags(fn)) or - (mf1[fn] != mf2[fn] and - (mf2[fn] or ctx1[fn].cmp(ctx2[fn]))))): + (mf1[fn] != mf2node and + (mf2node or ctx1[fn].cmp(ctx2[fn]))))): modified.append(fn) elif listclean: clean.append(fn) @@ -1724,17 +1717,15 @@ # should be seen as public phases.advanceboundary(self, phases.public, subset) - if obsolete._enabled: - self.ui.debug('fetching remote obsolete markers\n') - remoteobs = remote.listkeys('obsolete') - if 'dump0' in remoteobs: - if tr is None: - tr = self.transaction(trname) - for key in sorted(remoteobs, reverse=True): - if key.startswith('dump'): - data = base85.b85decode(remoteobs[key]) - self.obsstore.mergemarkers(tr, data) - self.invalidatevolatilesets() + def gettransaction(): + if tr is None: + return self.transaction(trname) + return tr + + obstr = obsolete.syncpull(self, remote, gettransaction) + if obstr is not None: + tr = obstr + if tr is not None: tr.close() finally: @@ -1919,17 +1910,7 @@ self.ui.warn(_('updating %s to public failed!\n') % newremotehead) self.ui.debug('try to push obsolete markers to remote\n') - if (obsolete._enabled and self.obsstore and - 'obsolete' in remote.listkeys('namespaces')): - rslts = [] - remotedata = self.listkeys('obsolete') - for key in sorted(remotedata, reverse=True): - # reverse sort to ensure we end with dump0 - data = remotedata[key] - rslts.append(remote.pushkey('obsolete', key, '', data)) - if [r for r in rslts if not r]: - msg = _('failed to push some obsolete markers!\n') - self.ui.warn(msg) + obsolete.syncpush(self, remote) finally: if lock is not None: lock.release() @@ -2411,6 +2392,12 @@ for n in added: self.hook("incoming", node=hex(n), source=srctype, url=url) + + newheads = [h for h in self.heads() if h not in oldheads] + self.ui.log("incoming", + "%s incoming changes - new heads: %s\n", + len(added), + ', '.join([hex(c[:6]) for c in newheads])) self._afterlock(runhooks) finally: @@ -2578,9 +2565,9 @@ def aftertrans(files): renamefiles = [tuple(t) for t in files] def a(): - for src, dest in renamefiles: + for vfs, src, dest in renamefiles: try: - util.rename(src, dest) + vfs.rename(src, dest) except OSError: # journal file does not yet exist pass return a
--- a/mercurial/lock.py Thu Apr 04 16:28:19 2013 -0500 +++ b/mercurial/lock.py Thu Apr 18 23:46:26 2013 -0500 @@ -36,6 +36,7 @@ self.releasefn = releasefn self.desc = desc self.postrelease = [] + self.pid = os.getpid() self.lock() def __del__(self): @@ -71,7 +72,7 @@ return if lock._host is None: lock._host = socket.gethostname() - lockname = '%s:%s' % (lock._host, os.getpid()) + lockname = '%s:%s' % (lock._host, self.pid) while not self.held: try: util.makelock(lockname, self.f) @@ -133,6 +134,9 @@ self.held -= 1 elif self.held == 1: self.held = 0 + if os.getpid() != self.pid: + # we forked, and are not the parent + return if self.releasefn: self.releasefn() try:
--- a/mercurial/lsprof.py Thu Apr 04 16:28:19 2013 -0500 +++ b/mercurial/lsprof.py Thu Apr 18 23:46:26 2013 -0500 @@ -50,9 +50,9 @@ ccount = 0 if climit and e.calls: for se in e.calls: - file.write(cols % ("+%s" % se.callcount, se.reccallcount, + file.write(cols % (se.callcount, se.reccallcount, se.totaltime, se.inlinetime, - "+%s" % label(se.code))) + " %s" % label(se.code))) count += 1 ccount += 1 if limit is not None and count == limit:
--- a/mercurial/mail.py Thu Apr 04 16:28:19 2013 -0500 +++ b/mercurial/mail.py Thu Apr 18 23:46:26 2013 -0500 @@ -6,8 +6,8 @@ # GNU General Public License version 2 or any later version. from i18n import _ -import util, encoding -import os, smtplib, socket, quopri, time +import util, encoding, sslutil +import os, smtplib, socket, quopri, time, sys import email.Header, email.MIMEText, email.Utils _oldheaderinit = email.Header.Header.__init__ @@ -30,6 +30,59 @@ email.Header.Header.__dict__['__init__'] = _unifiedheaderinit +class STARTTLS(smtplib.SMTP): + '''Derived class to verify the peer certificate for STARTTLS. + + This class allows to pass any keyword arguments to SSL socket creation. + ''' + def __init__(self, sslkwargs, **kwargs): + smtplib.SMTP.__init__(self, **kwargs) + self._sslkwargs = sslkwargs + + def starttls(self, keyfile=None, certfile=None): + if not self.has_extn("starttls"): + msg = "STARTTLS extension not supported by server" + raise smtplib.SMTPException(msg) + (resp, reply) = self.docmd("STARTTLS") + if resp == 220: + self.sock = sslutil.ssl_wrap_socket(self.sock, keyfile, certfile, + **self._sslkwargs) + if not util.safehasattr(self.sock, "read"): + # using httplib.FakeSocket with Python 2.5.x or earlier + self.sock.read = self.sock.recv + self.file = smtplib.SSLFakeFile(self.sock) + self.helo_resp = None + self.ehlo_resp = None + self.esmtp_features = {} + self.does_esmtp = 0 + return (resp, reply) + +if util.safehasattr(smtplib.SMTP, '_get_socket'): + class SMTPS(smtplib.SMTP): + '''Derived class to verify the peer certificate for SMTPS. + + This class allows to pass any keyword arguments to SSL socket creation. + ''' + def __init__(self, sslkwargs, keyfile=None, certfile=None, **kwargs): + self.keyfile = keyfile + self.certfile = certfile + smtplib.SMTP.__init__(self, **kwargs) + self.default_port = smtplib.SMTP_SSL_PORT + self._sslkwargs = sslkwargs + + def _get_socket(self, host, port, timeout): + if self.debuglevel > 0: + print >> sys.stderr, 'connect:', (host, port) + new_socket = socket.create_connection((host, port), timeout) + new_socket = sslutil.ssl_wrap_socket(new_socket, + self.keyfile, self.certfile, + **self._sslkwargs) + self.file = smtplib.SSLFakeFile(new_socket) + return new_socket +else: + def SMTPS(sslkwargs, keyfile=None, certfile=None, **kwargs): + raise util.Abort(_('SMTPS requires Python 2.6 or later')) + def _smtp(ui): '''build an smtp connection and return a function to send mail''' local_hostname = ui.config('smtp', 'local_hostname') @@ -39,15 +92,30 @@ smtps = tls == 'smtps' if (starttls or smtps) and not util.safehasattr(socket, 'ssl'): raise util.Abort(_("can't use TLS: Python SSL support not installed")) - if smtps: - ui.note(_('(using smtps)\n')) - s = smtplib.SMTP_SSL(local_hostname=local_hostname) - else: - s = smtplib.SMTP(local_hostname=local_hostname) mailhost = ui.config('smtp', 'host') if not mailhost: raise util.Abort(_('smtp.host not configured - cannot send mail')) - mailport = util.getport(ui.config('smtp', 'port', 25)) + verifycert = ui.config('smtp', 'verifycert', 'strict') + if verifycert not in ['strict', 'loose']: + if util.parsebool(verifycert) is not False: + raise util.Abort(_('invalid smtp.verifycert configuration: %s') + % (verifycert)) + if (starttls or smtps) and verifycert: + sslkwargs = sslutil.sslkwargs(ui, mailhost) + else: + sslkwargs = {} + if smtps: + ui.note(_('(using smtps)\n')) + s = SMTPS(sslkwargs, local_hostname=local_hostname) + elif starttls: + s = STARTTLS(sslkwargs, local_hostname=local_hostname) + else: + s = smtplib.SMTP(local_hostname=local_hostname) + if smtps: + defaultport = 465 + else: + defaultport = 25 + mailport = util.getport(ui.config('smtp', 'port', defaultport)) ui.note(_('sending mail: smtp host %s, port %s\n') % (mailhost, mailport)) s.connect(host=mailhost, port=mailport) @@ -56,6 +124,9 @@ s.ehlo() s.starttls() s.ehlo() + if (starttls or smtps) and verifycert: + ui.note(_('(verifying remote certificate)\n')) + sslutil.validator(ui, mailhost)(s.sock, verifycert == 'strict') username = ui.config('smtp', 'username') password = ui.config('smtp', 'password') if username and not password:
--- a/mercurial/manifest.py Thu Apr 04 16:28:19 2013 -0500 +++ b/mercurial/manifest.py Thu Apr 18 23:46:26 2013 -0500 @@ -6,7 +6,7 @@ # GNU General Public License version 2 or any later version. from i18n import _ -import mdiff, parsers, error, revlog, util +import mdiff, parsers, error, revlog, util, dicthelpers import array, struct class manifestdict(dict): @@ -25,10 +25,13 @@ self._flags[f] = flags def copy(self): return manifestdict(self, dict.copy(self._flags)) + def flagsdiff(self, d2): + return dicthelpers.diff(self._flags, d2._flags, "") class manifest(revlog.revlog): def __init__(self, opener): - self._mancache = None + # we expect to deal with not more than three revs at a time in merge + self._mancache = util.lrucachedict(3) revlog.revlog.__init__(self, opener, "00manifest.i") def parse(self, lines): @@ -51,12 +54,12 @@ def read(self, node): if node == revlog.nullid: return manifestdict() # don't upset local cache - if self._mancache and self._mancache[0] == node: - return self._mancache[1] + if node in self._mancache: + return self._mancache[node][0] text = self.revision(node) arraytext = array.array('c', text) mapping = self.parse(text) - self._mancache = (node, mapping, arraytext) + self._mancache[node] = (mapping, arraytext) return mapping def _search(self, m, s, lo=0, hi=None): @@ -102,8 +105,9 @@ def find(self, node, f): '''look up entry for a single file efficiently. return (node, flags) pair if found, (None, None) if not.''' - if self._mancache and self._mancache[0] == node: - return self._mancache[1].get(f), self._mancache[1].flags(f) + if node in self._mancache: + mapping = self._mancache[node][0] + return mapping.get(f), mapping.flags(f) text = self.revision(node) start, end = self._search(text, f) if start == end: @@ -143,7 +147,7 @@ # if we're using the cache, make sure it is valid and # parented by the same node we're diffing against - if not (changed and self._mancache and p1 and self._mancache[0] == p1): + if not (changed and p1 and (p1 in self._mancache)): files = sorted(map) checkforbidden(files) @@ -156,7 +160,7 @@ cachedelta = None else: added, removed = changed - addlist = self._mancache[2] + addlist = self._mancache[p1][1] checkforbidden(added) # combine the changed lists into one list for sorting @@ -208,6 +212,6 @@ text = util.buffer(arraytext) n = self.addrevision(text, transaction, link, p1, p2, cachedelta) - self._mancache = (n, map, arraytext) + self._mancache[n] = (map, arraytext) return n
--- a/mercurial/match.py Thu Apr 04 16:28:19 2013 -0500 +++ b/mercurial/match.py Thu Apr 18 23:46:26 2013 -0500 @@ -62,6 +62,7 @@ self._files = [] self._anypats = bool(include or exclude) self._ctx = ctx + self._always = False if include: pats = _normalize(include, 'glob', root, cwd, auditor) @@ -103,6 +104,7 @@ m = lambda f: not em(f) else: m = lambda f: True + self._always = True self.matchfn = m self._fmap = set(self._files) @@ -130,7 +132,7 @@ def anypats(self): return self._anypats def always(self): - return False + return self._always class exact(match): def __init__(self, root, cwd, files): @@ -139,8 +141,7 @@ class always(match): def __init__(self, root, cwd): match.__init__(self, root, cwd, []) - def always(self): - return True + self._always = True class narrowmatcher(match): """Adapt a matcher to work on a subdirectory only. @@ -175,6 +176,7 @@ self._cwd = matcher._cwd self._path = path self._matcher = matcher + self._always = matcher._always self._files = [f[len(path) + 1:] for f in matcher._files if f.startswith(path + "/")]
--- a/mercurial/merge.py Thu Apr 04 16:28:19 2013 -0500 +++ b/mercurial/merge.py Thu Apr 18 23:46:26 2013 -0500 @@ -7,7 +7,8 @@ from node import nullid, nullrev, hex, bin from i18n import _ -import error, util, filemerge, copies, subrepo +from mercurial import obsolete +import error, util, filemerge, copies, subrepo, worker, dicthelpers import errno, os, shutil class mergestate(object): @@ -176,146 +177,232 @@ state = branchmerge and 'r' or 'f' for f in wctx.deleted(): if f not in mctx: - actions.append((f, state)) + actions.append((f, state, None, "forget deleted")) if not branchmerge: for f in wctx.removed(): if f not in mctx: - actions.append((f, "f")) + actions.append((f, "f", None, "forget removed")) return actions -def manifestmerge(repo, p1, p2, pa, overwrite, partial): +def manifestmerge(repo, wctx, p2, pa, branchmerge, force, partial, + acceptremote=False): """ Merge p1 and p2 with ancestor pa and generate merge action list - overwrite = whether we clobber working files + branchmerge and force are as passed in to update partial = function to filter file lists + acceptremote = accept the incoming changes without prompting """ - def act(msg, m, f, *args): - repo.ui.debug(" %s: %s -> %s\n" % (f, msg, m)) - actions.append((f, m) + args) - + overwrite = force and not branchmerge actions, copy, movewithdir = [], {}, {} + followcopies = False if overwrite: - pa = p1 + pa = wctx elif pa == p2: # backwards - pa = p1.p1() + pa = wctx.p1() + elif not branchmerge and not wctx.dirty(missing=True): + pass elif pa and repo.ui.configbool("merge", "followcopies", True): - ret = copies.mergecopies(repo, p1, p2, pa) + followcopies = True + + # manifests fetched in order are going to be faster, so prime the caches + [x.manifest() for x in + sorted(wctx.parents() + [p2, pa], key=lambda x: x.rev())] + + if followcopies: + ret = copies.mergecopies(repo, wctx, p2, pa) copy, movewithdir, diverge, renamedelete = ret for of, fl in diverge.iteritems(): - act("divergent renames", "dr", of, fl) + actions.append((of, "dr", (fl,), "divergent renames")) for of, fl in renamedelete.iteritems(): - act("rename and delete", "rd", of, fl) + actions.append((of, "rd", (fl,), "rename and delete")) repo.ui.note(_("resolving manifests\n")) - repo.ui.debug(" overwrite: %s, partial: %s\n" - % (bool(overwrite), bool(partial))) - repo.ui.debug(" ancestor: %s, local: %s, remote: %s\n" % (pa, p1, p2)) + repo.ui.debug(" branchmerge: %s, force: %s, partial: %s\n" + % (bool(branchmerge), bool(force), bool(partial))) + repo.ui.debug(" ancestor: %s, local: %s, remote: %s\n" % (pa, wctx, p2)) - m1, m2, ma = p1.manifest(), p2.manifest(), pa.manifest() + m1, m2, ma = wctx.manifest(), p2.manifest(), pa.manifest() copied = set(copy.values()) copied.update(movewithdir.values()) if '.hgsubstate' in m1: # check whether sub state is modified - for s in sorted(p1.substate): - if p1.sub(s).dirty(): + for s in sorted(wctx.substate): + if wctx.sub(s).dirty(): m1['.hgsubstate'] += "+" break + aborts, prompts = [], [] # Compare manifests - visit = m1.iteritems() - if repo.ui.debugflag: - visit = sorted(visit) - for f, n in visit: + fdiff = dicthelpers.diff(m1, m2) + flagsdiff = m1.flagsdiff(m2) + diff12 = dicthelpers.join(fdiff, flagsdiff) + + for f, (n12, fl12) in diff12.iteritems(): + if n12: + n1, n2 = n12 + else: # file contents didn't change, but flags did + n1 = n2 = m1.get(f, None) + if n1 is None: + # Since n1 == n2, the file isn't present in m2 either. This + # means that the file was removed or deleted locally and + # removed remotely, but that residual entries remain in flags. + # This can happen in manifests generated by workingctx. + continue + if fl12: + fl1, fl2 = fl12 + else: # flags didn't change, file contents did + fl1 = fl2 = m1.flags(f) + if partial and not partial(f): continue - if f in m2: - n2 = m2[f] - fl1, fl2, fla = m1.flags(f), m2.flags(f), ma.flags(f) + if n1 and n2: + fla = ma.flags(f) nol = 'l' not in fl1 + fl2 + fla a = ma.get(f, nullid) - if n == n2 and fl1 == fl2: - pass # same - keep local - elif n2 == a and fl2 == fla: + if n2 == a and fl2 == fla: pass # remote unchanged - keep local - elif n == a and fl1 == fla: # local unchanged - use remote - if n == n2: # optimization: keep local content - act("update permissions", "e", f, fl2) + elif n1 == a and fl1 == fla: # local unchanged - use remote + if n1 == n2: # optimization: keep local content + actions.append((f, "e", (fl2,), "update permissions")) else: - act("remote is newer", "g", f, fl2) + actions.append((f, "g", (fl2,), "remote is newer")) elif nol and n2 == a: # remote only changed 'x' - act("update permissions", "e", f, fl2) - elif nol and n == a: # local only changed 'x' - act("remote is newer", "g", f, fl1) + actions.append((f, "e", (fl2,), "update permissions")) + elif nol and n1 == a: # local only changed 'x' + actions.append((f, "g", (fl1,), "remote is newer")) else: # both changed something - act("versions differ", "m", f, f, f, False) + actions.append((f, "m", (f, f, False), "versions differ")) elif f in copied: # files we'll deal with on m2 side pass - elif f in movewithdir: # directory rename + elif n1 and f in movewithdir: # directory rename f2 = movewithdir[f] - act("remote renamed directory to " + f2, "d", f, None, f2, - m1.flags(f)) - elif f in copy: + actions.append((f, "d", (None, f2, fl1), + "remote renamed directory to " + f2)) + elif n1 and f in copy: f2 = copy[f] - act("local copied/moved to " + f2, "m", f, f2, f, False) - elif f in ma: # clean, a different, no remote - if n != ma[f]: - if repo.ui.promptchoice( - _(" local changed %s which remote deleted\n" - "use (c)hanged version or (d)elete?") % f, - (_("&Changed"), _("&Delete")), 0): - act("prompt delete", "r", f) - else: - act("prompt keep", "a", f) - elif n[20:] == "a": # added, no remote - act("remote deleted", "f", f) + actions.append((f, "m", (f2, f, False), + "local copied/moved to " + f2)) + elif n1 and f in ma: # clean, a different, no remote + if n1 != ma[f]: + prompts.append((f, "cd")) # prompt changed/deleted + elif n1[20:] == "a": # added, no remote + actions.append((f, "f", None, "remote deleted")) else: - act("other deleted", "r", f) - - visit = m2.iteritems() - if repo.ui.debugflag: - visit = sorted(visit) - for f, n in visit: - if partial and not partial(f): - continue - if f in m1 or f in copied: # files already visited - continue - if f in movewithdir: + actions.append((f, "r", None, "other deleted")) + elif n2 and f in movewithdir: f2 = movewithdir[f] - act("local renamed directory to " + f2, "d", None, f, f2, - m2.flags(f)) - elif f in copy: + actions.append((None, "d", (f, f2, fl2), + "local renamed directory to " + f2)) + elif n2 and f in copy: f2 = copy[f] if f2 in m2: - act("remote copied to " + f, "m", - f2, f, f, False) + actions.append((f2, "m", (f, f, False), + "remote copied to " + f)) + else: + actions.append((f2, "m", (f, f, True), + "remote moved to " + f)) + elif n2 and f not in ma: + # local unknown, remote created: the logic is described by the + # following table: + # + # force branchmerge different | action + # n * n | get + # n * y | abort + # y n * | get + # y y n | get + # y y y | merge + # + # Checking whether the files are different is expensive, so we + # don't do that when we can avoid it. + if force and not branchmerge: + actions.append((f, "g", (fl2,), "remote created")) else: - act("remote moved to " + f, "m", - f2, f, f, True) - elif f not in ma: - if (not overwrite - and _checkunknownfile(repo, p1, p2, f)): - act("remote differs from untracked local", - "m", f, f, f, False) + different = _checkunknownfile(repo, wctx, p2, f) + if force and branchmerge and different: + actions.append((f, "m", (f, f, False), + "remote differs from untracked local")) + elif not force and different: + aborts.append((f, "ud")) + else: + actions.append((f, "g", (fl2,), "remote created")) + elif n2 and n2 != ma[f]: + prompts.append((f, "dc")) # prompt deleted/changed + + for f, m in sorted(aborts): + if m == "ud": + repo.ui.warn(_("%s: untracked file differs\n") % f) + else: assert False, m + if aborts: + raise util.Abort(_("untracked files in working directory differ " + "from files in requested revision")) + + for f, m in sorted(prompts): + if m == "cd": + if acceptremote: + actions.append((f, "r", None, "remote delete")) + elif repo.ui.promptchoice( + _("local changed %s which remote deleted\n" + "use (c)hanged version or (d)elete?") % f, + (_("&Changed"), _("&Delete")), 0): + actions.append((f, "r", None, "prompt delete")) else: - act("remote created", "g", f, m2.flags(f)) - elif n != ma[f]: - if repo.ui.promptchoice( + actions.append((f, "a", None, "prompt keep")) + elif m == "dc": + if acceptremote: + actions.append((f, "g", (m2.flags(f),), "remote recreating")) + elif repo.ui.promptchoice( _("remote changed %s which local deleted\n" "use (c)hanged version or leave (d)eleted?") % f, (_("&Changed"), _("&Deleted")), 0) == 0: - act("prompt recreating", "g", f, m2.flags(f)) - + actions.append((f, "g", (m2.flags(f),), "prompt recreating")) + else: assert False, m return actions def actionkey(a): return a[1] == "r" and -1 or 0, a +def getremove(repo, mctx, overwrite, args): + """apply usually-non-interactive updates to the working directory + + mctx is the context to be merged into the working copy + + yields tuples for progress updates + """ + verbose = repo.ui.verbose + unlink = util.unlinkpath + wjoin = repo.wjoin + fctx = mctx.filectx + wwrite = repo.wwrite + audit = repo.wopener.audit + i = 0 + for arg in args: + f = arg[0] + if arg[1] == 'r': + if verbose: + repo.ui.note(_("removing %s\n") % f) + audit(f) + try: + unlink(wjoin(f), ignoremissing=True) + except OSError, inst: + repo.ui.warn(_("update failed to remove %s: %s!\n") % + (f, inst.strerror)) + else: + if verbose: + repo.ui.note(_("getting %s\n") % f) + wwrite(f, fctx(f).data(), arg[2][0]) + if i == 100: + yield i, f + i = 0 + i += 1 + if i > 0: + yield i, f + def applyupdates(repo, actions, wctx, mctx, actx, overwrite): """apply the merge action list to the working directory @@ -335,12 +422,13 @@ # prescan for merges for a in actions: - f, m = a[:2] + f, m, args, msg = a + repo.ui.debug(" %s: %s -> %s\n" % (f, msg, m)) if m == "m": # merge - f2, fd, move = a[2:] + f2, fd, move = args if fd == '.hgsubstate': # merged internally continue - repo.ui.debug("preserving %s for resolve of %s\n" % (f, fd)) + repo.ui.debug(" preserving %s for resolve of %s\n" % (f, fd)) fcl = wctx[f] fco = mctx[f2] if mctx == actx: # backwards, use working dir parent as ancestor @@ -366,23 +454,35 @@ util.unlinkpath(repo.wjoin(f)) numupdates = len(actions) - for i, a in enumerate(actions): - f, m = a[:2] - repo.ui.progress(_('updating'), i + 1, item=f, total=numupdates, + workeractions = [a for a in actions if a[1] in 'gr'] + updated = len([a for a in workeractions if a[1] == 'g']) + removed = len([a for a in workeractions if a[1] == 'r']) + actions = [a for a in actions if a[1] not in 'gr'] + + hgsub = [a[1] for a in workeractions if a[0] == '.hgsubstate'] + if hgsub and hgsub[0] == 'r': + subrepo.submerge(repo, wctx, mctx, wctx, overwrite) + + z = 0 + prog = worker.worker(repo.ui, 0.001, getremove, (repo, mctx, overwrite), + workeractions) + for i, item in prog: + z += i + repo.ui.progress(_('updating'), z, item=item, total=numupdates, unit=_('files')) - if m == "r": # remove - repo.ui.note(_("removing %s\n") % f) - audit(f) - if f == '.hgsubstate': # subrepo states need updating - subrepo.submerge(repo, wctx, mctx, wctx, overwrite) - try: - util.unlinkpath(repo.wjoin(f), ignoremissing=True) - except OSError, inst: - repo.ui.warn(_("update failed to remove %s: %s!\n") % - (f, inst.strerror)) - removed += 1 - elif m == "m": # merge - f2, fd, move = a[2:] + + if hgsub and hgsub[0] == 'g': + subrepo.submerge(repo, wctx, mctx, wctx, overwrite) + + _updating = _('updating') + _files = _('files') + progress = repo.ui.progress + + for i, a in enumerate(actions): + f, m, args, msg = a + progress(_updating, z + i + 1, item=f, total=numupdates, unit=_files) + if m == "m": # merge + f2, fd, move = args if fd == '.hgsubstate': # subrepo states need updating subrepo.submerge(repo, wctx, mctx, wctx.ancestor(mctx), overwrite) @@ -396,15 +496,8 @@ updated += 1 else: merged += 1 - elif m == "g": # get - flags = a[2] - repo.ui.note(_("getting %s\n") % f) - repo.wwrite(f, mctx.filectx(f).data(), flags) - updated += 1 - if f == '.hgsubstate': # subrepo states need updating - subrepo.submerge(repo, wctx, mctx, wctx, overwrite) elif m == "d": # directory rename - f2, fd, flags = a[2:] + f2, fd, flags = args if f: repo.ui.note(_("moving %s to %s\n") % (f, fd)) audit(f) @@ -415,28 +508,29 @@ repo.wwrite(fd, mctx.filectx(f2).data(), flags) updated += 1 elif m == "dr": # divergent renames - fl = a[2] + fl, = args repo.ui.warn(_("note: possible conflict - %s was renamed " "multiple times to:\n") % f) for nf in fl: repo.ui.warn(" %s\n" % nf) elif m == "rd": # rename and delete - fl = a[2] + fl, = args repo.ui.warn(_("note: possible conflict - %s was deleted " "and renamed to:\n") % f) for nf in fl: repo.ui.warn(" %s\n" % nf) elif m == "e": # exec - flags = a[2] + flags, = args audit(f) util.setflags(repo.wjoin(f), 'l' in flags, 'x' in flags) updated += 1 ms.commit() - repo.ui.progress(_('updating'), None, total=numupdates, unit=_('files')) + progress(_updating, None, total=numupdates, unit=_files) return updated, merged, removed, unresolved -def calculateupdates(repo, tctx, mctx, ancestor, branchmerge, force, partial): +def calculateupdates(repo, tctx, mctx, ancestor, branchmerge, force, partial, + acceptremote=False): "Calculate the actions needed to merge mctx into tctx" actions = [] folding = not util.checkcase(repo.path) @@ -447,21 +541,19 @@ _checkcollision(mctx, None) else: _checkcollision(mctx, (tctx, ancestor)) - if not force: - _checkunknown(repo, tctx, mctx) + actions += manifestmerge(repo, tctx, mctx, + ancestor, + branchmerge, force, + partial, acceptremote) if tctx.rev() is None: actions += _forgetremoved(tctx, mctx, branchmerge) - actions += manifestmerge(repo, tctx, mctx, - ancestor, - force and not branchmerge, - partial) return actions def recordupdates(repo, actions, branchmerge): "record merge actions to the dirstate" for a in actions: - f, m = a[:2] + f, m, args, msg = a if m == "r": # remove if branchmerge: repo.dirstate.remove(f) @@ -480,7 +572,7 @@ else: repo.dirstate.normal(f) elif m == "m": # merge - f2, fd, move = a[2:] + f2, fd, move = args if branchmerge: # We've done a branch merge, mark this file as merged # so that we properly record the merger later @@ -503,7 +595,7 @@ if move: repo.dirstate.drop(f) elif m == "d": # directory rename - f2, fd, flag = a[2:] + f2, fd, flag = args if not f2 and f not in repo.dirstate: # untracked file moved continue @@ -528,10 +620,11 @@ branchmerge = whether to merge between branches force = whether to force branch merging or file overwriting partial = a function to filter file lists (dirstate not updated) - mergeancestor = if false, merging with an ancestor (fast-forward) - is only allowed between different named branches. This flag - is used by rebase extension as a temporary fix and should be - avoided in general. + mergeancestor = whether it is merging with an ancestor. If true, + we should accept the incoming changes for any prompts that occur. + If false, merging with an ancestor (fast-forward) is only allowed + between different named branches. This flag is used by rebase extension + as a temporary fix and should be avoided in general. The table below shows all the behaviors of the update command given the -c and -C or no options, whether the working directory @@ -605,21 +698,30 @@ "subrepository '%s'") % s) elif not overwrite: - if pa == p1 or pa == p2: # linear - pass # all good - elif wc.dirty(missing=True): - raise util.Abort(_("crosses branches (merge branches or use" - " --clean to discard changes)")) - elif onode is None: - raise util.Abort(_("crosses branches (merge branches or update" - " --check to force update)")) - else: - # Allow jumping branches if clean and specific rev given - pa = p1 + if pa not in (p1, p2): # nolinear + dirty = wc.dirty(missing=True) + if dirty or onode is None: + # Branching is a bit strange to ensure we do the minimal + # amount of call to obsolete.background. + foreground = obsolete.foreground(repo, [p1.node()]) + # note: the <node> variable contains a random identifier + if repo[node].node() in foreground: + pa = p1 # allow updating to successors + elif dirty: + msg = _("crosses branches (merge branches or use" + " --clean to discard changes)") + raise util.Abort(msg) + else: # node is none + msg = _("crosses branches (merge branches or update" + " --check to force update)") + raise util.Abort(msg) + else: + # Allow jumping branches if clean and specific rev given + pa = p1 ### calculate phase actions = calculateupdates(repo, wc, p2, pa, - branchmerge, force, partial) + branchmerge, force, partial, mergeancestor) ### apply phase if not branchmerge: # just jump to the new rev
--- a/mercurial/minirst.py Thu Apr 04 16:28:19 2013 -0500 +++ b/mercurial/minirst.py Thu Apr 18 23:46:26 2013 -0500 @@ -22,6 +22,20 @@ import util, encoding from i18n import _ +import cgi + +def section(s): + return "%s\n%s\n\n" % (s, "\"" * encoding.colwidth(s)) + +def subsection(s): + return "%s\n%s\n\n" % (s, '=' * encoding.colwidth(s)) + +def subsubsection(s): + return "%s\n%s\n\n" % (s, "-" * encoding.colwidth(s)) + +def subsubsubsection(s): + return "%s\n%s\n\n" % (s, "." * encoding.colwidth(s)) + def replace(text, substs): ''' Apply a list of (find, replace) pairs to a text. @@ -512,6 +526,9 @@ headernest = '' listnest = [] + def escape(s): + return cgi.escape(s, True) + def openlist(start, level): if not listnest or listnest[-1][0] != start: listnest.append((start, level)) @@ -525,37 +542,38 @@ lines = b['lines'] if btype == 'admonition': - admonition = _admonitiontitles[b['admonitiontitle']] - text = ' '.join(map(str.strip, lines)) + admonition = escape(_admonitiontitles[b['admonitiontitle']]) + text = escape(' '.join(map(str.strip, lines))) out.append('<p>\n<b>%s</b> %s\n</p>\n' % (admonition, text)) elif btype == 'paragraph': - out.append('<p>\n%s\n</p>\n' % '\n'.join(lines)) + out.append('<p>\n%s\n</p>\n' % escape('\n'.join(lines))) elif btype == 'margin': pass elif btype == 'literal': - out.append('<pre>\n%s\n</pre>\n' % '\n'.join(lines)) + out.append('<pre>\n%s\n</pre>\n' % escape('\n'.join(lines))) elif btype == 'section': i = b['underline'] if i not in headernest: headernest += i level = headernest.index(i) + 1 - out.append('<h%d>%s</h%d>\n' % (level, lines[0], level)) + out.append('<h%d>%s</h%d>\n' % (level, escape(lines[0]), level)) elif btype == 'table': table = b['table'] - t = [] + out.append('<table>\n') for row in table: - l = [] - for v in zip(row): - if not t: - l.append('<th>%s</th>' % v) - else: - l.append('<td>%s</td>' % v) - t.append(' <tr>%s</tr>\n' % ''.join(l)) - out.append('<table>\n%s</table>\n' % ''.join(t)) + out.append('<tr>') + for v in row: + out.append('<td>') + out.append(escape(v)) + out.append('</td>') + out.append('\n') + out.pop() + out.append('</tr>\n') + out.append('</table>\n') elif btype == 'definition': openlist('dl', level) - term = lines[0] - text = ' '.join(map(str.strip, lines[1:])) + term = escape(lines[0]) + text = escape(' '.join(map(str.strip, lines[1:]))) out.append(' <dt>%s\n <dd>%s\n' % (term, text)) elif btype == 'bullet': bullet, head = lines[0].split(' ', 1) @@ -563,16 +581,16 @@ openlist('ul', level) else: openlist('ol', level) - out.append(' <li> %s\n' % ' '.join([head] + lines[1:])) + out.append(' <li> %s\n' % escape(' '.join([head] + lines[1:]))) elif btype == 'field': openlist('dl', level) - key = b['key'] - text = ' '.join(map(str.strip, lines)) + key = escape(b['key']) + text = escape(' '.join(map(str.strip, lines))) out.append(' <dt>%s\n <dd>%s\n' % (key, text)) elif btype == 'option': openlist('dl', level) - opt = b['optstr'] - desc = ' '.join(map(str.strip, lines)) + opt = escape(b['optstr']) + desc = escape(' '.join(map(str.strip, lines))) out.append(' <dt>%s\n <dd>%s\n' % (opt, desc)) # close lists if indent level of next block is lower
--- a/mercurial/obsolete.py Thu Apr 04 16:28:19 2013 -0500 +++ b/mercurial/obsolete.py Thu Apr 18 23:46:26 2013 -0500 @@ -46,7 +46,7 @@ (A, (C, C)) We use a single marker to distinct the "split" case from the "divergence" - case. If two independants operation rewrite the same changeset A in to A' and + case. If two independents operation rewrite the same changeset A in to A' and A'' when have an error case: divergent rewriting. We can detect it because two markers will be created independently: @@ -129,8 +129,9 @@ # # But by transitivity Ad is also a successors of A. To avoid having Ad marked # as bumped too, we add the `bumpedfix` flag to the marker. <A', (Ad,)>. -# This flag mean that the successors are an interdiff that fix the bumped -# situation, breaking the transitivity of "bumped" here. +# This flag mean that the successors express the changes between the public and +# bumped version and fix the situation, breaking the transitivity of +# "bumped" here. bumpedfix = 1 def _readmarkers(data): @@ -249,6 +250,8 @@ """ if metadata is None: metadata = {} + if 'date' not in metadata: + metadata['date'] = "%d %d" % util.makedate() if len(prec) != 20: raise ValueError(prec) for succ in succs: @@ -367,6 +370,43 @@ finally: lock.release() +def syncpush(repo, remote): + """utility function to push bookmark to a remote + + Exist mostly to allow overridding for experimentation purpose""" + if (_enabled and repo.obsstore and + 'obsolete' in remote.listkeys('namespaces')): + rslts = [] + remotedata = repo.listkeys('obsolete') + for key in sorted(remotedata, reverse=True): + # reverse sort to ensure we end with dump0 + data = remotedata[key] + rslts.append(remote.pushkey('obsolete', key, '', data)) + if [r for r in rslts if not r]: + msg = _('failed to push some obsolete markers!\n') + repo.ui.warn(msg) + +def syncpull(repo, remote, gettransaction): + """utility function to pull bookmark to a remote + + The `gettransaction` is function that return the pull transaction, creating + one if necessary. We return the transaction to inform the calling code that + a new transaction have been created (when applicable). + + Exists mostly to allow overridding for experimentation purpose""" + tr = None + if _enabled: + repo.ui.debug('fetching remote obsolete markers\n') + remoteobs = remote.listkeys('obsolete') + if 'dump0' in remoteobs: + tr = gettransaction() + for key in sorted(remoteobs, reverse=True): + if key.startswith('dump'): + data = base85.b85decode(remoteobs[key]) + repo.obsstore.mergemarkers(tr, data) + repo.invalidatevolatilesets() + return tr + def allmarkers(repo): """all obsolete markers known in a repository""" for markerdata in repo.obsstore: @@ -402,6 +442,33 @@ seen.add(suc) remaining.add(suc) +def foreground(repo, nodes): + """return all nodes in the "foreground" of other node + + The foreground of a revision is anything reachable using parent -> children + or precursor -> sucessor relation. It is very similars to "descendant" but + augmented with obsolescence information. + + Beware that possible obsolescence cycle may result if complexe situation. + """ + repo = repo.unfiltered() + foreground = set(repo.set('%ln::', nodes)) + if repo.obsstore: + # We only need this complicated logic if there is obsolescence + # XXX will probably deserve an optimised revset. + nm = repo.changelog.nodemap + plen = -1 + # compute the whole set of successors or descendants + while len(foreground) != plen: + plen = len(foreground) + succs = set(c.node() for c in foreground) + mutable = [c.node() for c in foreground if c.mutable()] + succs.update(allsuccessors(repo.obsstore, mutable)) + known = (n for n in succs if n in nm) + foreground = set(repo.set('%ln::', known)) + return set(c.node() for c in foreground) + + def successorssets(repo, initialnode, cache=None): """Return all set of successors of initial nodes @@ -510,7 +577,7 @@ # In such a situation, we arbitrary set the successors sets of # the node to nothing (node pruned) to break the cycle. # - # If no break was encountered we proceeed to phase 2. + # If no break was encountered we proceed to phase 2. # # Phase 2 computes successors sets of CURRENT (case 4); see details # in phase 2 itself. @@ -551,13 +618,13 @@ # successors sets of all its "successors" node. # # Each different marker is a divergence in the obsolescence - # history. It contributes successors sets dictinct from other + # history. It contributes successors sets distinct from other # markers. # # Within a marker, a successor may have divergent successors # sets. In such a case, the marker will contribute multiple # divergent successors sets. If multiple successors have - # divergents successors sets, a cartesian product is used. + # divergent successors sets, a cartesian product is used. # # At the end we post-process successors sets to remove # duplicated entry and successors set that are strict subset of
--- a/mercurial/parsers.c Thu Apr 04 16:28:19 2013 -0500 +++ b/mercurial/parsers.c Thu Apr 18 23:46:26 2013 -0500 @@ -326,7 +326,8 @@ if (getintat(v, 3, &mtime) == -1) goto bail; if (*s == 'n' && mtime == (uint32_t)now) { - /* See dirstate.py:write for why we do this. */ + /* See pure/parsers.py:pack_dirstate for why we do + * this. */ if (PyDict_SetItem(map, k, dirstate_unset) == -1) goto bail; mode = 0, size = -1, mtime = -1; @@ -1162,6 +1163,367 @@ } } +static inline void index_get_parents(indexObject *self, int rev, int *ps) +{ + if (rev >= self->length - 1) { + PyObject *tuple = PyList_GET_ITEM(self->added, + rev - self->length + 1); + ps[0] = (int)PyInt_AS_LONG(PyTuple_GET_ITEM(tuple, 5)); + ps[1] = (int)PyInt_AS_LONG(PyTuple_GET_ITEM(tuple, 6)); + } else { + const char *data = index_deref(self, rev); + ps[0] = getbe32(data + 24); + ps[1] = getbe32(data + 28); + } +} + +typedef uint64_t bitmask; + +/* + * Given a disjoint set of revs, return all candidates for the + * greatest common ancestor. In revset notation, this is the set + * "heads(::a and ::b and ...)" + */ +static PyObject *find_gca_candidates(indexObject *self, const int *revs, + int revcount) +{ + const bitmask allseen = (1ull << revcount) - 1; + const bitmask poison = 1ull << revcount; + PyObject *gca = PyList_New(0); + int i, v, interesting, left; + int maxrev = -1; + long sp; + bitmask *seen; + + for (i = 0; i < revcount; i++) { + if (revs[i] > maxrev) + maxrev = revs[i]; + } + + seen = calloc(sizeof(*seen), maxrev + 1); + if (seen == NULL) + return PyErr_NoMemory(); + + for (i = 0; i < revcount; i++) + seen[revs[i]] = 1ull << i; + + interesting = left = revcount; + + for (v = maxrev; v >= 0 && interesting; v--) { + long sv = seen[v]; + int parents[2]; + + if (!sv) + continue; + + if (sv < poison) { + interesting -= 1; + if (sv == allseen) { + PyObject *obj = PyInt_FromLong(v); + if (obj == NULL) + goto bail; + if (PyList_Append(gca, obj) == -1) { + Py_DECREF(obj); + goto bail; + } + sv |= poison; + for (i = 0; i < revcount; i++) { + if (revs[i] == v) { + if (--left <= 1) + goto done; + break; + } + } + } + } + index_get_parents(self, v, parents); + + for (i = 0; i < 2; i++) { + int p = parents[i]; + if (p == -1) + continue; + sp = seen[p]; + if (sv < poison) { + if (sp == 0) { + seen[p] = sv; + interesting++; + } + else if (sp != sv) + seen[p] |= sv; + } else { + if (sp && sp < poison) + interesting--; + seen[p] = sv; + } + } + } + +done: + free(seen); + return gca; +bail: + free(seen); + Py_XDECREF(gca); + return NULL; +} + +/* + * Given a disjoint set of revs, return the subset with the longest + * path to the root. + */ +static PyObject *find_deepest(indexObject *self, PyObject *revs) +{ + const Py_ssize_t revcount = PyList_GET_SIZE(revs); + static const Py_ssize_t capacity = 24; + int *depth, *interesting = NULL; + int i, j, v, ninteresting; + PyObject *dict = NULL, *keys; + long *seen = NULL; + int maxrev = -1; + long final; + + if (revcount > capacity) { + PyErr_Format(PyExc_OverflowError, + "bitset size (%ld) > capacity (%ld)", + (long)revcount, (long)capacity); + return NULL; + } + + for (i = 0; i < revcount; i++) { + int n = (int)PyInt_AsLong(PyList_GET_ITEM(revs, i)); + if (n > maxrev) + maxrev = n; + } + + depth = calloc(sizeof(*depth), maxrev + 1); + if (depth == NULL) + return PyErr_NoMemory(); + + seen = calloc(sizeof(*seen), maxrev + 1); + if (seen == NULL) { + PyErr_NoMemory(); + goto bail; + } + + interesting = calloc(sizeof(*interesting), 2 << revcount); + if (interesting == NULL) { + PyErr_NoMemory(); + goto bail; + } + + for (i = 0; i < revcount; i++) { + int n = (int)PyInt_AsLong(PyList_GET_ITEM(revs, i)); + long b = 1l << i; + depth[n] = 1; + seen[n] = b; + interesting[b] = 1; + } + + ninteresting = (int)revcount; + + for (v = maxrev; v >= 0 && ninteresting > 1; v--) { + int dv = depth[v]; + int parents[2]; + long sv; + + if (dv == 0) + continue; + + sv = seen[v]; + index_get_parents(self, v, parents); + + for (i = 0; i < 2; i++) { + int p = parents[i]; + long nsp, sp; + int dp; + + if (p == -1) + continue; + + dp = depth[p]; + nsp = sp = seen[p]; + if (dp <= dv) { + depth[p] = dv + 1; + if (sp != sv) { + interesting[sv] += 1; + nsp = seen[p] = sv; + if (sp) { + interesting[sp] -= 1; + if (interesting[sp] == 0) + ninteresting -= 1; + } + } + } + else if (dv == dp - 1) { + nsp = sp | sv; + if (nsp == sp) + continue; + seen[p] = nsp; + interesting[nsp] += 1; + interesting[sp] -= 1; + if (interesting[sp] == 0) + ninteresting -= 1; + } + } + interesting[sv] -= 1; + if (interesting[sv] == 0) + ninteresting -= 1; + } + + final = 0; + j = ninteresting; + for (i = 0; i < (int)(2 << revcount) && j > 0; i++) { + if (interesting[i] == 0) + continue; + final |= i; + j -= 1; + } + if (final == 0) + return PyList_New(0); + + dict = PyDict_New(); + if (dict == NULL) + goto bail; + + j = ninteresting; + for (i = 0; i < revcount && j > 0; i++) { + PyObject *key; + + if ((final & (1 << i)) == 0) + continue; + + key = PyList_GET_ITEM(revs, i); + Py_INCREF(key); + Py_INCREF(Py_None); + if (PyDict_SetItem(dict, key, Py_None) == -1) { + Py_DECREF(key); + Py_DECREF(Py_None); + goto bail; + } + j -= 1; + } + + keys = PyDict_Keys(dict); + + free(depth); + free(seen); + free(interesting); + Py_DECREF(dict); + + return keys; +bail: + free(depth); + free(seen); + free(interesting); + Py_XDECREF(dict); + + return NULL; +} + +/* + * Given a (possibly overlapping) set of revs, return the greatest + * common ancestors: those with the longest path to the root. + */ +static PyObject *index_ancestors(indexObject *self, PyObject *args) +{ + PyObject *ret = NULL, *gca = NULL; + Py_ssize_t argcount, i, len; + bitmask repeat = 0; + int revcount = 0; + int *revs; + + argcount = PySequence_Length(args); + revs = malloc(argcount * sizeof(*revs)); + if (argcount > 0 && revs == NULL) + return PyErr_NoMemory(); + len = index_length(self) - 1; + + for (i = 0; i < argcount; i++) { + static const int capacity = 24; + PyObject *obj = PySequence_GetItem(args, i); + bitmask x; + long val; + + if (!PyInt_Check(obj)) { + PyErr_SetString(PyExc_TypeError, + "arguments must all be ints"); + goto bail; + } + val = PyInt_AsLong(obj); + if (val == -1) { + ret = PyList_New(0); + goto done; + } + if (val < 0 || val >= len) { + PyErr_SetString(PyExc_IndexError, + "index out of range"); + goto bail; + } + /* this cheesy bloom filter lets us avoid some more + * expensive duplicate checks in the common set-is-disjoint + * case */ + x = 1ull << (val & 0x3f); + if (repeat & x) { + int k; + for (k = 0; k < revcount; k++) { + if (val == revs[k]) + goto duplicate; + } + } + else repeat |= x; + if (revcount >= capacity) { + PyErr_Format(PyExc_OverflowError, + "bitset size (%d) > capacity (%d)", + revcount, capacity); + goto bail; + } + revs[revcount++] = (int)val; + duplicate:; + } + + if (revcount == 0) { + ret = PyList_New(0); + goto done; + } + if (revcount == 1) { + PyObject *obj; + ret = PyList_New(1); + if (ret == NULL) + goto bail; + obj = PyInt_FromLong(revs[0]); + if (obj == NULL) + goto bail; + PyList_SET_ITEM(ret, 0, obj); + goto done; + } + + gca = find_gca_candidates(self, revs, revcount); + if (gca == NULL) + goto bail; + + if (PyList_GET_SIZE(gca) <= 1) { + ret = gca; + Py_INCREF(gca); + } + else if (PyList_GET_SIZE(gca) == 1) { + ret = PyList_GET_ITEM(gca, 0); + Py_INCREF(ret); + } + else ret = find_deepest(self, gca); + +done: + free(revs); + Py_XDECREF(gca); + + return ret; + +bail: + free(revs); + Py_XDECREF(gca); + Py_XDECREF(ret); + return NULL; +} + /* * Invalidate any trie entries introduced by added revs. */ @@ -1405,6 +1767,8 @@ }; static PyMethodDef index_methods[] = { + {"ancestors", (PyCFunction)index_ancestors, METH_VARARGS, + "return the gca set of the given revs"}, {"clearcaches", (PyCFunction)index_clearcaches, METH_NOARGS, "clear the index caches"}, {"get", (PyCFunction)index_m_get, METH_VARARGS, @@ -1527,8 +1891,12 @@ {NULL, NULL} }; +void dirs_module_init(PyObject *mod); + static void module_init(PyObject *mod) { + dirs_module_init(mod); + indexType.tp_new = PyType_GenericNew; if (PyType_Ready(&indexType) < 0) return;
--- a/mercurial/patch.py Thu Apr 04 16:28:19 2013 -0500 +++ b/mercurial/patch.py Thu Apr 18 23:46:26 2013 -0500 @@ -314,7 +314,7 @@ gitpatches = [] for line in lr: line = line.rstrip(' \r\n') - if line.startswith('diff --git'): + if line.startswith('diff --git a/'): m = gitre.match(line) if m: if gp: @@ -1211,7 +1211,7 @@ emitfile = False yield 'file', (afile, bfile, h, gp and gp.copy() or None) yield 'hunk', h - elif x.startswith('diff --git'): + elif x.startswith('diff --git a/'): m = gitre.match(x.rstrip(' \r\n')) if not m: continue @@ -1756,6 +1756,8 @@ else: header.append('deleted file mode %s\n' % gitmode[man1.flags(f)]) + if util.binary(to): + dodiff = 'binary' elif not to or util.binary(to): # regular diffs cannot represent empty file deletion losedatafn(f) @@ -1813,7 +1815,7 @@ addresult() # set numbers to 0 anyway when starting new file adds, removes, isbinary = 0, 0, False - if line.startswith('diff --git'): + if line.startswith('diff --git a/'): filename = gitre.search(line).group(1) elif line.startswith('diff -r'): # format: "diff -r ... -r ... filename"
--- a/mercurial/pathencode.c Thu Apr 04 16:28:19 2013 -0500 +++ b/mercurial/pathencode.c Thu Apr 18 23:46:26 2013 -0500 @@ -45,7 +45,7 @@ H, /* ".h" */ HGDI, /* ".hg", ".d", or ".i" */ SPACE, - DEFAULT, /* byte of a path component after the first */ + DEFAULT /* byte of a path component after the first */ }; /* state machine for dir-encoding */ @@ -53,7 +53,7 @@ DDOT, DH, DHGDI, - DDEFAULT, + DDEFAULT }; static inline int inset(const uint32_t bitset[], char c)
--- a/mercurial/phases.py Thu Apr 04 16:28:19 2013 -0500 +++ b/mercurial/phases.py Thu Apr 18 23:46:26 2013 -0500 @@ -266,7 +266,15 @@ filtered = True if filtered: self.dirty = True - self._phaserevs = None + # filterunknown is called by repo.destroyed, we may have no changes in + # root but phaserevs contents is certainly invalide (or at least we + # have not proper way to check that. related to issue 3858. + # + # The other caller is __init__ that have no _phaserevs initialized + # anyway. If this change we should consider adding a dedicated + # "destroyed" function to phasecache or a proper cache key mechanisme + # (see branchmap one) + self._phaserevs = None def advanceboundary(repo, targetphase, nodes): """Add nodes to a phase changing other nodes phases if necessary.
--- a/mercurial/posix.py Thu Apr 04 16:28:19 2013 -0500 +++ b/mercurial/posix.py Thu Apr 18 23:46:26 2013 -0500 @@ -557,3 +557,11 @@ if self.realpath != self.path: okayifmissing(os.unlink, self.realpath) okayifmissing(os.rmdir, os.path.dirname(self.realpath)) + +def statislink(st): + '''check whether a stat result is a symlink''' + return st and stat.S_ISLNK(st.st_mode) + +def statisexec(st): + '''check whether a stat result is an executable file''' + return st and (st.st_mode & 0100 != 0)
--- a/mercurial/pure/osutil.py Thu Apr 04 16:28:19 2013 -0500 +++ b/mercurial/pure/osutil.py Thu Apr 18 23:46:26 2013 -0500 @@ -68,7 +68,7 @@ _INVALID_HANDLE_VALUE = _HANDLE(-1).value - # CreateFile + # CreateFile _FILE_SHARE_READ = 0x00000001 _FILE_SHARE_WRITE = 0x00000002 _FILE_SHARE_DELETE = 0x00000004
--- a/mercurial/pure/parsers.py Thu Apr 04 16:28:19 2013 -0500 +++ b/mercurial/pure/parsers.py Thu Apr 18 23:46:26 2013 -0500 @@ -7,7 +7,7 @@ from mercurial.node import bin, nullid from mercurial import util -import struct, zlib +import struct, zlib, cStringIO _pack = struct.pack _unpack = struct.unpack @@ -87,3 +87,29 @@ copymap[f] = c dmap[f] = e[:4] return parents + +def pack_dirstate(dmap, copymap, pl, now): + now = int(now) + cs = cStringIO.StringIO() + write = cs.write + write("".join(pl)) + for f, e in dmap.iteritems(): + if e[0] == 'n' and e[3] == now: + # The file was last modified "simultaneously" with the current + # write to dirstate (i.e. within the same second for file- + # systems with a granularity of 1 sec). This commonly happens + # for at least a couple of files on 'update'. + # The user could change the file without changing its size + # within the same second. Invalidate the file's stat data in + # dirstate, forcing future 'status' calls to compare the + # contents of the file. This prevents mistakenly treating such + # files as clean. + e = (e[0], 0, -1, -1) # mark entry as 'unset' + dmap[f] = e + + if f in copymap: + f = "%s\0%s" % (f, copymap[f]) + e = _pack(">cllll", e[0], e[1], e[2], e[3], len(f)) + write(e) + write(f) + return cs.getvalue()
--- a/mercurial/pvec.py Thu Apr 04 16:28:19 2013 -0500 +++ b/mercurial/pvec.py Thu Apr 18 23:46:26 2013 -0500 @@ -169,7 +169,7 @@ self._bs = hashorctx self._depth, self._vec = _split(base85.b85decode(hashorctx)) else: - self._vec = ctxpvec(ctx) + self._vec = ctxpvec(hashorctx) def __str__(self): return self._bs
--- a/mercurial/repair.py Thu Apr 04 16:28:19 2013 -0500 +++ b/mercurial/repair.py Thu Apr 18 23:46:26 2013 -0500 @@ -119,6 +119,7 @@ if backup == "all": backupfile = _bundle(repo, stripbases, cl.heads(), node, topic) repo.ui.status(_("saved backup bundle to %s\n") % backupfile) + repo.ui.log("backupbundle", "saved backup bundle to %s\n", backupfile) if saveheads or savebases: # do not compress partial bundle if we remove it from disk later chgrpfile = _bundle(repo, savebases, saveheads, node, 'temp',
--- a/mercurial/repoview.py Thu Apr 04 16:28:19 2013 -0500 +++ b/mercurial/repoview.py Thu Apr 18 23:46:26 2013 -0500 @@ -55,7 +55,6 @@ return frozenset(hiddens | secrets) else: return hiddens - return frozenset() def computemutable(repo): """compute the set of revision that should be filtered when used a server @@ -149,7 +148,7 @@ repoview.method() --> repo.__class__.method(repoview) The inheritance has to be done dynamically because `repo` can be of any - subclasses of `localrepo`. Eg: `bundlerepo` or `httprepo`. + subclasses of `localrepo`. Eg: `bundlerepo` or `statichttprepo`. """ def __init__(self, repo, filtername): @@ -158,7 +157,7 @@ object.__setattr__(self, '_clcachekey', None) object.__setattr__(self, '_clcache', None) - # not a cacheproperty on purpose we shall implement a proper cache later + # not a propertycache on purpose we shall implement a proper cache later @property def changelog(self): """return a filtered version of the changeset @@ -210,7 +209,7 @@ def __delattr__(self, attr): return delattr(self._unfilteredrepo, attr) - # The `requirement` attribut is initialiazed during __init__. But + # The `requirements` attribute is initialized during __init__. But # __getattr__ won't be called as it also exists on the class. We need # explicit forwarding to main repo here @property
--- a/mercurial/revlog.py Thu Apr 04 16:28:19 2013 -0500 +++ b/mercurial/revlog.py Thu Apr 18 23:46:26 2013 -0500 @@ -91,6 +91,14 @@ return bin[1:] raise RevlogError(_("unknown compression type %r") % t) +# index v0: +# 4 bytes: offset +# 4 bytes: compressed length +# 4 bytes: base rev +# 4 bytes: link rev +# 32 bytes: parent 1 nodeid +# 32 bytes: parent 2 nodeid +# 32 bytes: nodeid indexformatv0 = ">4l20s20s20s" v0shaoffset = 56 @@ -697,20 +705,15 @@ def ancestor(self, a, b): """calculate the least common ancestor of nodes a and b""" - # fast path, check if it is a descendant a, b = self.rev(a), self.rev(b) - start, end = sorted((a, b)) - if self.descendant(start, end): - return self.node(start) - - def parents(rev): - return [p for p in self.parentrevs(rev) if p != nullrev] - - c = ancestor.ancestor(a, b, parents) - if c is None: - return nullid - - return self.node(c) + try: + ancs = self.index.ancestors(a, b) + except (AttributeError, OverflowError): + ancs = ancestor.ancestors(self.parentrevs, a, b) + if ancs: + # choose a consistent winner when there's a tie + return min(map(self.node, ancs)) + return nullid def _match(self, id): if isinstance(id, int):
--- a/mercurial/revset.py Thu Apr 04 16:28:19 2013 -0500 +++ b/mercurial/revset.py Thu Apr 18 23:46:26 2013 -0500 @@ -238,12 +238,10 @@ return [x for x in r if x in s] def dagrange(repo, subset, x, y): - if subset: - r = list(repo) - xs = _revsbetween(repo, getset(repo, r, x), getset(repo, r, y)) - s = set(subset) - return [r for r in xs if r in s] - return [] + r = list(repo) + xs = _revsbetween(repo, getset(repo, r, x), getset(repo, r, y)) + s = set(subset) + return [r for r in xs if r in s] def andset(repo, subset, x, y): return getset(repo, getset(repo, subset, x), y) @@ -277,20 +275,32 @@ return checkstatus(repo, subset, pat, 1) def ancestor(repo, subset, x): - """``ancestor(single, single)`` - Greatest common ancestor of the two changesets. + """``ancestor(*changeset)`` + Greatest common ancestor of the changesets. + + Accepts 0 or more changesets. + Will return empty list when passed no args. + Greatest common ancestor of a single changeset is that changeset. """ # i18n: "ancestor" is a keyword - l = getargs(x, 2, 2, _("ancestor requires two arguments")) - r = list(repo) - a = getset(repo, r, l[0]) - b = getset(repo, r, l[1]) - if len(a) != 1 or len(b) != 1: - # i18n: "ancestor" is a keyword - raise error.ParseError(_("ancestor arguments must be single revisions")) - an = [repo[a[0]].ancestor(repo[b[0]]).rev()] + l = getlist(x) + rl = list(repo) + anc = None - return [r for r in an if r in subset] + # (getset(repo, rl, i) for i in l) generates a list of lists + rev = repo.changelog.rev + ancestor = repo.changelog.ancestor + node = repo.changelog.node + for revs in (getset(repo, rl, i) for i in l): + for r in revs: + if anc is None: + anc = r + else: + anc = rev(ancestor(node(anc), node(r))) + + if anc is not None and anc in subset: + return [anc] + return [] def _ancestors(repo, subset, x, followfirst=False): args = getset(repo, list(repo), x) @@ -1484,8 +1494,6 @@ s = set([repo[tn].rev()]) else: s = set([cl.rev(n) for t, n in repo.tagslist() if matcher(t)]) - if not s: - raise util.Abort(_("no tags exist that match '%s'") % pattern) else: s = set([cl.rev(n) for t, n in repo.tagslist() if t != 'tip']) return [r for r in subset if r in s]
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/mercurial/scmposix.py Thu Apr 18 23:46:26 2013 -0500 @@ -0,0 +1,32 @@ +import sys, os +import osutil + +def _rcfiles(path): + rcs = [os.path.join(path, 'hgrc')] + rcdir = os.path.join(path, 'hgrc.d') + try: + rcs.extend([os.path.join(rcdir, f) + for f, kind in osutil.listdir(rcdir) + if f.endswith(".rc")]) + except OSError: + pass + return rcs + +def systemrcpath(): + path = [] + if sys.platform == 'plan9': + root = 'lib/mercurial' + else: + root = 'etc/mercurial' + # old mod_python does not set sys.argv + if len(getattr(sys, 'argv', [])) > 0: + p = os.path.dirname(os.path.dirname(sys.argv[0])) + path.extend(_rcfiles(os.path.join(p, root))) + path.extend(_rcfiles('/' + root)) + return path + +def userrcpath(): + if sys.platform == 'plan9': + return [os.environ['home'] + '/lib/hgrc'] + else: + return [os.path.expanduser('~/.hgrc')]
--- a/mercurial/scmutil.py Thu Apr 04 16:28:19 2013 -0500 +++ b/mercurial/scmutil.py Thu Apr 18 23:46:26 2013 -0500 @@ -7,9 +7,17 @@ from i18n import _ from mercurial.node import nullrev -import util, error, osutil, revset, similar, encoding, phases +import util, error, osutil, revset, similar, encoding, phases, parsers import match as matchmod -import os, errno, re, stat, sys, glob +import os, errno, re, stat, glob + +if os.name == 'nt': + import scmwindows as scmplatform +else: + import scmposix as scmplatform + +systemrcpath = scmplatform.systemrcpath +userrcpath = scmplatform.userrcpath def nochangesfound(ui, repo, excluded=None): '''Report no changes for push/pull, excluded is None or a list of @@ -38,6 +46,11 @@ for c in (':', '\0', '\n', '\r'): if c in lbl: raise util.Abort(_("%r cannot be used in a name") % c) + try: + int(lbl) + raise util.Abort(_("a %s cannot have an integer as its name") % kind) + except ValueError: + pass def checkfilename(f): '''Check that the filename f is an acceptable filename for a tracked file''' @@ -179,6 +192,13 @@ # want to add "foo/bar/baz" before checking if there's a "foo/.hg" self.auditeddir.update(prefixes) + def check(self, path): + try: + self(path) + return True + except (OSError, util.Abort): + return False + class abstractvfs(object): """Abstract base class; cannot be instantiated""" @@ -222,6 +242,9 @@ def isdir(self, path=None): return os.path.isdir(self.join(path)) + def islink(self, path=None): + return os.path.islink(self.join(path)) + def makedir(self, path=None, notindexed=True): return util.makedir(self.join(path), notindexed) @@ -234,6 +257,15 @@ def readdir(self, path=None, stat=None, skip=None): return osutil.listdir(self.join(path), stat, skip) + def rename(self, src, dst): + return util.rename(self.join(src), self.join(dst)) + + def readlink(self, path): + return os.readlink(self.join(path)) + + def setflags(self, path, l, x): + return util.setflags(self.join(path), l, x) + def stat(self, path=None): return os.stat(self.join(path)) @@ -243,9 +275,11 @@ This class is used to hide the details of COW semantics and remote file access from higher level code. ''' - def __init__(self, base, audit=True, expand=False): - if expand: - base = os.path.realpath(util.expandpath(base)) + def __init__(self, base, audit=True, expandpath=False, realpath=False): + if expandpath: + base = util.expandpath(base) + if realpath: + base = os.path.realpath(base) self.base = base self._setmustaudit(audit) self.createmode = None @@ -294,8 +328,7 @@ # 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) + util.ensuredirs(dirname, self.createmode) return util.atomictempfile(f, mode, self.createmode) try: if 'w' in mode: @@ -313,8 +346,7 @@ if e.errno != errno.ENOENT: raise nlink = 0 - if not os.path.isdir(dirname): - util.makedirs(dirname, self.createmode) + util.ensuredirs(dirname, self.createmode) if nlink > 0: if self._trustnlink is None: self._trustnlink = nlink > 1 or util.checknlink(f) @@ -333,9 +365,7 @@ except OSError: pass - dirname = os.path.dirname(linkname) - if not os.path.exists(dirname): - util.makedirs(dirname, self.createmode) + util.ensuredirs(os.path.dirname(linkname), self.createmode) if self._cansymlink: try: @@ -523,84 +553,6 @@ _rcpath = osrcpath() return _rcpath -if os.name != 'nt': - - def rcfiles(path): - rcs = [os.path.join(path, 'hgrc')] - rcdir = os.path.join(path, 'hgrc.d') - try: - rcs.extend([os.path.join(rcdir, f) - for f, kind in osutil.listdir(rcdir) - if f.endswith(".rc")]) - except OSError: - pass - return rcs - - def systemrcpath(): - path = [] - if sys.platform == 'plan9': - root = 'lib/mercurial' - else: - root = 'etc/mercurial' - # old mod_python does not set sys.argv - if len(getattr(sys, 'argv', [])) > 0: - p = os.path.dirname(os.path.dirname(sys.argv[0])) - path.extend(rcfiles(os.path.join(p, root))) - path.extend(rcfiles('/' + root)) - return path - - def userrcpath(): - if sys.platform == 'plan9': - return [os.environ['home'] + '/lib/hgrc'] - else: - return [os.path.expanduser('~/.hgrc')] - -else: - - import _winreg - - def systemrcpath(): - '''return default os-specific hgrc search path''' - rcpath = [] - filename = util.executablepath() - # Use mercurial.ini found in directory with hg.exe - progrc = os.path.join(os.path.dirname(filename), 'mercurial.ini') - if os.path.isfile(progrc): - rcpath.append(progrc) - return rcpath - # Use hgrc.d found in directory with hg.exe - progrcd = os.path.join(os.path.dirname(filename), 'hgrc.d') - if os.path.isdir(progrcd): - for f, kind in osutil.listdir(progrcd): - if f.endswith('.rc'): - rcpath.append(os.path.join(progrcd, f)) - return rcpath - # else look for a system rcpath in the registry - value = util.lookupreg('SOFTWARE\\Mercurial', None, - _winreg.HKEY_LOCAL_MACHINE) - if not isinstance(value, str) or not value: - return rcpath - value = util.localpath(value) - for p in value.split(os.pathsep): - if p.lower().endswith('mercurial.ini'): - rcpath.append(p) - elif os.path.isdir(p): - for f, kind in osutil.listdir(p): - if f.endswith('.rc'): - rcpath.append(os.path.join(p, f)) - return rcpath - - def userrcpath(): - '''return os-specific hgrc search path to the user dir''' - home = os.path.expanduser('~') - path = [os.path.join(home, 'mercurial.ini'), - os.path.join(home, '.hgrc')] - userprofile = os.environ.get('USERPROFILE') - if userprofile: - path.append(os.path.join(userprofile, 'mercurial.ini')) - path.append(os.path.join(userprofile, '.hgrc')) - return path - def revsingle(repo, revspec, default='.'): if not revspec: return repo[default] @@ -737,30 +689,33 @@ rejected = [] m.bad = lambda x, y: rejected.append(x) - for abs in repo.walk(m): - target = repo.wjoin(abs) - good = True - try: - audit_path(abs) - except (OSError, util.Abort): - good = False - rel = m.rel(abs) - exact = m.exact(abs) - if good and abs not in repo.dirstate: + ctx = repo[None] + dirstate = repo.dirstate + walkresults = dirstate.walk(m, sorted(ctx.substate), True, False) + for abs, st in walkresults.iteritems(): + dstate = dirstate[abs] + if dstate == '?' and audit_path.check(abs): unknown.append(abs) - if repo.ui.verbose or not exact: - repo.ui.status(_('adding %s\n') % ((pats and rel) or abs)) - elif (repo.dirstate[abs] != 'r' and - (not good or not os.path.lexists(target) or - (os.path.isdir(target) and not os.path.islink(target)))): + elif dstate != 'r' and not st: deleted.append(abs) - if repo.ui.verbose or not exact: - repo.ui.status(_('removing %s\n') % ((pats and rel) or abs)) # for finding renames - elif repo.dirstate[abs] == 'r': + elif dstate == 'r': removed.append(abs) - elif repo.dirstate[abs] == 'a': + elif dstate == 'a': added.append(abs) + + unknownset = set(unknown) + toprint = unknownset.copy() + toprint.update(deleted) + for abs in sorted(toprint): + if repo.ui.verbose or not m.exact(abs): + rel = m.rel(abs) + if abs in unknownset: + status = _('adding %s\n') % ((pats and rel) or abs) + else: + status = _('removing %s\n') % ((pats and rel) or abs) + repo.ui.status(status) + copies = {} if similarity > 0: for old, new, score in similar.findrenames(repo, @@ -787,49 +742,6 @@ return 1 return 0 -def updatedir(ui, repo, patches, similarity=0): - '''Update dirstate after patch application according to metadata''' - if not patches: - return [] - copies = [] - removes = set() - cfiles = patches.keys() - cwd = repo.getcwd() - if cwd: - cfiles = [util.pathto(repo.root, cwd, f) for f in patches.keys()] - for f in patches: - gp = patches[f] - if not gp: - continue - if gp.op == 'RENAME': - copies.append((gp.oldpath, gp.path)) - removes.add(gp.oldpath) - elif gp.op == 'COPY': - copies.append((gp.oldpath, gp.path)) - elif gp.op == 'DELETE': - removes.add(gp.path) - - wctx = repo[None] - for src, dst in copies: - dirstatecopy(ui, repo, wctx, src, dst, cwd=cwd) - if (not similarity) and removes: - wctx.remove(sorted(removes), True) - - for f in patches: - gp = patches[f] - if gp and gp.mode: - islink, isexec = gp.mode - dst = repo.wjoin(gp.path) - # patch won't create empty files - if gp.op == 'ADD' and not os.path.lexists(dst): - flags = (isexec and 'x' or '') + (islink and 'l' or '') - repo.wwrite(gp.path, '', flags) - util.setflags(dst, islink, isexec) - addremove(repo, cfiles, similarity=similarity) - files = patches.keys() - files.extend([r for r in removes if r not in files]) - return sorted(files) - def dirstatecopy(ui, repo, wctx, src, dst, dryrun=False, cwd=None): """Update the dirstate to reflect the intent of copying src to dst. For different reasons it might not end with dst being marked as copied from src. @@ -992,3 +904,48 @@ del obj.__dict__[self.name] except KeyError: raise AttributeError(self.name) + +class dirs(object): + '''a multiset of directory names from a dirstate or manifest''' + + def __init__(self, map, skip=None): + self._dirs = {} + addpath = self.addpath + if util.safehasattr(map, 'iteritems') and skip is not None: + for f, s in map.iteritems(): + if s[0] != skip: + addpath(f) + else: + for f in map: + addpath(f) + + def addpath(self, path): + dirs = self._dirs + for base in finddirs(path): + if base in dirs: + dirs[base] += 1 + return + dirs[base] = 1 + + def delpath(self, path): + dirs = self._dirs + for base in finddirs(path): + if dirs[base] > 1: + dirs[base] -= 1 + return + del dirs[base] + + def __iter__(self): + return self._dirs.iterkeys() + + def __contains__(self, d): + return d in self._dirs + +if util.safehasattr(parsers, 'dirs'): + dirs = parsers.dirs + +def finddirs(path): + pos = path.rfind('/') + while pos != -1: + yield path[:pos] + pos = path.rfind('/', 0, pos)
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/mercurial/scmwindows.py Thu Apr 18 23:46:26 2013 -0500 @@ -0,0 +1,46 @@ +import os +import osutil +import util +import _winreg + +def systemrcpath(): + '''return default os-specific hgrc search path''' + rcpath = [] + filename = util.executablepath() + # Use mercurial.ini found in directory with hg.exe + progrc = os.path.join(os.path.dirname(filename), 'mercurial.ini') + if os.path.isfile(progrc): + rcpath.append(progrc) + return rcpath + # Use hgrc.d found in directory with hg.exe + progrcd = os.path.join(os.path.dirname(filename), 'hgrc.d') + if os.path.isdir(progrcd): + for f, kind in osutil.listdir(progrcd): + if f.endswith('.rc'): + rcpath.append(os.path.join(progrcd, f)) + return rcpath + # else look for a system rcpath in the registry + value = util.lookupreg('SOFTWARE\\Mercurial', None, + _winreg.HKEY_LOCAL_MACHINE) + if not isinstance(value, str) or not value: + return rcpath + value = util.localpath(value) + for p in value.split(os.pathsep): + if p.lower().endswith('mercurial.ini'): + rcpath.append(p) + elif os.path.isdir(p): + for f, kind in osutil.listdir(p): + if f.endswith('.rc'): + rcpath.append(os.path.join(p, f)) + return rcpath + +def userrcpath(): + '''return os-specific hgrc search path to the user dir''' + home = os.path.expanduser('~') + path = [os.path.join(home, 'mercurial.ini'), + os.path.join(home, '.hgrc')] + userprofile = os.environ.get('USERPROFILE') + if userprofile: + path.append(os.path.join(userprofile, 'mercurial.ini')) + path.append(os.path.join(userprofile, '.hgrc')) + return path
--- a/mercurial/sshpeer.py Thu Apr 04 16:28:19 2013 -0500 +++ b/mercurial/sshpeer.py Thu Apr 18 23:46:26 2013 -0500 @@ -70,7 +70,10 @@ (_serverquote(remotecmd), _serverquote(self.path)))) ui.note(_('running %s\n') % cmd) cmd = util.quotecommand(cmd) - self.pipeo, self.pipei, self.pipee = util.popen3(cmd) + + # while self.subprocess isn't used, having it allows the subprocess to + # to clean up correctly later + self.pipeo, self.pipei, self.pipee, self.subprocess = util.popen4(cmd) # skip any noise generated by remote shell self._callstream("hello")
--- a/mercurial/sslutil.py Thu Apr 04 16:28:19 2013 -0500 +++ b/mercurial/sslutil.py Thu Apr 18 23:46:26 2013 -0500 @@ -99,7 +99,7 @@ self.ui = ui self.host = host - def __call__(self, sock): + def __call__(self, sock, strict=False): host = self.host cacerts = self.ui.config('web', 'cacerts') hostfingerprint = self.ui.config('hostfingerprints', host) @@ -107,13 +107,22 @@ if hostfingerprint: raise util.Abort(_("host fingerprint for %s can't be " "verified (Python too old)") % host) + if strict: + raise util.Abort(_("certificate for %s can't be verified " + "(Python too old)") % host) if self.ui.configbool('ui', 'reportoldssl', True): self.ui.warn(_("warning: certificate for %s can't be verified " "(Python too old)\n") % host) return + if not sock.cipher(): # work around http://bugs.python.org/issue13721 raise util.Abort(_('%s ssl connection error') % host) - peercert = sock.getpeercert(True) + try: + peercert = sock.getpeercert(True) + peercert2 = sock.getpeercert() + except AttributeError: + raise util.Abort(_('%s ssl connection error') % host) + if not peercert: raise util.Abort(_('%s certificate error: ' 'no certificate received') % host) @@ -129,13 +138,18 @@ self.ui.debug('%s certificate matched fingerprint %s\n' % (host, nicefingerprint)) elif cacerts: - msg = _verifycert(sock.getpeercert(), host) + msg = _verifycert(peercert2, host) if msg: raise util.Abort(_('%s certificate error: %s') % (host, msg), hint=_('configure hostfingerprint %s or use ' '--insecure to connect insecurely') % nicefingerprint) self.ui.debug('%s certificate successfully verified\n' % host) + elif strict: + raise util.Abort(_('%s certificate with fingerprint %s not ' + 'verified') % (host, nicefingerprint), + hint=_('check hostfingerprints or web.cacerts ' + 'config setting')) else: self.ui.warn(_('warning: %s certificate with fingerprint %s not ' 'verified (check hostfingerprints or web.cacerts '
--- a/mercurial/statichttprepo.py Thu Apr 04 16:28:19 2013 -0500 +++ b/mercurial/statichttprepo.py Thu Apr 18 23:46:26 2013 -0500 @@ -10,7 +10,7 @@ from i18n import _ import changelog, byterange, url, error import localrepo, manifest, util, scmutil, store -import urllib, urllib2, errno +import urllib, urllib2, errno, os class httprangereader(object): def __init__(self, url, opener):
--- a/mercurial/subrepo.py Thu Apr 04 16:28:19 2013 -0500 +++ b/mercurial/subrepo.py Thu Apr 18 23:46:26 2013 -0500 @@ -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 errno, os, re, xml.dom.minidom, shutil, posixpath +import errno, os, re, xml.dom.minidom, shutil, posixpath, sys import stat, subprocess, tarfile from i18n import _ import config, scmutil, util, node, error, cmdutil, bookmarks, match as matchmod @@ -14,11 +14,34 @@ nullstate = ('', '', 'empty') +def _expandedabspath(path): + ''' + get a path or url and if it is a path expand it and return an absolute path + ''' + expandedpath = util.urllocalpath(util.expandpath(path)) + u = util.url(expandedpath) + if not u.scheme: + path = util.normpath(os.path.abspath(u.path)) + return path + +def _getstorehashcachename(remotepath): + '''get a unique filename for the store hash cache of a remote repository''' + return util.sha1(_expandedabspath(remotepath)).hexdigest()[0:12] + +def _calcfilehash(filename): + data = '' + if os.path.exists(filename): + fd = open(filename) + data = fd.read() + fd.close() + return util.sha1(data).hexdigest() + class SubrepoAbort(error.Abort): """Exception class used to avoid handling a subrepo error more than once""" def __init__(self, *args, **kw): error.Abort.__init__(self, *args, **kw) self.subrepo = kw.get('subrepo') + self.cause = kw.get('cause') def annotatesubrepoerror(func): def decoratedmethod(self, *args, **kargs): @@ -31,7 +54,8 @@ subrepo = subrelpath(self) errormsg = str(ex) + ' ' + _('(in subrepo %s)') % subrepo # avoid handling this exception by raising a SubrepoAbort exception - raise SubrepoAbort(errormsg, hint=ex.hint, subrepo=subrepo) + raise SubrepoAbort(errormsg, hint=ex.hint, subrepo=subrepo, + cause=sys.exc_info()) return res return decoratedmethod @@ -300,6 +324,13 @@ class abstractsubrepo(object): + def storeclean(self, path): + """ + returns true if the repository has not changed since it was last + cloned from or pushed to a given repository. + """ + return False + def dirty(self, ignoreupdate=False): """returns true if the dirstate of the subrepo is dirty or does not match current stored state. If ignoreupdate is true, only check @@ -392,6 +423,7 @@ ui.progress(_('archiving (%s)') % relpath, i + 1, unit=_('files'), total=total) ui.progress(_('archiving (%s)') % relpath, None) + return total def walk(self, match): ''' @@ -426,6 +458,72 @@ self._repo.ui.setconfig('ui', '_usedassubrepo', 'True') self._initrepo(r, state[0], create) + def storeclean(self, path): + clean = True + lock = self._repo.lock() + itercache = self._calcstorehash(path) + try: + for filehash in self._readstorehashcache(path): + if filehash != itercache.next(): + clean = False + break + except StopIteration: + # the cached and current pull states have a different size + clean = False + if clean: + try: + itercache.next() + # the cached and current pull states have a different size + clean = False + except StopIteration: + pass + lock.release() + return clean + + def _calcstorehash(self, remotepath): + '''calculate a unique "store hash" + + This method is used to to detect when there are changes that may + require a push to a given remote path.''' + # sort the files that will be hashed in increasing (likely) file size + filelist = ('bookmarks', 'store/phaseroots', 'store/00changelog.i') + yield '# %s\n' % _expandedabspath(remotepath) + for relname in filelist: + absname = os.path.normpath(self._repo.join(relname)) + yield '%s = %s\n' % (relname, _calcfilehash(absname)) + + def _getstorehashcachepath(self, remotepath): + '''get a unique path for the store hash cache''' + return self._repo.join(os.path.join( + 'cache', 'storehash', _getstorehashcachename(remotepath))) + + def _readstorehashcache(self, remotepath): + '''read the store hash cache for a given remote repository''' + cachefile = self._getstorehashcachepath(remotepath) + if not os.path.exists(cachefile): + return '' + fd = open(cachefile, 'r') + pullstate = fd.readlines() + fd.close() + return pullstate + + def _cachestorehash(self, remotepath): + '''cache the current store hash + + Each remote repo requires its own store hash cache, because a subrepo + store may be "clean" versus a given remote repo, but not versus another + ''' + cachefile = self._getstorehashcachepath(remotepath) + lock = self._repo.lock() + storehash = list(self._calcstorehash(remotepath)) + cachedir = os.path.dirname(cachefile) + if not os.path.exists(cachedir): + util.makedirs(cachedir, notindexed=True) + fd = open(cachefile, 'w') + fd.writelines(storehash) + fd.close() + lock.release() + @annotatesubrepoerror def _initrepo(self, parentrepo, source, create): self._repo._subparent = parentrepo @@ -483,14 +581,15 @@ @annotatesubrepoerror def archive(self, ui, archiver, prefix, match=None): self._get(self._state + ('hg',)) - abstractsubrepo.archive(self, ui, archiver, prefix, match) - + total = abstractsubrepo.archive(self, ui, archiver, prefix, match) rev = self._state[1] ctx = self._repo[rev] for subpath in ctx.substate: s = subrepo(ctx, subpath) submatch = matchmod.narrowmatcher(subpath, match) - s.archive(ui, archiver, os.path.join(prefix, self._path), submatch) + total += s.archive( + ui, archiver, os.path.join(prefix, self._path), submatch) + return total @annotatesubrepoerror def dirty(self, ignoreupdate=False): @@ -544,12 +643,18 @@ update=False) self._repo = cloned.local() self._initrepo(parentrepo, source, create=True) + self._cachestorehash(srcurl) else: self._repo.ui.status(_('pulling subrepo %s from %s\n') % (subrelpath(self), srcurl)) + cleansub = self.storeclean(srcurl) + remotebookmarks = other.listkeys('bookmarks') self._repo.pull(other) - bookmarks.updatefromremote(self._repo.ui, self._repo, other, - srcurl) + bookmarks.updatefromremote(self._repo.ui, self._repo, + remotebookmarks, srcurl) + if cleansub: + # keep the repo clean after pull + self._cachestorehash(srcurl) @annotatesubrepoerror def get(self, state, overwrite=False): @@ -599,10 +704,20 @@ return False dsturl = _abssource(self._repo, True) + if not force: + if self.storeclean(dsturl): + self._repo.ui.status( + _('no changes made to subrepo %s since last push to %s\n') + % (subrelpath(self), dsturl)) + return None self._repo.ui.status(_('pushing subrepo %s to %s\n') % (subrelpath(self), dsturl)) other = hg.peer(self._repo, {'ssh': ssh}, dsturl) - return self._repo.push(other, force, newbranch=newbranch) + res = self._repo.push(other, force, newbranch=newbranch) + + # the repo is now clean + self._cachestorehash(dsturl) + return res @annotatesubrepoerror def outgoing(self, ui, dest, opts): @@ -653,7 +768,7 @@ opts['rev'] = substate[1] pats = [] - if not opts['all']: + if not opts.get('all'): pats = ['set:modified()'] self.filerevert(ui, *pats, **opts) @@ -663,7 +778,7 @@ def filerevert(self, ui, *pats, **opts): ctx = self._repo[opts['rev']] parents = self._repo.dirstate.parents() - if opts['all']: + if opts.get('all'): pats = ['set:modified()'] else: pats = [] @@ -1151,7 +1266,7 @@ if remote not in tracking: # create a new local tracking branch - local = remote.split('/', 2)[2] + local = remote.split('/', 3)[3] checkout(['-b', local, remote]) elif self._gitisancestor(branch2rev[tracking[remote]], remote): # When updating to a tracked remote branch, @@ -1270,9 +1385,10 @@ os.remove(path) def archive(self, ui, archiver, prefix, match=None): + total = 0 source, revision = self._state if not revision: - return + return total self._fetch(source, revision) # Parse git's native archive command. @@ -1293,9 +1409,11 @@ data = tar.extractfile(info).read() archiver.addfile(os.path.join(prefix, self._path, info.name), info.mode, info.issym(), data) + total += 1 ui.progress(_('archiving (%s)') % relpath, i + 1, unit=_('files')) ui.progress(_('archiving (%s)') % relpath, None) + return total @annotatesubrepoerror
--- a/mercurial/templatefilters.py Thu Apr 04 16:28:19 2013 -0500 +++ b/mercurial/templatefilters.py Thu Apr 18 23:46:26 2013 -0500 @@ -392,6 +392,15 @@ "xmlescape": xmlescape, } +def websub(text, websubtable): + """:websub: Any text. Only applies to hgweb. Applies the regular + expression replacements defined in the websub section. + """ + if websubtable: + for regexp, format in websubtable: + text = regexp.sub(format, text) + return text + def fillfunc(context, mapping, args): if not (1 <= len(args) <= 2): raise error.ParseError(_("fill expects one or two arguments"))
--- a/mercurial/templatekw.py Thu Apr 04 16:28:19 2013 -0500 +++ b/mercurial/templatekw.py Thu Apr 18 23:46:26 2013 -0500 @@ -14,9 +14,13 @@ # "{files % '{file}\n'}" (hgweb-style with inlining and function support) class _hybrid(object): - def __init__(self, gen, values): + def __init__(self, gen, values, joinfmt=None): self.gen = gen self.values = values + if joinfmt: + self.joinfmt = joinfmt + else: + self.joinfmt = lambda x: x.values()[0] def __iter__(self): return self.gen def __call__(self): @@ -244,8 +248,8 @@ copies.append((fn, rename[0])) c = [{'name': x[0], 'source': x[1]} for x in copies] - return showlist('file_copy', c, plural='file_copies', - element='file', **args) + f = _showlist('file_copy', c, plural='file_copies', **args) + return _hybrid(f, c, lambda x: '%s (%s)' % (x['name'], x['source'])) # showfilecopiesswitch() displays file copies only if copy records are # provided before calling the templater, usually with a --copies @@ -256,8 +260,8 @@ """ copies = args['revcache'].get('copies') or [] c = [{'name': x[0], 'source': x[1]} for x in copies] - return showlist('file_copy', c, plural='file_copies', - element='file', **args) + f = _showlist('file_copy', c, plural='file_copies', **args) + return _hybrid(f, c, lambda x: '%s (%s)' % (x['name'], x['source'])) def showfiledels(**args): """:file_dels: List of strings. Files removed by this changeset."""
--- a/mercurial/templater.py Thu Apr 04 16:28:19 2013 -0500 +++ b/mercurial/templater.py Thu Apr 18 23:46:26 2013 -0500 @@ -9,6 +9,7 @@ import sys, os, re import util, config, templatefilters, parser, error import types +import minirst # template parsing @@ -207,6 +208,19 @@ f = context._filters[n] return (runfilter, (args[0][0], args[0][1], f)) +def get(context, mapping, args): + if len(args) != 2: + # i18n: "get" is a keyword + raise error.ParseError(_("get() expects two arguments")) + + dictarg = args[0][0](context, mapping, args[0][1]) + if not util.safehasattr(dictarg, 'get'): + # i18n: "get" is a keyword + raise error.ParseError(_("get() expects a dict as first argument")) + + key = args[1][0](context, mapping, args[1][1]) + yield dictarg.get(key) + def join(context, mapping, args): if not (1 <= len(args) <= 2): # i18n: "join" is a keyword @@ -214,7 +228,8 @@ joinset = args[0][0](context, mapping, args[0][1]) if util.safehasattr(joinset, '__call__'): - joinset = [x.values()[0] for x in joinset()] + jf = joinset.joinfmt + joinset = [jf(x) for x in joinset()] joiner = " " if len(args) > 1: @@ -236,6 +251,8 @@ pat = stringify(args[0][0](context, mapping, args[0][1])) rpl = stringify(args[1][0](context, mapping, args[1][1])) src = stringify(args[2][0](context, mapping, args[2][1])) + src = stringify(runtemplate(context, mapping, + compiletemplate(src, context))) yield re.sub(pat, rpl, src) def if_(context, mapping, args): @@ -274,6 +291,16 @@ t = stringify(args[1][0](context, mapping, args[1][1])) yield runtemplate(context, mapping, compiletemplate(t, context)) +def rstdoc(context, mapping, args): + if len(args) != 2: + # i18n: "rstdoc" is a keyword + raise error.ParseError(_("rstdoc expects two arguments")) + + text = stringify(args[0][0](context, mapping, args[0][1])) + style = stringify(args[1][0](context, mapping, args[1][1])) + + return minirst.format(text, style=style) + methods = { "string": lambda e, c: (runstring, e[1]), "symbol": lambda e, c: (runsymbol, e[1]), @@ -285,11 +312,13 @@ } funcs = { + "get": get, "if": if_, "ifeq": ifeq, "join": join, + "label": label, + "rstdoc": rstdoc, "sub": sub, - "label": label, } # template engine
--- a/mercurial/templates/coal/map Thu Apr 04 16:28:19 2013 -0500 +++ b/mercurial/templates/coal/map Thu Apr 18 23:46:26 2013 -0500 @@ -13,7 +13,14 @@ help = ../paper/help.tmpl helptopics = ../paper/helptopics.tmpl -helpentry = '<tr><td><a href="{url|urlescape}help/{topic|escape}{sessionvars%urlparameter}">{topic|escape}</a></td><td>{summary|escape}</td></tr>' +helpentry = ' + <tr><td> + <a href="{url|urlescape}help/{topic|escape}{sessionvars%urlparameter}"> + {topic|escape} + </a> + </td><td> + {summary|escape} + </td></tr>' naventry = '<a href="{url|urlescape}log/{node|short}{sessionvars%urlparameter}">{label|escape}</a> ' navshortentry = '<a href="{url|urlescape}shortlog/{node|short}{sessionvars%urlparameter}">{label|escape}</a> ' @@ -217,7 +224,7 @@ index = ../paper/index.tmpl archiveentry = ' <li> - <a href="{url|urlescape}archive/{node|short}{extension|urlescape}">{type|escape}</a> + <a href="{url|urlescape}archive/{node|short}{extension|urlescape}{ifeq(path,'/','',path|urlescape)}">{type|escape}</a> </li>' notfound = ../paper/notfound.tmpl error = ../paper/error.tmpl
--- a/mercurial/templates/gitweb/changelogentry.tmpl Thu Apr 04 16:28:19 2013 -0500 +++ b/mercurial/templates/gitweb/changelogentry.tmpl Thu Apr 18 23:46:26 2013 -0500 @@ -8,7 +8,7 @@ <i>{author|obfuscate} [{date|rfc822date}] rev {rev}</i><br/> </div> <div class="log_body"> -{desc|strip|escape|addbreaks|nonempty} +{desc|strip|escape|websub|addbreaks|nonempty} <br/> <br/> </div>
--- a/mercurial/templates/gitweb/changeset.tmpl Thu Apr 04 16:28:19 2013 -0500 +++ b/mercurial/templates/gitweb/changeset.tmpl Thu Apr 18 23:46:26 2013 -0500 @@ -41,7 +41,7 @@ </table></div> <div class="page_body"> -{desc|strip|escape|addbreaks|nonempty} +{desc|strip|escape|websub|addbreaks|nonempty} </div> <div class="list_head"></div> <div class="title_text">
--- a/mercurial/templates/gitweb/fileannotate.tmpl Thu Apr 04 16:28:19 2013 -0500 +++ b/mercurial/templates/gitweb/fileannotate.tmpl Thu Apr 18 23:46:26 2013 -0500 @@ -56,7 +56,7 @@ </div> <div class="page_path"> -{desc|strip|escape|addbreaks|nonempty} +{desc|strip|escape|websub|addbreaks|nonempty} </div> <div class="page_body"> <table>
--- a/mercurial/templates/gitweb/filerevision.tmpl Thu Apr 04 16:28:19 2013 -0500 +++ b/mercurial/templates/gitweb/filerevision.tmpl Thu Apr 18 23:46:26 2013 -0500 @@ -56,7 +56,7 @@ </div> <div class="page_path"> -{desc|strip|escape|addbreaks|nonempty} +{desc|strip|escape|websub|addbreaks|nonempty} </div> <div class="page_body">
--- a/mercurial/templates/gitweb/help.tmpl Thu Apr 04 16:28:19 2013 -0500 +++ b/mercurial/templates/gitweb/help.tmpl Thu Apr 18 23:46:26 2013 -0500 @@ -27,8 +27,8 @@ <div class="title"> </div> -<pre> -{doc|escape} -</pre> +<div id="doc"> +{rstdoc(doc, "html")} +</div> {footer}
--- a/mercurial/templates/gitweb/map Thu Apr 04 16:28:19 2013 -0500 +++ b/mercurial/templates/gitweb/map Thu Apr 18 23:46:26 2013 -0500 @@ -11,7 +11,14 @@ help = help.tmpl helptopics = helptopics.tmpl -helpentry = '<tr><td><a href="{url|urlescape}help/{topic|escape}{sessionvars%urlparameter}">{topic|escape}</a></td><td>{summary|escape}</td></tr>' +helpentry = ' + <tr><td> + <a href="{url|urlescape}help/{topic|escape}{sessionvars%urlparameter}"> + {topic|escape} + </a> + </td><td> + {summary|escape} + </td></tr>' naventry = '<a href="{url|urlescape}log/{node|short}{sessionvars%urlparameter}">{label|escape}</a> ' navshortentry = '<a href="{url|urlescape}shortlog/{node|short}{sessionvars%urlparameter}">{label|escape}</a> ' @@ -282,7 +289,7 @@ <td class="link"> <a href="{url|urlescape}file/{node|short}/{file|urlescape}{sessionvars%urlparameter}">file</a> | <a href="{url|urlescape}diff/{node|short}/{file|urlescape}{sessionvars%urlparameter}">diff</a> | <a href="{url|urlescape}annotate/{node|short}/{file|urlescape}{sessionvars%urlparameter}">annotate</a> {rename%filelogrename}</td> </tr>' -archiveentry = ' | <a href="{url|urlescape}archive/{node|short}{extension}">{type|escape}</a> ' +archiveentry = ' | <a href="{url|urlescape}archive/{node|short}{extension}{ifeq(path,'/','',path|urlescape)}">{type|escape}</a> ' indexentry = ' <tr class="parity{parity}"> <td>
--- a/mercurial/templates/monoblue/changelogentry.tmpl Thu Apr 04 16:28:19 2013 -0500 +++ b/mercurial/templates/monoblue/changelogentry.tmpl Thu Apr 18 23:46:26 2013 -0500 @@ -2,5 +2,5 @@ <ul class="changelog-entry"> <li class="age">{date|rfc822date}</li> <li>by <span class="name">{author|obfuscate}</span> <span class="revdate">[{date|rfc822date}] rev {rev}</span></li> - <li class="description">{desc|strip|escape|addbreaks|nonempty}</li> + <li class="description">{desc|strip|escape|websub|addbreaks|nonempty}</li> </ul>
--- a/mercurial/templates/monoblue/changeset.tmpl Thu Apr 04 16:28:19 2013 -0500 +++ b/mercurial/templates/monoblue/changeset.tmpl Thu Apr 18 23:46:26 2013 -0500 @@ -52,7 +52,7 @@ {child%changesetchild} </dl> - <p class="description">{desc|strip|escape|addbreaks|nonempty}</p> + <p class="description">{desc|strip|escape|websub|addbreaks|nonempty}</p> <table> {files}
--- a/mercurial/templates/monoblue/fileannotate.tmpl Thu Apr 04 16:28:19 2013 -0500 +++ b/mercurial/templates/monoblue/fileannotate.tmpl Thu Apr 18 23:46:26 2013 -0500 @@ -57,7 +57,7 @@ <dd>{permissions|permissions}</dd> </dl> - <p class="description">{desc|strip|escape|addbreaks|nonempty}</p> + <p class="description">{desc|strip|escape|websub|addbreaks|nonempty}</p> <table class="annotated"> {annotate%annotateline}
--- a/mercurial/templates/monoblue/filerevision.tmpl Thu Apr 04 16:28:19 2013 -0500 +++ b/mercurial/templates/monoblue/filerevision.tmpl Thu Apr 18 23:46:26 2013 -0500 @@ -57,7 +57,7 @@ <dd>{permissions|permissions}</dd> </dl> - <p class="description">{desc|strip|escape|addbreaks|nonempty}</p> + <p class="description">{desc|strip|escape|websub|addbreaks|nonempty}</p> <div class="source"> {text%fileline}
--- a/mercurial/templates/monoblue/help.tmpl Thu Apr 04 16:28:19 2013 -0500 +++ b/mercurial/templates/monoblue/help.tmpl Thu Apr 18 23:46:26 2013 -0500 @@ -31,8 +31,8 @@ </div> <h2 class="no-link no-border">branches</h2> - <pre> - {doc|escape} - </pre> + <div id="doc"> + {rstdoc(doc, "html")} + </div> {footer}
--- a/mercurial/templates/monoblue/map Thu Apr 04 16:28:19 2013 -0500 +++ b/mercurial/templates/monoblue/map Thu Apr 18 23:46:26 2013 -0500 @@ -11,7 +11,14 @@ help = help.tmpl helptopics = helptopics.tmpl -helpentry = '<tr><td><a href="{url|urlescape}help/{topic|escape}{sessionvars%urlparameter}">{topic|escape}</a></td><td>{summary|escape}</td></tr>' +helpentry = ' + <tr><td> + <a href="{url|urlescape}help/{topic|escape}{sessionvars%urlparameter}"> + {topic|escape} + </a> + </td><td> + {summary|escape} + </td></tr>' naventry = '<a href="{url|urlescape}log/{node|short}{sessionvars%urlparameter}">{label|escape}</a> ' navshortentry = '<a href="{url|urlescape}shortlog/{node|short}{sessionvars%urlparameter}">{label|escape}</a> ' @@ -238,7 +245,7 @@ {rename%filelogrename} </td> </tr>' -archiveentry = '<li><a href="{url|urlescape}archive/{node|short}{extension}">{type|escape}</a></li>' +archiveentry = '<li><a href="{url|urlescape}archive/{node|short}{extension}{ifeq(path,'/','',path|urlescape)}">{type|escape}</a></li>' indexentry = ' <tr class="parity{parity}"> <td><a href="{url|urlescape}{sessionvars%urlparameter}">{name|escape}</a></td>
--- a/mercurial/templates/paper/changeset.tmpl Thu Apr 04 16:28:19 2013 -0500 +++ b/mercurial/templates/paper/changeset.tmpl Thu Apr 18 23:46:26 2013 -0500 @@ -40,7 +40,7 @@ files, or words in the commit message</div> </form> -<div class="description">{desc|strip|escape|nonempty}</div> +<div class="description">{desc|strip|escape|websub|nonempty}</div> <table id="changesetEntry"> <tr>
--- a/mercurial/templates/paper/fileannotate.tmpl Thu Apr 04 16:28:19 2013 -0500 +++ b/mercurial/templates/paper/fileannotate.tmpl Thu Apr 18 23:46:26 2013 -0500 @@ -46,7 +46,7 @@ files, or words in the commit message</div> </form> -<div class="description">{desc|strip|escape|nonempty}</div> +<div class="description">{desc|strip|escape|websub|nonempty}</div> <table id="changesetEntry"> <tr>
--- a/mercurial/templates/paper/filecomparison.tmpl Thu Apr 04 16:28:19 2013 -0500 +++ b/mercurial/templates/paper/filecomparison.tmpl Thu Apr 18 23:46:26 2013 -0500 @@ -45,7 +45,7 @@ files, or words in the commit message</div> </form> -<div class="description">{desc|strip|escape|nonempty}</div> +<div class="description">{desc|strip|escape|websub|nonempty}</div> <table id="changesetEntry"> <tr>
--- a/mercurial/templates/paper/filediff.tmpl Thu Apr 04 16:28:19 2013 -0500 +++ b/mercurial/templates/paper/filediff.tmpl Thu Apr 18 23:46:26 2013 -0500 @@ -45,7 +45,7 @@ files, or words in the commit message</div> </form> -<div class="description">{desc|strip|escape|nonempty}</div> +<div class="description">{desc|strip|escape|websub|nonempty}</div> <table id="changesetEntry"> <tr>
--- a/mercurial/templates/paper/filerevision.tmpl Thu Apr 04 16:28:19 2013 -0500 +++ b/mercurial/templates/paper/filerevision.tmpl Thu Apr 18 23:46:26 2013 -0500 @@ -44,7 +44,7 @@ files, or words in the commit message</div> </form> -<div class="description">{desc|strip|escape|nonempty}</div> +<div class="description">{desc|strip|escape|websub|nonempty}</div> <table id="changesetEntry"> <tr>
--- a/mercurial/templates/paper/help.tmpl Thu Apr 04 16:28:19 2013 -0500 +++ b/mercurial/templates/paper/help.tmpl Thu Apr 18 23:46:26 2013 -0500 @@ -31,9 +31,9 @@ <div id="hint">find changesets by author, revision, files, or words in the commit message</div> </form> -<pre> -{doc|escape} -</pre> +<div id="doc"> +{rstdoc(doc, "html")} +</div> </div> </div>
--- a/mercurial/templates/paper/map Thu Apr 04 16:28:19 2013 -0500 +++ b/mercurial/templates/paper/map Thu Apr 18 23:46:26 2013 -0500 @@ -12,7 +12,14 @@ help = help.tmpl helptopics = helptopics.tmpl -helpentry = '<tr><td><a href="{url|urlescape}help/{topic|escape}{sessionvars%urlparameter}">{topic|escape}</a></td><td>{summary|escape}</td></tr>' +helpentry = ' + <tr><td> + <a href="{url|urlescape}help/{topic|escape}{sessionvars%urlparameter}"> + {topic|escape} + </a> + </td><td> + {summary|escape} + </td></tr>' naventry = '<a href="{url|urlescape}log/{node|short}{sessionvars%urlparameter}">{label|escape}</a> ' navshortentry = '<a href="{url|urlescape}shortlog/{node|short}{sessionvars%urlparameter}">{label|escape}</a> ' @@ -225,7 +232,7 @@ index = index.tmpl archiveentry = ' <li> - <a href="{url|urlescape}archive/{node|short}{extension|urlescape}">{type|escape}</a> + <a href="{url|urlescape}archive/{node|short}{extension|urlescape}{ifeq(path,'/','',path|urlescape)}">{type|escape}</a> </li>' notfound = notfound.tmpl error = error.tmpl
--- a/mercurial/templates/spartan/changeset.tmpl Thu Apr 04 16:28:19 2013 -0500 +++ b/mercurial/templates/spartan/changeset.tmpl Thu Apr 18 23:46:26 2013 -0500 @@ -39,7 +39,7 @@ </tr> <tr> <th class="description">description:</th> - <td class="description">{desc|strip|escape|addbreaks|nonempty}</td> + <td class="description">{desc|strip|escape|websub|addbreaks|nonempty}</td> </tr> </table>
--- a/mercurial/templates/spartan/fileannotate.tmpl Thu Apr 04 16:28:19 2013 -0500 +++ b/mercurial/templates/spartan/fileannotate.tmpl Thu Apr 18 23:46:26 2013 -0500 @@ -38,7 +38,7 @@ </tr> <tr> <td class="metatag">description:</td> - <td>{desc|strip|escape|addbreaks|nonempty}</td> + <td>{desc|strip|escape|websub|addbreaks|nonempty}</td> </tr> </table>
--- a/mercurial/templates/spartan/filerevision.tmpl Thu Apr 04 16:28:19 2013 -0500 +++ b/mercurial/templates/spartan/filerevision.tmpl Thu Apr 18 23:46:26 2013 -0500 @@ -36,7 +36,7 @@ <td>{permissions|permissions}</td></tr> <tr> <td class="metatag">description:</td> - <td>{desc|strip|escape|addbreaks|nonempty}</td> + <td>{desc|strip|escape|websub|addbreaks|nonempty}</td> </tr> </table>
--- a/mercurial/templates/static/style-coal.css Thu Apr 04 16:28:19 2013 -0500 +++ b/mercurial/templates/static/style-coal.css Thu Apr 18 23:46:26 2013 -0500 @@ -191,7 +191,7 @@ } .bigtable tr { border: none; } .bigtable .age { width: 6em; } -.bigtable .author { width: 12em; } +.bigtable .author { width: 15em; } .bigtable .description { } .bigtable .description .base { font-size: 70%; float: right; line-height: 1.66; } .bigtable .node { width: 5em; font-family: monospace;}
--- a/mercurial/templates/static/style-monoblue.css Thu Apr 04 16:28:19 2013 -0500 +++ b/mercurial/templates/static/style-monoblue.css Thu Apr 18 23:46:26 2013 -0500 @@ -204,8 +204,8 @@ table tr td.nowrap { white-space: nowrap; } -table tr td.closed { - background-color: #99f; +table tr td.closed { + background-color: #99f; } /* table tr.parity0:hover,
--- a/mercurial/templates/static/style-paper.css Thu Apr 04 16:28:19 2013 -0500 +++ b/mercurial/templates/static/style-paper.css Thu Apr 18 23:46:26 2013 -0500 @@ -188,7 +188,7 @@ } .bigtable tr { border: none; } .bigtable .age { width: 7em; } -.bigtable .author { width: 12em; } +.bigtable .author { width: 15em; } .bigtable .description { } .bigtable .description .base { font-size: 70%; float: right; line-height: 1.66; } .bigtable .node { width: 5em; font-family: monospace;}
--- a/mercurial/ui.py Thu Apr 04 16:28:19 2013 -0500 +++ b/mercurial/ui.py Thu Apr 18 23:46:26 2013 -0500 @@ -6,7 +6,7 @@ # GNU General Public License version 2 or any later version. from i18n import _ -import errno, getpass, os, socket, sys, tempfile, traceback +import errno, getpass, os, re, socket, sys, tempfile, traceback import config, scmutil, util, error, formatter class ui(object): @@ -262,6 +262,45 @@ raise error.ConfigError(_("%s.%s is not an integer ('%s')") % (section, name, v)) + def configbytes(self, section, name, default=0, untrusted=False): + """parse a configuration element as a quantity in bytes + + Units can be specified as b (bytes), k or kb (kilobytes), m or + mb (megabytes), g or gb (gigabytes). + + >>> u = ui(); s = 'foo' + >>> u.setconfig(s, 'val1', '42') + >>> u.configbytes(s, 'val1') + 42 + >>> u.setconfig(s, 'val2', '42.5 kb') + >>> u.configbytes(s, 'val2') + 43520 + >>> u.configbytes(s, 'unknown', '7 MB') + 7340032 + >>> u.setconfig(s, 'invalid', 'somevalue') + >>> u.configbytes(s, 'invalid') + Traceback (most recent call last): + ... + ConfigError: foo.invalid is not a byte quantity ('somevalue') + """ + + orig = string = self.config(section, name) + if orig is None: + if not isinstance(default, str): + return default + orig = string = default + multiple = 1 + m = re.match(r'([^kmbg]+?)\s*([kmg]?)b?$', string, re.I) + if m: + string, key = m.groups() + key = key.lower() + multiple = dict(k=1024, m=1048576, g=1073741824).get(key, 1) + try: + return int(float(string) * multiple) + except ValueError: + raise error.ConfigError(_("%s.%s is not a byte quantity ('%s')") + % (section, name, orig)) + def configlist(self, section, name, default=None, untrusted=False): """parse a configuration element as a list of comma/space separated strings @@ -681,17 +720,29 @@ return t - def traceback(self, exc=None): - '''print exception traceback if traceback printing enabled. + def traceback(self, exc=None, force=False): + '''print exception traceback if traceback printing enabled or forced. only to call in exception handler. returns true if traceback printed.''' - if self.tracebackflag: - if exc: + if self.tracebackflag or force: + if exc is None: + exc = sys.exc_info() + cause = getattr(exc[1], 'cause', None) + + if cause is not None: + causetb = traceback.format_tb(cause[2]) + exctb = traceback.format_tb(exc[2]) + exconly = traceback.format_exception_only(cause[0], cause[1]) + + # exclude frame where 'exc' was chained and rethrown from exctb + self.write_err('Traceback (most recent call last):\n', + ''.join(exctb[:-1]), + ''.join(causetb), + ''.join(exconly)) + else: traceback.print_exception(exc[0], exc[1], exc[2], file=self.ferr) - else: - traceback.print_exc(file=self.ferr) - return self.tracebackflag + return self.tracebackflag or force def geteditor(self): '''return editor to use''' @@ -739,7 +790,7 @@ else: self.debug('%s:%s %s%s\n' % (topic, item, pos, unit)) - def log(self, service, message): + def log(self, service, *msg, **opts): '''hook for logging facility extensions service should be a readily-identifiable subsystem, which will
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/mercurial/unionrepo.py Thu Apr 18 23:46:26 2013 -0500 @@ -0,0 +1,208 @@ +# unionrepo.py - repository class for viewing union of repository changesets +# +# Derived from bundlerepo.py +# Copyright 2006, 2007 Benoit Boissinot <bboissin@gmail.com> +# Copyright 2013 Unity Technologies, Mads Kiilerich <madski@unity3d.com> +# +# This software may be used and distributed according to the terms of the +# GNU General Public License version 2 or any later version. + +"""Repository class for "in-memory pull" of one local repository to another, +allowing operations like diff and log with revsets. +""" + +from node import nullid +from i18n import _ +import os +import util, mdiff, cmdutil, scmutil +import localrepo, changelog, manifest, filelog, revlog + +class unionrevlog(revlog.revlog): + def __init__(self, opener, indexfile, revlog2, linkmapper): + # How it works: + # To retrieve a revision, we just need to know the node id so we can + # look it up in revlog2. + # + # To differentiate a rev in the second revlog from a rev in the revlog, + # we check revision against repotiprev. + opener = scmutil.readonlyvfs(opener) + revlog.revlog.__init__(self, opener, indexfile) + self.revlog2 = revlog2 + + n = len(self) + self.repotiprev = n - 1 + self.bundlerevs = set() # used by 'bundle()' revset expression + for rev2 in self.revlog2: + rev = self.revlog2.index[rev2] + # rev numbers - in revlog2, very different from self.rev + _start, _csize, _rsize, _base, linkrev, p1rev, p2rev, node = rev + + if linkmapper is None: # link is to same revlog + assert linkrev == rev2 # we never link back + link = n + else: # rev must be mapped from repo2 cl to unified cl by linkmapper + link = linkmapper(linkrev) + + if node in self.nodemap: + # this happens for the common revlog revisions + self.bundlerevs.add(self.nodemap[node]) + continue + + p1node = self.revlog2.node(p1rev) + p2node = self.revlog2.node(p2rev) + + e = (None, None, None, None, + link, self.rev(p1node), self.rev(p2node), node) + self.index.insert(-1, e) + self.nodemap[node] = n + self.bundlerevs.add(n) + n += 1 + + def _chunk(self, rev): + if rev <= self.repotiprev: + return revlog.revlog._chunk(self, rev) + return self.revlog2._chunk(self.node(rev)) + + def revdiff(self, rev1, rev2): + """return or calculate a delta between two revisions""" + if rev1 > self.repotiprev and rev2 > self.repotiprev: + return self.revlog2.revdiff( + self.revlog2.rev(self.node(rev1)), + self.revlog2.rev(self.node(rev2))) + elif rev1 <= self.repotiprev and rev2 <= self.repotiprev: + return revlog.revlog.revdiff(self, rev1, rev2) + + return mdiff.textdiff(self.revision(self.node(rev1)), + self.revision(self.node(rev2))) + + def revision(self, nodeorrev): + """return an uncompressed revision of a given node or revision + number. + """ + if isinstance(nodeorrev, int): + rev = nodeorrev + node = self.node(rev) + else: + node = nodeorrev + rev = self.rev(node) + + if node == nullid: + return "" + + if rev > self.repotiprev: + text = self.revlog2.revision(node) + self._cache = (node, rev, text) + else: + text = revlog.revlog.revision(self, rev) + # already cached + return text + + def addrevision(self, text, transaction, link, p1=None, p2=None, d=None): + raise NotImplementedError + def addgroup(self, revs, linkmapper, transaction): + raise NotImplementedError + def strip(self, rev, minlink): + raise NotImplementedError + def checksize(self): + raise NotImplementedError + +class unionchangelog(unionrevlog, changelog.changelog): + def __init__(self, opener, opener2): + changelog.changelog.__init__(self, opener) + linkmapper = None + changelog2 = changelog.changelog(opener2) + unionrevlog.__init__(self, opener, self.indexfile, changelog2, + linkmapper) + +class unionmanifest(unionrevlog, manifest.manifest): + def __init__(self, opener, opener2, linkmapper): + manifest.manifest.__init__(self, opener) + manifest2 = manifest.manifest(opener2) + unionrevlog.__init__(self, opener, self.indexfile, manifest2, + linkmapper) + +class unionfilelog(unionrevlog, filelog.filelog): + def __init__(self, opener, path, opener2, linkmapper, repo): + filelog.filelog.__init__(self, opener, path) + filelog2 = filelog.filelog(opener2, path) + unionrevlog.__init__(self, opener, self.indexfile, filelog2, + linkmapper) + self._repo = repo + + def _file(self, f): + self._repo.file(f) + +class unionpeer(localrepo.localpeer): + def canpush(self): + return False + +class unionrepository(localrepo.localrepository): + def __init__(self, ui, path, path2): + localrepo.localrepository.__init__(self, ui, path) + self.ui.setconfig('phases', 'publish', False) + + self._url = 'union:%s+%s' % (util.expandpath(path), + util.expandpath(path2)) + self.repo2 = localrepo.localrepository(ui, path2) + + @localrepo.unfilteredpropertycache + def changelog(self): + return unionchangelog(self.sopener, self.repo2.sopener) + + def _clrev(self, rev2): + """map from repo2 changelog rev to temporary rev in self.changelog""" + node = self.repo2.changelog.node(rev2) + return self.changelog.rev(node) + + @localrepo.unfilteredpropertycache + def manifest(self): + return unionmanifest(self.sopener, self.repo2.sopener, + self._clrev) + + def url(self): + return self._url + + def file(self, f): + return unionfilelog(self.sopener, f, self.repo2.sopener, + self._clrev, self) + + def close(self): + self.repo2.close() + + def cancopy(self): + return False + + def peer(self): + return unionpeer(self) + + def getcwd(self): + return os.getcwd() # always outside the repo + +def instance(ui, path, create): + if create: + raise util.Abort(_('cannot create new union repository')) + parentpath = ui.config("bundle", "mainreporoot", "") + if not parentpath: + # try to find the correct path to the working directory repo + parentpath = cmdutil.findrepo(os.getcwd()) + if parentpath is None: + parentpath = '' + if parentpath: + # Try to make the full path relative so we get a nice, short URL. + # In particular, we don't want temp dir names in test outputs. + cwd = os.getcwd() + if parentpath == cwd: + parentpath = '' + else: + cwd = os.path.join(cwd,'') + if parentpath.startswith(cwd): + parentpath = parentpath[len(cwd):] + if path.startswith('union:'): + s = path.split(":", 1)[1].split("+", 1) + if len(s) == 1: + repopath, repopath2 = parentpath, s[0] + else: + repopath, repopath2 = s + else: + repopath, repopath2 = parentpath, path + return unionrepository(ui, repopath, repopath2)
--- a/mercurial/util.py Thu Apr 04 16:28:19 2013 -0500 +++ b/mercurial/util.py Thu Apr 18 23:46:26 2013 -0500 @@ -65,6 +65,8 @@ split = platform.split sshargs = platform.sshargs statfiles = getattr(osutil, 'statfiles', platform.statfiles) +statisexec = platform.statisexec +statislink = platform.statislink termwidth = platform.termwidth testpid = platform.testpid umask = platform.umask @@ -129,13 +131,17 @@ return p.stdin, p.stdout def popen3(cmd, env=None, newlines=False): + stdin, stdout, stderr, p = popen4(cmd, env, newlines) + return stdin, stdout, stderr + +def popen4(cmd, env=None, newlines=False): p = subprocess.Popen(cmd, shell=True, bufsize=-1, close_fds=closefds, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE, universal_newlines=newlines, env=env) - return p.stdin, p.stdout, p.stderr + return p.stdin, p.stdout, p.stderr, p def version(): """Return version information if available.""" @@ -211,6 +217,31 @@ del self[i] break +class lrucachedict(object): + '''cache most recent gets from or sets to this dictionary''' + def __init__(self, maxsize): + self._cache = {} + self._maxsize = maxsize + self._order = deque() + + def __getitem__(self, key): + value = self._cache[key] + self._order.remove(key) + self._order.append(key) + return value + + def __setitem__(self, key, value): + if key not in self._cache: + if len(self._cache) >= self._maxsize: + del self._cache[self._order.popleft()] + else: + self._order.remove(key) + self._cache[key] = value + self._order.append(key) + + def __contains__(self, key): + return key in self._cache + def lrucachefunc(func): '''cache most recent results of function calls''' cache = {} @@ -606,7 +637,7 @@ def checkcase(path): """ - Check whether the given path is on a case-sensitive filesystem + Return true if the given path is on a case-sensitive filesystem Requires a path (like /foo/.hg) ending with a foldable final directory component. @@ -633,10 +664,12 @@ except ImportError: _re2 = False -def compilere(pat): +def compilere(pat, flags=0): '''Compile a regular expression, using re2 if possible - For best performance, use only re2-compatible regexp features.''' + For best performance, use only re2-compatible regexp features. The + only flags from the re module that are re2-compatible are + IGNORECASE and MULTILINE.''' global _re2 if _re2 is None: try: @@ -644,12 +677,16 @@ _re2 = True except ImportError: _re2 = False - if _re2: + if _re2 and (flags & ~(re.IGNORECASE | re.MULTILINE)) == 0: + if flags & re.IGNORECASE: + pat = '(?i)' + pat + if flags & re.MULTILINE: + pat = '(?m)' + pat try: return re2.compile(pat) except re2.error: pass - return re.compile(pat) + return re.compile(pat, flags) _fspathcache = {} def fspath(name, root): @@ -732,8 +769,6 @@ except OSError: pass - return False - def endswithsep(path): '''Check path ends with os.sep or os.altsep.''' return path.endswith(os.sep) or os.altsep and path.endswith(os.altsep) @@ -838,10 +873,10 @@ if safehasattr(self, '_fp'): # constructor actually did something self.discard() -def makedirs(name, mode=None): +def makedirs(name, mode=None, notindexed=False): """recursive directory creation with parent mode inheritance""" try: - os.mkdir(name) + makedir(name, notindexed) except OSError, err: if err.errno == errno.EEXIST: return @@ -850,8 +885,25 @@ parent = os.path.dirname(os.path.abspath(name)) if parent == name: raise - makedirs(parent, mode) + makedirs(parent, mode, notindexed) + makedir(name, notindexed) + if mode is not None: + os.chmod(name, mode) + +def ensuredirs(name, mode=None): + """race-safe recursive directory creation""" + if os.path.isdir(name): + return + parent = os.path.dirname(os.path.abspath(name)) + if parent != name: + ensuredirs(parent, mode) + try: os.mkdir(name) + except OSError, err: + if err.errno == errno.EEXIST and os.path.isdir(name): + # someone else seems to have won a directory creation race + return + raise if mode is not None: os.chmod(name, mode) @@ -1027,6 +1079,20 @@ The date may be a "unixtime offset" string or in one of the specified formats. If the date already is a (unixtime, offset) tuple, it is returned. + + >>> parsedate(' today ') == parsedate(\ + datetime.date.today().strftime('%b %d')) + True + >>> parsedate( 'yesterday ') == parsedate((datetime.date.today() -\ + datetime.timedelta(days=1)\ + ).strftime('%b %d')) + True + >>> now, tz = makedate() + >>> strnow, strtz = parsedate('now') + >>> (strnow - now) < 1 + True + >>> tz == strtz + True """ if not date: return 0, 0 @@ -1035,6 +1101,15 @@ if not formats: formats = defaultdateformats date = date.strip() + + if date == _('now'): + return makedate() + if date == _('today'): + date = datetime.date.today().strftime('%b %d') + elif date == _('yesterday'): + date = (datetime.date.today() - + datetime.timedelta(days=1)).strftime('%b %d') + try: when, offset = map(int, date.split(' ')) except ValueError: @@ -1203,7 +1278,18 @@ except (UnicodeDecodeError, UnicodeEncodeError): return _ellipsis(text, maxlength)[0] -_byteunits = ( +def unitcountfn(*unittable): + '''return a function that renders a readable count of some quantity''' + + def go(count): + for multiplier, divisor, format in unittable: + if count >= divisor * multiplier: + return format % (count / float(divisor)) + return unittable[-1][2] % count + + return go + +bytecount = unitcountfn( (100, 1 << 30, _('%.0f GB')), (10, 1 << 30, _('%.1f GB')), (1, 1 << 30, _('%.2f GB')), @@ -1216,14 +1302,6 @@ (1, 1, _('%.0f bytes')), ) -def bytecount(nbytes): - '''return byte count formatted as readable string, with units''' - - for multiplier, divisor, format in _byteunits: - if nbytes >= divisor * multiplier: - return format % (nbytes / float(divisor)) - return _byteunits[-1][2] % nbytes - def uirepr(s): # Avoid double backslash in Windows path repr() return repr(s).replace('\\\\', '\\') @@ -1614,7 +1692,6 @@ parts = path[2:].split('/', 1) if len(parts) > 1: self.host, path = parts - path = path else: self.host = parts[0] path = None @@ -1804,3 +1881,46 @@ return fd.isatty() except AttributeError: return False + +timecount = unitcountfn( + (1, 1e3, _('%.0f s')), + (100, 1, _('%.1f s')), + (10, 1, _('%.2f s')), + (1, 1, _('%.3f s')), + (100, 0.001, _('%.1f ms')), + (10, 0.001, _('%.2f ms')), + (1, 0.001, _('%.3f ms')), + (100, 0.000001, _('%.1f us')), + (10, 0.000001, _('%.2f us')), + (1, 0.000001, _('%.3f us')), + (100, 0.000000001, _('%.1f ns')), + (10, 0.000000001, _('%.2f ns')), + (1, 0.000000001, _('%.3f ns')), + ) + +_timenesting = [0] + +def timed(func): + '''Report the execution time of a function call to stderr. + + During development, use as a decorator when you need to measure + the cost of a function, e.g. as follows: + + @util.timed + def foo(a, b, c): + pass + ''' + + def wrapper(*args, **kwargs): + start = time.time() + indent = 2 + _timenesting[0] += indent + try: + return func(*args, **kwargs) + finally: + elapsed = time.time() - start + _timenesting[0] -= indent + sys.stderr.write('%s%s: %s\n' % + (' ' * _timenesting[0], func.__name__, + timecount(elapsed))) + return wrapper
--- a/mercurial/win32.py Thu Apr 04 16:28:19 2013 -0500 +++ b/mercurial/win32.py Thu Apr 18 23:46:26 2013 -0500 @@ -52,7 +52,7 @@ ('nFileIndexHigh', _DWORD), ('nFileIndexLow', _DWORD)] -# CreateFile +# CreateFile _FILE_SHARE_READ = 0x00000001 _FILE_SHARE_WRITE = 0x00000002 _FILE_SHARE_DELETE = 0x00000004
--- a/mercurial/windows.py Thu Apr 04 16:28:19 2013 -0500 +++ b/mercurial/windows.py Thu Apr 18 23:46:26 2013 -0500 @@ -337,3 +337,11 @@ pass expandglobs = True + +def statislink(st): + '''check whether a stat result is a symlink''' + return False + +def statisexec(st): + '''check whether a stat result is an executable file''' + return False
--- a/mercurial/wireproto.py Thu Apr 04 16:28:19 2013 -0500 +++ b/mercurial/wireproto.py Thu Apr 18 23:46:26 2013 -0500 @@ -602,7 +602,8 @@ # fail early if possible if not check_heads(): - return pusherr('unsynced changes') + return pusherr('repository changed while preparing changes - ' + 'please try again') # write bundle data to temporary file because it can be big fd, tempname = tempfile.mkstemp(prefix='hg-unbundle-') @@ -615,7 +616,8 @@ if not check_heads(): # someone else committed/pushed/unbundled while we # were transferring data - return pusherr('unsynced changes') + return pusherr('repository changed while uploading changes - ' + 'please try again') # push can proceed fp.seek(0)
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/mercurial/worker.py Thu Apr 18 23:46:26 2013 -0500 @@ -0,0 +1,160 @@ +# worker.py - master-slave parallelism support +# +# Copyright 2013 Facebook, Inc. +# +# This software may be used and distributed according to the terms of the +# GNU General Public License version 2 or any later version. + +from i18n import _ +import errno, os, signal, sys, threading, util + +def countcpus(): + '''try to count the number of CPUs on the system''' + + # posix + try: + n = int(os.sysconf('SC_NPROCESSORS_ONLN')) + if n > 0: + return n + except (AttributeError, ValueError): + pass + + # windows + try: + n = int(os.environ['NUMBER_OF_PROCESSORS']) + if n > 0: + return n + except (KeyError, ValueError): + pass + + return 1 + +def _numworkers(ui): + s = ui.config('worker', 'numcpus') + if s: + try: + n = int(s) + if n >= 1: + return n + except ValueError: + raise util.Abort(_('number of cpus must be an integer')) + return min(max(countcpus(), 4), 32) + +if os.name == 'posix': + _startupcost = 0.01 +else: + _startupcost = 1e30 + +def worthwhile(ui, costperop, nops): + '''try to determine whether the benefit of multiple processes can + outweigh the cost of starting them''' + linear = costperop * nops + workers = _numworkers(ui) + benefit = linear - (_startupcost * workers + linear / workers) + return benefit >= 0.15 + +def worker(ui, costperarg, func, staticargs, args): + '''run a function, possibly in parallel in multiple worker + processes. + + returns a progress iterator + + costperarg - cost of a single task + + func - function to run + + staticargs - arguments to pass to every invocation of the function + + args - arguments to split into chunks, to pass to individual + workers + ''' + if worthwhile(ui, costperarg, len(args)): + return _platformworker(ui, func, staticargs, args) + return func(*staticargs + (args,)) + +def _posixworker(ui, func, staticargs, args): + rfd, wfd = os.pipe() + workers = _numworkers(ui) + oldhandler = signal.getsignal(signal.SIGINT) + signal.signal(signal.SIGINT, signal.SIG_IGN) + pids, problem = [], [0] + for pargs in partition(args, workers): + pid = os.fork() + if pid == 0: + signal.signal(signal.SIGINT, oldhandler) + try: + os.close(rfd) + for i, item in func(*(staticargs + (pargs,))): + os.write(wfd, '%d %s\n' % (i, item)) + os._exit(0) + except KeyboardInterrupt: + os._exit(255) + except: # re-raises (close enough for debugging anyway) + try: + ui.traceback() + finally: + os._exit(255) + pids.append(pid) + pids.reverse() + os.close(wfd) + fp = os.fdopen(rfd, 'rb', 0) + def killworkers(): + # if one worker bails, there's no good reason to wait for the rest + for p in pids: + try: + os.kill(p, signal.SIGTERM) + except OSError, err: + if err.errno != errno.ESRCH: + raise + def waitforworkers(): + for _ in pids: + st = _exitstatus(os.wait()[1]) + if st and not problem: + problem[0] = st + killworkers() + t = threading.Thread(target=waitforworkers) + t.start() + def cleanup(): + signal.signal(signal.SIGINT, oldhandler) + t.join() + status = problem[0] + if status: + if status < 0: + os.kill(os.getpid(), -status) + sys.exit(status) + try: + for line in fp: + l = line.split(' ', 1) + yield int(l[0]), l[1][:-1] + except: # re-raises + killworkers() + cleanup() + raise + cleanup() + +def _posixexitstatus(code): + '''convert a posix exit status into the same form returned by + os.spawnv + + returns None if the process was stopped instead of exiting''' + if os.WIFEXITED(code): + return os.WEXITSTATUS(code) + elif os.WIFSIGNALED(code): + return -os.WTERMSIG(code) + +if os.name != 'nt': + _platformworker = _posixworker + _exitstatus = _posixexitstatus + +def partition(lst, nslices): + '''partition a list into N slices of equal size''' + n = len(lst) + chunk, slop = n / nslices, n % nslices + end = 0 + for i in xrange(nslices): + start = end + end = start + chunk + if slop: + end += 1 + slop -= 1 + yield lst[start:end]
--- a/setup.py Thu Apr 04 16:28:19 2013 -0500 +++ b/setup.py Thu Apr 18 23:46:26 2013 -0500 @@ -297,9 +297,10 @@ self.py_modules.append("mercurial.pure.%s" % ext.name[10:]) self.distribution.ext_modules = [] else: - if not os.path.exists(os.path.join(get_python_inc(), 'Python.h')): + h = os.path.join(get_python_inc(), 'Python.h') + if not os.path.exists(h): raise SystemExit('Python headers are required to build ' - 'Mercurial') + 'Mercurial but weren\'t found in %s' % h) def find_modules(self): modules = build_py.find_modules(self) @@ -427,7 +428,8 @@ Extension('mercurial.bdiff', ['mercurial/bdiff.c']), Extension('mercurial.diffhelpers', ['mercurial/diffhelpers.c']), Extension('mercurial.mpatch', ['mercurial/mpatch.c']), - Extension('mercurial.parsers', ['mercurial/parsers.c', + Extension('mercurial.parsers', ['mercurial/dirs.c', + 'mercurial/parsers.c', 'mercurial/pathencode.c']), ]
--- a/tests/run-tests.py Thu Apr 04 16:28:19 2013 -0500 +++ b/tests/run-tests.py Thu Apr 18 23:46:26 2013 -0500 @@ -52,6 +52,7 @@ import sys import tempfile import time +import random import re import threading import killdaemons as killmod @@ -197,6 +198,8 @@ help="enable Py3k warnings on Python 2.6+") parser.add_option('--extra-config-opt', action="append", help='set the given config opt in the test hgrc') + parser.add_option('--random', action="store_true", + help='run tests in random order') for option, (envvar, default) in defaults.items(): defaults[option] = type(default)(os.environ.get(envvar, default)) @@ -540,6 +543,13 @@ def globmatch(el, l): # The only supported special characters are * and ? plus / which also # matches \ on windows. Escaping of these caracters is supported. + if el + '\n' == l: + if os.name == 'nt': + # matching on "/" is not needed for this line + iolock.acquire() + print "\nInfo, unnecessary glob: %s (glob)" % el + iolock.release() + return True i, n = 0, len(el) res = '' while i < n: @@ -622,6 +632,7 @@ script.append('set -x\n') if os.getenv('MSYSTEM'): script.append('alias pwd="pwd -W"\n') + n = 0 for n, l in enumerate(t): if not l.endswith('\n'): l += '\n' @@ -1235,11 +1246,13 @@ checktools() if len(args) == 0: - args = os.listdir(".") - args.sort() + args = sorted(os.listdir(".")) tests = args + if options.random: + random.shuffle(tests) + # Reset some environment variables to well-known values so that # the tests produce repeatable output. os.environ['LANG'] = os.environ['LC_ALL'] = os.environ['LANGUAGE'] = 'C' @@ -1252,7 +1265,11 @@ os.environ['no_proxy'] = '' os.environ['NO_PROXY'] = '' os.environ['TERM'] = 'xterm' - os.environ['PYTHONHASHSEED'] = os.environ.get('PYTHONHASHSEED', 'random') + if 'PYTHONHASHSEED' not in os.environ: + # use a random python hash seed all the time + # we do the randomness ourself to know what seed is used + os.environ['PYTHONHASHSEED'] = str(random.getrandbits(32)) + print 'python hash seed:', os.environ['PYTHONHASHSEED'] # unset env related to hooks for k in os.environ.keys(): @@ -1264,6 +1281,9 @@ # can't remove on solaris os.environ['HG'] = '' del os.environ['HG'] + if 'HGPROF' in os.environ: + os.environ['HGPROF'] = '' + del os.environ['HGPROF'] global TESTDIR, HGTMP, INST, BINDIR, PYTHONDIR, COVERAGE_FILE TESTDIR = os.environ["TESTDIR"] = os.getcwd()
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tests/silenttestrunner.py Thu Apr 18 23:46:26 2013 -0500 @@ -0,0 +1,18 @@ +import unittest, sys + +def main(modulename): + '''run the tests found in module, printing nothing when all tests pass''' + module = sys.modules[modulename] + suite = unittest.defaultTestLoader.loadTestsFromModule(module) + results = unittest.TestResult() + suite.run(results) + if results.errors or results.failures: + for tc, exc in results.errors: + print 'ERROR:', tc + print + sys.stdout.write(exc) + for tc, exc in results.failures: + print 'FAIL:', tc + print + sys.stdout.write(exc) + sys.exit(1)
--- a/tests/test-alias.t Thu Apr 04 16:28:19 2013 -0500 +++ b/tests/test-alias.t Thu Apr 18 23:46:26 2013 -0500 @@ -17,6 +17,7 @@ > no-R = status -R elsewhere > no--repo = status --repo elsewhere > no--repository = status --repository elsewhere + > no--config = status --config a.config=1 > mylog = log > lognull = log -r null > shortlog = log --template '{rev} {node|short} | {date|isodate}\n' @@ -106,6 +107,8 @@ error in definition for alias 'no--repository': --repository may only be given on the command line $ hg help no--repository error in definition for alias 'no--repository': --repository may only be given on the command line + $ hg no--config + error in definition for alias 'no--config': --config may only be given on the command line optional repository @@ -181,6 +184,7 @@ # HG changeset patch # User test # Date 0 0 + # Thu Jan 01 00:00:00 1970 +0000 # Node ID e63c23eaa88ae77967edcf4ea194d31167c478b0 # Parent 0000000000000000000000000000000000000000 foo @@ -224,7 +228,7 @@ 2 $ hg tglog - @ 1:7e7f92de180e: 'bar' + @ 1:042423737847: 'bar' | o 0:e63c23eaa88a: 'foo' @@ -237,15 +241,15 @@ idalias idaliaslong idaliasshell identify import incoming init [255] $ hg id - 7e7f92de180e tip + 042423737847 tip $ hg ida hg: command 'ida' is ambiguous: idalias idaliaslong idaliasshell [255] $ hg idalias - 7e7f92de180e tip + 042423737847 tip $ hg idaliasl - 7e7f92de180e tip + 042423737847 tip $ hg idaliass test $ hg parentsshell @@ -263,10 +267,13 @@ $ hg init sub $ cd sub $ hg count 'branch(default)' + abort: unknown revision 'default'! 0 $ hg -v count 'branch(default)' + abort: unknown revision 'default'! 0 $ hg -R .. count 'branch(default)' + abort: unknown revision 'default'! 0 $ hg --cwd .. count 'branch(default)' 2
--- a/tests/test-annotate.t Thu Apr 04 16:28:19 2013 -0500 +++ b/tests/test-annotate.t Thu Apr 18 23:46:26 2013 -0500 @@ -267,6 +267,114 @@ [0-9]+: a (re) [0-9]+: b (re) +Issue3841: check annotation of the file of which filelog includes +merging between the revision and its ancestor + +to reproduce the situation with recent Mercurial, this script uses (1) +"hg debugsetparents" to merge without ancestor check by "hg merge", +and (2) the extension to allow filelog merging between the revision +and its ancestor by overriding "repo._filecommit". + + $ cat > ../legacyrepo.py <<EOF + > from mercurial import node, util + > def reposetup(ui, repo): + > class legacyrepo(repo.__class__): + > def _filecommit(self, fctx, manifest1, manifest2, + > linkrev, tr, changelist): + > fname = fctx.path() + > text = fctx.data() + > flog = self.file(fname) + > fparent1 = manifest1.get(fname, node.nullid) + > fparent2 = manifest2.get(fname, node.nullid) + > meta = {} + > copy = fctx.renamed() + > if copy and copy[0] != fname: + > raise util.Abort('copying is not supported') + > if fparent2 != node.nullid: + > changelist.append(fname) + > return flog.add(text, meta, tr, linkrev, + > fparent1, fparent2) + > raise util.Abort('only merging is supported') + > repo.__class__ = legacyrepo + > EOF + + $ cat > baz <<EOF + > 1 + > 2 + > 3 + > 4 + > 5 + > EOF + $ hg add baz + $ hg commit -m "baz:0" + + $ cat > baz <<EOF + > 1 baz:1 + > 2 + > 3 + > 4 + > 5 + > EOF + $ hg commit -m "baz:1" + + $ cat > baz <<EOF + > 1 baz:1 + > 2 baz:2 + > 3 + > 4 + > 5 + > EOF + $ hg debugsetparents 17 17 + $ hg --config extensions.legacyrepo=../legacyrepo.py commit -m "baz:2" + $ hg debugindexdot .hg/store/data/baz.i + digraph G { + -1 -> 0 + 0 -> 1 + 1 -> 2 + 1 -> 2 + } + $ hg annotate baz + 17: 1 baz:1 + 18: 2 baz:2 + 16: 3 + 16: 4 + 16: 5 + + $ cat > baz <<EOF + > 1 baz:1 + > 2 baz:2 + > 3 baz:3 + > 4 + > 5 + > EOF + $ hg commit -m "baz:3" + + $ cat > baz <<EOF + > 1 baz:1 + > 2 baz:2 + > 3 baz:3 + > 4 baz:4 + > 5 + > EOF + $ hg debugsetparents 19 18 + $ hg --config extensions.legacyrepo=../legacyrepo.py commit -m "baz:4" + $ hg debugindexdot .hg/store/data/baz.i + digraph G { + -1 -> 0 + 0 -> 1 + 1 -> 2 + 1 -> 2 + 2 -> 3 + 3 -> 4 + 2 -> 4 + } + $ hg annotate baz + 17: 1 baz:1 + 18: 2 baz:2 + 19: 3 baz:3 + 20: 4 baz:4 + 16: 5 + Test annotate with whitespace options $ cd ..
--- a/tests/test-archive.t Thu Apr 04 16:28:19 2013 -0500 +++ b/tests/test-archive.t Thu Apr 18 23:46:26 2013 -0500 @@ -69,10 +69,18 @@ > msvcrt.setmode(sys.stdout.fileno(), os.O_BINARY) > except ImportError: > pass - > node, archive = sys.argv[1:] - > f = urllib2.urlopen('http://127.0.0.1:%s/?cmd=archive;node=%s;type=%s' - > % (os.environ['HGPORT'], node, archive)) - > sys.stdout.write(f.read()) + > if len(sys.argv) <= 3: + > node, archive = sys.argv[1:] + > requeststr = 'cmd=archive;node=%s;type=%s' % (node, archive) + > else: + > node, archive, file = sys.argv[1:] + > requeststr = 'cmd=archive;node=%s;type=%s;file=%s' % (node, archive, file) + > try: + > f = urllib2.urlopen('http://127.0.0.1:%s/?%s' + > % (os.environ['HGPORT'], requeststr)) + > sys.stdout.write(f.read()) + > except urllib2.HTTPError, e: + > sys.stderr.write(str(e) + '\n') > EOF $ python getarchive.py "$TIP" gz | gunzip | tar tf - 2>/dev/null test-archive-2c0277f05ed4/.hg_archival.txt @@ -93,6 +101,23 @@ testing: test-archive-2c0277f05ed4/foo OK No errors detected in compressed data of archive.zip. +test that we can download single directories and files + + $ python getarchive.py "$TIP" gz baz | gunzip | tar tf - 2>/dev/null + test-archive-2c0277f05ed4/baz/bletch + $ python getarchive.py "$TIP" gz foo | gunzip | tar tf - 2>/dev/null + test-archive-2c0277f05ed4/foo + +test that we detect file patterns that match no files + + $ python getarchive.py "$TIP" gz foobar + HTTP Error 404: file(s) not found: foobar + +test that we reject unsafe patterns + + $ python getarchive.py "$TIP" gz relre:baz + HTTP Error 404: file(s) not found: relre:baz + $ "$TESTDIR/killdaemons.py" $DAEMON_PIDS $ hg archive -t tar test.tar @@ -269,6 +294,16 @@ *-----* (glob) \s*147\s+2 files (re) +show an error when a provided pattern matches no files + + $ hg archive -I file_that_does_not_exist.foo ../empty.zip + abort: no files match the archive pattern + [255] + + $ hg archive -X * ../empty.zip + abort: no files match the archive pattern + [255] + $ cd .. issue3600: check whether "hg archive" can create archive files which
--- a/tests/test-atomictempfile.py Thu Apr 04 16:28:19 2013 -0500 +++ b/tests/test-atomictempfile.py Thu Apr 18 23:46:26 2013 -0500 @@ -1,48 +1,42 @@ import os import glob +import unittest +import silenttestrunner + from mercurial.util import atomictempfile -# basic usage -def test1_simple(): - if os.path.exists('foo'): - os.remove('foo') - file = atomictempfile('foo') - (dir, basename) = os.path.split(file._tempname) - assert not os.path.isfile('foo') - assert basename in glob.glob('.foo-*') +class testatomictempfile(unittest.TestCase): + def test1_simple(self): + if os.path.exists('foo'): + os.remove('foo') + file = atomictempfile('foo') + (dir, basename) = os.path.split(file._tempname) + self.assertFalse(os.path.isfile('foo')) + self.assertTrue(basename in glob.glob('.foo-*')) - file.write('argh\n') - file.close() + file.write('argh\n') + file.close() - assert os.path.isfile('foo') - assert basename not in glob.glob('.foo-*') - print 'OK' + self.assertTrue(os.path.isfile('foo')) + self.assertTrue(basename not in glob.glob('.foo-*')) -# discard() removes the temp file without making the write permanent -def test2_discard(): - if os.path.exists('foo'): - os.remove('foo') - file = atomictempfile('foo') - (dir, basename) = os.path.split(file._tempname) - - file.write('yo\n') - file.discard() + # discard() removes the temp file without making the write permanent + def test2_discard(self): + if os.path.exists('foo'): + os.remove('foo') + file = atomictempfile('foo') + (dir, basename) = os.path.split(file._tempname) - assert not os.path.isfile('foo') - assert basename not in os.listdir('.') - print 'OK' + file.write('yo\n') + file.discard() -# if a programmer screws up and passes bad args to atomictempfile, they -# get a plain ordinary TypeError, not infinite recursion -def test3_oops(): - try: - file = atomictempfile() - except TypeError: - print "OK" - else: - print "expected TypeError" + self.assertFalse(os.path.isfile('foo')) + self.assertTrue(basename not in os.listdir('.')) + + # if a programmer screws up and passes bad args to atomictempfile, they + # get a plain ordinary TypeError, not infinite recursion + def test3_oops(self): + self.assertRaises(TypeError, atomictempfile) if __name__ == '__main__': - test1_simple() - test2_discard() - test3_oops() + silenttestrunner.main(__name__)
--- a/tests/test-atomictempfile.py.out Thu Apr 04 16:28:19 2013 -0500 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,3 +0,0 @@ -OK -OK -OK
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tests/test-blackbox.t Thu Apr 18 23:46:26 2013 -0500 @@ -0,0 +1,150 @@ +setup + $ cat > mock.py <<EOF + > from mercurial import util + > + > def makedate(): + > return 0, 0 + > def getuser(): + > return 'bob' + > # mock the date and user apis so the output is always the same + > def uisetup(ui): + > util.makedate = makedate + > util.getuser = getuser + > EOF + $ cat >> $HGRCPATH <<EOF + > [extensions] + > blackbox= + > mock=`pwd`/mock.py + > mq= + > EOF + $ hg init blackboxtest + $ cd blackboxtest + +command, exit codes, and duration + + $ echo a > a + $ hg add a + $ hg blackbox + 1970/01/01 00:00:00 bob> add a + 1970/01/01 00:00:00 bob> add exited 0 after * seconds (glob) + +incoming change tracking + +create two heads to verify that we only see one change in the log later + $ hg commit -ma + $ hg up null + 0 files updated, 0 files merged, 1 files removed, 0 files unresolved + $ echo b > b + $ hg commit -Amb + adding b + created new head + +clone, commit, pull + $ hg clone . ../blackboxtest2 + updating to branch default + 1 files updated, 0 files merged, 0 files removed, 0 files unresolved + $ echo c > c + $ hg commit -Amc + adding c + $ cd ../blackboxtest2 + $ hg pull + pulling from $TESTTMP/blackboxtest (glob) + searching for changes + adding changesets + adding manifests + adding file changes + added 1 changesets with 1 changes to 1 files + (run 'hg update' to get a working copy) + $ hg blackbox -l 3 + 1970/01/01 00:00:00 bob> pull + 1970/01/01 00:00:00 bob> 1 incoming changes - new heads: d02f48003e62 + 1970/01/01 00:00:00 bob> pull exited None after * seconds (glob) + +we must not cause a failure if we cannot write to the log + + $ hg rollback + repository tip rolled back to revision 1 (undo pull) + $ chmod 000 .hg/blackbox.log + $ hg --debug incoming + warning: cannot write to blackbox.log: Permission denied + comparing with $TESTTMP/blackboxtest (glob) + query 1; heads + searching for changes + all local heads known remotely + changeset: 2:d02f48003e62c24e2659d97d30f2a83abe5d5d51 + tag: tip + phase: draft + parent: 1:6563da9dcf87b1949716e38ff3e3dfaa3198eb06 + parent: -1:0000000000000000000000000000000000000000 + manifest: 2:ab9d46b053ebf45b7996f2922b9893ff4b63d892 + user: test + date: Thu Jan 01 00:00:00 1970 +0000 + files+: c + extra: branch=default + description: + c + + + $ hg pull + pulling from $TESTTMP/blackboxtest (glob) + searching for changes + adding changesets + adding manifests + adding file changes + added 1 changesets with 1 changes to 1 files + (run 'hg update' to get a working copy) + +a failure reading from the log is fine + + $ hg blackbox -l 3 + abort: Permission denied: $TESTTMP/blackboxtest2/.hg/blackbox.log + [255] + + $ chmod 600 .hg/blackbox.log + +backup bundles get logged + + $ touch d + $ hg commit -Amd + adding d + created new head + $ hg strip tip + 0 files updated, 0 files merged, 1 files removed, 0 files unresolved + saved backup bundle to $TESTTMP/blackboxtest2/.hg/strip-backup/*-backup.hg (glob) + $ hg blackbox -l 3 + 1970/01/01 00:00:00 bob> strip tip + 1970/01/01 00:00:00 bob> saved backup bundle to $TESTTMP/blackboxtest2/.hg/strip-backup/*-backup.hg (glob) + 1970/01/01 00:00:00 bob> strip exited 0 after * seconds (glob) + +extension and python hooks - use the eol extension for a pythonhook + + $ echo '[extensions]' >> .hg/hgrc + $ echo 'eol=' >> .hg/hgrc + $ echo '[hooks]' >> .hg/hgrc + $ echo 'update = echo hooked' >> .hg/hgrc + $ hg update + hooked + 1 files updated, 0 files merged, 0 files removed, 0 files unresolved + $ hg blackbox -l 4 + 1970/01/01 00:00:00 bob> update + 1970/01/01 00:00:00 bob> pythonhook-preupdate: hgext.eol.preupdate finished in * seconds (glob) + 1970/01/01 00:00:00 bob> exthook-update: echo hooked finished in * seconds (glob) + 1970/01/01 00:00:00 bob> update exited False after * seconds (glob) + +log rotation + + $ echo '[blackbox]' >> .hg/hgrc + $ echo 'maxsize = 20 b' >> .hg/hgrc + $ echo 'maxfiles = 3' >> .hg/hgrc + $ hg status + $ hg status + $ hg status + $ hg tip -q + 2:d02f48003e62 + $ ls .hg/blackbox.log* + .hg/blackbox.log + .hg/blackbox.log.1 + .hg/blackbox.log.2 + +cleanup + $ cd ..
--- a/tests/test-bookmarks-pushpull.t Thu Apr 04 16:28:19 2013 -0500 +++ b/tests/test-bookmarks-pushpull.t Thu Apr 18 23:46:26 2013 -0500 @@ -204,6 +204,39 @@ Y 3:f6fc62dde3c0 Z 1:0d2164f0ce0d +update a bookmark in the middle of a client pulling changes + + $ cd .. + $ hg clone -q a pull-race + $ hg clone -q pull-race pull-race2 + $ cd pull-race + $ hg up -q Y + $ echo c4 > f2 + $ hg ci -Am4 + $ echo c5 > f3 + $ cat <<EOF > .hg/hgrc + > [hooks] + > outgoing.makecommit = hg ci -Am5; echo committed in pull-race + > EOF + $ cd ../pull-race2 + $ hg pull + pulling from $TESTTMP/pull-race (glob) + searching for changes + adding changesets + adding f3 + committed in pull-race + adding manifests + adding file changes + added 1 changesets with 1 changes to 1 files + updating bookmark Y + (run 'hg update' to get a working copy) + $ hg book + * @ 1:0d2164f0ce0d + X 1:0d2164f0ce0d + Y 4:b0a5eff05604 + Z 1:0d2164f0ce0d + $ cd ../b + diverging a remote bookmark fails $ hg up -q 4e3505fd9583
--- a/tests/test-bookmarks.t Thu Apr 04 16:28:19 2013 -0500 +++ b/tests/test-bookmarks.t Thu Apr 18 23:46:26 2013 -0500 @@ -239,8 +239,8 @@ bookmark with existing name - $ hg bookmark Z - abort: bookmark 'Z' already exists (use -f to force) + $ hg bookmark X2 + abort: bookmark 'X2' already exists (use -f to force) [255] $ hg bookmark -m Y Z @@ -257,6 +257,12 @@ abort: a bookmark cannot have the name of an existing branch [255] +bookmark with integer name + + $ hg bookmark 10 + abort: a bookmark cannot have an integer as its name + [255] + incompatible options $ hg bookmark -m Y -d Z @@ -273,7 +279,21 @@ force bookmark with existing name - $ hg bookmark -f Z + $ hg bookmark -f X2 + +force bookmark back to where it was, should deactivate it + + $ hg bookmark -fr1 X2 + $ hg bookmarks + X2 1:925d80f479bb + Y 2:db815d6d32e6 + Z 0:f7b1eb17ad24 + x y 2:db815d6d32e6 + +forward bookmark to descendant without --force + + $ hg bookmark Z + moving bookmark 'Z' forward from f7b1eb17ad24 list bookmarks @@ -348,9 +368,14 @@ $ hg bookmarks X2 1:925d80f479bb Y 2:db815d6d32e6 - * Z 2:db815d6d32e6 + Z 2:db815d6d32e6 x y 2:db815d6d32e6 +activate bookmark on working dir parent without --force + + $ hg bookmark --inactive Z + $ hg bookmark Z + test clone $ hg bookmark -r 2 -i @ @@ -461,6 +486,13 @@ update to current bookmark if it's not the parent + $ hg summary + parent: 2:db815d6d32e6 + 2 + branch: default + bookmarks: [Z] Y x y + commit: 1 added, 1 unknown (new branch head) + update: 2 new changesets (update) $ hg update updating to active bookmark Z 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
--- a/tests/test-branches.t Thu Apr 04 16:28:19 2013 -0500 +++ b/tests/test-branches.t Thu Apr 18 23:46:26 2013 -0500 @@ -256,6 +256,27 @@ 1 files updated, 0 files merged, 0 files removed, 0 files unresolved $ hg commit -d '9 0' --close-branch -m 'close this part branch too' + $ hg commit -d '9 0' --close-branch -m 're-closing this branch' + $ hg log -r tip --debug + changeset: 13:c2601d54b1427e99506bee25a566ef3a5963af0b + branch: b + tag: tip + phase: draft + parent: 12:e3d49c0575d8fc2cb1cd6859c747c14f5f6d499f + parent: -1:0000000000000000000000000000000000000000 + manifest: 8:6f9ed32d2b310e391a4f107d5f0f071df785bfee + user: test + date: Thu Jan 01 00:00:09 1970 +0000 + extra: branch=b + extra: close=1 + description: + re-closing this branch + + + $ hg rollback + repository tip rolled back to revision 12 (undo commit) + working directory now based on revision 12 + --- b branch should be inactive $ hg branches
--- a/tests/test-check-code-hg.t Thu Apr 04 16:28:19 2013 -0500 +++ b/tests/test-check-code-hg.t Thu Apr 18 23:46:26 2013 -0500 @@ -1,6 +1,6 @@ $ check_code="$TESTDIR"/../contrib/check-code.py $ cd "$TESTDIR"/.. - $ if hg identify -q > /dev/null; then : + $ if hg identify -q > /dev/null 2>&1; then : > else > echo "skipped: not a Mercurial working dir" >&2 > exit 80 @@ -8,4 +8,22 @@ New errors are not allowed. Warnings are strongly discouraged. - $ hg manifest | xargs "$check_code" --warnings --nolineno --per-file=0 + $ hg manifest 2>/dev/null \ + > | xargs "$check_code" --warnings --nolineno --per-file=0 \ + > || false + +Check Python files without py extension + + $ cp \ + > hg \ + > hgweb.cgi \ + > contrib/convert-repo \ + > contrib/dumprevlog \ + > contrib/hgweb.fcgi \ + > contrib/hgweb.wsgi \ + > contrib/simplemerge \ + > contrib/undumprevlog \ + > "$TESTTMP"/ + $ for f in "$TESTTMP"/*; do cp "$f" "$f.py"; done + $ "$check_code" --warnings --nolineno --per-file=0 "$TESTTMP"/*.py \ + > || false
--- a/tests/test-check-code.t Thu Apr 04 16:28:19 2013 -0500 +++ b/tests/test-check-code.t Thu Apr 18 23:46:26 2013 -0500 @@ -163,13 +163,13 @@ object comparison with literal [1] - $ cat > warning.py <<EOF + $ cat > for-nolineno.py <<EOF > except: > EOF - $ "$check_code" warning.py --warning --nolineno - warning.py:0: + $ "$check_code" for-nolineno.py --nolineno + for-nolineno.py:0: > except: - warning: naked except clause + naked except clause [1] $ cat > raise-format.py <<EOF
--- a/tests/test-command-template.t Thu Apr 04 16:28:19 2013 -0500 +++ b/tests/test-command-template.t Thu Apr 18 23:46:26 2013 -0500 @@ -43,6 +43,11 @@ $ hg mv second fourth $ hg commit -m third -d "2020-01-01 10:01" + $ hg log --template '{join(file_copies, ",\n")}\n' -r . + fourth (second) + $ hg log --template '{file_copies % "{source} -> {name}\n"}' -r . + second -> fourth + Quoting for ui.logtemplate $ hg tip --config "ui.logtemplate={rev}\n" @@ -1526,3 +1531,7 @@ $ hg log -R latesttag -r tip --template 'modified files: {file_mods % " {file}\n"}\n' modified files: .hgtags +Test the sub function of templating for expansion: + + $ hg log -R latesttag -r 10 --template '{sub("[0-9]", "x", "{rev}")}\n' + xx
--- a/tests/test-commit-amend.t Thu Apr 04 16:28:19 2013 -0500 +++ b/tests/test-commit-amend.t Thu Apr 18 23:46:26 2013 -0500 @@ -304,7 +304,7 @@ $ hg branches default 2:ce12b0b57d46 -Refuse to amend merges: +Refuse to amend during a merge: $ hg up -q default $ hg merge foo @@ -314,9 +314,6 @@ abort: cannot amend while merging [255] $ hg ci -m 'merge' - $ hg ci --amend - abort: cannot amend merge changesets - [255] Follow copies/renames: @@ -518,3 +515,231 @@ date: Thu Jan 01 00:00:00 1970 +0000 summary: babar + +Amend a merge changeset (with renames and conflicts from the second parent): + + $ hg up -q default + $ hg branch -q bar + $ hg cp a aa + $ hg mv z zz + $ echo cc > cc + $ hg add cc + $ hg ci -m aazzcc + $ hg up -q default + $ echo a >> a + $ echo dd > cc + $ hg add cc + $ hg ci -m aa + $ hg merge -q bar + warning: conflicts during merge. + merging cc incomplete! (edit conflicts, then use 'hg resolve --mark') + [1] + $ hg resolve -m cc + $ hg ci -m 'merge bar' + $ hg log --config diff.git=1 -pr . + changeset: 23:d51446492733 + tag: tip + parent: 22:30d96aeaf27b + parent: 21:1aa437659d19 + user: test + date: Thu Jan 01 00:00:00 1970 +0000 + summary: merge bar + + diff --git a/a b/aa + copy from a + copy to aa + diff --git a/cc b/cc + --- a/cc + +++ b/cc + @@ -1,1 +1,5 @@ + +<<<<<<< local + dd + +======= + +cc + +>>>>>>> other + diff --git a/z b/zz + rename from z + rename to zz + + $ hg debugrename aa + aa renamed from a:a80d06849b333b8a3d5c445f8ba3142010dcdc9e + $ hg debugrename zz + zz renamed from z:69a1b67522704ec122181c0890bd16e9d3e7516a + $ hg debugrename cc + cc not renamed + $ hg ci --amend -m 'merge bar (amend message)' + $ hg log --config diff.git=1 -pr . + changeset: 24:59de3dce7a79 + tag: tip + parent: 22:30d96aeaf27b + parent: 21:1aa437659d19 + user: test + date: Thu Jan 01 00:00:00 1970 +0000 + summary: merge bar (amend message) + + diff --git a/a b/aa + copy from a + copy to aa + diff --git a/cc b/cc + --- a/cc + +++ b/cc + @@ -1,1 +1,5 @@ + +<<<<<<< local + dd + +======= + +cc + +>>>>>>> other + diff --git a/z b/zz + rename from z + rename to zz + + $ hg debugrename aa + aa renamed from a:a80d06849b333b8a3d5c445f8ba3142010dcdc9e + $ hg debugrename zz + zz renamed from z:69a1b67522704ec122181c0890bd16e9d3e7516a + $ hg debugrename cc + cc not renamed + $ hg mv zz z + $ hg ci --amend -m 'merge bar (undo rename)' + $ hg log --config diff.git=1 -pr . + changeset: 26:7fb89c461f81 + tag: tip + parent: 22:30d96aeaf27b + parent: 21:1aa437659d19 + user: test + date: Thu Jan 01 00:00:00 1970 +0000 + summary: merge bar (undo rename) + + diff --git a/a b/aa + copy from a + copy to aa + diff --git a/cc b/cc + --- a/cc + +++ b/cc + @@ -1,1 +1,5 @@ + +<<<<<<< local + dd + +======= + +cc + +>>>>>>> other + + $ hg debugrename z + z not renamed + +Amend a merge changeset (with renames during the merge): + + $ hg up -q bar + $ echo x > x + $ hg add x + $ hg ci -m x + $ hg up -q default + $ hg merge -q bar + $ hg mv aa aaa + $ echo aa >> aaa + $ hg ci -m 'merge bar again' + $ hg log --config diff.git=1 -pr . + changeset: 28:982d7a34ffee + tag: tip + parent: 26:7fb89c461f81 + parent: 27:4c94d5bc65f5 + user: test + date: Thu Jan 01 00:00:00 1970 +0000 + summary: merge bar again + + diff --git a/aa b/aa + deleted file mode 100644 + --- a/aa + +++ /dev/null + @@ -1,2 +0,0 @@ + -a + -a + diff --git a/aaa b/aaa + new file mode 100644 + --- /dev/null + +++ b/aaa + @@ -0,0 +1,3 @@ + +a + +a + +aa + diff --git a/x b/x + new file mode 100644 + --- /dev/null + +++ b/x + @@ -0,0 +1,1 @@ + +x + + $ hg debugrename aaa + aaa renamed from aa:37d9b5d994eab34eda9c16b195ace52c7b129980 + $ hg mv aaa aa + $ hg ci --amend -m 'merge bar again (undo rename)' + $ hg log --config diff.git=1 -pr . + changeset: 30:522688c0e71b + tag: tip + parent: 26:7fb89c461f81 + parent: 27:4c94d5bc65f5 + user: test + date: Thu Jan 01 00:00:00 1970 +0000 + summary: merge bar again (undo rename) + + diff --git a/aa b/aa + --- a/aa + +++ b/aa + @@ -1,2 +1,3 @@ + a + a + +aa + diff --git a/x b/x + new file mode 100644 + --- /dev/null + +++ b/x + @@ -0,0 +1,1 @@ + +x + + $ hg debugrename aa + aa not renamed + $ hg debugrename -r '.^' aa + aa renamed from a:a80d06849b333b8a3d5c445f8ba3142010dcdc9e + +Amend a merge changeset (with manifest-level conflicts): + + $ hg up -q bar + $ hg rm aa + $ hg ci -m 'rm aa' + $ hg up -q default + $ echo aa >> aa + $ hg ci -m aa + $ hg merge -q bar + local changed aa which remote deleted + use (c)hanged version or (d)elete? c + $ hg ci -m 'merge bar (with conflicts)' + $ hg log --config diff.git=1 -pr . + changeset: 33:5f9904c491b8 + tag: tip + parent: 32:01780b896f58 + parent: 31:67db8847a540 + user: test + date: Thu Jan 01 00:00:00 1970 +0000 + summary: merge bar (with conflicts) + + + $ hg rm aa + $ hg ci --amend -m 'merge bar (with conflicts, amended)' + $ hg log --config diff.git=1 -pr . + changeset: 35:6ce0c89781a3 + tag: tip + parent: 32:01780b896f58 + parent: 31:67db8847a540 + user: test + date: Thu Jan 01 00:00:00 1970 +0000 + summary: merge bar (with conflicts, amended) + + diff --git a/aa b/aa + deleted file mode 100644 + --- a/aa + +++ /dev/null + @@ -1,4 +0,0 @@ + -a + -a + -aa + -aa +
--- a/tests/test-commit.t Thu Apr 04 16:28:19 2013 -0500 +++ b/tests/test-commit.t Thu Apr 18 23:46:26 2013 -0500 @@ -263,6 +263,7 @@ $ cd commitmsg $ echo changed > changed $ echo removed > removed + $ hg book currentbookmark $ hg ci -qAm init $ hg rm removed @@ -277,6 +278,7 @@ HG: -- HG: user: test HG: branch 'default' + HG: bookmark 'currentbookmark' HG: added added HG: changed changed HG: removed removed
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tests/test-completion.t Thu Apr 18 23:46:26 2013 -0500 @@ -0,0 +1,338 @@ +Show all commands except debug commands + $ hg debugcomplete + add + addremove + annotate + archive + backout + bisect + bookmarks + branch + branches + bundle + cat + clone + commit + copy + diff + export + forget + graft + grep + heads + help + identify + import + incoming + init + locate + log + manifest + merge + outgoing + parents + paths + phase + pull + push + recover + remove + rename + resolve + revert + rollback + root + serve + showconfig + status + summary + tag + tags + tip + unbundle + update + verify + version + +Show all commands that start with "a" + $ hg debugcomplete a + add + addremove + annotate + archive + +Do not show debug commands if there are other candidates + $ hg debugcomplete d + diff + +Show debug commands if there are no other candidates + $ hg debugcomplete debug + debugancestor + debugbuilddag + debugbundle + debugcheckstate + debugcommands + debugcomplete + debugconfig + debugdag + debugdata + debugdate + debugdirstate + debugdiscovery + debugfileset + debugfsinfo + debuggetbundle + debugignore + debugindex + debugindexdot + debuginstall + debugknown + debuglabelcomplete + debugobsolete + debugpathcomplete + debugpushkey + debugpvec + debugrebuilddirstate + debugrename + debugrevlog + debugrevspec + debugsetparents + debugsub + debugsuccessorssets + debugwalk + debugwireargs + +Do not show the alias of a debug command if there are other candidates +(this should hide rawcommit) + $ hg debugcomplete r + recover + remove + rename + resolve + revert + rollback + root +Show the alias of a debug command if there are no other candidates + $ hg debugcomplete rawc + + +Show the global options + $ hg debugcomplete --options | sort + --config + --cwd + --debug + --debugger + --encoding + --encodingmode + --help + --hidden + --noninteractive + --profile + --quiet + --repository + --time + --traceback + --verbose + --version + -R + -h + -q + -v + -y + +Show the options for the "serve" command + $ hg debugcomplete --options serve | sort + --accesslog + --address + --certificate + --cmdserver + --config + --cwd + --daemon + --daemon-pipefds + --debug + --debugger + --encoding + --encodingmode + --errorlog + --help + --hidden + --ipv6 + --name + --noninteractive + --pid-file + --port + --prefix + --profile + --quiet + --repository + --stdio + --style + --templates + --time + --traceback + --verbose + --version + --web-conf + -6 + -A + -E + -R + -a + -d + -h + -n + -p + -q + -t + -v + -y + +Show an error if we use --options with an ambiguous abbreviation + $ hg debugcomplete --options s + hg: command 's' is ambiguous: + serve showconfig status summary + [255] + +Show all commands + options + $ hg debugcommands + add: include, exclude, subrepos, dry-run + annotate: rev, follow, no-follow, text, user, file, date, number, changeset, line-number, ignore-all-space, ignore-space-change, ignore-blank-lines, include, exclude + clone: noupdate, updaterev, rev, branch, pull, uncompressed, ssh, remotecmd, insecure + commit: addremove, close-branch, amend, include, exclude, message, logfile, date, user, subrepos + diff: rev, change, text, git, nodates, show-function, reverse, ignore-all-space, ignore-space-change, ignore-blank-lines, unified, stat, include, exclude, subrepos + export: output, switch-parent, rev, text, git, nodates + forget: include, exclude + init: ssh, remotecmd, insecure + log: follow, follow-first, date, copies, keyword, rev, removed, only-merges, user, only-branch, branch, prune, patch, git, limit, no-merges, stat, graph, style, template, include, exclude + merge: force, rev, preview, tool + pull: update, force, rev, bookmark, branch, ssh, remotecmd, insecure + push: force, rev, bookmark, branch, new-branch, ssh, remotecmd, insecure + remove: after, force, include, exclude + serve: accesslog, daemon, daemon-pipefds, errorlog, port, address, prefix, name, web-conf, webdir-conf, pid-file, stdio, cmdserver, templates, style, ipv6, certificate + status: all, modified, added, removed, deleted, clean, unknown, ignored, no-status, copies, print0, rev, change, include, exclude, subrepos + summary: remote + update: clean, check, date, rev + addremove: similarity, include, exclude, dry-run + archive: no-decode, prefix, rev, type, subrepos, include, exclude + backout: merge, parent, rev, tool, include, exclude, message, logfile, date, user + bisect: reset, good, bad, skip, extend, command, noupdate + bookmarks: force, rev, delete, rename, inactive + branch: force, clean + branches: active, closed + bundle: force, rev, branch, base, all, type, ssh, remotecmd, insecure + cat: output, rev, decode, include, exclude + copy: after, force, include, exclude, dry-run + debugancestor: + debugbuilddag: mergeable-file, overwritten-file, new-file + debugbundle: all + debugcheckstate: + debugcommands: + debugcomplete: options + debugdag: tags, branches, dots, spaces + debugdata: changelog, manifest + debugdate: extended + debugdirstate: nodates, datesort + debugdiscovery: old, nonheads, ssh, remotecmd, insecure + debugfileset: rev + debugfsinfo: + debuggetbundle: head, common, type + debugignore: + debugindex: changelog, manifest, format + debugindexdot: + debuginstall: + debugknown: + debuglabelcomplete: + debugobsolete: flags, date, user + debugpathcomplete: full, normal, added, removed + debugpushkey: + debugpvec: + debugrebuilddirstate: rev + debugrename: rev + debugrevlog: changelog, manifest, dump + debugrevspec: + debugsetparents: + debugsub: rev + debugsuccessorssets: + debugwalk: include, exclude + debugwireargs: three, four, five, ssh, remotecmd, insecure + graft: rev, continue, edit, log, currentdate, currentuser, date, user, tool, dry-run + grep: print0, all, text, follow, ignore-case, files-with-matches, line-number, rev, user, date, include, exclude + heads: rev, topo, active, closed, style, template + help: extension, command, keyword + identify: rev, num, id, branch, tags, bookmarks, ssh, remotecmd, insecure + import: strip, base, edit, force, no-commit, bypass, exact, import-branch, message, logfile, date, user, similarity + incoming: force, newest-first, bundle, rev, bookmarks, branch, patch, git, limit, no-merges, stat, graph, style, template, ssh, remotecmd, insecure, subrepos + locate: rev, print0, fullpath, include, exclude + manifest: rev, all + outgoing: force, rev, newest-first, bookmarks, branch, patch, git, limit, no-merges, stat, graph, style, template, ssh, remotecmd, insecure, subrepos + parents: rev, style, template + paths: + phase: public, draft, secret, force, rev + recover: + rename: after, force, include, exclude, dry-run + resolve: all, list, mark, unmark, no-status, tool, include, exclude + revert: all, date, rev, no-backup, include, exclude, dry-run + rollback: dry-run, force + root: + showconfig: untrusted + tag: force, local, rev, remove, edit, message, date, user + tags: + tip: patch, git, style, template + unbundle: update + verify: + version: + + $ hg init a + $ cd a + $ echo fee > fee + $ hg ci -q -Amfee + $ hg tag fee + $ mkdir fie + $ echo dead > fie/dead + $ echo live > fie/live + $ hg bookmark fo + $ hg branch -q fie + $ hg ci -q -Amfie + $ echo fo > fo + $ hg branch -qf default + $ hg ci -q -Amfo + $ echo Fum > Fum + $ hg ci -q -AmFum + $ hg bookmark Fum + +Test debugpathcomplete + + $ hg debugpathcomplete f + fee + fie/ + fo + $ hg debugpathcomplete -f f + fee + fie/dead + fie/live + fo + + $ hg rm Fum + $ hg debugpathcomplete -r F + Fum + +If one directory and no files match, give an ambiguous answer + + $ hg debugpathcomplete fi + fie/ + fie/. + +Test debuglabelcomplete + + $ hg debuglabelcomplete + Fum + default + fee + fie + fo + tip + $ hg debuglabelcomplete f + fee + fie + fo
--- a/tests/test-convert-datesort.t Thu Apr 04 16:28:19 2013 -0500 +++ b/tests/test-convert-datesort.t Thu Apr 18 23:46:26 2013 -0500 @@ -38,6 +38,26 @@ 2 files updated, 0 files merged, 0 files removed, 0 files unresolved $ echo b >> b $ hg ci -m b1 -d '9 0' + $ hg up -C 0 + 0 files updated, 0 files merged, 1 files removed, 0 files unresolved + $ echo c >> c + $ hg branch branchc + marked working directory as branch branchc + (branches are permanent and global, did you want a bookmark?) + $ hg ci -Am c0 -d '10 0' + adding c + $ hg up -C brancha + 1 files updated, 0 files merged, 1 files removed, 0 files unresolved + $ hg ci --close-branch -m a7x -d '11 0' + $ hg up -C branchb + 2 files updated, 0 files merged, 0 files removed, 0 files unresolved + $ hg ci --close-branch -m b2x -d '12 0' + $ hg up -C branchc + 1 files updated, 0 files merged, 1 files removed, 0 files unresolved + $ hg merge branchb + 1 files updated, 0 files merged, 0 files removed, 0 files unresolved + (branch merge, don't forget to commit) + $ hg ci -m c1 -d '13 0' $ cd .. convert with datesort @@ -47,27 +67,39 @@ scanning source... sorting... converting... - 8 a0 - 7 a1 - 6 a2 - 5 a3 - 4 a4 - 3 b0 - 2 a5 - 1 a6 - 0 b1 + 12 a0 + 11 a1 + 10 a2 + 9 a3 + 8 a4 + 7 b0 + 6 a5 + 5 a6 + 4 b1 + 3 c0 + 2 a7x + 1 b2x + 0 c1 graph converted repo $ hg -R t-datesort glog --template '{rev} "{desc}"\n' - o 8 "b1" - | - | o 7 "a6" + o 12 "c1" + |\ + | o 11 "b2x" | | - | o 6 "a5" - | | - o | 5 "b0" - | | + | | o 10 "a7x" + | | | + o | | 9 "c0" + | | | + | o | 8 "b1" + | | | + | | o 7 "a6" + | | | + | | o 6 "a5" + | | | + | o | 5 "b0" + |/ / | o 4 "a4" | | | o 3 "a3" @@ -86,29 +118,41 @@ scanning source... sorting... converting... - 8 a0 - 7 a1 - 6 a2 - 5 a3 - 4 b0 - 3 a4 - 2 a5 - 1 a6 - 0 b1 + 12 a0 + 11 a1 + 10 a2 + 9 a3 + 8 b0 + 7 a4 + 6 a5 + 5 a6 + 4 b1 + 3 c0 + 2 a7x + 1 b2x + 0 c1 graph converted repo $ hg -R t-sourcesort glog --template '{rev} "{desc}"\n' - o 8 "b1" - | - | o 7 "a6" + o 12 "c1" + |\ + | o 11 "b2x" | | - | o 6 "a5" - | | - | o 5 "a4" - | | - o | 4 "b0" - | | + | | o 10 "a7x" + | | | + o | | 9 "c0" + | | | + | o | 8 "b1" + | | | + | | o 7 "a6" + | | | + | | o 6 "a5" + | | | + | | o 5 "a4" + | | | + | o | 4 "b0" + |/ / | o 3 "a3" | | | o 2 "a2" @@ -117,3 +161,54 @@ |/ o 0 "a0" + +convert with closesort + + $ hg convert --closesort t t-closesort + initializing destination t-closesort repository + scanning source... + sorting... + converting... + 12 a0 + 11 a1 + 10 a2 + 9 a3 + 8 b0 + 7 a4 + 6 a5 + 5 a6 + 4 a7x + 3 b1 + 2 b2x + 1 c0 + 0 c1 + +graph converted repo + + $ hg -R t-closesort glog --template '{rev} "{desc}"\n' + o 12 "c1" + |\ + | o 11 "c0" + | | + o | 10 "b2x" + | | + o | 9 "b1" + | | + | | o 8 "a7x" + | | | + | | o 7 "a6" + | | | + | | o 6 "a5" + | | | + | | o 5 "a4" + | | | + o | | 4 "b0" + |/ / + | o 3 "a3" + | | + | o 2 "a2" + | | + | o 1 "a1" + |/ + o 0 "a0" +
--- a/tests/test-convert-git.t Thu Apr 04 16:28:19 2013 -0500 +++ b/tests/test-convert-git.t Thu Apr 18 23:46:26 2013 -0500 @@ -281,24 +281,6 @@ abort: --sourcesort is not supported by this data source [255] -damage git repository and convert again - - $ cat > damage.py <<EOF - > import os - > import stat - > for root, dirs, files in os.walk('git-repo4/.git/objects'): - > if files: - > path = os.path.join(root, files[0]) - > if os.name == 'nt': - > os.chmod(path, stat.S_IWUSR) - > os.remove(path) - > break - > EOF - $ python damage.py - $ hg convert git-repo4 git-repo4-broken-hg 2>&1 | \ - > grep 'abort:' | sed 's/abort:.*/abort:/g' - abort: - test sub modules $ mkdir git-repo5 @@ -345,3 +327,32 @@ $ cd git-repo5 $ cat foo sub + + $ cd ../.. + +damaged git repository tests: +In case the hard-coded hashes change, the following commands can be used to +list the hashes and their corresponding types in the repository: +cd git-repo4/.git/objects +find . -type f | cut -c 3- | sed 's_/__' | xargs -n 1 -t git cat-file -t +cd ../../.. + +damage git repository by renaming a commit object + $ COMMIT_OBJ=1c/0ce3c5886f83a1d78a7b517cdff5cf9ca17bdd + $ mv git-repo4/.git/objects/$COMMIT_OBJ git-repo4/.git/objects/$COMMIT_OBJ.tmp + $ hg convert git-repo4 git-repo4-broken-hg 2>&1 | grep 'abort:' + abort: cannot read tags from git-repo4/.git + $ mv git-repo4/.git/objects/$COMMIT_OBJ.tmp git-repo4/.git/objects/$COMMIT_OBJ +damage git repository by renaming a blob object + + $ BLOB_OBJ=8b/137891791fe96927ad78e64b0aad7bded08bdc + $ mv git-repo4/.git/objects/$BLOB_OBJ git-repo4/.git/objects/$BLOB_OBJ.tmp + $ hg convert git-repo4 git-repo4-broken-hg 2>&1 | grep 'abort:' + abort: cannot read 'blob' object at 8b137891791fe96927ad78e64b0aad7bded08bdc + $ mv git-repo4/.git/objects/$BLOB_OBJ.tmp git-repo4/.git/objects/$BLOB_OBJ +damage git repository by renaming a tree object + + $ TREE_OBJ=72/49f083d2a63a41cc737764a86981eb5f3e4635 + $ mv git-repo4/.git/objects/$TREE_OBJ git-repo4/.git/objects/$TREE_OBJ.tmp + $ hg convert git-repo4 git-repo4-broken-hg 2>&1 | grep 'abort:' + abort: cannot read changes in 1c0ce3c5886f83a1d78a7b517cdff5cf9ca17bdd
--- a/tests/test-convert.t Thu Apr 04 16:28:19 2013 -0500 +++ b/tests/test-convert.t Thu Apr 18 23:46:26 2013 -0500 @@ -46,6 +46,8 @@ larger than the same ones generated by --branchsort. --sourcesort try to preserve source revisions order, only supported by Mercurial sources. + --closesort try to move closed revisions as close as possible to parent + branches, only supported by Mercurial sources. If "REVMAP" isn't given, it will be put in a default location ("<dest>/.hg/shamap" by default). The "REVMAP" is a simple text file that @@ -268,6 +270,7 @@ --branchsort try to sort changesets by branches --datesort try to sort changesets by date --sourcesort preserve source changesets order + --closesort try to reorder closed revisions use "hg -v help convert" to show the global options $ hg init a
--- a/tests/test-copy-move-merge.t Thu Apr 04 16:28:19 2013 -0500 +++ b/tests/test-copy-move-merge.t Thu Apr 18 23:46:26 2013 -0500 @@ -29,12 +29,12 @@ src: 'a' -> dst: 'c' * checking for directory renames resolving manifests - overwrite: False, partial: False + branchmerge: True, force: False, partial: False ancestor: b8bf91eeebbc, local: add3f11052fa+, remote: 17c05bb7fcb6 a: remote moved to b -> m + preserving a for resolve of b a: remote moved to c -> m - preserving a for resolve of b - preserving a for resolve of c + preserving a for resolve of c removing a updating: a 1/2 files (50.00%) picked tool 'internal:merge' for b (binary False symlink False)
--- a/tests/test-debugcomplete.t Thu Apr 04 16:28:19 2013 -0500 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,280 +0,0 @@ -Show all commands except debug commands - $ hg debugcomplete - add - addremove - annotate - archive - backout - bisect - bookmarks - branch - branches - bundle - cat - clone - commit - copy - diff - export - forget - graft - grep - heads - help - identify - import - incoming - init - locate - log - manifest - merge - outgoing - parents - paths - phase - pull - push - recover - remove - rename - resolve - revert - rollback - root - serve - showconfig - status - summary - tag - tags - tip - unbundle - update - verify - version - -Show all commands that start with "a" - $ hg debugcomplete a - add - addremove - annotate - archive - -Do not show debug commands if there are other candidates - $ hg debugcomplete d - diff - -Show debug commands if there are no other candidates - $ hg debugcomplete debug - debugancestor - debugbuilddag - debugbundle - debugcheckstate - debugcommands - debugcomplete - debugconfig - debugdag - debugdata - debugdate - debugdiscovery - debugfileset - debugfsinfo - debuggetbundle - debugignore - debugindex - debugindexdot - debuginstall - debugknown - debugobsolete - debugpushkey - debugpvec - debugrebuildstate - debugrename - debugrevlog - debugrevspec - debugsetparents - debugstate - debugsub - debugsuccessorssets - debugwalk - debugwireargs - -Do not show the alias of a debug command if there are other candidates -(this should hide rawcommit) - $ hg debugcomplete r - recover - remove - rename - resolve - revert - rollback - root -Show the alias of a debug command if there are no other candidates - $ hg debugcomplete rawc - - -Show the global options - $ hg debugcomplete --options | sort - --config - --cwd - --debug - --debugger - --encoding - --encodingmode - --help - --hidden - --noninteractive - --profile - --quiet - --repository - --time - --traceback - --verbose - --version - -R - -h - -q - -v - -y - -Show the options for the "serve" command - $ hg debugcomplete --options serve | sort - --accesslog - --address - --certificate - --cmdserver - --config - --cwd - --daemon - --daemon-pipefds - --debug - --debugger - --encoding - --encodingmode - --errorlog - --help - --hidden - --ipv6 - --name - --noninteractive - --pid-file - --port - --prefix - --profile - --quiet - --repository - --stdio - --style - --templates - --time - --traceback - --verbose - --version - --web-conf - -6 - -A - -E - -R - -a - -d - -h - -n - -p - -q - -t - -v - -y - -Show an error if we use --options with an ambiguous abbreviation - $ hg debugcomplete --options s - hg: command 's' is ambiguous: - serve showconfig status summary - [255] - -Show all commands + options - $ hg debugcommands - add: include, exclude, subrepos, dry-run - annotate: rev, follow, no-follow, text, user, file, date, number, changeset, line-number, ignore-all-space, ignore-space-change, ignore-blank-lines, include, exclude - clone: noupdate, updaterev, rev, branch, pull, uncompressed, ssh, remotecmd, insecure - commit: addremove, close-branch, amend, include, exclude, message, logfile, date, user, subrepos - diff: rev, change, text, git, nodates, show-function, reverse, ignore-all-space, ignore-space-change, ignore-blank-lines, unified, stat, include, exclude, subrepos - export: output, switch-parent, rev, text, git, nodates - forget: include, exclude - init: ssh, remotecmd, insecure - log: follow, follow-first, date, copies, keyword, rev, removed, only-merges, user, only-branch, branch, prune, patch, git, limit, no-merges, stat, graph, style, template, include, exclude - merge: force, rev, preview, tool - pull: update, force, rev, bookmark, branch, ssh, remotecmd, insecure - push: force, rev, bookmark, branch, new-branch, ssh, remotecmd, insecure - remove: after, force, include, exclude - serve: accesslog, daemon, daemon-pipefds, errorlog, port, address, prefix, name, web-conf, webdir-conf, pid-file, stdio, cmdserver, templates, style, ipv6, certificate - status: all, modified, added, removed, deleted, clean, unknown, ignored, no-status, copies, print0, rev, change, include, exclude, subrepos - summary: remote - update: clean, check, date, rev - addremove: similarity, include, exclude, dry-run - archive: no-decode, prefix, rev, type, subrepos, include, exclude - backout: merge, parent, rev, tool, include, exclude, message, logfile, date, user - bisect: reset, good, bad, skip, extend, command, noupdate - bookmarks: force, rev, delete, rename, inactive - branch: force, clean - branches: active, closed - bundle: force, rev, branch, base, all, type, ssh, remotecmd, insecure - cat: output, rev, decode, include, exclude - copy: after, force, include, exclude, dry-run - debugancestor: - debugbuilddag: mergeable-file, overwritten-file, new-file - debugbundle: all - debugcheckstate: - debugcommands: - debugcomplete: options - debugdag: tags, branches, dots, spaces - debugdata: changelog, manifest - debugdate: extended - debugdiscovery: old, nonheads, ssh, remotecmd, insecure - debugfileset: rev - debugfsinfo: - debuggetbundle: head, common, type - debugignore: - debugindex: changelog, manifest, format - debugindexdot: - debuginstall: - debugknown: - debugobsolete: flags, date, user - debugpushkey: - debugpvec: - debugrebuildstate: rev - debugrename: rev - debugrevlog: changelog, manifest, dump - debugrevspec: - debugsetparents: - debugstate: nodates, datesort - debugsub: rev - debugsuccessorssets: - debugwalk: include, exclude - debugwireargs: three, four, five, ssh, remotecmd, insecure - graft: rev, continue, edit, log, currentdate, currentuser, date, user, tool, dry-run - grep: print0, all, text, follow, ignore-case, files-with-matches, line-number, rev, user, date, include, exclude - heads: rev, topo, active, closed, style, template - help: extension, command, keyword - identify: rev, num, id, branch, tags, bookmarks, ssh, remotecmd, insecure - import: strip, base, edit, force, no-commit, bypass, exact, import-branch, message, logfile, date, user, similarity - incoming: force, newest-first, bundle, rev, bookmarks, branch, patch, git, limit, no-merges, stat, graph, style, template, ssh, remotecmd, insecure, subrepos - locate: rev, print0, fullpath, include, exclude - manifest: rev, all - outgoing: force, rev, newest-first, bookmarks, branch, patch, git, limit, no-merges, stat, graph, style, template, ssh, remotecmd, insecure, subrepos - parents: rev, style, template - paths: - phase: public, draft, secret, force, rev - recover: - rename: after, force, include, exclude, dry-run - resolve: all, list, mark, unmark, no-status, tool, include, exclude - revert: all, date, rev, no-backup, include, exclude, dry-run - rollback: dry-run, force - root: - showconfig: untrusted - tag: force, local, rev, remove, edit, message, date, user - tags: - tip: patch, git, style, template - unbundle: update - verify: - version:
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tests/test-dicthelpers.py Thu Apr 18 23:46:26 2013 -0500 @@ -0,0 +1,59 @@ +from mercurial.dicthelpers import diff, join +import unittest +import silenttestrunner + +class testdicthelpers(unittest.TestCase): + def test_dicthelpers(self): + # empty dicts + self.assertEqual(diff({}, {}), {}) + self.assertEqual(join({}, {}), {}) + + d1 = {} + d1['a'] = 'foo' + d1['b'] = 'bar' + d1['c'] = 'baz' + + # same identity + self.assertEqual(diff(d1, d1), {}) + self.assertEqual(join(d1, d1), {'a': ('foo', 'foo'), + 'b': ('bar', 'bar'), + 'c': ('baz', 'baz')}) + + # vs empty + self.assertEqual(diff(d1, {}), {'a': ('foo', None), + 'b': ('bar', None), + 'c': ('baz', None)}) + self.assertEqual(diff(d1, {}), {'a': ('foo', None), + 'b': ('bar', None), + 'c': ('baz', None)}) + + d2 = {} + d2['a'] = 'foo2' + d2['b'] = 'bar' + d2['d'] = 'quux' + + self.assertEqual(diff(d1, d2), {'a': ('foo', 'foo2'), + 'c': ('baz', None), + 'd': (None, 'quux')}) + self.assertEqual(join(d1, d2), {'a': ('foo', 'foo2'), + 'b': ('bar', 'bar'), + 'c': ('baz', None), + 'd': (None, 'quux')}) + + # with default argument + self.assertEqual(diff(d1, d2, 123), {'a': ('foo', 'foo2'), + 'c': ('baz', 123), + 'd': (123, 'quux')}) + self.assertEqual(join(d1, d2, 456), {'a': ('foo', 'foo2'), + 'b': ('bar', 'bar'), + 'c': ('baz', 456), + 'd': (456, 'quux')}) + + # check that we compare against default + self.assertEqual(diff(d1, d2, 'baz'), {'a': ('foo', 'foo2'), + 'd': ('baz', 'quux')}) + self.assertEqual(diff(d1, d2, 'quux'), {'a': ('foo', 'foo2'), + 'c': ('baz', 'quux')}) + +if __name__ == '__main__': + silenttestrunner.main(__name__)
--- a/tests/test-diff-color.t Thu Apr 04 16:28:19 2013 -0500 +++ b/tests/test-diff-color.t Thu Apr 18 23:46:26 2013 -0500 @@ -152,7 +152,7 @@ c c \x1b[0;32m+aa\x1b[0m (esc) - \x1b[0;1mdiff --git a/sub/b b/sub/b\x1b[0m (glob) (esc) + \x1b[0;1mdiff --git a/sub/b b/sub/b\x1b[0m (esc) \x1b[0;31;1m--- a/sub/b\x1b[0m (esc) \x1b[0;32;1m+++ b/sub/b\x1b[0m (esc) \x1b[0;35m@@ -1,1 +1,2 @@\x1b[0m (esc)
--- a/tests/test-diff-upgrade.t Thu Apr 04 16:28:19 2013 -0500 +++ b/tests/test-diff-upgrade.t Thu Apr 18 23:46:26 2013 -0500 @@ -200,7 +200,11 @@ % git=auto: git diff for rmbinary diff --git a/rmbinary b/rmbinary deleted file mode 100644 - Binary file rmbinary has changed + index f76dd238ade08917e6712764a16a22005a50573d..0000000000000000000000000000000000000000 + GIT binary patch + literal 0 + Hc$@<O00001 + % git=auto: git diff for bintoregular diff --git a/bintoregular b/bintoregular @@ -210,6 +214,7 @@ Uc$`bh%qz(+N=+}#Ni5<5043uE82|tP + git=warn: regular diff with data loss warnings $ hg autodiff --git=warn
--- a/tests/test-double-merge.t Thu Apr 04 16:28:19 2013 -0500 +++ b/tests/test-double-merge.t Thu Apr 18 23:46:26 2013 -0500 @@ -33,12 +33,12 @@ src: 'foo' -> dst: 'bar' * checking for directory renames resolving manifests - overwrite: False, partial: False + branchmerge: True, force: False, partial: False ancestor: e6dc8efe11cc, local: 6a0df1dad128+, remote: 484bf6903104 + foo: remote copied to bar -> m + preserving foo for resolve of bar foo: versions differ -> m - foo: remote copied to bar -> m - preserving foo for resolve of bar - preserving foo for resolve of foo + preserving foo for resolve of foo updating: foo 1/2 files (50.00%) picked tool 'internal:merge' for bar (binary False symlink False) merging foo and bar to bar
--- a/tests/test-export.t Thu Apr 04 16:28:19 2013 -0500 +++ b/tests/test-export.t Thu Apr 18 23:46:26 2013 -0500 @@ -91,13 +91,28 @@ foo-foo_10.patch foo-foo_11.patch +Doing it again clobbers the files rather than appending: + $ hg export -v -o "foo-%m.patch" 2:3 + exporting patches: + foo-foo_2.patch + foo-foo_3.patch + $ grep HG foo-foo_2.patch | wc -l + \s*1 (re) + $ grep HG foo-foo_3.patch | wc -l + \s*1 (re) + Exporting 4 changesets to a file: $ hg export -o export_internal 1 2 3 4 $ grep HG export_internal | wc -l \s*4 (re) -Exporting 4 changesets to a file: +Doing it again clobbers the file rather than appending: + $ hg export -o export_internal 1 2 3 4 + $ grep HG export_internal | wc -l + \s*4 (re) + +Exporting 4 changesets to stdout: $ hg export 1 2 3 4 | grep HG | wc -l \s*4 (re) @@ -108,6 +123,7 @@ # HG changeset patch # User test # Date 0 0 + # Thu Jan 01 00:00:00 1970 +0000 # Node ID 5f17a83f5fbd9414006a5e563eab4c8a00729efd # Parent 747d3c68f8ec44bb35816bfcd59aeb50b9654c2f foo-10 @@ -132,8 +148,23 @@ Catch exporting unknown revisions (especially empty revsets, see issue3353) $ hg export - abort: export requires at least one changeset - [255] + # HG changeset patch + # User test + # Date 0 0 + # Thu Jan 01 00:00:00 1970 +0000 + # Node ID 197ecd81a57f760b54f34a58817ad5b04991fa47 + # Parent f3acbafac161ec68f1598af38f794f28847ca5d3 + !"#$%&(,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~ + + diff -r f3acbafac161 -r 197ecd81a57f foo + --- a/foo Thu Jan 01 00:00:00 1970 +0000 + +++ b/foo Thu Jan 01 00:00:00 1970 +0000 + @@ -10,3 +10,4 @@ + foo-9 + foo-10 + foo-11 + +line + $ hg export "" hg: parse error: empty query [255] @@ -154,6 +185,7 @@ # HG changeset patch # User test # Date 0 0 + # Thu Jan 01 00:00:00 1970 +0000 # Node ID * (glob) # Parent * (glob) !"#$%&(,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~
--- a/tests/test-extension.t Thu Apr 04 16:28:19 2013 -0500 +++ b/tests/test-extension.t Thu Apr 18 23:46:26 2013 -0500 @@ -112,7 +112,8 @@ > wsgicgi.launch(application) > EOF - $ SCRIPT_NAME='/' SERVER_PORT='80' SERVER_NAME='localhost' python hgweb.cgi \ + $ REQUEST_METHOD='GET' PATH_INFO='/' SCRIPT_NAME='' QUERY_STRING='' \ + > SERVER_PORT='80' SERVER_NAME='localhost' python hgweb.cgi \ > | grep '^[0-9]) ' # ignores HTML output 1) foo imported 1) bar imported @@ -352,6 +353,7 @@ $ hg help multirevs Specifying Multiple Revisions + """"""""""""""""""""""""""""" When Mercurial accepts more than one revision, they may be specified individually, or provided as a topologically continuous range, separated
--- a/tests/test-fileset.t Thu Apr 04 16:28:19 2013 -0500 +++ b/tests/test-fileset.t Thu Apr 18 23:46:26 2013 -0500 @@ -226,3 +226,21 @@ b2 c1 + >>> open('dos', 'wb').write("dos\r\n") + >>> open('mixed', 'wb').write("dos\r\nunix\n") + >>> open('mac', 'wb').write("mac\r") + $ hg add dos mixed mac + + $ fileset 'eol(dos)' + dos + mixed + $ fileset 'eol(unix)' + .hgsub + .hgsubstate + a1 + b1 + b2 + c1 + mixed + $ fileset 'eol(mac)' + mac
--- a/tests/test-graft.t Thu Apr 04 16:28:19 2013 -0500 +++ b/tests/test-graft.t Thu Apr 18 23:46:26 2013 -0500 @@ -84,6 +84,7 @@ # HG changeset patch # User foo # Date 0 0 + # Thu Jan 01 00:00:00 1970 +0000 # Node ID ef0ef43d49e79e81ddafdc7997401ba0041efc82 # Parent 68795b066622ca79a25816a662041d8f78f3cd9e 2 @@ -134,10 +135,10 @@ src: 'a' -> dst: 'b' * checking for directory renames resolving manifests - overwrite: False, partial: False + branchmerge: True, force: True, partial: False ancestor: 68795b066622, local: ef0ef43d49e7+, remote: 5d205f8b35b6 b: local copied/moved to a -> m - preserving b for resolve of b + preserving b for resolve of b updating: b 1/1 files (100.00%) picked tool 'internal:merge' for b (binary False symlink False) merging b and a to b @@ -147,22 +148,22 @@ grafting revision 5 searching for copies back to rev 1 resolving manifests - overwrite: False, partial: False + branchmerge: True, force: True, partial: False ancestor: 4c60f11aa304, local: 6b9e5368ca4e+, remote: 97f8bfe72746 e: remote is newer -> g + getting e updating: e 1/1 files (100.00%) - getting e e grafting revision 4 searching for copies back to rev 1 resolving manifests - overwrite: False, partial: False + branchmerge: True, force: True, partial: False ancestor: 4c60f11aa304, local: 1905859650ec+, remote: 9c233e8e184d d: remote is newer -> g e: versions differ -> m - preserving e for resolve of e + preserving e for resolve of e + getting d updating: d 1/2 files (50.00%) - getting d updating: e 2/2 files (100.00%) picked tool 'internal:merge' for e (binary False symlink False) merging e @@ -324,6 +325,7 @@ # HG changeset patch # User bar # Date 0 0 + # Thu Jan 01 00:00:00 1970 +0000 # Node ID 64ecd9071ce83c6e62f538d8ce7709d53f32ebf7 # Parent 4bdb9a9d0b84ffee1d30f0dfc7744cade17aa19c 1 @@ -351,6 +353,7 @@ # HG changeset patch # User test # Date 0 0 + # Thu Jan 01 00:00:00 1970 +0000 # Node ID 2e80e1351d6ed50302fe1e05f8bd1d4d412b6e11 # Parent e5a51ae854a8bbaaf25cc5c6a57ff46042dadbb4 2
--- a/tests/test-help.t Thu Apr 04 16:28:19 2013 -0500 +++ b/tests/test-help.t Thu Apr 18 23:46:26 2013 -0500 @@ -712,6 +712,7 @@ $ hg help revs Specifying Single Revisions + """"""""""""""""""""""""""" Mercurial supports several ways to specify individual revisions. @@ -857,6 +858,7 @@ no commands defined $ hg help topic-containing-verbose This is the topic to test omit indicating. + """""""""""""""""""""""""""""""""""""""""" This paragraph is never omitted (for topic). @@ -865,6 +867,7 @@ use "hg help -v topic-containing-verbose" to show more complete help $ hg help -v topic-containing-verbose This is the topic to test omit indicating. + """""""""""""""""""""""""""""""""""""""""" This paragraph is never omitted (for topic). @@ -876,3 +879,989 @@ $ cd "$TESTDIR"/../doc $ python check-seclevel.py + $ cd $TESTTMP + +#if serve + +Test the help pages in hgweb. + +Dish up an empty repo; serve it cold. + + $ hg init "$TESTTMP/test" + $ hg serve -R "$TESTTMP/test" -n test -p $HGPORT -d --pid-file=hg.pid + $ cat hg.pid >> $DAEMON_PIDS + + $ "$TESTDIR/get-with-headers.py" 127.0.0.1:$HGPORT "help" + 200 Script output follows + + <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd"> + <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en-US"> + <head> + <link rel="icon" href="/static/hgicon.png" type="image/png" /> + <meta name="robots" content="index, nofollow" /> + <link rel="stylesheet" href="/static/style-paper.css" type="text/css" /> + <script type="text/javascript" src="/static/mercurial.js"></script> + + <title>Help: Index</title> + </head> + <body> + + <div class="container"> + <div class="menu"> + <div class="logo"> + <a href="http://mercurial.selenic.com/"> + <img src="/static/hglogo.png" alt="mercurial" /></a> + </div> + <ul> + <li><a href="/shortlog">log</a></li> + <li><a href="/graph">graph</a></li> + <li><a href="/tags">tags</a></li> + <li><a href="/bookmarks">bookmarks</a></li> + <li><a href="/branches">branches</a></li> + </ul> + <ul> + <li class="active">help</li> + </ul> + </div> + + <div class="main"> + <h2 class="breadcrumb"><a href="/">Mercurial</a> </h2> + <form class="search" action="/log"> + + <p><input name="rev" id="search1" type="text" size="30" /></p> + <div id="hint">find changesets by author, revision, + files, or words in the commit message</div> + </form> + <table class="bigtable"> + <tr><td colspan="2"><h2><a name="main" href="#topics">Topics</a></h2></td></tr> + + <tr><td> + <a href="/help/config"> + config + </a> + </td><td> + Configuration Files + </td></tr> + <tr><td> + <a href="/help/dates"> + dates + </a> + </td><td> + Date Formats + </td></tr> + <tr><td> + <a href="/help/diffs"> + diffs + </a> + </td><td> + Diff Formats + </td></tr> + <tr><td> + <a href="/help/environment"> + environment + </a> + </td><td> + Environment Variables + </td></tr> + <tr><td> + <a href="/help/extensions"> + extensions + </a> + </td><td> + Using Additional Features + </td></tr> + <tr><td> + <a href="/help/filesets"> + filesets + </a> + </td><td> + Specifying File Sets + </td></tr> + <tr><td> + <a href="/help/glossary"> + glossary + </a> + </td><td> + Glossary + </td></tr> + <tr><td> + <a href="/help/hgignore"> + hgignore + </a> + </td><td> + Syntax for Mercurial Ignore Files + </td></tr> + <tr><td> + <a href="/help/hgweb"> + hgweb + </a> + </td><td> + Configuring hgweb + </td></tr> + <tr><td> + <a href="/help/merge-tools"> + merge-tools + </a> + </td><td> + Merge Tools + </td></tr> + <tr><td> + <a href="/help/multirevs"> + multirevs + </a> + </td><td> + Specifying Multiple Revisions + </td></tr> + <tr><td> + <a href="/help/patterns"> + patterns + </a> + </td><td> + File Name Patterns + </td></tr> + <tr><td> + <a href="/help/phases"> + phases + </a> + </td><td> + Working with Phases + </td></tr> + <tr><td> + <a href="/help/revisions"> + revisions + </a> + </td><td> + Specifying Single Revisions + </td></tr> + <tr><td> + <a href="/help/revsets"> + revsets + </a> + </td><td> + Specifying Revision Sets + </td></tr> + <tr><td> + <a href="/help/subrepos"> + subrepos + </a> + </td><td> + Subrepositories + </td></tr> + <tr><td> + <a href="/help/templating"> + templating + </a> + </td><td> + Template Usage + </td></tr> + <tr><td> + <a href="/help/urls"> + urls + </a> + </td><td> + URL Paths + </td></tr> + <tr><td> + <a href="/help/topic-containing-verbose"> + topic-containing-verbose + </a> + </td><td> + This is the topic to test omit indicating. + </td></tr> + + <tr><td colspan="2"><h2><a name="main" href="#main">Main Commands</a></h2></td></tr> + + <tr><td> + <a href="/help/add"> + add + </a> + </td><td> + add the specified files on the next commit + </td></tr> + <tr><td> + <a href="/help/annotate"> + annotate + </a> + </td><td> + show changeset information by line for each file + </td></tr> + <tr><td> + <a href="/help/clone"> + clone + </a> + </td><td> + make a copy of an existing repository + </td></tr> + <tr><td> + <a href="/help/commit"> + commit + </a> + </td><td> + commit the specified files or all outstanding changes + </td></tr> + <tr><td> + <a href="/help/diff"> + diff + </a> + </td><td> + diff repository (or selected files) + </td></tr> + <tr><td> + <a href="/help/export"> + export + </a> + </td><td> + dump the header and diffs for one or more changesets + </td></tr> + <tr><td> + <a href="/help/forget"> + forget + </a> + </td><td> + forget the specified files on the next commit + </td></tr> + <tr><td> + <a href="/help/init"> + init + </a> + </td><td> + create a new repository in the given directory + </td></tr> + <tr><td> + <a href="/help/log"> + log + </a> + </td><td> + show revision history of entire repository or files + </td></tr> + <tr><td> + <a href="/help/merge"> + merge + </a> + </td><td> + merge working directory with another revision + </td></tr> + <tr><td> + <a href="/help/pull"> + pull + </a> + </td><td> + pull changes from the specified source + </td></tr> + <tr><td> + <a href="/help/push"> + push + </a> + </td><td> + push changes to the specified destination + </td></tr> + <tr><td> + <a href="/help/remove"> + remove + </a> + </td><td> + remove the specified files on the next commit + </td></tr> + <tr><td> + <a href="/help/serve"> + serve + </a> + </td><td> + start stand-alone webserver + </td></tr> + <tr><td> + <a href="/help/status"> + status + </a> + </td><td> + show changed files in the working directory + </td></tr> + <tr><td> + <a href="/help/summary"> + summary + </a> + </td><td> + summarize working directory state + </td></tr> + <tr><td> + <a href="/help/update"> + update + </a> + </td><td> + update working directory (or switch revisions) + </td></tr> + + <tr><td colspan="2"><h2><a name="other" href="#other">Other Commands</a></h2></td></tr> + + <tr><td> + <a href="/help/addremove"> + addremove + </a> + </td><td> + add all new files, delete all missing files + </td></tr> + <tr><td> + <a href="/help/archive"> + archive + </a> + </td><td> + create an unversioned archive of a repository revision + </td></tr> + <tr><td> + <a href="/help/backout"> + backout + </a> + </td><td> + reverse effect of earlier changeset + </td></tr> + <tr><td> + <a href="/help/bisect"> + bisect + </a> + </td><td> + subdivision search of changesets + </td></tr> + <tr><td> + <a href="/help/bookmarks"> + bookmarks + </a> + </td><td> + track a line of development with movable markers + </td></tr> + <tr><td> + <a href="/help/branch"> + branch + </a> + </td><td> + set or show the current branch name + </td></tr> + <tr><td> + <a href="/help/branches"> + branches + </a> + </td><td> + list repository named branches + </td></tr> + <tr><td> + <a href="/help/bundle"> + bundle + </a> + </td><td> + create a changegroup file + </td></tr> + <tr><td> + <a href="/help/cat"> + cat + </a> + </td><td> + output the current or given revision of files + </td></tr> + <tr><td> + <a href="/help/copy"> + copy + </a> + </td><td> + mark files as copied for the next commit + </td></tr> + <tr><td> + <a href="/help/graft"> + graft + </a> + </td><td> + copy changes from other branches onto the current branch + </td></tr> + <tr><td> + <a href="/help/grep"> + grep + </a> + </td><td> + search for a pattern in specified files and revisions + </td></tr> + <tr><td> + <a href="/help/heads"> + heads + </a> + </td><td> + show current repository heads or show branch heads + </td></tr> + <tr><td> + <a href="/help/help"> + help + </a> + </td><td> + show help for a given topic or a help overview + </td></tr> + <tr><td> + <a href="/help/identify"> + identify + </a> + </td><td> + identify the working copy or specified revision + </td></tr> + <tr><td> + <a href="/help/import"> + import + </a> + </td><td> + import an ordered set of patches + </td></tr> + <tr><td> + <a href="/help/incoming"> + incoming + </a> + </td><td> + show new changesets found in source + </td></tr> + <tr><td> + <a href="/help/locate"> + locate + </a> + </td><td> + locate files matching specific patterns + </td></tr> + <tr><td> + <a href="/help/manifest"> + manifest + </a> + </td><td> + output the current or given revision of the project manifest + </td></tr> + <tr><td> + <a href="/help/nohelp"> + nohelp + </a> + </td><td> + (no help text available) + </td></tr> + <tr><td> + <a href="/help/outgoing"> + outgoing + </a> + </td><td> + show changesets not found in the destination + </td></tr> + <tr><td> + <a href="/help/parents"> + parents + </a> + </td><td> + show the parents of the working directory or revision + </td></tr> + <tr><td> + <a href="/help/paths"> + paths + </a> + </td><td> + show aliases for remote repositories + </td></tr> + <tr><td> + <a href="/help/phase"> + phase + </a> + </td><td> + set or show the current phase name + </td></tr> + <tr><td> + <a href="/help/recover"> + recover + </a> + </td><td> + roll back an interrupted transaction + </td></tr> + <tr><td> + <a href="/help/rename"> + rename + </a> + </td><td> + rename files; equivalent of copy + remove + </td></tr> + <tr><td> + <a href="/help/resolve"> + resolve + </a> + </td><td> + redo merges or set/view the merge status of files + </td></tr> + <tr><td> + <a href="/help/revert"> + revert + </a> + </td><td> + restore files to their checkout state + </td></tr> + <tr><td> + <a href="/help/rollback"> + rollback + </a> + </td><td> + roll back the last transaction (dangerous) + </td></tr> + <tr><td> + <a href="/help/root"> + root + </a> + </td><td> + print the root (top) of the current working directory + </td></tr> + <tr><td> + <a href="/help/showconfig"> + showconfig + </a> + </td><td> + show combined config settings from all hgrc files + </td></tr> + <tr><td> + <a href="/help/tag"> + tag + </a> + </td><td> + add one or more tags for the current or given revision + </td></tr> + <tr><td> + <a href="/help/tags"> + tags + </a> + </td><td> + list repository tags + </td></tr> + <tr><td> + <a href="/help/tip"> + tip + </a> + </td><td> + show the tip revision + </td></tr> + <tr><td> + <a href="/help/unbundle"> + unbundle + </a> + </td><td> + apply one or more changegroup files + </td></tr> + <tr><td> + <a href="/help/verify"> + verify + </a> + </td><td> + verify the integrity of the repository + </td></tr> + <tr><td> + <a href="/help/version"> + version + </a> + </td><td> + output version and copyright information + </td></tr> + </table> + </div> + </div> + + <script type="text/javascript">process_dates()</script> + + + </body> + </html> + + + $ "$TESTDIR/get-with-headers.py" 127.0.0.1:$HGPORT "help/add" + 200 Script output follows + + <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd"> + <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en-US"> + <head> + <link rel="icon" href="/static/hgicon.png" type="image/png" /> + <meta name="robots" content="index, nofollow" /> + <link rel="stylesheet" href="/static/style-paper.css" type="text/css" /> + <script type="text/javascript" src="/static/mercurial.js"></script> + + <title>Help: add</title> + </head> + <body> + + <div class="container"> + <div class="menu"> + <div class="logo"> + <a href="http://mercurial.selenic.com/"> + <img src="/static/hglogo.png" alt="mercurial" /></a> + </div> + <ul> + <li><a href="/shortlog">log</a></li> + <li><a href="/graph">graph</a></li> + <li><a href="/tags">tags</a></li> + <li><a href="/bookmarks">bookmarks</a></li> + <li><a href="/branches">branches</a></li> + </ul> + <ul> + <li class="active"><a href="/help">help</a></li> + </ul> + </div> + + <div class="main"> + <h2 class="breadcrumb"><a href="/">Mercurial</a> </h2> + <h3>Help: add</h3> + + <form class="search" action="/log"> + + <p><input name="rev" id="search1" type="text" size="30" /></p> + <div id="hint">find changesets by author, revision, + files, or words in the commit message</div> + </form> + <div id="doc"> + <p> + hg add [OPTION]... [FILE]... + </p> + <p> + add the specified files on the next commit + </p> + <p> + Schedule files to be version controlled and added to the + repository. + </p> + <p> + The files will be added to the repository at the next commit. To + undo an add before that, see "hg forget". + </p> + <p> + If no names are given, add all files to the repository. + </p> + <p> + Returns 0 if all files are successfully added. + </p> + <p> + options: + </p> + <table> + <tr><td>-I</td> + <td>--include PATTERN [+]</td> + <td>include names matching the given patterns</td></tr> + <tr><td>-X</td> + <td>--exclude PATTERN [+]</td> + <td>exclude names matching the given patterns</td></tr> + <tr><td>-S</td> + <td>--subrepos</td> + <td>recurse into subrepositories</td></tr> + <tr><td>-n</td> + <td>--dry-run</td> + <td>do not perform actions, just print output</td></tr> + </table> + <p> + [+] marked option can be specified multiple times + </p> + <p> + global options: + </p> + <table> + <tr><td>-R</td> + <td>--repository REPO</td> + <td>repository root directory or name of overlay bundle file</td></tr> + <tr><td></td> + <td>--cwd DIR</td> + <td>change working directory</td></tr> + <tr><td>-y</td> + <td>--noninteractive</td> + <td>do not prompt, automatically pick the first choice for all prompts</td></tr> + <tr><td>-q</td> + <td>--quiet</td> + <td>suppress output</td></tr> + <tr><td>-v</td> + <td>--verbose</td> + <td>enable additional output</td></tr> + <tr><td></td> + <td>--config CONFIG [+]</td> + <td>set/override config option (use 'section.name=value')</td></tr> + <tr><td></td> + <td>--debug</td> + <td>enable debugging output</td></tr> + <tr><td></td> + <td>--debugger</td> + <td>start debugger</td></tr> + <tr><td></td> + <td>--encoding ENCODE</td> + <td>set the charset encoding (default: ascii)</td></tr> + <tr><td></td> + <td>--encodingmode MODE</td> + <td>set the charset encoding mode (default: strict)</td></tr> + <tr><td></td> + <td>--traceback</td> + <td>always print a traceback on exception</td></tr> + <tr><td></td> + <td>--time</td> + <td>time how long the command takes</td></tr> + <tr><td></td> + <td>--profile</td> + <td>print command execution profile</td></tr> + <tr><td></td> + <td>--version</td> + <td>output version information and exit</td></tr> + <tr><td>-h</td> + <td>--help</td> + <td>display help and exit</td></tr> + <tr><td></td> + <td>--hidden</td> + <td>consider hidden changesets</td></tr> + </table> + <p> + [+] marked option can be specified multiple times + </p> + + </div> + </div> + </div> + + <script type="text/javascript">process_dates()</script> + + + </body> + </html> + + + $ "$TESTDIR/get-with-headers.py" 127.0.0.1:$HGPORT "help/remove" + 200 Script output follows + + <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd"> + <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en-US"> + <head> + <link rel="icon" href="/static/hgicon.png" type="image/png" /> + <meta name="robots" content="index, nofollow" /> + <link rel="stylesheet" href="/static/style-paper.css" type="text/css" /> + <script type="text/javascript" src="/static/mercurial.js"></script> + + <title>Help: remove</title> + </head> + <body> + + <div class="container"> + <div class="menu"> + <div class="logo"> + <a href="http://mercurial.selenic.com/"> + <img src="/static/hglogo.png" alt="mercurial" /></a> + </div> + <ul> + <li><a href="/shortlog">log</a></li> + <li><a href="/graph">graph</a></li> + <li><a href="/tags">tags</a></li> + <li><a href="/bookmarks">bookmarks</a></li> + <li><a href="/branches">branches</a></li> + </ul> + <ul> + <li class="active"><a href="/help">help</a></li> + </ul> + </div> + + <div class="main"> + <h2 class="breadcrumb"><a href="/">Mercurial</a> </h2> + <h3>Help: remove</h3> + + <form class="search" action="/log"> + + <p><input name="rev" id="search1" type="text" size="30" /></p> + <div id="hint">find changesets by author, revision, + files, or words in the commit message</div> + </form> + <div id="doc"> + <p> + hg remove [OPTION]... FILE... + </p> + <p> + aliases: rm + </p> + <p> + remove the specified files on the next commit + </p> + <p> + Schedule the indicated files for removal from the current branch. + </p> + <p> + This command schedules the files to be removed at the next commit. + To undo a remove before that, see "hg revert". To undo added + files, see "hg forget". + </p> + <p> + Returns 0 on success, 1 if any warnings encountered. + </p> + <p> + options: + </p> + <table> + <tr><td>-A</td> + <td>--after</td> + <td>record delete for missing files</td></tr> + <tr><td>-f</td> + <td>--force</td> + <td>remove (and delete) file even if added or modified</td></tr> + <tr><td>-I</td> + <td>--include PATTERN [+]</td> + <td>include names matching the given patterns</td></tr> + <tr><td>-X</td> + <td>--exclude PATTERN [+]</td> + <td>exclude names matching the given patterns</td></tr> + </table> + <p> + [+] marked option can be specified multiple times + </p> + <p> + global options: + </p> + <table> + <tr><td>-R</td> + <td>--repository REPO</td> + <td>repository root directory or name of overlay bundle file</td></tr> + <tr><td></td> + <td>--cwd DIR</td> + <td>change working directory</td></tr> + <tr><td>-y</td> + <td>--noninteractive</td> + <td>do not prompt, automatically pick the first choice for all prompts</td></tr> + <tr><td>-q</td> + <td>--quiet</td> + <td>suppress output</td></tr> + <tr><td>-v</td> + <td>--verbose</td> + <td>enable additional output</td></tr> + <tr><td></td> + <td>--config CONFIG [+]</td> + <td>set/override config option (use 'section.name=value')</td></tr> + <tr><td></td> + <td>--debug</td> + <td>enable debugging output</td></tr> + <tr><td></td> + <td>--debugger</td> + <td>start debugger</td></tr> + <tr><td></td> + <td>--encoding ENCODE</td> + <td>set the charset encoding (default: ascii)</td></tr> + <tr><td></td> + <td>--encodingmode MODE</td> + <td>set the charset encoding mode (default: strict)</td></tr> + <tr><td></td> + <td>--traceback</td> + <td>always print a traceback on exception</td></tr> + <tr><td></td> + <td>--time</td> + <td>time how long the command takes</td></tr> + <tr><td></td> + <td>--profile</td> + <td>print command execution profile</td></tr> + <tr><td></td> + <td>--version</td> + <td>output version information and exit</td></tr> + <tr><td>-h</td> + <td>--help</td> + <td>display help and exit</td></tr> + <tr><td></td> + <td>--hidden</td> + <td>consider hidden changesets</td></tr> + </table> + <p> + [+] marked option can be specified multiple times + </p> + + </div> + </div> + </div> + + <script type="text/javascript">process_dates()</script> + + + </body> + </html> + + + $ "$TESTDIR/get-with-headers.py" 127.0.0.1:$HGPORT "help/revisions" + 200 Script output follows + + <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd"> + <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en-US"> + <head> + <link rel="icon" href="/static/hgicon.png" type="image/png" /> + <meta name="robots" content="index, nofollow" /> + <link rel="stylesheet" href="/static/style-paper.css" type="text/css" /> + <script type="text/javascript" src="/static/mercurial.js"></script> + + <title>Help: revisions</title> + </head> + <body> + + <div class="container"> + <div class="menu"> + <div class="logo"> + <a href="http://mercurial.selenic.com/"> + <img src="/static/hglogo.png" alt="mercurial" /></a> + </div> + <ul> + <li><a href="/shortlog">log</a></li> + <li><a href="/graph">graph</a></li> + <li><a href="/tags">tags</a></li> + <li><a href="/bookmarks">bookmarks</a></li> + <li><a href="/branches">branches</a></li> + </ul> + <ul> + <li class="active"><a href="/help">help</a></li> + </ul> + </div> + + <div class="main"> + <h2 class="breadcrumb"><a href="/">Mercurial</a> </h2> + <h3>Help: revisions</h3> + + <form class="search" action="/log"> + + <p><input name="rev" id="search1" type="text" size="30" /></p> + <div id="hint">find changesets by author, revision, + files, or words in the commit message</div> + </form> + <div id="doc"> + <h1>Specifying Single Revisions</h1> + <p> + Mercurial supports several ways to specify individual revisions. + </p> + <p> + A plain integer is treated as a revision number. Negative integers are + treated as sequential offsets from the tip, with -1 denoting the tip, + -2 denoting the revision prior to the tip, and so forth. + </p> + <p> + A 40-digit hexadecimal string is treated as a unique revision + identifier. + </p> + <p> + A hexadecimal string less than 40 characters long is treated as a + unique revision identifier and is referred to as a short-form + identifier. A short-form identifier is only valid if it is the prefix + of exactly one full-length identifier. + </p> + <p> + Any other string is treated as a bookmark, tag, or branch name. A + bookmark is a movable pointer to a revision. A tag is a permanent name + associated with a revision. A branch name denotes the tipmost revision + of that branch. Bookmark, tag, and branch names must not contain the ":" + character. + </p> + <p> + The reserved name "tip" always identifies the most recent revision. + </p> + <p> + The reserved name "null" indicates the null revision. This is the + revision of an empty repository, and the parent of revision 0. + </p> + <p> + The reserved name "." indicates the working directory parent. If no + working directory is checked out, it is equivalent to null. If an + uncommitted merge is in progress, "." is the revision of the first + parent. + </p> + + </div> + </div> + </div> + + <script type="text/javascript">process_dates()</script> + + + </body> + </html> + + + $ "$TESTDIR/killdaemons.py" $DAEMON_PIDS + +#endif
--- a/tests/test-hgk.t Thu Apr 04 16:28:19 2013 -0500 +++ b/tests/test-hgk.t Thu Apr 18 23:46:26 2013 -0500 @@ -13,6 +13,7 @@ author test 0 0 revision 0 branch default + phase draft adda
--- a/tests/test-hgweb-no-path-info.t Thu Apr 04 16:28:19 2013 -0500 +++ b/tests/test-hgweb-no-path-info.t Thu Apr 18 23:46:26 2013 -0500 @@ -39,6 +39,7 @@ > 'wsgi.multiprocess': False, > 'wsgi.run_once': False, > 'REQUEST_METHOD': 'GET', + > 'PATH_INFO': '/', > 'SCRIPT_NAME': '', > 'SERVER_NAME': '127.0.0.1', > 'SERVER_PORT': os.environ['HGPORT'], @@ -49,6 +50,7 @@ > content = app(env, startrsp) > sys.stdout.write(output.getvalue()) > sys.stdout.write(''.join(content)) + > getattr(content, 'close', lambda : None)() > print '---- ERRORS' > print errors.getvalue() > @@ -101,7 +103,7 @@ [('Content-Type', 'text/plain; charset=ascii')] ---- DATA - repo/ + /repo/ ---- ERRORS
--- a/tests/test-hgweb-no-request-uri.t Thu Apr 04 16:28:19 2013 -0500 +++ b/tests/test-hgweb-no-request-uri.t Thu Apr 18 23:46:26 2013 -0500 @@ -49,6 +49,7 @@ > content = app(env, startrsp) > sys.stdout.write(output.getvalue()) > sys.stdout.write(''.join(content)) + > getattr(content, 'close', lambda : None)() > print '---- ERRORS' > print errors.getvalue() >
--- a/tests/test-hgweb-raw.t Thu Apr 04 16:28:19 2013 -0500 +++ b/tests/test-hgweb-raw.t Thu Apr 18 23:46:26 2013 -0500 @@ -19,7 +19,7 @@ $ cat hg.pid >> $DAEMON_PIDS $ ("$TESTDIR/get-with-headers.py" localhost:$HGPORT '?f=bf0ff59095c9;file=sub/some%20text%25.txt;style=raw' content-type content-length content-disposition) >getoutput.txt - $ while kill `cat hg.pid` 2>/dev/null; do sleep 0; done + $ "$TESTDIR/killdaemons.py" hg.pid $ cat getoutput.txt 200 Script output follows @@ -40,7 +40,7 @@ $ cat hg.pid >> $DAEMON_PIDS $ ("$TESTDIR/get-with-headers.py" localhost:$HGPORT '?f=bf0ff59095c9;file=sub/some%20text%25.txt;style=raw' content-type content-length content-disposition) >getoutput.txt - $ while kill `cat hg.pid` 2>/dev/null; do sleep 0; done + $ "$TESTDIR/killdaemons.py" hg.pid $ cat getoutput.txt 200 Script output follows
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tests/test-histedit-arguments.t Thu Apr 18 23:46:26 2013 -0500 @@ -0,0 +1,169 @@ +Test argument handling and various data parsing +================================================== + + +Enable extensions used by this test. + $ cat >>$HGRCPATH <<EOF + > [extensions] + > histedit= + > EOF + +Repo setup. + $ hg init foo + $ cd foo + $ echo alpha >> alpha + $ hg addr + adding alpha + $ hg ci -m one + $ echo alpha >> alpha + $ hg ci -m two + $ echo alpha >> alpha + $ hg ci -m three + $ echo alpha >> alpha + $ hg ci -m four + $ echo alpha >> alpha + $ hg ci -m five + + $ hg log --style compact --graph + @ 4[tip] 08d98a8350f3 1970-01-01 00:00 +0000 test + | five + | + o 3 c8e68270e35a 1970-01-01 00:00 +0000 test + | four + | + o 2 eb57da33312f 1970-01-01 00:00 +0000 test + | three + | + o 1 579e40513370 1970-01-01 00:00 +0000 test + | two + | + o 0 6058cbb6cfd7 1970-01-01 00:00 +0000 test + one + + +Run a dummy edit to make sure we get tip^^ correctly via revsingle. +-------------------------------------------------------------------- + + $ HGEDITOR=cat hg histedit "tip^^" + pick eb57da33312f 2 three + pick c8e68270e35a 3 four + pick 08d98a8350f3 4 five + + # Edit history between eb57da33312f and 08d98a8350f3 + # + # Commands: + # p, pick = use commit + # e, edit = use commit, but stop for amending + # f, fold = use commit, but fold into previous commit (combines N and N-1) + # d, drop = remove commit from history + # m, mess = edit message without changing commit content + # + 0 files updated, 0 files merged, 0 files removed, 0 files unresolved + +Run on a revision not ancestors of the current working directory. +-------------------------------------------------------------------- + + $ hg up 2 + 1 files updated, 0 files merged, 0 files removed, 0 files unresolved + $ hg histedit -r 4 + abort: 08d98a8350f3 is not an ancestor of working directory + [255] + $ hg up --quiet + +Test that missing revisions are detected +--------------------------------------- + + $ HGEDITOR=cat hg histedit "tip^^" --commands - << EOF + > pick eb57da33312f 2 three + > pick 08d98a8350f3 4 five + > EOF + abort: missing rules for changeset c8e68270e35a + (do you want to use the drop action?) + [255] + +Test that extra revisions are detected +--------------------------------------- + + $ HGEDITOR=cat hg histedit "tip^^" --commands - << EOF + > pick 6058cbb6cfd7 0 one + > pick c8e68270e35a 3 four + > pick 08d98a8350f3 4 five + > EOF + abort: may not use changesets other than the ones listed + [255] + +Test malformed line +--------------------------------------- + + $ HGEDITOR=cat hg histedit "tip^^" --commands - << EOF + > pickeb57da33312f2three + > pick c8e68270e35a 3 four + > pick 08d98a8350f3 4 five + > EOF + abort: malformed line "pickeb57da33312f2three" + [255] + +Test unknown changeset +--------------------------------------- + + $ HGEDITOR=cat hg histedit "tip^^" --commands - << EOF + > pick 0123456789ab 2 three + > pick c8e68270e35a 3 four + > pick 08d98a8350f3 4 five + > EOF + abort: unknown changeset 0123456789ab listed + [255] + +Test unknown command +--------------------------------------- + + $ HGEDITOR=cat hg histedit "tip^^" --commands - << EOF + > coin eb57da33312f 2 three + > pick c8e68270e35a 3 four + > pick 08d98a8350f3 4 five + > EOF + abort: unknown action "coin" + [255] + +Test duplicated changeset +--------------------------------------- + +So one is missing and one appear twice. + + $ HGEDITOR=cat hg histedit "tip^^" --commands - << EOF + > pick eb57da33312f 2 three + > pick eb57da33312f 2 three + > pick 08d98a8350f3 4 five + > EOF + abort: duplicated command for changeset eb57da33312f + [255] + +Test short version of command +--------------------------------------- + +Note: we use varying amounts of white space between command name and changeset +short hash. This tests issue3893. + + $ HGEDITOR=cat hg histedit "tip^^" --commands - << EOF + > pick eb57da33312f 2 three + > p c8e68270e35a 3 four + > f 08d98a8350f3 4 five + > EOF + 1 files updated, 0 files merged, 0 files removed, 0 files unresolved + reverting alpha + 1 files updated, 0 files merged, 0 files removed, 0 files unresolved + four + *** + five + + + + HG: Enter commit message. Lines beginning with 'HG:' are removed. + HG: Leave message empty to abort commit. + HG: -- + HG: user: test + HG: branch 'default' + HG: changed alpha + 1 files updated, 0 files merged, 0 files removed, 0 files unresolved + 0 files updated, 0 files merged, 0 files removed, 0 files unresolved + saved backup bundle to $TESTTMP/foo/.hg/strip-backup/*-backup.hg (glob)
--- a/tests/test-histedit-bookmark-motion.t Thu Apr 04 16:28:19 2013 -0500 +++ b/tests/test-histedit-bookmark-motion.t Thu Apr 18 23:46:26 2013 -0500 @@ -76,14 +76,13 @@ # m, mess = edit message without changing commit content # 0 files updated, 0 files merged, 0 files removed, 0 files unresolved - $ cat >> commands.txt <<EOF + $ hg histedit 1 --commands - --verbose << EOF | grep histedit > pick 177f92b77385 2 c > drop d2ae7f538514 1 b > pick 055a42cdd887 3 d > fold e860deea161a 4 e > pick 652413bf663e 5 f > EOF - $ hg histedit 1 --commands commands.txt --verbose | grep histedit histedit: moving bookmarks also-two from 177f92b77385 to b346ab9a313d histedit: moving bookmarks five from 652413bf663e to cacdfd884a93 histedit: moving bookmarks four from e860deea161a to 59d9f330561f @@ -135,12 +134,11 @@ # m, mess = edit message without changing commit content # 0 files updated, 0 files merged, 0 files removed, 0 files unresolved - $ cat > commands.txt << EOF + $ hg histedit 1 --commands - --verbose << EOF | grep histedit > pick b346ab9a313d 1 c > pick cacdfd884a93 3 f > pick 59d9f330561f 2 d > EOF - $ hg histedit 1 --commands commands.txt --verbose | grep histedit histedit: moving bookmarks five from cacdfd884a93 to c04e50810e4b histedit: moving bookmarks four from 59d9f330561f to c04e50810e4b histedit: moving bookmarks three from 59d9f330561f to c04e50810e4b
--- a/tests/test-histedit-commute.t Thu Apr 04 16:28:19 2013 -0500 +++ b/tests/test-histedit-commute.t Thu Apr 18 23:46:26 2013 -0500 @@ -6,13 +6,6 @@ > histedit= > EOF - $ EDITED="$TESTTMP/editedhistory" - $ cat > $EDITED <<EOF - > pick 177f92b77385 c - > pick e860deea161a e - > pick 652413bf663e f - > pick 055a42cdd887 d - > EOF $ initrepo () > { > hg init r @@ -79,6 +72,15 @@ 0 files updated, 0 files merged, 0 files removed, 0 files unresolved edit the history +(use a hacky editor to check histedit-last-edit.txt backup) + + $ EDITED="$TESTTMP/editedhistory" + $ cat > $EDITED <<EOF + > pick 177f92b77385 c + > pick e860deea161a e + > pick 652413bf663e f + > pick 055a42cdd887 d + > EOF $ HGEDITOR="cat \"$EDITED\" > " hg histedit 177f92b77385 2>&1 | fixbundle 0 files updated, 0 files merged, 3 files removed, 0 files unresolved 0 files updated, 0 files merged, 0 files removed, 0 files unresolved @@ -128,13 +130,12 @@ put things back - $ cat > $EDITED <<EOF + $ hg histedit 177f92b77385 --commands - 2>&1 << EOF | fixbundle > pick 177f92b77385 c > pick 07114f51870f d > pick d8249471110a e > pick 8ade9693061e f > EOF - $ HGEDITOR="cat \"$EDITED\" > " hg histedit 177f92b77385 2>&1 | fixbundle 0 files updated, 0 files merged, 3 files removed, 0 files unresolved 0 files updated, 0 files merged, 0 files removed, 0 files unresolved 0 files updated, 0 files merged, 0 files removed, 0 files unresolved @@ -175,13 +176,12 @@ slightly different this time - $ cat > $EDITED <<EOF + $ hg histedit 177f92b77385 --commands - << EOF 2>&1 | fixbundle > pick 10517e47bbbb d > pick 7eca9b5b1148 f > pick 915da888f2de e > pick 177f92b77385 c > EOF - $ HGEDITOR="cat \"$EDITED\" > " hg histedit 177f92b77385 2>&1 | fixbundle 0 files updated, 0 files merged, 4 files removed, 0 files unresolved 0 files updated, 0 files merged, 0 files removed, 0 files unresolved 0 files updated, 0 files merged, 0 files removed, 0 files unresolved @@ -221,21 +221,16 @@ keep prevents stripping dead revs - $ cat > $EDITED <<EOF + $ hg histedit 799205341b6b --keep --commands - 2>&1 << EOF | fixbundle > pick 799205341b6b d > pick be9ae3a309c6 f > pick 38b92f448761 c > pick de71b079d9ce e > EOF - $ HGEDITOR="cat \"$EDITED\" > " hg histedit 799205341b6b --keep 2>&1 | fixbundle 0 files updated, 0 files merged, 2 files removed, 0 files unresolved 0 files updated, 0 files merged, 0 files removed, 0 files unresolved 0 files updated, 0 files merged, 0 files removed, 0 files unresolved $ hg log --graph - > cat > $EDITED <<EOF - > pick de71b079d9ce e - > pick 38b92f448761 c - > EOF @ changeset: 7:803ef1c6fcfd | tag: tip | user: test @@ -280,7 +275,10 @@ try with --rev - $ hg histedit --commands "$EDITED" --rev -2 2>&1 | fixbundle + $ hg histedit --commands - --rev -2 2>&1 <<EOF | fixbundle + > pick de71b079d9ce e + > pick 38b92f448761 c + > EOF abort: may not use changesets other than the ones listed $ hg log --graph @ changeset: 7:803ef1c6fcfd
--- a/tests/test-histedit-drop.t Thu Apr 04 16:28:19 2013 -0500 +++ b/tests/test-histedit-drop.t Thu Apr 18 23:46:26 2013 -0500 @@ -6,13 +6,6 @@ > histedit= > EOF - $ EDITED="$TESTTMP/editedhistory" - $ cat > $EDITED <<EOF - > drop 177f92b77385 c - > pick e860deea161a e - > pick 652413bf663e f - > pick 055a42cdd887 d - > EOF $ initrepo () > { > hg init r @@ -61,7 +54,12 @@ edit the history - $ HGEDITOR="cat \"$EDITED\" > " hg histedit 177f92b77385 2>&1 | fixbundle + $ hg histedit 177f92b77385 --commands - 2>&1 << EOF | fixbundle + > drop 177f92b77385 c + > pick e860deea161a e + > pick 652413bf663e f + > pick 055a42cdd887 d + > EOF 0 files updated, 0 files merged, 4 files removed, 0 files unresolved 0 files updated, 0 files merged, 0 files removed, 0 files unresolved 0 files updated, 0 files merged, 0 files removed, 0 files unresolved @@ -125,12 +123,11 @@ Drop the last changeset - $ cat > $EDITED <<EOF + $ hg histedit ee283cb5f2d5 --commands - 2>&1 << EOF | fixbundle > pick ee283cb5f2d5 e > pick a4f7421b80f7 f > drop f518305ce889 d > EOF - $ HGEDITOR="cat \"$EDITED\" > " hg histedit ee283cb5f2d5 2>&1 | fixbundle 0 files updated, 0 files merged, 1 files removed, 0 files unresolved $ hg log --graph @ changeset: 3:a4f7421b80f7
--- a/tests/test-histedit-edit.t Thu Apr 04 16:28:19 2013 -0500 +++ b/tests/test-histedit-edit.t Thu Apr 18 23:46:26 2013 -0500 @@ -6,13 +6,6 @@ > histedit= > EOF - $ EDITED="$TESTTMP/editedhistory" - $ cat > $EDITED <<EOF - > pick 177f92b77385 c - > pick 055a42cdd887 d - > edit e860deea161a e - > pick 652413bf663e f - > EOF $ initrepo () > { > hg init r @@ -61,9 +54,14 @@ edit the history - $ HGEDITOR="cat \"$EDITED\" > " hg histedit 177f92b77385 2>&1 | fixbundle + $ hg histedit 177f92b77385 --commands - 2>&1 << EOF| fixbundle + > pick 177f92b77385 c + > pick 055a42cdd887 d + > edit e860deea161a e + > pick 652413bf663e f + > EOF 0 files updated, 0 files merged, 2 files removed, 0 files unresolved - abort: Make changes as needed, you may commit or record as needed now. + Make changes as needed, you may commit or record as needed now. When you are finished, run hg histedit --continue to resume. Go at a random point and try to continue @@ -73,7 +71,7 @@ $ hg up 0 0 files updated, 0 files merged, 3 files removed, 0 files unresolved $ HGEDITOR='echo foobaz > ' hg histedit --continue - abort: working directory parent is not a descendant of 055a42cdd887 + abort: 055a42cdd887 is not an ancestor of working directory (update to 055a42cdd887 or descendant and run "hg histedit --continue" again) [255] $ hg up 3 @@ -146,12 +144,11 @@ - $ cat > $EDITED <<EOF + $ hg histedit tip --commands - 2>&1 <<EOF| fixbundle > edit b5f70786f9b0 f > EOF - $ HGEDITOR="cat \"$EDITED\" > " hg histedit tip 2>&1 | fixbundle 0 files updated, 0 files merged, 1 files removed, 0 files unresolved - abort: Make changes as needed, you may commit or record as needed now. + Make changes as needed, you may commit or record as needed now. When you are finished, run hg histedit --continue to resume. $ hg status A f @@ -188,19 +185,18 @@ modify the message - $ cat > $EDITED <<EOF + $ hg histedit tip --commands - 2>&1 << EOF | fixbundle > mess 1fd3b2fe7754 f > EOF - $ HGEDITOR="cat \"$EDITED\" > " hg histedit tip 2>&1 | fixbundle 0 files updated, 0 files merged, 1 files removed, 0 files unresolved 0 files updated, 0 files merged, 0 files removed, 0 files unresolved $ hg status $ hg log --limit 1 - changeset: 6:5585e802ef99 + changeset: 6:62feedb1200e tag: tip user: test date: Thu Jan 01 00:00:00 1970 +0000 - summary: mess 1fd3b2fe7754 f + summary: f rollback should not work after a histedit @@ -209,3 +205,17 @@ [1] $ cd .. + $ hg clone -qr0 r r0 + $ cd r0 + $ hg phase -fdr0 + $ hg histedit --commands - 0 2>&1 << EOF + > edit cb9a9f314b8b a > $EDITED + > EOF + 0 files updated, 0 files merged, 1 files removed, 0 files unresolved + adding a + Make changes as needed, you may commit or record as needed now. + When you are finished, run hg histedit --continue to resume. + [1] + $ HGEDITOR=true hg histedit --continue + 0 files updated, 0 files merged, 0 files removed, 0 files unresolved + saved backup bundle to $TESTTMP/r0/.hg/strip-backup/cb9a9f314b8b-backup.hg (glob)
--- a/tests/test-histedit-fold-non-commute.t Thu Apr 04 16:28:19 2013 -0500 +++ b/tests/test-histedit-fold-non-commute.t Thu Apr 18 23:46:26 2013 -0500 @@ -86,12 +86,12 @@ edit the history - $ HGEDITOR="cat \"$EDITED\" > " hg histedit 3 2>&1 | fixbundle + $ hg histedit 3 --commands $EDITED 2>&1 | fixbundle 2 files updated, 0 files merged, 0 files removed, 0 files unresolved merging e warning: conflicts during merge. merging e incomplete! (edit conflicts, then use 'hg resolve --mark') - abort: Fix up the change and run hg histedit --continue + Fix up the change and run hg histedit --continue fix up $ echo 'I can haz no commute' > e @@ -125,7 +125,7 @@ merging e warning: conflicts during merge. merging e incomplete! (edit conflicts, then use 'hg resolve --mark') - abort: Fix up the change and run hg histedit --continue + Fix up the change and run hg histedit --continue just continue this time $ hg revert -r 'p1()' e
--- a/tests/test-histedit-fold.t Thu Apr 04 16:28:19 2013 -0500 +++ b/tests/test-histedit-fold.t Thu Apr 18 23:46:26 2013 -0500 @@ -1,18 +1,26 @@ +Test histedit extention: Fold commands +====================================== + +This test file is dedicated to testing the fold command in non conflicting +case. + +Initialization +--------------- + + $ . "$TESTDIR/histedit-helpers.sh" $ cat >> $HGRCPATH <<EOF + > [alias] + > logt = log --template '{rev}:{node|short} {desc|firstline}\n' > [extensions] > graphlog= > histedit= > EOF - $ EDITED="$TESTTMP/editedhistory" - $ cat > $EDITED <<EOF - > pick e860deea161a e - > pick 652413bf663e f - > fold 177f92b77385 c - > pick 055a42cdd887 d - > EOF + +Simple folding +-------------------- $ initrepo () > { > hg init r @@ -27,41 +35,26 @@ $ initrepo log before edit - $ hg log --graph - @ changeset: 5:652413bf663e - | tag: tip - | user: test - | date: Thu Jan 01 00:00:00 1970 +0000 - | summary: f + $ hg logt --graph + @ 5:652413bf663e f | - o changeset: 4:e860deea161a - | user: test - | date: Thu Jan 01 00:00:00 1970 +0000 - | summary: e + o 4:e860deea161a e | - o changeset: 3:055a42cdd887 - | user: test - | date: Thu Jan 01 00:00:00 1970 +0000 - | summary: d + o 3:055a42cdd887 d | - o changeset: 2:177f92b77385 - | user: test - | date: Thu Jan 01 00:00:00 1970 +0000 - | summary: c + o 2:177f92b77385 c | - o changeset: 1:d2ae7f538514 - | user: test - | date: Thu Jan 01 00:00:00 1970 +0000 - | summary: b + o 1:d2ae7f538514 b | - o changeset: 0:cb9a9f314b8b - user: test - date: Thu Jan 01 00:00:00 1970 +0000 - summary: a + o 0:cb9a9f314b8b a -edit the history - $ HGEDITOR="cat \"$EDITED\" > " hg histedit 177f92b77385 2>&1 | fixbundle + $ hg histedit 177f92b77385 --commands - 2>&1 <<EOF | fixbundle + > pick e860deea161a e + > pick 652413bf663e f + > fold 177f92b77385 c + > pick 055a42cdd887 d + > EOF 0 files updated, 0 files merged, 4 files removed, 0 files unresolved 0 files updated, 0 files merged, 0 files removed, 0 files unresolved 0 files updated, 0 files merged, 0 files removed, 0 files unresolved @@ -71,32 +64,16 @@ 0 files updated, 0 files merged, 0 files removed, 0 files unresolved log after edit - $ hg log --graph - @ changeset: 4:7e0a290363ed - | tag: tip - | user: test - | date: Thu Jan 01 00:00:00 1970 +0000 - | summary: d + $ hg logt --graph + @ 4:9c277da72c9b d | - o changeset: 3:5e24935bad3d - | user: test - | date: Thu Jan 01 00:00:00 1970 +0000 - | summary: pick e860deea161a e + o 3:6de59d13424a f | - o changeset: 2:ee283cb5f2d5 - | user: test - | date: Thu Jan 01 00:00:00 1970 +0000 - | summary: e + o 2:ee283cb5f2d5 e | - o changeset: 1:d2ae7f538514 - | user: test - | date: Thu Jan 01 00:00:00 1970 +0000 - | summary: b + o 1:d2ae7f538514 b | - o changeset: 0:cb9a9f314b8b - user: test - date: Thu Jan 01 00:00:00 1970 +0000 - summary: a + o 0:cb9a9f314b8b a post-fold manifest @@ -112,7 +89,7 @@ check histedit_source $ hg log --debug --rev 3 - changeset: 3:5e24935bad3d5a4486de3b90f233e991465ced72 + changeset: 3:6de59d13424a8a13acd3e975514aed29dd0d9b2d phase: draft parent: 2:ee283cb5f2d5955443f23a27b697a04339e9a39a parent: -1:0000000000000000000000000000000000000000 @@ -123,16 +100,19 @@ extra: branch=default extra: histedit_source=a4f7421b80f79fcc59fff01bcbf4a53d127dd6d3,177f92b773850b59254aa5e923436f921b55483b description: - pick e860deea161a e - pick 652413bf663e f - fold 177f92b77385 c - pick 055a42cdd887 d + f + *** + c $ cd .. folding and creating no new change doesn't break: +------------------------------------------------- + +folded content is dropped during a merge. The folded commit should properly disapear. + $ mkdir fold-to-empty-test $ cd fold-to-empty-test $ hg init @@ -145,50 +125,31 @@ $ hg commit -m '+5' $ echo 6 >> file $ hg commit -m '+6' - $ hg log --graph - @ changeset: 3:251d831eeec5 - | tag: tip - | user: test - | date: Thu Jan 01 00:00:00 1970 +0000 - | summary: +6 + $ hg logt --graph + @ 3:251d831eeec5 +6 | - o changeset: 2:888f9082bf99 - | user: test - | date: Thu Jan 01 00:00:00 1970 +0000 - | summary: +5 + o 2:888f9082bf99 +5 | - o changeset: 1:617f94f13c0f - | user: test - | date: Thu Jan 01 00:00:00 1970 +0000 - | summary: +4 + o 1:617f94f13c0f +4 | - o changeset: 0:0189ba417d34 - user: test - date: Thu Jan 01 00:00:00 1970 +0000 - summary: 1+2+3 + o 0:0189ba417d34 1+2+3 - $ cat > editor.py <<EOF - > import re, sys - > rules = sys.argv[1] - > data = open(rules).read() - > data = re.sub(r'pick ([0-9a-f]{12} 2 \+5)', r'drop \1', data) - > data = re.sub(r'pick ([0-9a-f]{12} 2 \+6)', r'fold \1', data) - > open(rules, 'w').write(data) + $ hg histedit 1 --commands - << EOF + > pick 617f94f13c0f 1 +4 + > drop 888f9082bf99 2 +5 + > fold 251d831eeec5 3 +6 > EOF - - $ HGEDITOR='python editor.py' hg histedit 1 1 files updated, 0 files merged, 0 files removed, 0 files unresolved merging file warning: conflicts during merge. merging file incomplete! (edit conflicts, then use 'hg resolve --mark') - abort: Fix up the change and run hg histedit --continue - [255] + Fix up the change and run hg histedit --continue + [1] There were conflicts, we keep P1 content. This should effectively drop the changes from +6. $ hg status M file - ? editor.py ? file.orig $ hg resolve -l U file @@ -197,21 +158,19 @@ $ hg histedit --continue 0 files updated, 0 files merged, 0 files removed, 0 files unresolved saved backup bundle to $TESTTMP/*-backup.hg (glob) - $ hg log --graph - @ changeset: 1:617f94f13c0f - | tag: tip - | user: test - | date: Thu Jan 01 00:00:00 1970 +0000 - | summary: +4 + $ hg logt --graph + @ 1:617f94f13c0f +4 | - o changeset: 0:0189ba417d34 - user: test - date: Thu Jan 01 00:00:00 1970 +0000 - summary: 1+2+3 + o 0:0189ba417d34 1+2+3 $ cd .. + +Test fold through dropped +------------------------- + + Test corner case where folded revision is separated from its parent by a dropped revision. @@ -227,7 +186,7 @@ $ hg commit -m '+5' $ echo 6 >> file $ hg commit -m '+6' - $ hg log -G --template '{rev}:{node|short} {desc|firstline}\n' + $ hg logt -G --template '{rev}:{node|short} {desc|firstline}\n' @ 3:251d831eeec5 +6 | o 2:888f9082bf99 +5 @@ -236,19 +195,17 @@ | o 0:0189ba417d34 1+2+3 - $ EDITED="$TESTTMP/editcommands" - $ cat > $EDITED <<EOF + $ hg histedit 1 --commands - << EOF > pick 617f94f13c0f 1 +4 > drop 888f9082bf99 2 +5 > fold 251d831eeec5 3 +6 > EOF - $ HGEDITOR="cat $EDITED >" hg histedit 1 1 files updated, 0 files merged, 0 files removed, 0 files unresolved merging file warning: conflicts during merge. merging file incomplete! (edit conflicts, then use 'hg resolve --mark') - abort: Fix up the change and run hg histedit --continue - [255] + Fix up the change and run hg histedit --continue + [1] $ cat > file << EOF > 1 > 2 @@ -279,22 +236,16 @@ 1 files updated, 0 files merged, 0 files removed, 0 files unresolved 0 files updated, 0 files merged, 0 files removed, 0 files unresolved saved backup bundle to $TESTTMP/fold-with-dropped/.hg/strip-backup/617f94f13c0f-backup.hg (glob) - $ hg log -G - @ changeset: 1:10c647b2cdd5 - | tag: tip - | user: test - | date: Thu Jan 01 00:00:00 1970 +0000 - | summary: +4 + $ hg logt -G + @ 1:10c647b2cdd5 +4 | - o changeset: 0:0189ba417d34 - user: test - date: Thu Jan 01 00:00:00 1970 +0000 - summary: 1+2+3 + o 0:0189ba417d34 1+2+3 $ hg export tip # HG changeset patch # User test # Date 0 0 + # Thu Jan 01 00:00:00 1970 +0000 # Node ID 10c647b2cdd54db0603ecb99b2ff5ce66d5a5323 # Parent 0189ba417d34df9dda55f88b637dcae9917b5964 +4
--- a/tests/test-histedit-no-change.t Thu Apr 04 16:28:19 2013 -0500 +++ b/tests/test-histedit-no-change.t Thu Apr 18 23:46:26 2013 -0500 @@ -92,7 +92,7 @@ | edit e860deea161a 4 e | pick 652413bf663e 5 f 0 files updated, 0 files merged, 2 files removed, 0 files unresolved - abort: Make changes as needed, you may commit or record as needed now. + Make changes as needed, you may commit or record as needed now. When you are finished, run hg histedit --continue to resume. $ continueediting true "(leaving commit message unaltered)" % finalize changeset editing (leaving commit message unaltered) @@ -145,12 +145,12 @@ | edit e860deea161a 4 e | pick 652413bf663e 5 f 0 files updated, 0 files merged, 3 files removed, 0 files unresolved - abort: Make changes as needed, you may commit or record as needed now. + Make changes as needed, you may commit or record as needed now. When you are finished, run hg histedit --continue to resume. $ continueediting true "(leaving commit message unaltered)" % finalize changeset editing (leaving commit message unaltered) 0 files updated, 0 files merged, 0 files removed, 0 files unresolved - abort: Make changes as needed, you may commit or record as needed now. + Make changes as needed, you may commit or record as needed now. When you are finished, run hg histedit --continue to resume. $ graphlog "log after first edit" % log after first edit
--- a/tests/test-histedit-non-commute-abort.t Thu Apr 04 16:28:19 2013 -0500 +++ b/tests/test-histedit-non-commute-abort.t Thu Apr 18 23:46:26 2013 -0500 @@ -6,14 +6,6 @@ > histedit= > EOF - $ EDITED="$TESTTMP/editedhistory" - $ cat > $EDITED <<EOF - > pick 177f92b77385 c - > pick 055a42cdd887 d - > pick bfa474341cc9 does not commute with e - > pick e860deea161a e - > pick 652413bf663e f - > EOF $ initrepo () > { > hg init r @@ -71,7 +63,13 @@ edit the history - $ HGEDITOR="cat \"$EDITED\" > " hg histedit 177f92b77385 2>&1 | fixbundle + $ hg histedit 177f92b77385 --commands - 2>&1 <<EOF | fixbundle + > pick 177f92b77385 c + > pick 055a42cdd887 d + > pick bfa474341cc9 does not commute with e + > pick e860deea161a e + > pick 652413bf663e f + > EOF 0 files updated, 0 files merged, 2 files removed, 0 files unresolved remote changed e which local deleted use (c)hanged version or leave (d)eleted? c @@ -79,7 +77,7 @@ merging e warning: conflicts during merge. merging e incomplete! (edit conflicts, then use 'hg resolve --mark') - abort: Fix up the change and run hg histedit --continue + Fix up the change and run hg histedit --continue abort the edit
--- a/tests/test-histedit-non-commute.t Thu Apr 04 16:28:19 2013 -0500 +++ b/tests/test-histedit-non-commute.t Thu Apr 18 23:46:26 2013 -0500 @@ -87,12 +87,12 @@ edit the history - $ HGEDITOR="cat \"$EDITED\" > " hg histedit 3 2>&1 | fixbundle + $ hg histedit 3 --commands $EDITED 2>&1 | fixbundle 2 files updated, 0 files merged, 0 files removed, 0 files unresolved merging e warning: conflicts during merge. merging e incomplete! (edit conflicts, then use 'hg resolve --mark') - abort: Fix up the change and run hg histedit --continue + Fix up the change and run hg histedit --continue abort the edit $ hg histedit --abort 2>&1 | fixbundle @@ -145,12 +145,12 @@ edit the history - $ HGEDITOR="cat \"$EDITED\" > " hg histedit 3 2>&1 | fixbundle + $ hg histedit 3 --commands $EDITED 2>&1 | fixbundle 2 files updated, 0 files merged, 0 files removed, 0 files unresolved merging e warning: conflicts during merge. merging e incomplete! (edit conflicts, then use 'hg resolve --mark') - abort: Fix up the change and run hg histedit --continue + Fix up the change and run hg histedit --continue fix up $ echo 'I can haz no commute' > e @@ -160,7 +160,7 @@ merging e warning: conflicts during merge. merging e incomplete! (edit conflicts, then use 'hg resolve --mark') - abort: Fix up the change and run hg histedit --continue + Fix up the change and run hg histedit --continue This failure is caused by 7b4e2f4b7bcd "e" not rebasing the non commutative former children. @@ -231,21 +231,21 @@ pick 500cac37a696 6 f edit the history, this time with a fold action - $ HGEDITOR="cat \"$EDITED\" > " hg histedit 3 2>&1 | fixbundle + $ hg histedit 3 --commands $EDITED 2>&1 | fixbundle 2 files updated, 0 files merged, 0 files removed, 0 files unresolved merging e warning: conflicts during merge. merging e incomplete! (edit conflicts, then use 'hg resolve --mark') - abort: Fix up the change and run hg histedit --continue + Fix up the change and run hg histedit --continue $ echo 'I can haz no commute' > e $ hg resolve --mark e - $ HGEDITOR="cat \"$EDITED\" > " hg histedit --continue 2>&1 | fixbundle + $ hg histedit --continue 2>&1 | fixbundle 0 files updated, 0 files merged, 0 files removed, 0 files unresolved merging e warning: conflicts during merge. merging e incomplete! (edit conflicts, then use 'hg resolve --mark') - abort: Fix up the change and run hg histedit --continue + Fix up the change and run hg histedit --continue second edit also fails, but just continue $ hg revert -r 'p1()' e $ hg resolve --mark e @@ -255,16 +255,16 @@ post message fix $ hg log --graph - @ changeset: 6:521c4c32c5e2 + @ changeset: 6:7efe1373e4bc | tag: tip | user: test | date: Thu Jan 01 00:00:00 1970 +0000 | summary: f | - o changeset: 5:f4f088e8adf6 + o changeset: 5:e334d87a1e55 | user: test | date: Thu Jan 01 00:00:00 1970 +0000 - | summary: pick 65a9a84f33fd 3 c + | summary: does not commute with e | o changeset: 4:00f1c5383965 | user: test
--- a/tests/test-histedit-obsolete.t Thu Apr 04 16:28:19 2013 -0500 +++ b/tests/test-histedit-obsolete.t Thu Apr 18 23:46:26 2013 -0500 @@ -59,14 +59,13 @@ # m, mess = edit message without changing commit content # 0 files updated, 0 files merged, 0 files removed, 0 files unresolved - $ cat > commands.txt <<EOF + $ hg histedit 1 --commands - --verbose <<EOF | grep histedit > pick 177f92b77385 2 c > drop d2ae7f538514 1 b > pick 055a42cdd887 3 d > fold e860deea161a 4 e > pick 652413bf663e 5 f > EOF - $ hg histedit 1 --commands commands.txt --verbose | grep histedit saved backup bundle to $TESTTMP/base/.hg/strip-backup/96e494a2d553-backup.hg (glob) $ hg log --graph --hidden @ 8:cacdfd884a93 f @@ -100,12 +99,11 @@ create an hidden revision - $ cat > commands.txt <<EOF + $ hg histedit 6 --commands - << EOF > pick b346ab9a313d 6 c > drop 59d9f330561f 7 d > pick cacdfd884a93 8 f > EOF - $ hg histedit 6 --commands commands.txt 0 files updated, 0 files merged, 3 files removed, 0 files unresolved 0 files updated, 0 files merged, 0 files removed, 0 files unresolved $ hg log --graph @@ -117,11 +115,10 @@ check hidden revision are ignored (6 have hidden children 7 and 8) - $ cat > commands.txt <<EOF + $ hg histedit 6 --commands - << EOF > pick b346ab9a313d 6 c > pick c13eb81022ca 8 f > EOF - $ hg histedit 6 --commands commands.txt 0 files updated, 0 files merged, 0 files removed, 0 files unresolved @@ -133,15 +130,14 @@ 0 files updated, 0 files merged, 1 files removed, 0 files unresolved $ hg log -r 'children(.)' 9:c13eb81022ca f (no-eol) - $ cat > commands.txt <<EOF + $ hg histedit -r '.' --commands - <<EOF > edit b346ab9a313d 6 c > EOF - $ hg histedit -r '.' --commands commands.txt 0 files updated, 0 files merged, 1 files removed, 0 files unresolved adding c - abort: Make changes as needed, you may commit or record as needed now. + Make changes as needed, you may commit or record as needed now. When you are finished, run hg histedit --continue to resume. - [255] + [1] $ echo c >> c $ hg histedit --continue 0 files updated, 0 files merged, 0 files removed, 0 files unresolved @@ -164,11 +160,10 @@ updating to branch default 3 files updated, 0 files merged, 0 files removed, 0 files unresolved $ cd droplast - $ cat > commands.txt <<EOF + $ hg histedit -r '40db8afa467b' --commands - << EOF > pick 40db8afa467b 10 c > drop b449568bf7fc 11 f > EOF - $ hg histedit -r '40db8afa467b' --commands commands.txt 0 files updated, 0 files merged, 1 files removed, 0 files unresolved $ hg log -G @ 10:40db8afa467b c @@ -184,12 +179,11 @@ $ echo f > f $ hg add f $ hg commit -m h - $ cat > commands.txt <<EOF + $ hg histedit -r '40db8afa467b' --commands - << EOF > pick 47a8561c0449 12 g > pick 40db8afa467b 10 c > drop 1b3b05f35ff0 13 h > EOF - $ hg histedit -r '40db8afa467b' --commands commands.txt 0 files updated, 0 files merged, 3 files removed, 0 files unresolved 0 files updated, 0 files merged, 0 files removed, 0 files unresolved 0 files updated, 0 files merged, 0 files removed, 0 files unresolved @@ -267,7 +261,7 @@ $ cp -r base simple-draft $ cd simple-draft - $ cat > commands.txt <<EOF + $ hg histedit -r 'b449568bf7fc' --commands - << EOF > edit b449568bf7fc 11 f > pick 6b70183d2492 12 g > pick 7395e1ff83bd 13 h @@ -275,12 +269,11 @@ > pick 3a6c53ee7f3d 15 j > pick ee118ab9fa44 16 k > EOF - $ hg histedit -r 'b449568bf7fc' --commands commands.txt 0 files updated, 0 files merged, 6 files removed, 0 files unresolved adding f - abort: Make changes as needed, you may commit or record as needed now. + Make changes as needed, you may commit or record as needed now. When you are finished, run hg histedit --continue to resume. - [255] + [1] $ echo f >> f $ hg histedit --continue 0 files updated, 0 files merged, 0 files removed, 0 files unresolved @@ -317,7 +310,7 @@ > [phases] > new-commit=secret > EOF - $ cat > commands.txt <<EOF + $ hg histedit -r 'b449568bf7fc' --commands - << EOF > edit b449568bf7fc 11 f > pick 6b70183d2492 12 g > pick 7395e1ff83bd 13 h @@ -325,12 +318,11 @@ > pick 3a6c53ee7f3d 15 j > pick ee118ab9fa44 16 k > EOF - $ hg histedit -r 'b449568bf7fc' --commands commands.txt 0 files updated, 0 files merged, 6 files removed, 0 files unresolved adding f - abort: Make changes as needed, you may commit or record as needed now. + Make changes as needed, you may commit or record as needed now. When you are finished, run hg histedit --continue to resume. - [255] + [1] $ echo f >> f $ hg histedit --continue 0 files updated, 0 files merged, 0 files removed, 0 files unresolved @@ -367,7 +359,7 @@ $ cp -r base reorder $ cd reorder - $ cat > commands.txt <<EOF + $ hg histedit -r 'b449568bf7fc' --commands - << EOF > pick b449568bf7fc 11 f > pick 3a6c53ee7f3d 15 j > pick 6b70183d2492 12 g @@ -375,7 +367,6 @@ > pick 7395e1ff83bd 13 h > pick ee118ab9fa44 16 k > EOF - $ hg histedit -r 'b449568bf7fc' --commands commands.txt 0 files updated, 0 files merged, 5 files removed, 0 files unresolved 0 files updated, 0 files merged, 0 files removed, 0 files unresolved 0 files updated, 0 files merged, 0 files removed, 0 files unresolved @@ -415,7 +406,7 @@ > [phases] > new-commit=secret > EOF - $ cat > commands.txt <<EOF + $ hg histedit -r 'b449568bf7fc' --commands - << EOF > pick 7395e1ff83bd 13 h > fold b449568bf7fc 11 f > pick 6b70183d2492 12 g @@ -423,7 +414,6 @@ > pick b605fb7503f2 14 i > fold ee118ab9fa44 16 k > EOF - $ hg histedit -r 'b449568bf7fc' --commands commands.txt 0 files updated, 0 files merged, 6 files removed, 0 files unresolved 0 files updated, 0 files merged, 0 files removed, 0 files unresolved 0 files updated, 0 files merged, 2 files removed, 0 files unresolved
--- a/tests/test-histedit-outgoing.t Thu Apr 04 16:28:19 2013 -0500 +++ b/tests/test-histedit-outgoing.t Thu Apr 18 23:46:26 2013 -0500 @@ -82,3 +82,24 @@ # 0 files updated, 0 files merged, 0 files removed, 0 files unresolved $ cd .. + +test sensitivity to branch in URL: + + $ cd r2 + $ hg -q update 2 + $ hg -q branch foo + $ hg commit -m 'create foo branch' + $ HGEDITOR=cat hg histedit --outgoing '../r#foo' | grep -v comparing | grep -v searching + pick f26599ee3441 6 create foo branch + + # Edit history between f26599ee3441 and f26599ee3441 + # + # Commands: + # p, pick = use commit + # e, edit = use commit, but stop for amending + # f, fold = use commit, but fold into previous commit (combines N and N-1) + # d, drop = remove commit from history + # m, mess = edit message without changing commit content + # + 0 files updated, 0 files merged, 0 files removed, 0 files unresolved + $ cd ..
--- a/tests/test-histedit-revspec.t Thu Apr 04 16:28:19 2013 -0500 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,69 +0,0 @@ -This test requires parentrevspec support in revsets, so check for that -and skip the test if we're on an unusual hg that supports .t tests but -not parentrevspec. - $ python -c 'from mercurial import revset ; revset.methods["parentpost"]' || exit 80 - -Enable extensions used by this test. - $ cat >>$HGRCPATH <<EOF - > [extensions] - > graphlog= - > histedit= - > EOF - -Repo setup. - $ hg init foo - $ cd foo - $ echo alpha >> alpha - $ hg addr - adding alpha - $ hg ci -m one - $ echo alpha >> alpha - $ hg ci -m two - $ echo alpha >> alpha - $ hg ci -m three - $ echo alpha >> alpha - $ hg ci -m four - $ echo alpha >> alpha - $ hg ci -m five - - $ hg log --style compact --graph - @ 4[tip] 08d98a8350f3 1970-01-01 00:00 +0000 test - | five - | - o 3 c8e68270e35a 1970-01-01 00:00 +0000 test - | four - | - o 2 eb57da33312f 1970-01-01 00:00 +0000 test - | three - | - o 1 579e40513370 1970-01-01 00:00 +0000 test - | two - | - o 0 6058cbb6cfd7 1970-01-01 00:00 +0000 test - one - - -Run a dummy edit to make sure we get tip^^ correctly via revsingle. - $ HGEDITOR=cat hg histedit "tip^^" - pick eb57da33312f 2 three - pick c8e68270e35a 3 four - pick 08d98a8350f3 4 five - - # Edit history between eb57da33312f and 08d98a8350f3 - # - # Commands: - # p, pick = use commit - # e, edit = use commit, but stop for amending - # f, fold = use commit, but fold into previous commit (combines N and N-1) - # d, drop = remove commit from history - # m, mess = edit message without changing commit content - # - 0 files updated, 0 files merged, 0 files removed, 0 files unresolved - -Run on a revision not ancestors of the current working directory. - - $ hg up 2 - 1 files updated, 0 files merged, 0 files removed, 0 files unresolved - $ hg histedit -r 4 - nothing to edit - [1]
--- a/tests/test-hook.t Thu Apr 04 16:28:19 2013 -0500 +++ b/tests/test-hook.t Thu Apr 18 23:46:26 2013 -0500 @@ -71,8 +71,8 @@ $ hg id pre-identify hook: HG_ARGS=id HG_OPTS={'bookmarks': None, 'branch': None, 'id': None, 'insecure': None, 'num': None, 'remotecmd': '', 'rev': '', 'ssh': '', 'tags': None} HG_PATS=[] - warning: pre-identify hook exited with status 1 - [1] + abort: pre-identify hook exited with status 1 + [255] $ hg cat b pre-cat hook: HG_ARGS=cat b HG_OPTS={'decode': None, 'exclude': [], 'include': [], 'output': '', 'rev': ''} HG_PATS=['b'] b @@ -198,7 +198,6 @@ listkeys hook: HG_NAMESPACE=bookmarks HG_VALUES={'bar': '0000000000000000000000000000000000000000', 'foo': '0000000000000000000000000000000000000000'} no changes found listkeys hook: HG_NAMESPACE=phases HG_VALUES={'cb9a9f314b8b07ba71012fcdbc544b5a4d82ff5b': '1', 'publishing': 'True'} - listkeys hook: HG_NAMESPACE=bookmarks HG_VALUES={'bar': '0000000000000000000000000000000000000000', 'foo': '0000000000000000000000000000000000000000'} adding remote bookmark bar importing bookmark bar $ cd ../a @@ -232,6 +231,7 @@ abort: prelistkeys hook exited with status 1 [255] $ cd ../a + $ rm .hg/hgrc prechangegroup hook can prevent incoming changes
--- a/tests/test-https.t Thu Apr 04 16:28:19 2013 -0500 +++ b/tests/test-https.t Thu Apr 18 23:46:26 2013 -0500 @@ -106,7 +106,7 @@ #if windows $ hg serve -p $HGPORT --certificate=$PRIV 2>&1 - abort: cannot start server at ':$HGPORT': (glob) + abort: cannot start server at ':$HGPORT': [255] #else $ hg serve -p $HGPORT --certificate=$PRIV 2>&1 @@ -233,11 +233,13 @@ (check hostfingerprint configuration) [255] + - ignores that certificate doesn't match hostname $ hg -R copy-pull id https://127.0.0.1:$HGPORT/ 5fed3813f7f5 - $ while kill `cat hg1.pid` 2>/dev/null; do sleep 0; done +HGPORT1 is reused below for tinyproxy tests. Kill that server. + $ "$TESTDIR/killdaemons.py" hg1.pid Prepare for connecting through proxy
--- a/tests/test-import-merge.t Thu Apr 04 16:28:19 2013 -0500 +++ b/tests/test-import-merge.t Thu Apr 18 23:46:26 2013 -0500 @@ -113,3 +113,41 @@ $ hg strip --no-backup tip $ cd .. + +Test that --exact on a bad header doesn't corrupt the repo (issue3616) + + $ hg init repo3 + $ cd repo3 + $ echo a>a + $ hg ci -Aqm0 + $ echo a>>a + $ hg ci -m1 + $ echo a>>a + $ hg ci -m2 + $ echo a>a + $ echo b>>a + $ echo a>>a + $ hg ci -m3 + $ hg export 2 | head -7 > ../a.patch + $ hg export tip | tail -n +8 >> ../a.patch + + $ cd .. + $ hg clone -qr0 repo3 repo3-clone + $ cd repo3-clone + $ hg pull -qr1 ../repo3 + + $ hg import --exact ../a.patch + applying ../a.patch + 1 files updated, 0 files merged, 0 files removed, 0 files unresolved + patching file a + Hunk #1 succeeded at 1 with fuzz 1 (offset -1 lines). + transaction abort! + rollback completed + abort: patch is damaged or loses information + [255] + $ hg verify + checking changesets + checking manifests + crosschecking files in changesets and manifests + checking files + 1 files, 2 changesets, 2 total revisions
--- a/tests/test-import.t Thu Apr 04 16:28:19 2013 -0500 +++ b/tests/test-import.t Thu Apr 18 23:46:26 2013 -0500 @@ -34,7 +34,7 @@ $ hg --cwd b import ../exported-tip.patch applying ../exported-tip.patch -message and committer should be same +message and committer and date should be same $ hg --cwd b tip changeset: 1:1d4bd90af0e4 @@ -853,6 +853,7 @@ # HG changeset patch # User User B # Date 0 0 + # Thu Jan 01 00:00:00 1970 +0000 # Node ID eb56ab91903632294ac504838508cb370c0901d2 # Parent 0000000000000000000000000000000000000000 from: tricky!
--- a/tests/test-inotify-debuginotify.t Thu Apr 04 16:28:19 2013 -0500 +++ b/tests/test-inotify-debuginotify.t Thu Apr 18 23:46:26 2013 -0500 @@ -38,4 +38,4 @@ directories being watched: / .hg/ - $ kill `cat hg.pid` + $ "$TESTDIR/killdaemons.py" hg.pid
--- a/tests/test-inotify-issue1371.t Thu Apr 04 16:28:19 2013 -0500 +++ b/tests/test-inotify-issue1371.t Thu Apr 18 23:46:26 2013 -0500 @@ -41,4 +41,4 @@ Are we able to kill the service? if not, the service died on some error - $ kill `cat hg.pid` + $ "$TESTDIR/killdaemons.py" hg.pid
--- a/tests/test-inotify-issue1542.t Thu Apr 04 16:28:19 2013 -0500 +++ b/tests/test-inotify-issue1542.t Thu Apr 18 23:46:26 2013 -0500 @@ -33,4 +33,4 @@ Are we able to kill the service? if not, the service died on some error - $ kill `cat hg.pid` + $ "$TESTDIR/killdaemons.py" hg.pid
--- a/tests/test-inotify-issue1556.t Thu Apr 04 16:28:19 2013 -0500 +++ b/tests/test-inotify-issue1556.t Thu Apr 18 23:46:26 2013 -0500 @@ -28,4 +28,4 @@ Are we able to kill the service? if not, the service died on some error - $ kill `cat hg.pid` + $ "$TESTDIR/killdaemons.py" hg.pid
--- a/tests/test-inotify-lookup.t Thu Apr 04 16:28:19 2013 -0500 +++ b/tests/test-inotify-lookup.t Thu Apr 18 23:46:26 2013 -0500 @@ -11,4 +11,4 @@ $ hg st $ cat a a - $ kill `cat .hg/inotify.pid` + $ "$TESTDIR/killdaemons.py" .hg/inotify.pid
--- a/tests/test-inotify.t Thu Apr 04 16:28:19 2013 -0500 +++ b/tests/test-inotify.t Thu Apr 18 23:46:26 2013 -0500 @@ -32,7 +32,7 @@ make sure that pidfile worked. Output should be silent. - $ kill `cat ../hg2.pid` + $ "$TESTDIR/killdaemons.py" ../hg2.pid $ cd ../repo1 inserve @@ -157,7 +157,7 @@ build/x & build/y shouldn't appear in "hg st" $ hg st - $ kill `cat hg.pid` + $ "$TESTDIR/killdaemons.py" hg.pid $ cd .. @@ -179,4 +179,4 @@ abort: inotify-server: cannot start: socket is already bound [255] - $ kill `cat hg3.pid` + $ "$TESTDIR/killdaemons.py" hg3.pid
--- a/tests/test-interhg.t Thu Apr 04 16:28:19 2013 -0500 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,33 +0,0 @@ - $ "$TESTDIR/hghave" serve || exit 80 - - $ hg init test - $ cd test - - $ cat > .hg/hgrc <<EOF - > [extensions] - > interhg = - > - > [interhg] - > issues = s|Issue(\d+)|<a href="http://bts.example.org/issue\1">Issue\1</a>| - > - > # yes, 'x' is a weird delimiter... - > markbugs = sxbugx<i class="\x">bug</i>x - > EOF - - $ touch foo - $ hg add foo - $ hg commit -d '1 0' -m 'Issue123: fixed the bug!' - - $ hg serve -n test -p $HGPORT -d --pid-file=hg.pid -A access.log -E errors.log - $ cat hg.pid >> $DAEMON_PIDS - -log - - $ "$TESTDIR/get-with-headers.py" localhost:$HGPORT '' | grep bts - <td class="description"><a href="/rev/1b0e7ece6bd6"><a href="http://bts.example.org/issue123">Issue123</a>: fixed the <i class="x">bug</i>!</a><span class="branchhead">default</span> <span class="tag">tip</span> </td> - -errors - - $ cat errors.log - - $ cd ..
--- a/tests/test-issue1175.t Thu Apr 04 16:28:19 2013 -0500 +++ b/tests/test-issue1175.t Thu Apr 18 23:46:26 2013 -0500 @@ -44,6 +44,7 @@ # HG changeset patch # User test # Date 0 0 + # Thu Jan 01 00:00:00 1970 +0000 # Node ID 732aafbecb501a198b3cc9323ad3899ff04ccf95 # Parent 1d1625283f71954f21d14c3d44d0ad3c019c597f 5
--- a/tests/test-issue1802.t Thu Apr 04 16:28:19 2013 -0500 +++ b/tests/test-issue1802.t Thu Apr 18 23:46:26 2013 -0500 @@ -55,7 +55,7 @@ unmatched files in local: b resolving manifests - overwrite: False, partial: False + branchmerge: True, force: False, partial: False ancestor: a03b0deabf2b, local: d6fa54f68ae1+, remote: 2d8bcf2dda39 a: update permissions -> e updating: a 1/1 files (100.00%)
--- a/tests/test-issue522.t Thu Apr 04 16:28:19 2013 -0500 +++ b/tests/test-issue522.t Thu Apr 18 23:46:26 2013 -0500 @@ -29,11 +29,11 @@ unmatched files in local: bar resolving manifests - overwrite: False, partial: False + branchmerge: True, force: False, partial: False ancestor: bbd179dfa0a7, local: 71766447bdbb+, remote: 4d9e78aaceee foo: remote is newer -> g + getting foo updating: foo 1/1 files (100.00%) - getting foo 1 files updated, 0 files merged, 0 files removed, 0 files unresolved (branch merge, don't forget to commit)
--- a/tests/test-issue672.t Thu Apr 04 16:28:19 2013 -0500 +++ b/tests/test-issue672.t Thu Apr 18 23:46:26 2013 -0500 @@ -32,14 +32,13 @@ src: '1' -> dst: '1a' checking for directory renames resolving manifests - overwrite: False, partial: False + branchmerge: True, force: False, partial: False ancestor: 81f4b099af3d, local: c64f439569a9+, remote: c12dcd37c90a 1: other deleted -> r 1a: remote created -> g - updating: 1 1/2 files (50.00%) removing 1 + getting 1a updating: 1a 2/2 files (100.00%) - getting 1a 1 files updated, 0 files merged, 1 files removed, 0 files unresolved (branch merge, don't forget to commit) @@ -63,10 +62,10 @@ src: '1' -> dst: '1a' * checking for directory renames resolving manifests - overwrite: False, partial: False + branchmerge: True, force: False, partial: False ancestor: c64f439569a9, local: e327dca35ac8+, remote: 746e9549ea96 1a: local copied/moved to 1 -> m - preserving 1a for resolve of 1a + preserving 1a for resolve of 1a updating: 1a 1/1 files (100.00%) picked tool 'internal:merge' for 1a (binary False symlink False) merging 1a and 1 to 1a @@ -86,10 +85,10 @@ src: '1' -> dst: '1a' * checking for directory renames resolving manifests - overwrite: False, partial: False + branchmerge: True, force: False, partial: False ancestor: c64f439569a9, local: 746e9549ea96+, remote: e327dca35ac8 1: remote moved to 1a -> m - preserving 1 for resolve of 1a + preserving 1 for resolve of 1a removing 1 updating: 1 1/1 files (100.00%) picked tool 'internal:merge' for 1a (binary False symlink False)
--- a/tests/test-keyword.t Thu Apr 04 16:28:19 2013 -0500 +++ b/tests/test-keyword.t Thu Apr 18 23:46:26 2013 -0500 @@ -528,6 +528,7 @@ # HG changeset patch # User User Name <user@example.com> # Date 1 0 + # Thu Jan 01 00:00:01 1970 +0000 # Node ID 40a904bbbe4cd4ab0a1f28411e35db26341a40ad # Parent ef63ca68695bc9495032c6fda1350c71e6d256e9 cndiff
--- a/tests/test-largefiles-cache.t Thu Apr 04 16:28:19 2013 -0500 +++ b/tests/test-largefiles-cache.t Thu Apr 18 23:46:26 2013 -0500 @@ -40,8 +40,6 @@ adding file changes added 2 changesets with 1 changes to 1 files (run 'hg update' to get a working copy) - caching new largefiles - 0 largefiles cached Update working directory to "tip", which requires largefile("large"), but there is no cache file for it. So, hg must treat it as @@ -49,7 +47,7 @@ $ hg update -r0 getting changed largefiles - error getting id 7f7097b041ccf68cc5561e9600da4655d21c6d18 from url file:$TESTTMP/mirror for file large: can't get file locally (glob) + large: largefile 7f7097b041ccf68cc5561e9600da4655d21c6d18 not available from file:$TESTTMP/mirror 0 largefiles updated, 0 removed 1 files updated, 0 files merged, 0 files removed, 0 files unresolved $ hg status @@ -66,7 +64,7 @@ $ hg update -r0 getting changed largefiles - error getting id 7f7097b041ccf68cc5561e9600da4655d21c6d18 from url file:$TESTTMP/mirror for file large: can't get file locally (glob) + large: largefile 7f7097b041ccf68cc5561e9600da4655d21c6d18 not available from file:$TESTTMP/mirror 0 largefiles updated, 0 removed 1 files updated, 0 files merged, 0 files removed, 0 files unresolved $ hg status @@ -83,8 +81,6 @@ adding file changes added 1 changesets with 1 changes to 1 files (run 'hg update' to get a working copy) - caching new largefiles - 1 largefiles cached #if unix-permissions
--- a/tests/test-largefiles.t Thu Apr 04 16:28:19 2013 -0500 +++ b/tests/test-largefiles.t Thu Apr 18 23:46:26 2013 -0500 @@ -180,6 +180,34 @@ $ cat sub/large4 large22 +Test repo method wrapping detection + + $ cat > $TESTTMP/wrapping1.py <<EOF + > from hgext import largefiles + > def reposetup(ui, repo): + > class derived(repo.__class__): + > def push(self, *args, **kwargs): + > return super(derived, self).push(*args, **kwargs) + > repo.__class__ = derived + > largefiles.reposetup(ui, repo) + > uisetup = largefiles.uisetup + > EOF + $ hg --config extensions.largefiles=$TESTTMP/wrapping1.py status + largefiles: repo method 'push' appears to have already been wrapped by another extension: largefiles may behave incorrectly + + $ cat > $TESTTMP/wrapping2.py <<EOF + > from hgext import largefiles + > def reposetup(ui, repo): + > orgpush = repo.push + > def push(*args, **kwargs): + > return orgpush(*args, **kwargs) + > repo.push = push + > largefiles.reposetup(ui, repo) + > uisetup = largefiles.uisetup + > EOF + $ hg --config extensions.largefiles=$TESTTMP/wrapping2.py status + largefiles: repo method 'push' appears to have already been wrapped by another extension: largefiles may behave incorrectly + Test copies and moves from a directory other than root (issue3516) $ cd .. @@ -883,9 +911,47 @@ adding file changes added 6 changesets with 16 changes to 8 files (run 'hg update' to get a working copy) - caching new largefiles - 3 largefiles cached - 3 additional largefiles cached + 6 largefiles cached + +redo pull with --lfrev and check it pulls largefiles for the right revs + + $ hg rollback + repository tip rolled back to revision 1 (undo pull) + $ hg pull -v --lfrev 'heads(pulled())+min(pulled())' + pulling from $TESTTMP/a (glob) + searching for changes + all local heads known remotely + 6 changesets found + adding changesets + adding manifests + adding file changes + added 6 changesets with 16 changes to 8 files + calling hook changegroup.lfiles: <function checkrequireslfiles at *> (glob) + (run 'hg update' to get a working copy) + pulling largefiles for revision 7 + found 971fb41e78fea4f8e0ba5244784239371cb00591 in store + found 0d6d75887db61b2c7e6c74b5dd8fc6ad50c0cc30 in store + found bb3151689acb10f0c3125c560d5e63df914bc1af in store + pulling largefiles for revision 2 + found eb7338044dc27f9bc59b8dd5a246b065ead7a9c4 in store + 0 largefiles cached + +lfpull + + $ hg lfpull -r : --config largefiles.usercache=usercache-lfpull + 2 largefiles cached + $ hg lfpull -v -r 4+2 --config largefiles.usercache=usercache-lfpull + pulling largefiles for revision 4 + found eb7338044dc27f9bc59b8dd5a246b065ead7a9c4 in store + found eb7338044dc27f9bc59b8dd5a246b065ead7a9c4 in store + pulling largefiles for revision 2 + found eb7338044dc27f9bc59b8dd5a246b065ead7a9c4 in store + 0 largefiles cached + + $ ls usercache-lfpull/* | sort + usercache-lfpull/1deebade43c8c498a3c8daddac0244dc55d1331d + usercache-lfpull/4669e532d5b2c093a78eca010077e708a071bb64 + $ cd .. Rebasing between two repositories does not revert largefiles to old @@ -936,7 +1002,7 @@ M sub/normal4 M sub2/large6 saved backup bundle to $TESTTMP/d/.hg/strip-backup/f574fb32bb45-backup.hg (glob) - 0 additional largefiles cached + 0 largefiles cached nothing to rebase $ [ -f .hg/largefiles/e166e74c7303192238d60af5a9c4ce9bef0b7928 ] $ hg log --template '{rev}:{node|short} {desc|firstline}\n' @@ -969,8 +1035,6 @@ adding file changes added 1 changesets with 2 changes to 2 files (+1 heads) (run 'hg heads' to see heads, 'hg merge' to merge) - caching new largefiles - 0 largefiles cached $ hg rebase Invoking status precommit hook M sub/normal4 @@ -1205,20 +1269,14 @@ checking files 10 files, 10 changesets, 28 total revisions searching 1 changesets for largefiles - changeset 9:598410d3eb9a: sub/large4 missing - (looked for hash e166e74c7303192238d60af5a9c4ce9bef0b7928) + changeset 9:598410d3eb9a: sub/large4 references missing $TESTTMP/d/.hg/largefiles/e166e74c7303192238d60af5a9c4ce9bef0b7928 (glob) verified existence of 3 revisions of 3 largefiles [1] - introduce corruption and make sure that it is caught when checking content: $ echo '5 cents' > $TESTTMP/d/.hg/largefiles/e166e74c7303192238d60af5a9c4ce9bef0b7928 $ hg verify -q --large --lfc - searching 1 changesets for largefiles - changeset 9:598410d3eb9a: sub/large4: contents differ - ($TESTTMP/d/.hg/largefiles/e166e74c7303192238d60af5a9c4ce9bef0b7928: (glob) - expected hash e166e74c7303192238d60af5a9c4ce9bef0b7928, - but got 1f19b76d5b3cad1472c87efb42b582c97e040060) - verified contents of 3 revisions of 3 largefiles + changeset 9:598410d3eb9a: sub/large4 references corrupted $TESTTMP/d/.hg/largefiles/e166e74c7303192238d60af5a9c4ce9bef0b7928 (glob) [1] - cleanup @@ -1226,37 +1284,17 @@ - verifying all revisions will fail because we didn't clone all largefiles to d: $ echo 'T-shirt' > $TESTTMP/d/.hg/largefiles/eb7338044dc27f9bc59b8dd5a246b065ead7a9c4 - $ hg verify -q --large --lfa --lfc - searching 10 changesets for largefiles - changeset 0:30d30fe6a5be: large1 missing - (looked for hash 4669e532d5b2c093a78eca010077e708a071bb64) - changeset 0:30d30fe6a5be: sub/large2 missing - (looked for hash 1deebade43c8c498a3c8daddac0244dc55d1331d) - changeset 1:ce8896473775: large1 missing - (looked for hash 5f78770c0e77ba4287ad6ef3071c9bf9c379742f) - changeset 1:ce8896473775: sub/large2: contents differ - ($TESTTMP/d/.hg/largefiles/eb7338044dc27f9bc59b8dd5a246b065ead7a9c4: (glob) - expected hash eb7338044dc27f9bc59b8dd5a246b065ead7a9c4, - but got cfef678f24d3e339944138ecdd8fd85ca21d820f) - changeset 3:9e8fbc4bce62: large1: contents differ - ($TESTTMP/d/.hg/largefiles/eb7338044dc27f9bc59b8dd5a246b065ead7a9c4: (glob) - expected hash eb7338044dc27f9bc59b8dd5a246b065ead7a9c4, - but got cfef678f24d3e339944138ecdd8fd85ca21d820f) - changeset 4:74c02385b94c: large3: contents differ - ($TESTTMP/d/.hg/largefiles/eb7338044dc27f9bc59b8dd5a246b065ead7a9c4: (glob) - expected hash eb7338044dc27f9bc59b8dd5a246b065ead7a9c4, - but got cfef678f24d3e339944138ecdd8fd85ca21d820f) - changeset 4:74c02385b94c: sub/large4: contents differ - ($TESTTMP/d/.hg/largefiles/eb7338044dc27f9bc59b8dd5a246b065ead7a9c4: (glob) - expected hash eb7338044dc27f9bc59b8dd5a246b065ead7a9c4, - but got cfef678f24d3e339944138ecdd8fd85ca21d820f) - changeset 5:9d5af5072dbd: large3 missing - (looked for hash baaf12afde9d8d67f25dab6dced0d2bf77dba47c) - changeset 5:9d5af5072dbd: sub/large4 missing - (looked for hash aeb2210d19f02886dde00dac279729a48471e2f9) - changeset 6:4355d653f84f: large3 missing - (looked for hash 7838695e10da2bb75ac1156565f40a2595fa2fa0) - verified contents of 15 revisions of 6 largefiles + $ hg verify -q --lfa --lfc + changeset 0:30d30fe6a5be: large1 references missing $TESTTMP/d/.hg/largefiles/4669e532d5b2c093a78eca010077e708a071bb64 (glob) + changeset 0:30d30fe6a5be: sub/large2 references missing $TESTTMP/d/.hg/largefiles/1deebade43c8c498a3c8daddac0244dc55d1331d (glob) + changeset 1:ce8896473775: large1 references missing $TESTTMP/d/.hg/largefiles/5f78770c0e77ba4287ad6ef3071c9bf9c379742f (glob) + changeset 1:ce8896473775: sub/large2 references corrupted $TESTTMP/d/.hg/largefiles/eb7338044dc27f9bc59b8dd5a246b065ead7a9c4 (glob) + changeset 3:9e8fbc4bce62: large1 references corrupted $TESTTMP/d/.hg/largefiles/eb7338044dc27f9bc59b8dd5a246b065ead7a9c4 (glob) + changeset 4:74c02385b94c: large3 references corrupted $TESTTMP/d/.hg/largefiles/eb7338044dc27f9bc59b8dd5a246b065ead7a9c4 (glob) + changeset 4:74c02385b94c: sub/large4 references corrupted $TESTTMP/d/.hg/largefiles/eb7338044dc27f9bc59b8dd5a246b065ead7a9c4 (glob) + changeset 5:9d5af5072dbd: large3 references missing $TESTTMP/d/.hg/largefiles/baaf12afde9d8d67f25dab6dced0d2bf77dba47c (glob) + changeset 5:9d5af5072dbd: sub/large4 references missing $TESTTMP/d/.hg/largefiles/aeb2210d19f02886dde00dac279729a48471e2f9 (glob) + changeset 6:4355d653f84f: large3 references missing $TESTTMP/d/.hg/largefiles/7838695e10da2bb75ac1156565f40a2595fa2fa0 (glob) [1] - cleanup @@ -1268,7 +1306,7 @@ $ rm ${USERCACHE}/7838695e10da2bb75ac1156565f40a2595fa2fa0 $ hg up -r 6 getting changed largefiles - error getting id 7838695e10da2bb75ac1156565f40a2595fa2fa0 from url file:$TESTTMP/d for file large3: can't get file locally (glob) + large3: largefile 7838695e10da2bb75ac1156565f40a2595fa2fa0 not available from file:$TESTTMP/d 1 largefiles updated, 2 removed 4 files updated, 0 files merged, 2 files removed, 0 files unresolved $ rm normal3 @@ -1289,7 +1327,7 @@ ! normal3 $ hg up -Cr. getting changed largefiles - error getting id 7838695e10da2bb75ac1156565f40a2595fa2fa0 from url file:$TESTTMP/d for file large3: can't get file locally (glob) + large3: largefile 7838695e10da2bb75ac1156565f40a2595fa2fa0 not available from file:$TESTTMP/d 0 largefiles updated, 0 removed 1 files updated, 0 files merged, 0 files removed, 0 files unresolved $ hg st @@ -1311,7 +1349,7 @@ 4 files updated, 0 files merged, 0 files removed, 0 files unresolved (branch merge, don't forget to commit) getting changed largefiles - error getting id 7838695e10da2bb75ac1156565f40a2595fa2fa0 from url file:$TESTTMP/d for file large3: can't get file locally (glob) + large3: largefile 7838695e10da2bb75ac1156565f40a2595fa2fa0 not available from file:$TESTTMP/d 1 largefiles updated, 0 removed $ hg rollback -q @@ -1323,9 +1361,6 @@ pulling from $TESTTMP/d (glob) searching for changes no changes found - caching new largefiles - 0 largefiles cached - 0 additional largefiles cached Merging does not revert to old versions of largefiles and also check that merging after having pulled from a non-default remote works @@ -1354,7 +1389,8 @@ $ hg commit -m "Modify large4 to test merge" Invoking status precommit hook M sub/large4 - $ hg pull ../e +# Test --cache-largefiles flag + $ hg pull --lfrev 'heads(pulled())' ../e pulling from ../e searching for changes adding changesets @@ -1362,7 +1398,6 @@ adding file changes added 2 changesets with 4 changes to 4 files (+1 heads) (run 'hg heads' to see heads, 'hg merge' to merge) - caching new largefiles 2 largefiles cached $ hg merge merging sub/large4 @@ -1489,7 +1524,10 @@ $ hg cat -r '.^' sub/large4 doesntexist large4-modified doesntexist: no such file in rev a381d2c8c80e - [1] + $ hg --cwd sub cat -r '.^' large4 + large4-modified + $ hg --cwd sub cat -r '.^' ../normal3 + normal3-modified Test that renaming a largefile results in correct output for status @@ -1730,15 +1768,16 @@ [1] $ mv 02a439e5c31c526465ab1a0ca1f431f76b827b90 empty/.hg/largefiles/ $ hg -R http-clone -q verify --large --lfa - searching 1 changesets for largefiles - verified existence of 1 revisions of 1 largefiles largefiles pulled on update - a largefile missing on the server: $ mv empty/.hg/largefiles/02a439e5c31c526465ab1a0ca1f431f76b827b90 . $ hg -R http-clone up --config largefiles.usercache=http-clone-usercache getting changed largefiles - abort: remotestore: largefile 02a439e5c31c526465ab1a0ca1f431f76b827b90 is missing - [255] + f1: largefile 02a439e5c31c526465ab1a0ca1f431f76b827b90 not available from http://localhost:$HGPORT2/ + 0 largefiles updated, 0 removed + 1 files updated, 0 files merged, 0 files removed, 0 files unresolved + $ hg -R http-clone st + ! f1 $ hg -R http-clone up -Cqr null largefiles pulled on update - a largefile corrupted on the server: @@ -1767,17 +1806,17 @@ $ mv 02a439e5c31c526465ab1a0ca1f431f76b827b90 empty/.hg/largefiles/ $ hg -R http-clone --debug up --config largefiles.usercache=http-clone-usercache resolving manifests - overwrite: False, partial: False + branchmerge: False, force: False, partial: False ancestor: 000000000000, local: 000000000000+, remote: cf03e5bb9936 .hglf/f1: remote created -> g + getting .hglf/f1 updating: .hglf/f1 1/1 files (100.00%) - getting .hglf/f1 getting changed largefiles using http://localhost:$HGPORT2/ sending capabilities command + sending batch command getting largefiles: 0/1 lfile (0.00%) getting f1:02a439e5c31c526465ab1a0ca1f431f76b827b90 - sending batch command sending getlfile command found 02a439e5c31c526465ab1a0ca1f431f76b827b90 in store 1 largefiles updated, 0 removed
--- a/tests/test-lfconvert.t Thu Apr 04 16:28:19 2013 -0500 +++ b/tests/test-lfconvert.t Thu Apr 18 23:46:26 2013 -0500 @@ -317,18 +317,12 @@ checking files 8 files, 7 changesets, 12 total revisions searching 7 changesets for largefiles - changeset 0:d4892ec57ce2: large missing - (looked for hash 2e000fa7e85759c7f4c254d4d9c33ef481e459a7) - changeset 1:334e5237836d: sub/maybelarge.dat missing - (looked for hash 34e163be8e43c5631d8b92e9c43ab0bf0fa62b9c) - changeset 2:261ad3f3f037: stuff/maybelarge.dat missing - (looked for hash 34e163be8e43c5631d8b92e9c43ab0bf0fa62b9c) - changeset 3:55759520c76f: sub/maybelarge.dat missing - (looked for hash 76236b6a2c6102826c61af4297dd738fb3b1de38) - changeset 5:9cc5aa7204f0: stuff/maybelarge.dat missing - (looked for hash 76236b6a2c6102826c61af4297dd738fb3b1de38) - changeset 6:17126745edfd: anotherlarge missing - (looked for hash 3b71f43ff30f4b15b5cd85dd9e95ebc7e84eb5a3) + changeset 0:d4892ec57ce2: large references missing $TESTTMP/largefiles-repo-hg/.hg/largefiles/2e000fa7e85759c7f4c254d4d9c33ef481e459a7 (glob) + changeset 1:334e5237836d: sub/maybelarge.dat references missing $TESTTMP/largefiles-repo-hg/.hg/largefiles/34e163be8e43c5631d8b92e9c43ab0bf0fa62b9c (glob) + changeset 2:261ad3f3f037: stuff/maybelarge.dat references missing $TESTTMP/largefiles-repo-hg/.hg/largefiles/34e163be8e43c5631d8b92e9c43ab0bf0fa62b9c (glob) + changeset 3:55759520c76f: sub/maybelarge.dat references missing $TESTTMP/largefiles-repo-hg/.hg/largefiles/76236b6a2c6102826c61af4297dd738fb3b1de38 (glob) + changeset 5:9cc5aa7204f0: stuff/maybelarge.dat references missing $TESTTMP/largefiles-repo-hg/.hg/largefiles/76236b6a2c6102826c61af4297dd738fb3b1de38 (glob) + changeset 6:17126745edfd: anotherlarge references missing $TESTTMP/largefiles-repo-hg/.hg/largefiles/3b71f43ff30f4b15b5cd85dd9e95ebc7e84eb5a3 (glob) verified existence of 6 revisions of 4 largefiles [1] $ hg -R largefiles-repo-hg showconfig paths @@ -349,7 +343,7 @@ $ rm largefiles-repo/.hg/largefiles/* $ hg lfconvert --to-normal issue3519 normalized3519 initializing destination normalized3519 - error getting id 2e000fa7e85759c7f4c254d4d9c33ef481e459a7 from url file:$TESTTMP/largefiles-repo for file large: can't get file locally (glob) + large: largefile 2e000fa7e85759c7f4c254d4d9c33ef481e459a7 not available from file:$TESTTMP/largefiles-repo abort: missing largefile 'large' from revision d4892ec57ce212905215fad1d9018f56b99202ad [255]
--- a/tests/test-log.t Thu Apr 04 16:28:19 2013 -0500 +++ b/tests/test-log.t Thu Apr 18 23:46:26 2013 -0500 @@ -1,3 +1,20 @@ +Log on empty repository: checking consistency + + $ hg init empty + $ cd empty + $ hg log + $ hg log -r 1 + abort: unknown revision '1'! + [255] + $ hg log -r -1:0 + abort: unknown revision '-1'! + [255] + $ hg log -r 'branch(name)' + abort: unknown revision 'name'! + [255] + $ hg log -r null -q + -1:000000000000 + The g is crafted to have 2 filelog topological heads in a linear changeset graph
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tests/test-lrucachedict.py Thu Apr 18 23:46:26 2013 -0500 @@ -0,0 +1,35 @@ +from mercurial import util + +def printifpresent(d, xs): + for x in xs: + present = x in d + print "'%s' in d: %s" % (x, present) + if present: + print "d['%s']: %s" % (x, d[x]) + +def test_lrucachedict(): + d = util.lrucachedict(4) + d['a'] = 'va' + d['b'] = 'vb' + d['c'] = 'vc' + d['d'] = 'vd' + + # all of these should be present + printifpresent(d, ['a', 'b', 'c', 'd']) + + # 'a' should be dropped because it was least recently used + d['e'] = 've' + printifpresent(d, ['a', 'b', 'c', 'd', 'e']) + + # touch entries in some order (get or set). + d['e'] + d['c'] = 'vc2' + d['d'] + d['b'] = 'vb2' + + # 'e' should be dropped now + d['f'] = 'vf' + printifpresent(d, ['b', 'c', 'd', 'e', 'f']) + +if __name__ == '__main__': + test_lrucachedict()
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tests/test-lrucachedict.py.out Thu Apr 18 23:46:26 2013 -0500 @@ -0,0 +1,26 @@ +'a' in d: True +d['a']: va +'b' in d: True +d['b']: vb +'c' in d: True +d['c']: vc +'d' in d: True +d['d']: vd +'a' in d: False +'b' in d: True +d['b']: vb +'c' in d: True +d['c']: vc +'d' in d: True +d['d']: vd +'e' in d: True +d['e']: ve +'b' in d: True +d['b']: vb2 +'c' in d: True +d['c']: vc2 +'d' in d: True +d['d']: vd +'e' in d: False +'f' in d: True +d['f']: vf
--- a/tests/test-merge-commit.t Thu Apr 04 16:28:19 2013 -0500 +++ b/tests/test-merge-commit.t Thu Apr 18 23:46:26 2013 -0500 @@ -69,10 +69,10 @@ $ hg --debug merge 3 searching for copies back to rev 1 resolving manifests - overwrite: False, partial: False + branchmerge: True, force: False, partial: False ancestor: 0f2ff26688b9, local: 2263c1be0967+, remote: 0555950ead28 bar: versions differ -> m - preserving bar for resolve of bar + preserving bar for resolve of bar updating: bar 1/1 files (100.00%) picked tool 'internal:merge' for bar (binary False symlink False) merging bar @@ -156,10 +156,10 @@ $ hg --debug merge 3 searching for copies back to rev 1 resolving manifests - overwrite: False, partial: False + branchmerge: True, force: False, partial: False ancestor: 0f2ff26688b9, local: 2263c1be0967+, remote: 3ffa6b9e35f0 bar: versions differ -> m - preserving bar for resolve of bar + preserving bar for resolve of bar updating: bar 1/1 files (100.00%) picked tool 'internal:merge' for bar (binary False symlink False) merging bar
--- a/tests/test-merge-prompt.t Thu Apr 04 16:28:19 2013 -0500 +++ b/tests/test-merge-prompt.t Thu Apr 18 23:46:26 2013 -0500 @@ -42,7 +42,7 @@ Non-interactive merge: $ hg merge -y - local changed file1 which remote deleted + local changed file1 which remote deleted use (c)hanged version or (d)elete? c remote changed file2 which local deleted use (c)hanged version or leave (d)eleted? c @@ -70,7 +70,7 @@ > c > d > EOF - local changed file1 which remote deleted + local changed file1 which remote deleted use (c)hanged version or (d)elete? remote changed file2 which local deleted use (c)hanged version or leave (d)eleted? 0 files updated, 0 files merged, 0 files removed, 0 files unresolved (branch merge, don't forget to commit) @@ -97,11 +97,11 @@ > baz > c > EOF - local changed file1 which remote deleted + local changed file1 which remote deleted use (c)hanged version or (d)elete? unrecognized response - local changed file1 which remote deleted + local changed file1 which remote deleted use (c)hanged version or (d)elete? unrecognized response - local changed file1 which remote deleted + local changed file1 which remote deleted use (c)hanged version or (d)elete? remote changed file2 which local deleted use (c)hanged version or leave (d)eleted? unrecognized response remote changed file2 which local deleted @@ -126,7 +126,7 @@ $ hg merge --config ui.interactive=true <<EOF > d > EOF - local changed file1 which remote deleted + local changed file1 which remote deleted use (c)hanged version or (d)elete? remote changed file2 which local deleted use (c)hanged version or leave (d)eleted? abort: response expected [255]
--- a/tests/test-merge-types.t Thu Apr 04 16:28:19 2013 -0500 +++ b/tests/test-merge-types.t Thu Apr 18 23:46:26 2013 -0500 @@ -32,10 +32,10 @@ $ hg merge --debug searching for copies back to rev 1 resolving manifests - overwrite: False, partial: False + branchmerge: True, force: False, partial: False ancestor: c334dc3be0da, local: 521a1e40188f+, remote: 3574f3e69b1c a: versions differ -> m - preserving a for resolve of a + preserving a for resolve of a updating: a 1/1 files (100.00%) picked tool 'internal:merge' for a (binary False symlink True) merging a @@ -65,10 +65,10 @@ $ hg merge --debug searching for copies back to rev 1 resolving manifests - overwrite: False, partial: False + branchmerge: True, force: False, partial: False ancestor: c334dc3be0da, local: 3574f3e69b1c+, remote: 521a1e40188f a: versions differ -> m - preserving a for resolve of a + preserving a for resolve of a updating: a 1/1 files (100.00%) picked tool 'internal:merge' for a (binary False symlink True) merging a @@ -99,10 +99,10 @@ $ HGMERGE= hg up -y --debug searching for copies back to rev 2 resolving manifests - overwrite: False, partial: False + branchmerge: False, force: False, partial: False ancestor: c334dc3be0da, local: c334dc3be0da+, remote: 521a1e40188f a: versions differ -> m - preserving a for resolve of a + preserving a for resolve of a updating: a 1/1 files (100.00%) (couldn't find merge tool hgmerge|tool hgmerge can't handle symlinks) (re) picked tool 'internal:prompt' for a (binary False symlink True)
--- a/tests/test-merge7.t Thu Apr 04 16:28:19 2013 -0500 +++ b/tests/test-merge7.t Thu Apr 18 23:46:26 2013 -0500 @@ -81,10 +81,10 @@ $ hg merge --debug searching for copies back to rev 1 resolving manifests - overwrite: False, partial: False + branchmerge: True, force: False, partial: False ancestor: 96b70246a118, local: 50c3a7e29886+, remote: 40d11a4173a8 test.txt: versions differ -> m - preserving test.txt for resolve of test.txt + preserving test.txt for resolve of test.txt updating: test.txt 1/1 files (100.00%) picked tool 'internal:merge' for test.txt (binary False symlink False) merging test.txt
--- a/tests/test-minirst.py.out Thu Apr 04 16:28:19 2013 -0500 +++ b/tests/test-minirst.py.out Thu Apr 18 23:46:26 2013 -0500 @@ -605,7 +605,7 @@ html format: ---------------------------------------------------------------------- <p> -Please see "hg add". +Please see "hg add". </p> ---------------------------------------------------------------------- @@ -645,7 +645,7 @@ <h1>Title</h1> <h2>Section</h2> <h3>Subsection</h3> -<h2>Markup: "foo" and "hg help"</h2> +<h2>Markup: "foo" and "hg help"</h2> ---------------------------------------------------------------------- == admonitions == @@ -758,9 +758,15 @@ html format: ---------------------------------------------------------------------- <table> - <tr><th>a</th><th>b</th><th>c</th></tr> - <tr><td>1</td><td>2</td><td>3</td></tr> - <tr><td>foo</td><td>bar</td><td>baz this list is very very very long man</td></tr> +<tr><td>a</td> +<td>b</td> +<td>c</td></tr> +<tr><td>1</td> +<td>2</td> +<td>3</td></tr> +<tr><td>foo</td> +<td>bar</td> +<td>baz this list is very very very long man</td></tr> </table> ----------------------------------------------------------------------
--- a/tests/test-mq-strip.t Thu Apr 04 16:28:19 2013 -0500 +++ b/tests/test-mq-strip.t Thu Apr 18 23:46:26 2013 -0500 @@ -420,6 +420,25 @@ $ hg status M bar ? b + +Strip adds, removes, modifies with --keep + + $ touch b + $ hg add b + $ hg commit -mb + $ touch c + $ hg add c + $ hg rm bar + $ hg commit -mc + $ echo b > b + $ echo d > d + $ hg strip --keep tip + saved backup bundle to $TESTTMP/test/.hg/strip-backup/*-backup.hg (glob) + $ hg status + M b + ! bar + ? c + ? d $ cd .. stripping many nodes on a complex graph (issue3299)
--- a/tests/test-mq.t Thu Apr 04 16:28:19 2013 -0500 +++ b/tests/test-mq.t Thu Apr 18 23:46:26 2013 -0500 @@ -1555,4 +1555,25 @@ 1: secret 2: secret +Test that secret mq patch does not break hgweb + + $ cat > hgweb.cgi <<HGWEB + > from mercurial import demandimport; demandimport.enable() + > from mercurial.hgweb import hgweb + > from mercurial.hgweb import wsgicgi + > import cgitb + > cgitb.enable() + > app = hgweb('.', 'test') + > wsgicgi.launch(app) + > HGWEB + $ . "$TESTDIR/cgienv" +#if msys + $ PATH_INFO=//tags; export PATH_INFO +#else + $ PATH_INFO=/tags; export PATH_INFO +#endif + $ QUERY_STRING='style=raw' + $ python hgweb.cgi | grep '^tip' + tip [0-9a-f]{40} (re) + $ cd ..
--- a/tests/test-obsolete-divergent.t Thu Apr 04 16:28:19 2013 -0500 +++ b/tests/test-obsolete-divergent.t Thu Apr 18 23:46:26 2013 -0500 @@ -1,6 +1,6 @@ Test file dedicated to testing the divergent troubles from obsolete changeset. -This is the most complexe troubles from far so we isolate it in a dedicated +This is the most complex troubles from far so we isolate it in a dedicated file. Enable obsolete @@ -294,7 +294,7 @@ e442cfc57690 $ hg log -r 'divergent()' -Check more complexe obsolescence graft (with divergence) +Check more complex obsolescence graft (with divergence) $ mkcommit B_0; hg up 0 0 files updated, 0 files merged, 2 files removed, 0 files unresolved
--- a/tests/test-obsolete.t Thu Apr 04 16:28:19 2013 -0500 +++ b/tests/test-obsolete.t Thu Apr 18 23:46:26 2013 -0500 @@ -752,7 +752,7 @@ check that web.view config option: - $ kill `cat hg.pid` + $ "$TESTDIR/killdaemons.py" hg.pid $ cat >> .hg/hgrc << EOF > [web] > view=all @@ -761,7 +761,7 @@ $ hg serve -n test -p $HGPORT -d --pid-file=hg.pid -A access.log -E errors.log $ "$TESTDIR/get-with-headers.py" --headeronly localhost:$HGPORT 'rev/67' 200 Script output follows - $ kill `cat hg.pid` + $ "$TESTDIR/killdaemons.py" hg.pid Checking _enable=False warning if obsolete marker exists
--- a/tests/test-parse-date.t Thu Apr 04 16:28:19 2013 -0500 +++ b/tests/test-parse-date.t Thu Apr 18 23:46:26 2013 -0500 @@ -234,3 +234,25 @@ Sat Apr 15 13:30:00 2006 +0000 Wed Feb 01 13:00:30 2006 -0500 Wed Feb 01 13:00:30 2006 +0000 + +Test issue 3764 (interpreting 'today' and 'yesterday') + $ echo "hello" >> a + >>> import datetime + >>> today = datetime.date.today().strftime("%b %d") + >>> yesterday = (datetime.date.today() - datetime.timedelta(days=1)).strftime("%b %d") + >>> dates = open('dates', 'w') + >>> dates.write(today + '\n') + >>> dates.write(yesterday) + >>> dates.close() + $ hg ci -d "`sed -n '1p' dates`" -m "today is a good day to code" + $ hg log -d today --template '{desc}\n' + today is a good day to code + $ echo "goodbye" >> a + $ hg ci -d "`sed -n '2p' dates`" -m "the time traveler's code" + $ hg log -d yesterday --template '{desc}\n' + the time traveler's code + $ echo "foo" >> a + $ hg commit -d now -m 'Explicitly committed now.' + $ hg log -d today --template '{desc}\n' + Explicitly committed now. + today is a good day to code
--- a/tests/test-patchbomb.t Thu Apr 04 16:28:19 2013 -0500 +++ b/tests/test-patchbomb.t Thu Apr 18 23:46:26 2013 -0500 @@ -27,6 +27,7 @@ # HG changeset patch # User test # Date 1 0 + # Thu Jan 01 00:00:01 1970 +0000 # Node ID 8580ff50825a50c8f716709acdf8de0deddcd6ab # Parent 0000000000000000000000000000000000000000 a @@ -98,6 +99,7 @@ # HG changeset patch # User test # Date 1 0 + # Thu Jan 01 00:00:01 1970 +0000 # Node ID 8580ff50825a50c8f716709acdf8de0deddcd6ab # Parent 0000000000000000000000000000000000000000 a @@ -126,6 +128,7 @@ # HG changeset patch # User test # Date 2 0 + # Thu Jan 01 00:00:02 1970 +0000 # Node ID 97d72e5f12c7e84f85064aa72e5a297142c36ed9 # Parent 8580ff50825a50c8f716709acdf8de0deddcd6ab b @@ -258,6 +261,7 @@ # HG changeset patch # User test # Date 4 0 + # Thu Jan 01 00:00:04 1970 +0000 # Node ID 909a00e13e9d78b575aeee23dddbada46d5a143f # Parent ff2c9fa2018b15fa74b33363bda9527323e2a99f utf-8 content @@ -297,21 +301,23 @@ To: foo Cc: bar - IyBIRyBjaGFuZ2VzZXQgcGF0Y2gKIyBVc2VyIHRlc3QKIyBEYXRlIDQgMAojIE5vZGUgSUQgOTA5 - YTAwZTEzZTlkNzhiNTc1YWVlZTIzZGRkYmFkYTQ2ZDVhMTQzZgojIFBhcmVudCAgZmYyYzlmYTIw - MThiMTVmYTc0YjMzMzYzYmRhOTUyNzMyM2UyYTk5Zgp1dGYtOCBjb250ZW50CgpkaWZmIC1yIGZm - MmM5ZmEyMDE4YiAtciA5MDlhMDBlMTNlOWQgZGVzY3JpcHRpb24KLS0tIC9kZXYvbnVsbAlUaHUg - SmFuIDAxIDAwOjAwOjAwIDE5NzAgKzAwMDAKKysrIGIvZGVzY3JpcHRpb24JVGh1IEphbiAwMSAw - MDowMDowNCAxOTcwICswMDAwCkBAIC0wLDAgKzEsMyBAQAorYSBtdWx0aWxpbmUKKworZGVzY3Jp - cHRpb24KZGlmZiAtciBmZjJjOWZhMjAxOGIgLXIgOTA5YTAwZTEzZTlkIHV0ZgotLS0gL2Rldi9u - dWxsCVRodSBKYW4gMDEgMDA6MDA6MDAgMTk3MCArMDAwMAorKysgYi91dGYJVGh1IEphbiAwMSAw - MDowMDowNCAxOTcwICswMDAwCkBAIC0wLDAgKzEsMSBAQAoraMO2bW1hIQo= + IyBIRyBjaGFuZ2VzZXQgcGF0Y2gKIyBVc2VyIHRlc3QKIyBEYXRlIDQgMAojICAgICAgVGh1IEph + biAwMSAwMDowMDowNCAxOTcwICswMDAwCiMgTm9kZSBJRCA5MDlhMDBlMTNlOWQ3OGI1NzVhZWVl + MjNkZGRiYWRhNDZkNWExNDNmCiMgUGFyZW50ICBmZjJjOWZhMjAxOGIxNWZhNzRiMzMzNjNiZGE5 + NTI3MzIzZTJhOTlmCnV0Zi04IGNvbnRlbnQKCmRpZmYgLXIgZmYyYzlmYTIwMThiIC1yIDkwOWEw + MGUxM2U5ZCBkZXNjcmlwdGlvbgotLS0gL2Rldi9udWxsCVRodSBKYW4gMDEgMDA6MDA6MDAgMTk3 + MCArMDAwMAorKysgYi9kZXNjcmlwdGlvbglUaHUgSmFuIDAxIDAwOjAwOjA0IDE5NzAgKzAwMDAK + QEAgLTAsMCArMSwzIEBACithIG11bHRpbGluZQorCitkZXNjcmlwdGlvbgpkaWZmIC1yIGZmMmM5 + ZmEyMDE4YiAtciA5MDlhMDBlMTNlOWQgdXRmCi0tLSAvZGV2L251bGwJVGh1IEphbiAwMSAwMDow + MDowMCAxOTcwICswMDAwCisrKyBiL3V0ZglUaHUgSmFuIDAxIDAwOjAwOjA0IDE5NzAgKzAwMDAK + QEAgLTAsMCArMSwxIEBACitow7ZtbWEhCg== $ python -c 'print open("mbox").read().split("\n\n")[1].decode("base64")' # HG changeset patch # User test # Date 4 0 + # Thu Jan 01 00:00:04 1970 +0000 # Node ID 909a00e13e9d78b575aeee23dddbada46d5a143f # Parent ff2c9fa2018b15fa74b33363bda9527323e2a99f utf-8 content @@ -357,6 +363,7 @@ # HG changeset patch # User test # Date 4 0 + # Thu Jan 01 00:00:04 1970 +0000 # Node ID a2ea8fc83dd8b93cfd86ac97b28287204ab806e1 # Parent 909a00e13e9d78b575aeee23dddbada46d5a143f long line @@ -407,6 +414,7 @@ # HG changeset patch # User test # Date 4 0 + # Thu Jan 01 00:00:04 1970 +0000 # Node ID a2ea8fc83dd8b93cfd86ac97b28287204ab806e1 # Parent 909a00e13e9d78b575aeee23dddbada46d5a143f long line @@ -465,6 +473,7 @@ # HG changeset patch # User test # Date 5 0 + # Thu Jan 01 00:00:05 1970 +0000 # Node ID 240fb913fc1b7ff15ddb9f33e73d82bf5277c720 # Parent a2ea8fc83dd8b93cfd86ac97b28287204ab806e1 isolatin 8-bit encoding @@ -513,6 +522,7 @@ # HG changeset patch # User test # Date 3 0 + # Thu Jan 01 00:00:03 1970 +0000 # Node ID ff2c9fa2018b15fa74b33363bda9527323e2a99f # Parent 97d72e5f12c7e84f85064aa72e5a297142c36ed9 c @@ -590,6 +600,7 @@ # HG changeset patch # User test # Date 1 0 + # Thu Jan 01 00:00:01 1970 +0000 # Node ID 8580ff50825a50c8f716709acdf8de0deddcd6ab # Parent 0000000000000000000000000000000000000000 a @@ -622,6 +633,7 @@ # HG changeset patch # User test # Date 2 0 + # Thu Jan 01 00:00:02 1970 +0000 # Node ID 97d72e5f12c7e84f85064aa72e5a297142c36ed9 # Parent 8580ff50825a50c8f716709acdf8de0deddcd6ab b @@ -659,6 +671,7 @@ # HG changeset patch # User test # Date 3 0 + # Thu Jan 01 00:00:03 1970 +0000 # Node ID ff2c9fa2018b15fa74b33363bda9527323e2a99f # Parent 97d72e5f12c7e84f85064aa72e5a297142c36ed9 c @@ -698,6 +711,7 @@ # HG changeset patch # User test # Date 4 0 + # Thu Jan 01 00:00:04 1970 +0000 # Node ID a2ea8fc83dd8b93cfd86ac97b28287204ab806e1 # Parent 909a00e13e9d78b575aeee23dddbada46d5a143f long line @@ -771,6 +785,7 @@ # HG changeset patch # User test # Date 1 0 + # Thu Jan 01 00:00:01 1970 +0000 # Node ID 8580ff50825a50c8f716709acdf8de0deddcd6ab # Parent 0000000000000000000000000000000000000000 a @@ -805,6 +820,7 @@ # HG changeset patch # User test # Date 2 0 + # Thu Jan 01 00:00:02 1970 +0000 # Node ID 97d72e5f12c7e84f85064aa72e5a297142c36ed9 # Parent 8580ff50825a50c8f716709acdf8de0deddcd6ab b @@ -839,6 +855,7 @@ # HG changeset patch # User test # Date 4 0 + # Thu Jan 01 00:00:04 1970 +0000 # Node ID a2ea8fc83dd8b93cfd86ac97b28287204ab806e1 # Parent 909a00e13e9d78b575aeee23dddbada46d5a143f long line @@ -902,6 +919,7 @@ # HG changeset patch # User test # Date 3 0 + # Thu Jan 01 00:00:03 1970 +0000 # Node ID ff2c9fa2018b15fa74b33363bda9527323e2a99f # Parent 97d72e5f12c7e84f85064aa72e5a297142c36ed9 c @@ -949,6 +967,7 @@ # HG changeset patch # User test # Date 4 0 + # Thu Jan 01 00:00:04 1970 +0000 # Node ID a2ea8fc83dd8b93cfd86ac97b28287204ab806e1 # Parent 909a00e13e9d78b575aeee23dddbada46d5a143f long line @@ -1002,6 +1021,7 @@ # HG changeset patch # User test # Date 3 0 + # Thu Jan 01 00:00:03 1970 +0000 # Node ID ff2c9fa2018b15fa74b33363bda9527323e2a99f # Parent 97d72e5f12c7e84f85064aa72e5a297142c36ed9 c @@ -1021,6 +1041,7 @@ # HG changeset patch # User test # Date 3 0 + # Thu Jan 01 00:00:03 1970 +0000 # Node ID ff2c9fa2018b15fa74b33363bda9527323e2a99f # Parent 97d72e5f12c7e84f85064aa72e5a297142c36ed9 c @@ -1087,6 +1108,7 @@ # HG changeset patch # User test # Date 1 0 + # Thu Jan 01 00:00:01 1970 +0000 # Node ID 8580ff50825a50c8f716709acdf8de0deddcd6ab # Parent 0000000000000000000000000000000000000000 a @@ -1130,6 +1152,7 @@ # HG changeset patch # User test # Date 2 0 + # Thu Jan 01 00:00:02 1970 +0000 # Node ID 97d72e5f12c7e84f85064aa72e5a297142c36ed9 # Parent 8580ff50825a50c8f716709acdf8de0deddcd6ab b @@ -1173,6 +1196,7 @@ # HG changeset patch # User test # Date 4 0 + # Thu Jan 01 00:00:04 1970 +0000 # Node ID a2ea8fc83dd8b93cfd86ac97b28287204ab806e1 # Parent 909a00e13e9d78b575aeee23dddbada46d5a143f long line @@ -1241,6 +1265,7 @@ # HG changeset patch # User test # Date 3 0 + # Thu Jan 01 00:00:03 1970 +0000 # Node ID ff2c9fa2018b15fa74b33363bda9527323e2a99f # Parent 97d72e5f12c7e84f85064aa72e5a297142c36ed9 c @@ -1291,6 +1316,7 @@ # HG changeset patch # User test # Date 3 0 + # Thu Jan 01 00:00:03 1970 +0000 # Node ID ff2c9fa2018b15fa74b33363bda9527323e2a99f # Parent 97d72e5f12c7e84f85064aa72e5a297142c36ed9 c @@ -1342,6 +1368,7 @@ # HG changeset patch # User test # Date 1 0 + # Thu Jan 01 00:00:01 1970 +0000 # Node ID 8580ff50825a50c8f716709acdf8de0deddcd6ab # Parent 0000000000000000000000000000000000000000 a @@ -1370,6 +1397,7 @@ # HG changeset patch # User test # Date 2 0 + # Thu Jan 01 00:00:02 1970 +0000 # Node ID 97d72e5f12c7e84f85064aa72e5a297142c36ed9 # Parent 8580ff50825a50c8f716709acdf8de0deddcd6ab b @@ -1404,6 +1432,7 @@ # HG changeset patch # User test # Date 3 0 + # Thu Jan 01 00:00:03 1970 +0000 # Node ID ff2c9fa2018b15fa74b33363bda9527323e2a99f # Parent 97d72e5f12c7e84f85064aa72e5a297142c36ed9 c @@ -1438,6 +1467,7 @@ # HG changeset patch # User test # Date 3 0 + # Thu Jan 01 00:00:03 1970 +0000 # Node ID ff2c9fa2018b15fa74b33363bda9527323e2a99f # Parent 97d72e5f12c7e84f85064aa72e5a297142c36ed9 c @@ -1480,6 +1510,7 @@ # HG changeset patch # User test # Date 3 0 + # Thu Jan 01 00:00:03 1970 +0000 # Node ID ff2c9fa2018b15fa74b33363bda9527323e2a99f # Parent 97d72e5f12c7e84f85064aa72e5a297142c36ed9 c @@ -1536,6 +1567,7 @@ # HG changeset patch # User test # Date 1 0 + # Thu Jan 01 00:00:01 1970 +0000 # Node ID 8580ff50825a50c8f716709acdf8de0deddcd6ab # Parent 0000000000000000000000000000000000000000 a @@ -1570,6 +1602,7 @@ # HG changeset patch # User test # Date 2 0 + # Thu Jan 01 00:00:02 1970 +0000 # Node ID 97d72e5f12c7e84f85064aa72e5a297142c36ed9 # Parent 8580ff50825a50c8f716709acdf8de0deddcd6ab b @@ -1607,6 +1640,7 @@ # HG changeset patch # User test # Date 0 0 + # Thu Jan 01 00:00:00 1970 +0000 # Node ID 7aead2484924c445ad8ce2613df91f52f9e502ed # Parent 045ca29b1ea20e4940411e695e20e521f2f0f98e Added tag two, two.diff for changeset ff2c9fa2018b @@ -1646,6 +1680,7 @@ # HG changeset patch # User test # Date 1 0 + # Thu Jan 01 00:00:01 1970 +0000 # Node ID 8580ff50825a50c8f716709acdf8de0deddcd6ab # Parent 0000000000000000000000000000000000000000 a @@ -1674,6 +1709,7 @@ # HG changeset patch # User test # Date 2 0 + # Thu Jan 01 00:00:02 1970 +0000 # Node ID 97d72e5f12c7e84f85064aa72e5a297142c36ed9 # Parent 8580ff50825a50c8f716709acdf8de0deddcd6ab b @@ -1728,6 +1764,7 @@ # HG changeset patch # User test # Date 1 0 + # Thu Jan 01 00:00:01 1970 +0000 # Node ID 8580ff50825a50c8f716709acdf8de0deddcd6ab # Parent 0000000000000000000000000000000000000000 a @@ -1756,6 +1793,7 @@ # HG changeset patch # User test # Date 2 0 + # Thu Jan 01 00:00:02 1970 +0000 # Node ID 97d72e5f12c7e84f85064aa72e5a297142c36ed9 # Parent 8580ff50825a50c8f716709acdf8de0deddcd6ab b @@ -1789,6 +1827,7 @@ # HG changeset patch # User test # Date 3 0 + # Thu Jan 01 00:00:03 1970 +0000 # Node ID ff2c9fa2018b15fa74b33363bda9527323e2a99f # Parent 97d72e5f12c7e84f85064aa72e5a297142c36ed9 c @@ -1840,6 +1879,7 @@ # HG changeset patch # User test # Date 1 0 + # Thu Jan 01 00:00:01 1970 +0000 # Node ID 8580ff50825a50c8f716709acdf8de0deddcd6ab # Parent 0000000000000000000000000000000000000000 a @@ -1868,6 +1908,7 @@ # HG changeset patch # User test # Date 2 0 + # Thu Jan 01 00:00:02 1970 +0000 # Node ID 97d72e5f12c7e84f85064aa72e5a297142c36ed9 # Parent 8580ff50825a50c8f716709acdf8de0deddcd6ab b @@ -1901,6 +1942,7 @@ # HG changeset patch # User test # Date 3 0 + # Thu Jan 01 00:00:03 1970 +0000 # Node ID ff2c9fa2018b15fa74b33363bda9527323e2a99f # Parent 97d72e5f12c7e84f85064aa72e5a297142c36ed9 c @@ -1952,6 +1994,7 @@ # HG changeset patch # User test # Date 1 0 + # Thu Jan 01 00:00:01 1970 +0000 # Node ID 8580ff50825a50c8f716709acdf8de0deddcd6ab # Parent 0000000000000000000000000000000000000000 a @@ -1980,6 +2023,7 @@ # HG changeset patch # User test # Date 2 0 + # Thu Jan 01 00:00:02 1970 +0000 # Node ID 97d72e5f12c7e84f85064aa72e5a297142c36ed9 # Parent 8580ff50825a50c8f716709acdf8de0deddcd6ab b @@ -2017,6 +2061,7 @@ # HG changeset patch # User test # Date 1 0 + # Thu Jan 01 00:00:01 1970 +0000 # Node ID 8580ff50825a50c8f716709acdf8de0deddcd6ab # Parent 0000000000000000000000000000000000000000 a @@ -2056,6 +2101,7 @@ # HG changeset patch # User test # Date 1 0 + # Thu Jan 01 00:00:01 1970 +0000 # Node ID 8580ff50825a50c8f716709acdf8de0deddcd6ab # Parent 0000000000000000000000000000000000000000 a @@ -2144,6 +2190,7 @@ # HG changeset patch # User test # Date 3 0 + # Thu Jan 01 00:00:03 1970 +0000 # Node ID ff2c9fa2018b15fa74b33363bda9527323e2a99f # Parent 97d72e5f12c7e84f85064aa72e5a297142c36ed9 c @@ -2171,6 +2218,7 @@ # HG changeset patch # User test # Date 4 0 + # Thu Jan 01 00:00:04 1970 +0000 # Node ID 909a00e13e9d78b575aeee23dddbada46d5a143f # Parent ff2c9fa2018b15fa74b33363bda9527323e2a99f utf-8 content @@ -2205,6 +2253,7 @@ # HG changeset patch # User test # Date 4 0 + # Thu Jan 01 00:00:04 1970 +0000 # Node ID a2ea8fc83dd8b93cfd86ac97b28287204ab806e1 # Parent 909a00e13e9d78b575aeee23dddbada46d5a143f long line @@ -2248,6 +2297,7 @@ # HG changeset patch # User test # Date 5 0 + # Thu Jan 01 00:00:05 1970 +0000 # Node ID 240fb913fc1b7ff15ddb9f33e73d82bf5277c720 # Parent a2ea8fc83dd8b93cfd86ac97b28287204ab806e1 isolatin 8-bit encoding @@ -2275,6 +2325,7 @@ # HG changeset patch # User test # Date 0 0 + # Thu Jan 01 00:00:00 1970 +0000 # Node ID 5d5ef15dfe5e7bd3a4ee154b5fff76c7945ec433 # Parent 240fb913fc1b7ff15ddb9f33e73d82bf5277c720 Added tag zero, zero.foo for changeset 8580ff50825a @@ -2303,6 +2354,7 @@ # HG changeset patch # User test # Date 4 0 + # Thu Jan 01 00:00:04 1970 +0000 # Branch test # Node ID 2f9fa9b998c5fe3ac2bd9a2b14bfcbeecbc7c268 # Parent 97d72e5f12c7e84f85064aa72e5a297142c36ed9 @@ -2338,6 +2390,7 @@ # HG changeset patch # User test # Date 4 0 + # Thu Jan 01 00:00:04 1970 +0000 # Branch test # Node ID 2f9fa9b998c5fe3ac2bd9a2b14bfcbeecbc7c268 # Parent 97d72e5f12c7e84f85064aa72e5a297142c36ed9
--- a/tests/test-progress.t Thu Apr 04 16:28:19 2013 -0500 +++ b/tests/test-progress.t Thu Apr 18 23:46:26 2013 -0500 @@ -167,6 +167,7 @@ $ hg -y loop 8 \r (no-eol) (esc) + loop [====> ] 1/8 1m18s\r (no-eol) (esc) loop [=========> ] 2/8 1m07s\r (no-eol) (esc) loop [===============> ] 3/8 56s\r (no-eol) (esc) loop [=====================> ] 4/8 45s\r (no-eol) (esc) @@ -203,6 +204,7 @@ Time estimates should not fail when there's no end point: $ hg -y loop -- -4 \r (no-eol) (esc) - loop [ <=> ] 2\r (no-eol) (esc) - loop [ <=> ] 3\r (no-eol) (esc) + loop [ <=> ] 1\r (no-eol) (esc) + loop [ <=> ] 2\r (no-eol) (esc) + loop [ <=> ] 3\r (no-eol) (esc) \r (no-eol) (esc)
--- a/tests/test-pull-http.t Thu Apr 04 16:28:19 2013 -0500 +++ b/tests/test-pull-http.t Thu Apr 18 23:46:26 2013 -0500 @@ -58,7 +58,6 @@ $ req pulling from http://localhost:$HGPORT/ - searching for changes abort: authorization failed % serve errors
--- a/tests/test-push-cgi.t Thu Apr 04 16:28:19 2013 -0500 +++ b/tests/test-push-cgi.t Thu Apr 18 23:46:26 2013 -0500 @@ -35,17 +35,17 @@ 1 changesets found $ CONTENT_LENGTH=279; export CONTENT_LENGTH; -expect unsynced changes +expect failure because heads doesn't match (formerly known as 'unsynced changes') $ QUERY_STRING="cmd=unbundle&heads=0000000000000000000000000000000000000000"; export QUERY_STRING $ python hgweb.cgi <bundle.hg >page1 2>&1 $ cat page1 Status: 200 Script output follows\r (esc) Content-Type: application/mercurial-0.1\r (esc) - Content-Length: 19\r (esc) + Content-Length: 64\r (esc) \r (esc) 0 - unsynced changes + repository changed while preparing changes - please try again successful force push
--- a/tests/test-rebase-abort.t Thu Apr 04 16:28:19 2013 -0500 +++ b/tests/test-rebase-abort.t Thu Apr 18 23:46:26 2013 -0500 @@ -55,8 +55,8 @@ merging common warning: conflicts during merge. merging common incomplete! (edit conflicts, then use 'hg resolve --mark') - abort: unresolved conflicts (see hg resolve, then hg rebase --continue) - [255] + unresolved conflicts (see hg resolve, then hg rebase --continue) + [1] Abort: @@ -126,8 +126,8 @@ merging c warning: conflicts during merge. merging c incomplete! (edit conflicts, then use 'hg resolve --mark') - abort: unresolved conflicts (see hg resolve, then hg rebase --continue) - [255] + unresolved conflicts (see hg resolve, then hg rebase --continue) + [1] $ hg tglog @ 4:draft 'C1'
--- a/tests/test-rebase-bookmarks.t Thu Apr 04 16:28:19 2013 -0500 +++ b/tests/test-rebase-bookmarks.t Thu Apr 18 23:46:26 2013 -0500 @@ -141,8 +141,8 @@ merging c warning: conflicts during merge. merging c incomplete! (edit conflicts, then use 'hg resolve --mark') - abort: unresolved conflicts (see hg resolve, then hg rebase --continue) - [255] + unresolved conflicts (see hg resolve, then hg rebase --continue) + [1] $ echo 'c' > c $ hg resolve --mark c $ hg rebase --continue
--- a/tests/test-rebase-cache.t Thu Apr 04 16:28:19 2013 -0500 +++ b/tests/test-rebase-cache.t Thu Apr 18 23:46:26 2013 -0500 @@ -385,3 +385,112 @@ $ hg theads 0: 'A' + +Make sure rebase does not break for phase/filter related reason +---------------------------------------------------------------- +(issue3858) + + $ cd .. + + $ cat >> $HGRCPATH << EOF + > [ui] + > logtemplate={rev} {desc} {phase}\n + > EOF + $ cat $HGRCPATH + [ui] + slash = True + interactive = False + [defaults] + backout = -d "0 0" + commit = -d "0 0" + tag = -d "0 0" + [extensions] + graphlog= + rebase= + mq= + + [phases] + publish=False + + [alias] + tglog = log -G --template "{rev}: '{desc}' {branches}\n" + theads = heads --template "{rev}: '{desc}' {branches}\n" + [ui] + logtemplate={rev} {desc} {phase}\n + + + $ hg init c4 + $ cd c4 + + $ echo a > a + $ hg ci -Am A + adding a + $ echo b > b + $ hg ci -Am B + adding b + $ hg up 0 + 0 files updated, 0 files merged, 1 files removed, 0 files unresolved + $ echo c > c + $ hg ci -Am C + adding c + created new head + $ hg up 1 + 1 files updated, 0 files merged, 1 files removed, 0 files unresolved + $ hg merge + 1 files updated, 0 files merged, 0 files removed, 0 files unresolved + (branch merge, don't forget to commit) + $ hg ci -m d + $ hg up 2 + 0 files updated, 0 files merged, 1 files removed, 0 files unresolved + $ echo e > e + $ hg ci -Am E + adding e + created new head + $ hg merge 3 + 1 files updated, 0 files merged, 0 files removed, 0 files unresolved + (branch merge, don't forget to commit) + $ hg ci -m F + $ hg up 3 + 0 files updated, 0 files merged, 1 files removed, 0 files unresolved + $ echo g > g + $ hg ci -Am G + adding g + created new head + $ echo h > h + $ hg ci -Am H + adding h + $ hg up 5 + 1 files updated, 0 files merged, 2 files removed, 0 files unresolved + $ echo i > i + $ hg ci -Am I + adding i + +Turn most changeset public + + $ hg ph -p 7 + + $ hg heads + 8 I draft + 7 H public + $ hg log -G + @ 8 I draft + | + | o 7 H public + | | + | o 6 G public + | | + o | 5 F draft + |\| + o | 4 E draft + | | + | o 3 d public + |/| + o | 2 C public + | | + | o 1 B public + |/ + o 0 A public + + + $ hg rebase --dest 7 --source 5 + saved backup bundle to $TESTTMP/a3/c4/.hg/strip-backup/*-backup.hg (glob)
--- a/tests/test-rebase-check-restore.t Thu Apr 04 16:28:19 2013 -0500 +++ b/tests/test-rebase-check-restore.t Thu Apr 18 23:46:26 2013 -0500 @@ -69,8 +69,8 @@ merging A warning: conflicts during merge. merging A incomplete! (edit conflicts, then use 'hg resolve --mark') - abort: unresolved conflicts (see hg resolve, then hg rebase --continue) - [255] + unresolved conflicts (see hg resolve, then hg rebase --continue) + [1] Solve the conflict and go on: @@ -122,8 +122,8 @@ merging A warning: conflicts during merge. merging A incomplete! (edit conflicts, then use 'hg resolve --mark') - abort: unresolved conflicts (see hg resolve, then hg rebase --continue) - [255] + unresolved conflicts (see hg resolve, then hg rebase --continue) + [1] Solve the conflict and go on:
--- a/tests/test-rebase-collapse.t Thu Apr 04 16:28:19 2013 -0500 +++ b/tests/test-rebase-collapse.t Thu Apr 18 23:46:26 2013 -0500 @@ -496,15 +496,15 @@ $ hg ci -Am 'A' adding a - $ hg branch '1' - marked working directory as branch 1 + $ hg branch 'one' + marked working directory as branch one (branches are permanent and global, did you want a bookmark?) $ echo 'b' > b $ hg ci -Am 'B' adding b - $ hg branch '2' - marked working directory as branch 2 + $ hg branch 'two' + marked working directory as branch two (branches are permanent and global, did you want a bookmark?) $ echo 'c' > c $ hg ci -Am 'C' @@ -518,9 +518,9 @@ $ hg tglog @ 3: 'D' | - | o 2: 'C' 2 + | o 2: 'C' two | | - | o 1: 'B' 1 + | o 1: 'B' one |/ o 0: 'A' @@ -546,9 +546,9 @@ |/ o 3: 'D' | - | o 2: 'C' 2 + | o 2: 'C' two | | - | o 1: 'B' 1 + | o 1: 'B' one |/ o 0: 'A' @@ -559,9 +559,9 @@ | o 3: 'D' | - | o 2: 'C' 2 + | o 2: 'C' two | | - | o 1: 'B' 1 + | o 1: 'B' one |/ o 0: 'A' @@ -569,6 +569,7 @@ # HG changeset patch # User user1 # Date 0 0 + # Thu Jan 01 00:00:00 1970 +0000 # Node ID f338eb3c2c7cc5b5915676a2376ba7ac558c5213 # Parent 41acb9dca9eb976e84cd21fcb756b4afa5a35c09 E @@ -718,6 +719,30 @@ $ cd .. +Test collapsing changes that add then remove a file + $ hg init collapseaddremove + $ cd collapseaddremove + $ touch base + $ hg commit -Am base + adding base + $ touch a + $ hg commit -Am a + adding a + $ hg rm a + $ touch b + $ hg commit -Am b + adding b + $ hg rebase -d 0 -r "1::2" --collapse -m collapsed + saved backup bundle to $TESTTMP/collapseaddremove/.hg/strip-backup/*-backup.hg (glob) + $ hg tglog + @ 1: 'collapsed' + | + o 0: 'base' + + $ hg manifest + b + base + $ cd ..
--- a/tests/test-rebase-conflicts.t Thu Apr 04 16:28:19 2013 -0500 +++ b/tests/test-rebase-conflicts.t Thu Apr 18 23:46:26 2013 -0500 @@ -65,8 +65,8 @@ merging common warning: conflicts during merge. merging common incomplete! (edit conflicts, then use 'hg resolve --mark') - abort: unresolved conflicts (see hg resolve, then hg rebase --continue) - [255] + unresolved conflicts (see hg resolve, then hg rebase --continue) + [1] Try to continue without solving the conflict:
--- a/tests/test-rebase-detach.t Thu Apr 04 16:28:19 2013 -0500 +++ b/tests/test-rebase-detach.t Thu Apr 18 23:46:26 2013 -0500 @@ -326,8 +326,6 @@ $ hg ci -m "J" $ hg rebase -s 8 -d 7 --collapse --config ui.merge=internal:other - remote changed E which local deleted - use (c)hanged version or leave (d)eleted? c saved backup bundle to $TESTTMP/a6/.hg/strip-backup/*-backup.hg (glob) $ hg tglog @@ -374,8 +372,8 @@ merging H warning: conflicts during merge. merging H incomplete! (edit conflicts, then use 'hg resolve --mark') - abort: unresolved conflicts (see hg resolve, then hg rebase --continue) - [255] + unresolved conflicts (see hg resolve, then hg rebase --continue) + [1] $ hg resolve --all -t internal:local $ hg rebase -c saved backup bundle to $TESTTMP/a7/.hg/strip-backup/6215fafa5447-backup.hg (glob)
--- a/tests/test-rebase-interruptions.t Thu Apr 04 16:28:19 2013 -0500 +++ b/tests/test-rebase-interruptions.t Thu Apr 18 23:46:26 2013 -0500 @@ -61,8 +61,8 @@ merging A warning: conflicts during merge. merging A incomplete! (edit conflicts, then use 'hg resolve --mark') - abort: unresolved conflicts (see hg resolve, then hg rebase --continue) - [255] + unresolved conflicts (see hg resolve, then hg rebase --continue) + [1] Force a commit on C during the interruption: @@ -97,8 +97,8 @@ merging A warning: conflicts during merge. merging A incomplete! (edit conflicts, then use 'hg resolve --mark') - abort: unresolved conflicts (see hg resolve, then hg rebase --continue) - [255] + unresolved conflicts (see hg resolve, then hg rebase --continue) + [1] Solve the conflict and go on: @@ -151,8 +151,8 @@ merging A warning: conflicts during merge. merging A incomplete! (edit conflicts, then use 'hg resolve --mark') - abort: unresolved conflicts (see hg resolve, then hg rebase --continue) - [255] + unresolved conflicts (see hg resolve, then hg rebase --continue) + [1] Force a commit on B' during the interruption: @@ -222,8 +222,8 @@ merging A warning: conflicts during merge. merging A incomplete! (edit conflicts, then use 'hg resolve --mark') - abort: unresolved conflicts (see hg resolve, then hg rebase --continue) - [255] + unresolved conflicts (see hg resolve, then hg rebase --continue) + [1] Change phase on B and B'
--- a/tests/test-rebase-mq-skip.t Thu Apr 04 16:28:19 2013 -0500 +++ b/tests/test-rebase-mq-skip.t Thu Apr 18 23:46:26 2013 -0500 @@ -108,8 +108,8 @@ $ hg up -q qtip $ HGMERGE=internal:fail hg rebase - abort: unresolved conflicts (see hg resolve, then hg rebase --continue) - [255] + unresolved conflicts (see hg resolve, then hg rebase --continue) + [1] $ HGMERGE=internal:local hg resolve --all
--- a/tests/test-rebase-mq.t Thu Apr 04 16:28:19 2013 -0500 +++ b/tests/test-rebase-mq.t Thu Apr 18 23:46:26 2013 -0500 @@ -63,8 +63,8 @@ merging f warning: conflicts during merge. merging f incomplete! (edit conflicts, then use 'hg resolve --mark') - abort: unresolved conflicts (see hg resolve, then hg rebase --continue) - [255] + unresolved conflicts (see hg resolve, then hg rebase --continue) + [1] Fix the 1st conflict: @@ -74,8 +74,8 @@ merging f warning: conflicts during merge. merging f incomplete! (edit conflicts, then use 'hg resolve --mark') - abort: unresolved conflicts (see hg resolve, then hg rebase --continue) - [255] + unresolved conflicts (see hg resolve, then hg rebase --continue) + [1] Fix the 2nd conflict: @@ -102,6 +102,7 @@ # HG changeset patch # User test # Date ?????????? ? (glob) + # * (glob) # Node ID ???????????????????????????????????????? (glob) # Parent bac9ed9960d8992bcad75864a879fa76cadaf1b0 P0 @@ -124,6 +125,7 @@ # HG changeset patch # User test # Date ?????????? ? (glob) + # * (glob) # Node ID ???????????????????????????????????????? (glob) # Parent ???????????????????????????????????????? (glob) P1 @@ -209,6 +211,7 @@ # HG changeset patch # User test # Date ?????????? ? (glob) + # * (glob) # Node ID ???????????????????????????????????????? (glob) # Parent bac9ed9960d8992bcad75864a879fa76cadaf1b0 P0 (git) @@ -224,6 +227,7 @@ # HG changeset patch # User test # Date ?????????? ? (glob) + # * (glob) # Node ID ???????????????????????????????????????? (glob) # Parent ???????????????????????????????????????? (glob) P1
--- a/tests/test-rebase-parameters.t Thu Apr 04 16:28:19 2013 -0500 +++ b/tests/test-rebase-parameters.t Thu Apr 18 23:46:26 2013 -0500 @@ -416,8 +416,8 @@ $ cd b3 $ hg rebase -s 2 -d 1 --tool internal:fail - abort: unresolved conflicts (see hg resolve, then hg rebase --continue) - [255] + unresolved conflicts (see hg resolve, then hg rebase --continue) + [1] $ hg resolve -l U c2
--- a/tests/test-rebase-scenario-global.t Thu Apr 04 16:28:19 2013 -0500 +++ b/tests/test-rebase-scenario-global.t Thu Apr 18 23:46:26 2013 -0500 @@ -276,6 +276,9 @@ Check rebasing public changeset $ hg pull --config phases.publish=True -q -r 6 . # update phase of 6 + $ hg rebase -d 0 -b 6 + nothing to rebase + [1] $ hg rebase -d 5 -b 6 abort: can't rebase immutable changeset e1c4361dd923 (see hg help phases for details) @@ -564,7 +567,7 @@ $ cd .. -More complexe rebase with multiple roots +More complex rebase with multiple roots each root have a different common ancestor with the destination and this is a detach (setup)
--- a/tests/test-record.t Thu Apr 04 16:28:19 2013 -0500 +++ b/tests/test-record.t Thu Apr 18 23:46:26 2013 -0500 @@ -1036,10 +1036,10 @@ $ hg up -C 0 files updated, 0 files merged, 1 files removed, 0 files unresolved -Editing patch +Editing patch (and ignoring trailing text) $ cat > editor.sh << '__EOF__' - > sed -e 7d -e '5s/^-/ /' "$1" > tmp + > sed -e 7d -e '5s/^-/ /' -e '/^# ---/itrailing\nditto' "$1" > tmp > mv tmp "$1" > __EOF__ $ cat > editedfile << '__EOF__' @@ -1176,6 +1176,55 @@ +That change will not be committed +That is the second line +That line has been added + +Malformed patch - error handling + + $ cat > editor.sh << '__EOF__' + > sed -e '/^@/p' "$1" > tmp + > mv tmp "$1" + > __EOF__ + $ HGEDITOR="\"sh\" \"`pwd`/editor.sh\"" hg record <<EOF + > y + > e + > EOF + diff --git a/editedfile b/editedfile + 1 hunks, 3 lines changed + examine changes to 'editedfile'? [Ynesfdaq?] + @@ -1,3 +1,3 @@ + -This is the first line + -This change will be committed + -This is the third line + +This change will not be committed + +This is the second line + +This line has been added + record this change to 'editedfile'? [Ynesfdaq?] + abort: error parsing patch: unhandled transition: range -> range + [255] + +random text in random positions is still an error + + $ cat > editor.sh << '__EOF__' + > sed -e '/^@/iother' "$1" > tmp + > mv tmp "$1" + > __EOF__ + $ HGEDITOR="\"sh\" \"`pwd`/editor.sh\"" hg record <<EOF + > y + > e + > EOF + diff --git a/editedfile b/editedfile + 1 hunks, 3 lines changed + examine changes to 'editedfile'? [Ynesfdaq?] + @@ -1,3 +1,3 @@ + -This is the first line + -This change will be committed + -This is the third line + +This change will not be committed + +This is the second line + +This line has been added + record this change to 'editedfile'? [Ynesfdaq?] + abort: error parsing patch: unhandled transition: file -> other + [255] + $ hg up -C 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
--- a/tests/test-rename-dir-merge.t Thu Apr 04 16:28:19 2013 -0500 +++ b/tests/test-rename-dir-merge.t Thu Apr 18 23:46:26 2013 -0500 @@ -37,23 +37,20 @@ discovered dir src: 'a/' -> dst: 'b/' pending file src: 'a/c' -> dst: 'b/c' resolving manifests - overwrite: False, partial: False + branchmerge: True, force: False, partial: False ancestor: f9b20c0d4c51, local: ce36d17b18fb+, remote: 397f8b00a740 a/a: other deleted -> r a/b: other deleted -> r a/c: remote renamed directory to b/c -> d b/a: remote created -> g b/b: remote created -> g - updating: a/a 1/5 files (20.00%) removing a/a - updating: a/b 2/5 files (40.00%) removing a/b - updating: a/c 3/5 files (60.00%) + getting b/a + getting b/b + updating: b/b 4/5 files (80.00%) + updating: a/c 5/5 files (100.00%) moving a/c to b/c - updating: b/a 4/5 files (80.00%) - getting b/a - updating: b/b 5/5 files (100.00%) - getting b/b 3 files updated, 0 files merged, 2 files removed, 0 files unresolved (branch merge, don't forget to commit) @@ -88,7 +85,7 @@ discovered dir src: 'a/' -> dst: 'b/' pending file src: 'a/c' -> dst: 'b/c' resolving manifests - overwrite: False, partial: False + branchmerge: True, force: False, partial: False ancestor: f9b20c0d4c51, local: 397f8b00a740+, remote: ce36d17b18fb None: local renamed directory to b/c -> d updating:None 1/1 files (100.00%)
--- a/tests/test-rename-merge1.t Thu Apr 04 16:28:19 2013 -0500 +++ b/tests/test-rename-merge1.t Thu Apr 18 23:46:26 2013 -0500 @@ -33,25 +33,25 @@ src: 'a2' -> dst: 'b2' ! src: 'a2' -> dst: 'c2' ! checking for directory renames - a2: divergent renames -> dr resolving manifests - overwrite: False, partial: False + branchmerge: True, force: False, partial: False ancestor: af1939970a1c, local: 044f8520aeeb+, remote: 85c198ef2f6c a: remote moved to b -> m + preserving a for resolve of b + a2: divergent renames -> dr b2: remote created -> g - preserving a for resolve of b removing a - updating: a 1/3 files (33.33%) + getting b2 + updating: b2 1/3 files (33.33%) + updating: a 2/3 files (66.67%) picked tool 'internal:merge' for b (binary False symlink False) merging a and b to b my b@044f8520aeeb+ other b@85c198ef2f6c ancestor a@af1939970a1c premerge successful - updating: a2 2/3 files (66.67%) + updating: a2 3/3 files (100.00%) note: possible conflict - a2 was renamed multiple times to: c2 b2 - updating: b2 3/3 files (100.00%) - getting b2 1 files updated, 1 files merged, 0 files removed, 0 files unresolved (branch merge, don't forget to commit) @@ -178,16 +178,16 @@ all copies found (* = to merge, ! = divergent, % = renamed and deleted): src: 'file' -> dst: 'newfile' % checking for directory renames + resolving manifests + branchmerge: True, force: False, partial: False + ancestor: 19d7f95df299, local: 0084274f6b67+, remote: 5d32493049f0 file: rename and delete -> rd - resolving manifests - overwrite: False, partial: False - ancestor: 19d7f95df299, local: 0084274f6b67+, remote: 5d32493049f0 newfile: remote created -> g - updating: file 1/2 files (50.00%) + getting newfile + updating: newfile 1/2 files (50.00%) + updating: file 2/2 files (100.00%) note: possible conflict - file was deleted and renamed to: newfile - updating: newfile 2/2 files (100.00%) - getting newfile 1 files updated, 0 files merged, 0 files removed, 0 files unresolved (branch merge, don't forget to commit) $ hg status
--- a/tests/test-rename-merge2.t Thu Apr 04 16:28:19 2013 -0500 +++ b/tests/test-rename-merge2.t Thu Apr 18 23:46:26 2013 -0500 @@ -84,12 +84,12 @@ src: 'a' -> dst: 'b' * checking for directory renames resolving manifests - overwrite: False, partial: False + branchmerge: True, force: False, partial: False ancestor: 924404dff337, local: e300d1c794ec+, remote: 4ce40f5aca24 + a: remote copied to b -> m + preserving a for resolve of b rev: versions differ -> m - a: remote copied to b -> m - preserving a for resolve of b - preserving rev for resolve of rev + preserving rev for resolve of rev updating: a 1/2 files (50.00%) picked tool 'python ../merge' for b (binary False symlink False) merging a and b to b @@ -119,15 +119,15 @@ src: 'a' -> dst: 'b' * checking for directory renames resolving manifests - overwrite: False, partial: False + branchmerge: True, force: False, partial: False ancestor: 924404dff337, local: 86a2aa42fc76+, remote: f4db7e329e71 a: remote is newer -> g b: local copied/moved to a -> m + preserving b for resolve of b rev: versions differ -> m - preserving b for resolve of b - preserving rev for resolve of rev + preserving rev for resolve of rev + getting a updating: a 1/3 files (33.33%) - getting a updating: b 2/3 files (66.67%) picked tool 'python ../merge' for b (binary False symlink False) merging b and a to b @@ -157,12 +157,12 @@ src: 'a' -> dst: 'b' * checking for directory renames resolving manifests - overwrite: False, partial: False + branchmerge: True, force: False, partial: False ancestor: 924404dff337, local: e300d1c794ec+, remote: bdb19105162a + a: remote moved to b -> m + preserving a for resolve of b rev: versions differ -> m - a: remote moved to b -> m - preserving a for resolve of b - preserving rev for resolve of rev + preserving rev for resolve of rev removing a updating: a 1/2 files (50.00%) picked tool 'python ../merge' for b (binary False symlink False) @@ -192,12 +192,12 @@ src: 'a' -> dst: 'b' * checking for directory renames resolving manifests - overwrite: False, partial: False + branchmerge: True, force: False, partial: False ancestor: 924404dff337, local: 02963e448370+, remote: f4db7e329e71 b: local copied/moved to a -> m + preserving b for resolve of b rev: versions differ -> m - preserving b for resolve of b - preserving rev for resolve of rev + preserving rev for resolve of rev updating: b 1/2 files (50.00%) picked tool 'python ../merge' for b (binary False symlink False) merging b and a to b @@ -226,13 +226,13 @@ src: 'a' -> dst: 'b' checking for directory renames resolving manifests - overwrite: False, partial: False + branchmerge: True, force: False, partial: False ancestor: 924404dff337, local: 94b33a1b7f2d+, remote: 4ce40f5aca24 - rev: versions differ -> m b: remote created -> g - preserving rev for resolve of rev + rev: versions differ -> m + preserving rev for resolve of rev + getting b updating: b 1/2 files (50.00%) - getting b updating: rev 2/2 files (100.00%) picked tool 'python ../merge' for rev (binary False symlink False) merging rev @@ -256,10 +256,10 @@ src: 'a' -> dst: 'b' checking for directory renames resolving manifests - overwrite: False, partial: False + branchmerge: True, force: False, partial: False ancestor: 924404dff337, local: 86a2aa42fc76+, remote: 97c705ade336 rev: versions differ -> m - preserving rev for resolve of rev + preserving rev for resolve of rev updating: rev 1/1 files (100.00%) picked tool 'python ../merge' for rev (binary False symlink False) merging rev @@ -283,16 +283,15 @@ src: 'a' -> dst: 'b' checking for directory renames resolving manifests - overwrite: False, partial: False + branchmerge: True, force: False, partial: False ancestor: 924404dff337, local: 94b33a1b7f2d+, remote: bdb19105162a a: other deleted -> r + b: remote created -> g rev: versions differ -> m - b: remote created -> g - preserving rev for resolve of rev - updating: a 1/3 files (33.33%) + preserving rev for resolve of rev removing a + getting b updating: b 2/3 files (66.67%) - getting b updating: rev 3/3 files (100.00%) picked tool 'python ../merge' for rev (binary False symlink False) merging rev @@ -315,10 +314,10 @@ src: 'a' -> dst: 'b' checking for directory renames resolving manifests - overwrite: False, partial: False + branchmerge: True, force: False, partial: False ancestor: 924404dff337, local: 02963e448370+, remote: 97c705ade336 rev: versions differ -> m - preserving rev for resolve of rev + preserving rev for resolve of rev updating: rev 1/1 files (100.00%) picked tool 'python ../merge' for rev (binary False symlink False) merging rev @@ -336,12 +335,12 @@ -------------- searching for copies back to rev 1 resolving manifests - overwrite: False, partial: False + branchmerge: True, force: False, partial: False ancestor: 924404dff337, local: 62e7bf090eba+, remote: 49b6d8032493 b: versions differ -> m + preserving b for resolve of b rev: versions differ -> m - preserving b for resolve of b - preserving rev for resolve of rev + preserving rev for resolve of rev updating: b 1/2 files (50.00%) picked tool 'python ../merge' for b (binary False symlink False) merging b @@ -373,19 +372,19 @@ src: 'a' -> dst: 'b' ! src: 'a' -> dst: 'c' ! checking for directory renames - a: divergent renames -> dr resolving manifests - overwrite: False, partial: False + branchmerge: True, force: False, partial: False ancestor: 924404dff337, local: 02963e448370+, remote: fe905ef2c33e - rev: versions differ -> m + a: divergent renames -> dr c: remote created -> g - preserving rev for resolve of rev - updating: a 1/3 files (33.33%) + rev: versions differ -> m + preserving rev for resolve of rev + getting c + updating: c 1/3 files (33.33%) + updating: a 2/3 files (66.67%) note: possible conflict - a was renamed multiple times to: b c - updating: c 2/3 files (66.67%) - getting c updating: rev 3/3 files (100.00%) picked tool 'python ../merge' for rev (binary False symlink False) merging rev @@ -404,12 +403,12 @@ -------------- searching for copies back to rev 1 resolving manifests - overwrite: False, partial: False + branchmerge: True, force: False, partial: False ancestor: 924404dff337, local: 86a2aa42fc76+, remote: af30c7647fc7 b: versions differ -> m + preserving b for resolve of b rev: versions differ -> m - preserving b for resolve of b - preserving rev for resolve of rev + preserving rev for resolve of rev updating: b 1/2 files (50.00%) picked tool 'python ../merge' for b (binary False symlink False) merging b @@ -432,15 +431,15 @@ -------------- searching for copies back to rev 1 resolving manifests - overwrite: False, partial: False + branchmerge: True, force: False, partial: False ancestor: 924404dff337, local: 59318016310c+, remote: bdb19105162a a: other deleted -> r b: versions differ -> m + preserving b for resolve of b rev: versions differ -> m - preserving b for resolve of b - preserving rev for resolve of rev + preserving rev for resolve of rev + removing a updating: a 1/3 files (33.33%) - removing a updating: b 2/3 files (66.67%) picked tool 'python ../merge' for b (binary False symlink False) merging b @@ -462,15 +461,15 @@ -------------- searching for copies back to rev 1 resolving manifests - overwrite: False, partial: False + branchmerge: True, force: False, partial: False ancestor: 924404dff337, local: 86a2aa42fc76+, remote: 8dbce441892a a: remote is newer -> g b: versions differ -> m + preserving b for resolve of b rev: versions differ -> m - preserving b for resolve of b - preserving rev for resolve of rev + preserving rev for resolve of rev + getting a updating: a 1/3 files (33.33%) - getting a updating: b 2/3 files (66.67%) picked tool 'python ../merge' for b (binary False symlink False) merging b @@ -493,15 +492,15 @@ -------------- searching for copies back to rev 1 resolving manifests - overwrite: False, partial: False + branchmerge: True, force: False, partial: False ancestor: 924404dff337, local: 59318016310c+, remote: bdb19105162a a: other deleted -> r b: versions differ -> m + preserving b for resolve of b rev: versions differ -> m - preserving b for resolve of b - preserving rev for resolve of rev + preserving rev for resolve of rev + removing a updating: a 1/3 files (33.33%) - removing a updating: b 2/3 files (66.67%) picked tool 'python ../merge' for b (binary False symlink False) merging b @@ -523,15 +522,15 @@ -------------- searching for copies back to rev 1 resolving manifests - overwrite: False, partial: False + branchmerge: True, force: False, partial: False ancestor: 924404dff337, local: 86a2aa42fc76+, remote: 8dbce441892a a: remote is newer -> g b: versions differ -> m + preserving b for resolve of b rev: versions differ -> m - preserving b for resolve of b - preserving rev for resolve of rev + preserving rev for resolve of rev + getting a updating: a 1/3 files (33.33%) - getting a updating: b 2/3 files (66.67%) picked tool 'python ../merge' for b (binary False symlink False) merging b @@ -554,12 +553,12 @@ -------------- searching for copies back to rev 1 resolving manifests - overwrite: False, partial: False + branchmerge: True, force: False, partial: False ancestor: 924404dff337, local: 0b76e65c8289+, remote: 4ce40f5aca24 b: versions differ -> m + preserving b for resolve of b rev: versions differ -> m - preserving b for resolve of b - preserving rev for resolve of rev + preserving rev for resolve of rev updating: b 1/2 files (50.00%) picked tool 'python ../merge' for b (binary False symlink False) merging b @@ -582,17 +581,17 @@ -------------- searching for copies back to rev 1 resolving manifests - overwrite: False, partial: False + branchmerge: True, force: False, partial: False ancestor: 924404dff337, local: 02963e448370+, remote: 8dbce441892a - b: versions differ -> m - rev: versions differ -> m remote changed a which local deleted use (c)hanged version or leave (d)eleted? c a: prompt recreating -> g - preserving b for resolve of b - preserving rev for resolve of rev + b: versions differ -> m + preserving b for resolve of b + rev: versions differ -> m + preserving rev for resolve of rev + getting a updating: a 1/3 files (33.33%) - getting a updating: b 2/3 files (66.67%) picked tool 'python ../merge' for b (binary False symlink False) merging b @@ -615,15 +614,15 @@ -------------- searching for copies back to rev 1 resolving manifests - overwrite: False, partial: False + branchmerge: True, force: False, partial: False ancestor: 924404dff337, local: 0b76e65c8289+, remote: bdb19105162a - local changed a which remote deleted + local changed a which remote deleted use (c)hanged version or (d)elete? c a: prompt keep -> a b: versions differ -> m + preserving b for resolve of b rev: versions differ -> m - preserving b for resolve of b - preserving rev for resolve of rev + preserving rev for resolve of rev updating: a 1/3 files (33.33%) updating: b 2/3 files (66.67%) picked tool 'python ../merge' for b (binary False symlink False) @@ -652,12 +651,12 @@ src: 'a' -> dst: 'b' * checking for directory renames resolving manifests - overwrite: False, partial: False + branchmerge: True, force: False, partial: False ancestor: 924404dff337, local: e300d1c794ec+, remote: 49b6d8032493 + a: remote moved to b -> m + preserving a for resolve of b rev: versions differ -> m - a: remote moved to b -> m - preserving a for resolve of b - preserving rev for resolve of rev + preserving rev for resolve of rev removing a updating: a 1/2 files (50.00%) picked tool 'python ../merge' for b (binary False symlink False) @@ -686,12 +685,12 @@ src: 'a' -> dst: 'b' * checking for directory renames resolving manifests - overwrite: False, partial: False + branchmerge: True, force: False, partial: False ancestor: 924404dff337, local: 62e7bf090eba+, remote: f4db7e329e71 b: local copied/moved to a -> m + preserving b for resolve of b rev: versions differ -> m - preserving b for resolve of b - preserving rev for resolve of rev + preserving rev for resolve of rev updating: b 1/2 files (50.00%) picked tool 'python ../merge' for b (binary False symlink False) merging b and a to b @@ -724,20 +723,20 @@ src: 'a' -> dst: 'b' * checking for directory renames resolving manifests - overwrite: False, partial: False + branchmerge: True, force: False, partial: False ancestor: 924404dff337, local: 02963e448370+, remote: 2b958612230f b: local copied/moved to a -> m - rev: versions differ -> m + preserving b for resolve of b c: remote created -> g - preserving b for resolve of b - preserving rev for resolve of rev - updating: b 1/3 files (33.33%) + rev: versions differ -> m + preserving rev for resolve of rev + getting c + updating: c 1/3 files (33.33%) + updating: b 2/3 files (66.67%) picked tool 'python ../merge' for b (binary False symlink False) merging b and a to b my b@02963e448370+ other a@2b958612230f ancestor a@924404dff337 premerge successful - updating: c 2/3 files (66.67%) - getting c updating: rev 3/3 files (100.00%) picked tool 'python ../merge' for rev (binary False symlink False) merging rev
--- a/tests/test-revset.t Thu Apr 04 16:28:19 2013 -0500 +++ b/tests/test-revset.t Thu Apr 18 23:46:26 2013 -0500 @@ -78,9 +78,6 @@ $ hg branch all marked working directory as branch all (branches are permanent and global, did you want a bookmark?) - $ hg ci --close-branch -Aqm8 - abort: can only close branch heads - [255] $ hg co 4 0 files updated, 0 files merged, 0 files removed, 0 files unresolved @@ -218,17 +215,29 @@ $ log 'date(2005) and 1::' 4 +ancestor can accept 0 or more arguments + + $ log 'ancestor()' $ log 'ancestor(1)' - hg: parse error: ancestor requires two arguments - [255] + 1 $ log 'ancestor(4,5)' 1 $ log 'ancestor(4,5) and 4' + $ log 'ancestor(0,0,1,3)' + 0 + $ log 'ancestor(3,1,5,3,5,1)' + 1 + $ log 'ancestor(0,1,3,5)' + 0 + $ log 'ancestor(1,2,3,4,5)' + 1 $ log 'ancestors(5)' 0 1 3 5 + $ log 'ancestor(ancestors(5))' + 0 $ log 'author(bob)' 2 $ log 'author("re:bob|test")' @@ -425,8 +434,6 @@ $ log 'tag("literal:1.0")' 6 $ log 'tag("re:0..*")' - abort: no tags exist that match '0..*' - [255] $ log 'tag(unknown)' abort: tag 'unknown' does not exist
--- a/tests/test-schemes.t Thu Apr 04 16:28:19 2013 -0500 +++ b/tests/test-schemes.t Thu Apr 18 23:46:26 2013 -0500 @@ -14,6 +14,15 @@ $ echo a > a $ hg ci -Am initial adding a + +invalid scheme + + $ hg log -R z:z + abort: no '://' in scheme url 'z:z' + [255] + +http scheme + $ hg serve -n test -p $HGPORT -d --pid-file=hg.pid -A access.log -E errors.log $ cat hg.pid >> $DAEMON_PIDS $ hg incoming l://
--- a/tests/test-serve.t Thu Apr 04 16:28:19 2013 -0500 +++ b/tests/test-serve.t Thu Apr 18 23:46:26 2013 -0500 @@ -9,12 +9,7 @@ > cat hg.pid >> "$DAEMON_PIDS" > echo % errors > cat errors.log - > if [ "$KILLQUIETLY" = "Y" ]; then - > kill `cat hg.pid` 2>/dev/null - > else - > kill `cat hg.pid` - > fi - > while kill -0 `cat hg.pid` 2>/dev/null; do sleep 0; done + > "$TESTDIR/killdaemons.py" hg.pid > } $ hg init test
--- a/tests/test-subrepo-git.t Thu Apr 04 16:28:19 2013 -0500 +++ b/tests/test-subrepo-git.t Thu Apr 18 23:46:26 2013 -0500 @@ -238,6 +238,32 @@ source ../gitroot revision 32a343883b74769118bb1d3b4b1fbf9156f4dddc +create a new git branch + + $ cd s + $ git checkout -b b2 + Switched to a new branch 'b2' + $ echo a>a + $ git add a + $ git commit -qm 'add a' + $ cd .. + $ hg commit -m 'add branch in s' + +pulling new git branch should not create tracking branch named 'origin/b2' +(issue3870) + $ cd ../td/s + $ git remote set-url origin $TESTTMP/tb/s + $ git branch --no-track oldtesting + $ cd .. + $ hg pull -q ../tb + $ hg up + From $TESTTMP/tb/s + * [new branch] b2 -> origin/b2 + Previous HEAD position was f47b465... merge + Switched to a new branch 'b2' + pulling subrepo s from $TESTTMP/tb/s + 1 files updated, 0 files merged, 0 files removed, 0 files unresolved + update to a revision without the subrepo, keeping the local git repository $ cd ../t
--- a/tests/test-subrepo.t Thu Apr 04 16:28:19 2013 -0500 +++ b/tests/test-subrepo.t Thu Apr 18 23:46:26 2013 -0500 @@ -203,20 +203,19 @@ $ hg merge 6 --debug # test change searching for copies back to rev 2 resolving manifests - overwrite: False, partial: False + branchmerge: True, force: False, partial: False ancestor: 1f14a2e2d3ec, local: f0d2028bf86d+, remote: 1831e14459c4 .hgsubstate: versions differ -> m updating: .hgsubstate 1/1 files (100.00%) subrepo merge f0d2028bf86d+ 1831e14459c4 1f14a2e2d3ec subrepo t: other changed, get t:6747d179aa9a688023c4b0cad32e4c92bb7f34ad:hg getting subrepo t - searching for copies back to rev 1 resolving manifests - overwrite: False, partial: False + branchmerge: False, force: False, partial: False ancestor: 60ca1237c194, local: 60ca1237c194+, remote: 6747d179aa9a t: remote is newer -> g + getting t updating: t 1/1 files (100.00%) - getting t 0 files updated, 0 files merged, 0 files removed, 0 files unresolved (branch merge, don't forget to commit) $ hg debugsub @@ -232,7 +231,7 @@ $ HGMERGE=internal:merge hg merge --debug 7 # test conflict searching for copies back to rev 2 resolving manifests - overwrite: False, partial: False + branchmerge: True, force: False, partial: False ancestor: 1831e14459c4, local: e45c8b14af55+, remote: f94576341bcf .hgsubstate: versions differ -> m updating: .hgsubstate 1/1 files (100.00%) @@ -241,10 +240,10 @@ merging subrepo t searching for copies back to rev 2 resolving manifests - overwrite: False, partial: False + branchmerge: True, force: False, partial: False ancestor: 6747d179aa9a, local: 20a0db6fbf6c+, remote: 7af322bc1198 t: versions differ -> m - preserving t for resolve of t + preserving t for resolve of t updating: t 1/1 files (100.00%) picked tool 'internal:merge' for t (binary False symlink False) merging t @@ -270,9 +269,9 @@ $ cd .. $ hg clone t tc updating to branch default - cloning subrepo s from $TESTTMP/t/s (glob) + cloning subrepo s from $TESTTMP/t/s cloning subrepo s/ss from $TESTTMP/t/s/ss (glob) - cloning subrepo t from $TESTTMP/t/t (glob) + cloning subrepo t from $TESTTMP/t/t 3 files updated, 0 files merged, 0 files removed, 0 files unresolved $ cd tc $ hg debugsub @@ -290,13 +289,9 @@ committing subrepository t $ hg push pushing to $TESTTMP/t (glob) - pushing subrepo s/ss to $TESTTMP/t/s/ss (glob) - searching for changes - no changes found - pushing subrepo s to $TESTTMP/t/s (glob) - searching for changes - no changes found - pushing subrepo t to $TESTTMP/t/t (glob) + no changes made to subrepo s/ss since last push to $TESTTMP/t/s/ss + no changes made to subrepo s since last push to $TESTTMP/t/s + pushing subrepo t to $TESTTMP/t/t searching for changes adding changesets adding manifests @@ -315,10 +310,8 @@ committing subrepository s $ hg push pushing to $TESTTMP/t (glob) - pushing subrepo s/ss to $TESTTMP/t/s/ss (glob) - searching for changes - no changes found - pushing subrepo s to $TESTTMP/t/s (glob) + no changes made to subrepo s/ss since last push to $TESTTMP/t/s/ss + pushing subrepo s to $TESTTMP/t/s searching for changes abort: push creates new remote head 12a213df6fa9! (in subrepo s) (did you forget to merge? use push -f to force) @@ -328,13 +321,13 @@ pushing subrepo s/ss to $TESTTMP/t/s/ss (glob) searching for changes no changes found - pushing subrepo s to $TESTTMP/t/s (glob) + pushing subrepo s to $TESTTMP/t/s searching for changes adding changesets adding manifests adding file changes added 1 changesets with 1 changes to 1 files (+1 heads) - pushing subrepo t to $TESTTMP/t/t (glob) + pushing subrepo t to $TESTTMP/t/t searching for changes no changes found searching for changes @@ -343,6 +336,122 @@ adding file changes added 1 changesets with 1 changes to 1 files +check that unmodified subrepos are not pushed + + $ hg clone . ../tcc + updating to branch default + cloning subrepo s from $TESTTMP/tc/s + cloning subrepo s/ss from $TESTTMP/tc/s/ss + cloning subrepo t from $TESTTMP/tc/t + 3 files updated, 0 files merged, 0 files removed, 0 files unresolved + +the subrepos on the new clone have nothing to push to its source + + $ hg push -R ../tcc . + pushing to . + no changes made to subrepo s/ss since last push to s/ss + no changes made to subrepo s since last push to s + no changes made to subrepo t since last push to t + searching for changes + no changes found + [1] + +the subrepos on the source do not have a clean store versus the clone target +because they were never explicitly pushed to the source + + $ hg push ../tcc + pushing to ../tcc + pushing subrepo s/ss to ../tcc/s/ss + searching for changes + no changes found + pushing subrepo s to ../tcc/s + searching for changes + no changes found + pushing subrepo t to ../tcc/t + searching for changes + no changes found + searching for changes + no changes found + [1] + +after push their stores become clean + + $ hg push ../tcc + pushing to ../tcc + no changes made to subrepo s/ss since last push to ../tcc/s/ss + no changes made to subrepo s since last push to ../tcc/s + no changes made to subrepo t since last push to ../tcc/t + searching for changes + no changes found + [1] + +updating a subrepo to a different revision or changing +its working directory does not make its store dirty + + $ hg -R s update '.^' + 1 files updated, 0 files merged, 0 files removed, 0 files unresolved + $ hg push + pushing to $TESTTMP/t + no changes made to subrepo s/ss since last push to $TESTTMP/t/s/ss + no changes made to subrepo s since last push to $TESTTMP/t/s + no changes made to subrepo t since last push to $TESTTMP/t/t + searching for changes + no changes found + [1] + $ echo foo >> s/a + $ hg push + pushing to $TESTTMP/t + no changes made to subrepo s/ss since last push to $TESTTMP/t/s/ss + no changes made to subrepo s since last push to $TESTTMP/t/s + no changes made to subrepo t since last push to $TESTTMP/t/t + searching for changes + no changes found + [1] + $ hg -R s update -C tip + 1 files updated, 0 files merged, 0 files removed, 0 files unresolved + +committing into a subrepo makes its store (but not its parent's store) dirty + + $ echo foo >> s/ss/a + $ hg -R s/ss commit -m 'test dirty store detection' + $ hg push + pushing to $TESTTMP/t + pushing subrepo s/ss to $TESTTMP/t/s/ss + searching for changes + adding changesets + adding manifests + adding file changes + added 1 changesets with 1 changes to 1 files + no changes made to subrepo s since last push to $TESTTMP/t/s + no changes made to subrepo t since last push to $TESTTMP/t/t + searching for changes + no changes found + [1] + +a subrepo store may be clean versus one repo but not versus another + + $ hg push + pushing to $TESTTMP/t + no changes made to subrepo s/ss since last push to $TESTTMP/t/s/ss + no changes made to subrepo s since last push to $TESTTMP/t/s + no changes made to subrepo t since last push to $TESTTMP/t/t + searching for changes + no changes found + [1] + $ hg push ../tcc + pushing to ../tcc + pushing subrepo s/ss to ../tcc/s/ss + searching for changes + adding changesets + adding manifests + adding file changes + added 1 changesets with 1 changes to 1 files + no changes made to subrepo s since last push to ../tcc/s + no changes made to subrepo t since last push to ../tcc/t + searching for changes + no changes found + [1] + update $ cd ../t @@ -352,6 +461,20 @@ $ hg ci -m13 committing subrepository t +backout calls revert internally with minimal opts, which should not raise +KeyError + + $ hg backout ".^" + reverting .hgsubstate + reverting subrepo s + reverting s/a + reverting subrepo ss + reverting subrepo t + 0 files updated, 0 files merged, 0 files removed, 0 files unresolved + + $ hg up -C # discard changes + 1 files updated, 0 files merged, 0 files removed, 0 files unresolved + pull $ cd ../tc @@ -367,13 +490,13 @@ should pull t $ hg up - pulling subrepo t from $TESTTMP/t/t (glob) + pulling subrepo t from $TESTTMP/t/t searching for changes adding changesets adding manifests adding file changes added 1 changesets with 1 changes to 1 files - 1 files updated, 0 files merged, 0 files removed, 0 files unresolved + 0 files updated, 0 files merged, 0 files removed, 0 files unresolved $ cat t/t blah @@ -573,7 +696,7 @@ adding .hgsub $ hg clone repo repo2 updating to branch default - cloning subrepo s from $TESTTMP/repo/s (glob) + cloning subrepo s from $TESTTMP/repo/s 2 files updated, 0 files merged, 0 files removed, 0 files unresolved $ hg -q -R repo2 pull -u $ echo 1 > repo2/s/a @@ -648,6 +771,11 @@ abort: default path for subrepository not found (in subrepo sub/repo) (glob) [255] +Ensure a full traceback, not just the SubrepoAbort part + + $ hg -R issue1852b update --traceback 2>&1 | grep 'raise util\.Abort' + raise util.Abort(_("default path for subrepository not found")) + Pull -u now doesn't help $ hg -R issue1852b pull -u issue1852a
--- a/tests/test-symlinks.t Thu Apr 04 16:28:19 2013 -0500 +++ b/tests/test-symlinks.t Thu Apr 18 23:46:26 2013 -0500 @@ -149,6 +149,10 @@ adding foo/a $ mv foo bar $ ln -s bar foo + $ hg status + ! foo/a + ? bar/a + ? foo now addremove should remove old files
--- a/tests/test-tag.t Thu Apr 04 16:28:19 2013 -0500 +++ b/tests/test-tag.t Thu Apr 18 23:46:26 2013 -0500 @@ -264,11 +264,11 @@ $ hg init empty $ hg tag -R empty nullrev - abort: null revision specified + abort: cannot tag null revision [255] $ hg tag -R empty -r 00000000000 -f nulltag - abort: null revision specified + abort: cannot tag null revision [255] $ cd ..
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tests/test-unionrepo.t Thu Apr 18 23:46:26 2013 -0500 @@ -0,0 +1,148 @@ +Test unionrepo functionality + +Create one repository + + $ hg init repo1 + $ cd repo1 + $ touch repo1-0 + $ echo repo1-0 > f + $ hg ci -Aqmrepo1-0 + $ touch repo1-1 + $ echo repo1-1 >> f + $ hg ci -Aqmrepo1-1 + $ touch repo1-2 + $ echo repo1-2 >> f + $ hg ci -Aqmrepo1-2 + $ hg log --template '{rev}:{node|short} {desc|firstline}\n' + 2:68c0685446a3 repo1-2 + 1:8a58db72e69d repo1-1 + 0:f093fec0529b repo1-0 + $ tip1=`hg id -q` + $ cd .. + +- and a clone with a not-completely-trivial history + + $ hg clone -q repo1 --rev 0 repo2 + $ cd repo2 + $ touch repo2-1 + $ sed '1irepo2-1 at top' f > f.tmp + $ mv f.tmp f + $ hg ci -Aqmrepo2-1 + $ touch repo2-2 + $ hg pull -q ../repo1 -r 1 + $ hg merge -q + $ hg ci -Aqmrepo2-2-merge + $ touch repo2-3 + $ echo repo2-3 >> f + $ hg ci -mrepo2-3 + $ hg log --template '{rev}:{node|short} {desc|firstline}\n' + 4:2f0d178c469c repo2-3 + 3:9e6fb3e0b9da repo2-2-merge + 2:8a58db72e69d repo1-1 + 1:c337dba826e7 repo2-1 + 0:f093fec0529b repo1-0 + $ cd .. + +revisions from repo2 appear as appended / pulled to repo1 + + $ hg -R union:repo1+repo2 log --template '{rev}:{node|short} {desc|firstline}\n' + 5:2f0d178c469c repo2-3 + 4:9e6fb3e0b9da repo2-2-merge + 3:c337dba826e7 repo2-1 + 2:68c0685446a3 repo1-2 + 1:8a58db72e69d repo1-1 + 0:f093fec0529b repo1-0 + +manifest can be retrieved for revisions in both repos + + $ hg -R union:repo1+repo2 mani -r $tip1 + f + repo1-0 + repo1-1 + repo1-2 + $ hg -R union:repo1+repo2 mani -r 4 + f + repo1-0 + repo1-1 + repo2-1 + repo2-2 + +files can be retrieved form both repos + + $ hg -R repo1 cat repo1/f -r2 + repo1-0 + repo1-1 + repo1-2 + + $ hg -R union:repo1+repo2 cat -r$tip1 repo1/f + repo1-0 + repo1-1 + repo1-2 + + $ hg -R union:repo1+repo2 cat -r4 $TESTTMP/repo1/f + repo2-1 at top + repo1-0 + repo1-1 + +files can be compared across repos + + $ hg -R union:repo1+repo2 diff -r$tip1 -rtip + diff -r 68c0685446a3 -r 2f0d178c469c f + --- a/f Thu Jan 01 00:00:00 1970 +0000 + +++ b/f Thu Jan 01 00:00:00 1970 +0000 + @@ -1,3 +1,4 @@ + +repo2-1 at top + repo1-0 + repo1-1 + -repo1-2 + +repo2-3 + +heads from both repos are found correctly + + $ hg -R union:repo1+repo2 heads --template '{rev}:{node|short} {desc|firstline}\n' + 5:2f0d178c469c repo2-3 + 2:68c0685446a3 repo1-2 + +revsets works across repos + + $ hg -R union:repo1+repo2 id -r "ancestor($tip1, 5)" + 8a58db72e69d + +annotate works - an indication that linkrevs works + + $ hg --cwd repo1 -R union:../repo2 annotate $TESTTMP/repo1/f -r tip + 3: repo2-1 at top + 0: repo1-0 + 1: repo1-1 + 5: repo2-3 + +union repos can be cloned ... and clones works correctly + + $ hg clone -U union:repo1+repo2 repo3 + requesting all changes + adding changesets + adding manifests + adding file changes + added 6 changesets with 11 changes to 6 files (+1 heads) + + $ hg -R repo3 paths + default = union:repo1+repo2 + + $ hg -R repo3 verify + checking changesets + checking manifests + crosschecking files in changesets and manifests + checking files + 6 files, 6 changesets, 11 total revisions + + $ hg -R repo3 heads --template '{rev}:{node|short} {desc|firstline}\n' + 5:2f0d178c469c repo2-3 + 2:68c0685446a3 repo1-2 + + $ hg -R repo3 log --template '{rev}:{node|short} {desc|firstline}\n' + 5:2f0d178c469c repo2-3 + 4:9e6fb3e0b9da repo2-2-merge + 3:c337dba826e7 repo2-1 + 2:68c0685446a3 repo1-2 + 1:8a58db72e69d repo1-1 + 0:f093fec0529b repo1-0
--- a/tests/test-up-local-change.t Thu Apr 04 16:28:19 2013 -0500 +++ b/tests/test-up-local-change.t Thu Apr 18 23:46:26 2013 -0500 @@ -44,17 +44,17 @@ unmatched files in other: b resolving manifests - overwrite: False, partial: False + branchmerge: False, force: False, partial: False ancestor: c19d34741b0a, local: c19d34741b0a+, remote: 1e71731e6fbb a: versions differ -> m + preserving a for resolve of a b: remote created -> g - preserving a for resolve of a - updating: a 1/2 files (50.00%) + getting b + updating: b 1/2 files (50.00%) + updating: a 2/2 files (100.00%) picked tool 'true' for a (binary False symlink False) merging a my a@c19d34741b0a+ other a@1e71731e6fbb ancestor a@c19d34741b0a - updating: b 2/2 files (100.00%) - getting b 1 files updated, 1 files merged, 0 files removed, 0 files unresolved $ hg parents changeset: 1:1e71731e6fbb @@ -65,13 +65,13 @@ $ hg --debug up 0 resolving manifests - overwrite: False, partial: False + branchmerge: False, force: False, partial: False ancestor: 1e71731e6fbb, local: 1e71731e6fbb+, remote: c19d34741b0a - a: versions differ -> m b: other deleted -> r - preserving a for resolve of a + a: versions differ -> m + preserving a for resolve of a + removing b updating: b 1/2 files (50.00%) - removing b updating: a 2/2 files (100.00%) picked tool 'true' for a (binary False symlink False) merging a @@ -98,17 +98,17 @@ unmatched files in other: b resolving manifests - overwrite: False, partial: False + branchmerge: False, force: False, partial: False ancestor: c19d34741b0a, local: c19d34741b0a+, remote: 1e71731e6fbb a: versions differ -> m + preserving a for resolve of a b: remote created -> g - preserving a for resolve of a - updating: a 1/2 files (50.00%) + getting b + updating: b 1/2 files (50.00%) + updating: a 2/2 files (100.00%) picked tool 'true' for a (binary False symlink False) merging a my a@c19d34741b0a+ other a@1e71731e6fbb ancestor a@c19d34741b0a - updating: b 2/2 files (100.00%) - getting b 1 files updated, 1 files merged, 0 files removed, 0 files unresolved $ hg parents changeset: 1:1e71731e6fbb @@ -176,12 +176,12 @@ $ hg --debug merge -f searching for copies back to rev 1 resolving manifests - overwrite: False, partial: False + branchmerge: True, force: True, partial: False ancestor: c19d34741b0a, local: 1e71731e6fbb+, remote: 83c51d0caff4 a: versions differ -> m + preserving a for resolve of a b: versions differ -> m - preserving a for resolve of a - preserving b for resolve of b + preserving b for resolve of b updating: a 1/2 files (50.00%) picked tool 'true' for a (binary False symlink False) merging a
--- a/tests/test-update-branches.t Thu Apr 04 16:28:19 2013 -0500 +++ b/tests/test-update-branches.t Thu Apr 18 23:46:26 2013 -0500 @@ -164,3 +164,63 @@ parent=1 M foo +Test obsolescence behavior +--------------------------------------------------------------------- + +successors should be taken in account when checking head destination + + $ cat << EOF >> $HGRCPATH + > [extensions] + > obs=$TESTTMP/obs.py + > [ui] + > logtemplate={rev}:{node|short} {desc|firstline} + > EOF + $ cat > $TESTTMP/obs.py << EOF + > import mercurial.obsolete + > mercurial.obsolete._enabled = True + > EOF + +Test no-argument update to a successor of an obsoleted changeset + + $ hg log -G + o 5:ff252e8273df 5 + | + o 4:d047485b3896 4 + | + | o 3:6efa171f091b 3 + | | + | | o 2:bd10386d478c 2 + | |/ + | @ 1:0786582aa4b1 1 + |/ + o 0:60829823a42a 0 + + $ hg status + M foo + +We add simple obsolescence marker between 3 and 4 (indirect successors) + + $ hg id --debug -i -r 3 + 6efa171f091b00a3c35edc15d48c52a498929953 + $ hg id --debug -i -r 4 + d047485b3896813b2a624e86201983520f003206 + $ hg debugobsolete 6efa171f091b00a3c35edc15d48c52a498929953 aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa + $ hg debugobsolete aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa d047485b3896813b2a624e86201983520f003206 + +Test that 5 is detected as a valid destination from 3 + $ hg up --quiet --hidden 3 + $ hg up 5 + 1 files updated, 0 files merged, 0 files removed, 0 files unresolved + +Test that 5 is detected as a valid destination from 1 + $ hg up --quiet 0 # we should be able to update to 3 directly + $ hg up --quiet --hidden 3 # but not implemented yet. + $ hg up 5 + 1 files updated, 0 files merged, 0 files removed, 0 files unresolved + +Test that 5 is not detected as a valid destination from 2 + $ hg up --quiet 0 + $ hg up --quiet 2 + $ hg up 5 + abort: crosses branches (merge branches or use --clean to discard changes) + [255]
--- a/tests/test-update-issue1456.t Thu Apr 04 16:28:19 2013 -0500 +++ b/tests/test-update-issue1456.t Thu Apr 18 23:46:26 2013 -0500 @@ -6,9 +6,16 @@ $ echo foo > foo $ hg ci -qAm0 - $ chmod +x foo - $ hg ci -m1 + $ echo toremove > toremove + $ echo todelete > todelete + $ chmod +x foo toremove todelete + $ hg ci -qAm1 + +Test that local removed/deleted, remote removed works with flags + $ hg rm toremove + $ rm todelete $ hg co -q 0 + $ echo dirty > foo $ hg up -c abort: uncommitted local changes @@ -18,11 +25,13 @@ dirty $ hg st -A M foo + C todelete + C toremove Validate update of standalone execute bit change: $ hg up -C 0 - 1 files updated, 0 files merged, 0 files removed, 0 files unresolved + 1 files updated, 0 files merged, 2 files removed, 0 files unresolved $ chmod -x foo $ hg ci -m removeexec nothing changed @@ -30,7 +39,7 @@ $ hg up -C 0 0 files updated, 0 files merged, 0 files removed, 0 files unresolved $ hg up - 1 files updated, 0 files merged, 0 files removed, 0 files unresolved + 3 files updated, 0 files merged, 0 files removed, 0 files unresolved $ hg st $ cd ..
--- a/tests/test-update-reverse.t Thu Apr 04 16:28:19 2013 -0500 +++ b/tests/test-update-reverse.t Thu Apr 18 23:46:26 2013 -0500 @@ -66,17 +66,15 @@ $ hg update --debug -C 1 resolving manifests - overwrite: True, partial: False + branchmerge: False, force: True, partial: False ancestor: 91ebc10ed028+, local: 91ebc10ed028+, remote: 71a760306caf side1: other deleted -> r side2: other deleted -> r main: remote created -> g - updating: side1 1/3 files (33.33%) removing side1 - updating: side2 2/3 files (66.67%) removing side2 + getting main updating: main 3/3 files (100.00%) - getting main 1 files updated, 0 files merged, 2 files removed, 0 files unresolved $ ls
--- a/tests/test-url-rev.t Thu Apr 04 16:28:19 2013 -0500 +++ b/tests/test-url-rev.t Thu Apr 18 23:46:26 2013 -0500 @@ -80,8 +80,35 @@ date: Thu Jan 01 00:00:00 1970 +0000 summary: add a + $ hg -q outgoing '../clone' + 2:faba9097cad4 + 3:4cd725637392 + $ hg summary --remote --config paths.default='../clone' + parent: 3:4cd725637392 tip + add bar + branch: default + commit: (clean) + update: (current) + remote: 2 outgoing $ hg -q outgoing '../clone#foo' 2:faba9097cad4 + $ hg summary --remote --config paths.default='../clone#foo' + parent: 3:4cd725637392 tip + add bar + branch: default + commit: (clean) + update: (current) + remote: 1 outgoing + + $ hg -q --cwd ../clone incoming '../repo#foo' + 2:faba9097cad4 + $ hg --cwd ../clone summary --remote --config paths.default='../repo#foo' + parent: 1:cd2a86ecc814 tip + change a + branch: foo + commit: (clean) + update: (current) + remote: 1 or more incoming $ hg -q push '../clone#foo' @@ -98,6 +125,16 @@ date: Thu Jan 01 00:00:00 1970 +0000 summary: add a + $ hg -q --cwd ../clone incoming '../repo#foo' + [1] + $ hg --cwd ../clone summary --remote --config paths.default='../repo#foo' + parent: 1:cd2a86ecc814 + change a + branch: foo + commit: (clean) + update: 1 new changesets (update) + remote: (synced) + $ cd .. $ cd clone @@ -208,3 +245,60 @@ [255] $ cd .. + +Test handling common incoming revisions between "default" and +"default-push" + + $ hg -R clone rollback + repository tip rolled back to revision 1 (undo pull) + working directory now based on revision 0 + + $ cd repo + + $ hg update -q -C default + $ echo modified >> bar + $ hg commit -m "new head to push current default head" + $ hg -q push -r ".^1" '../clone' + + $ hg -q outgoing '../clone' + 2:faba9097cad4 + 4:d515801a8f3d + + $ hg summary --remote --config paths.default='../clone#default' --config paths.default-push='../clone#foo' + parent: 4:d515801a8f3d tip + new head to push current default head + branch: default + commit: (clean) + update: (current) + remote: 1 outgoing + + $ hg summary --remote --config paths.default='../clone#foo' --config paths.default-push='../clone' + parent: 4:d515801a8f3d tip + new head to push current default head + branch: default + commit: (clean) + update: (current) + remote: 2 outgoing + + $ hg summary --remote --config paths.default='../clone' --config paths.default-push='../clone#foo' + parent: 4:d515801a8f3d tip + new head to push current default head + branch: default + commit: (clean) + update: (current) + remote: 1 outgoing + + $ hg clone -q -r 0 . ../another + $ hg -q outgoing '../another#default' + 3:4cd725637392 + 4:d515801a8f3d + + $ hg summary --remote --config paths.default='../another#default' --config paths.default-push='../clone#default' + parent: 4:d515801a8f3d tip + new head to push current default head + branch: default + commit: (clean) + update: (current) + remote: 1 outgoing + + $ cd ..
--- a/tests/test-walk.t Thu Apr 04 16:28:19 2013 -0500 +++ b/tests/test-walk.t Thu Apr 18 23:46:26 2013 -0500 @@ -155,7 +155,7 @@ abort: path 'mammals/.hg' is inside nested repo 'mammals' (glob) [255] $ hg debugwalk ../.hg - abort: path contains illegal component: .hg (glob) + abort: path contains illegal component: .hg [255] $ cd .. @@ -187,10 +187,10 @@ abort: beans/../.. not under root '$TESTTMP/t' (glob) [255] $ hg debugwalk .hg - abort: path contains illegal component: .hg (glob) + abort: path contains illegal component: .hg [255] $ hg debugwalk beans/../.hg - abort: path contains illegal component: .hg (glob) + abort: path contains illegal component: .hg [255] $ hg debugwalk beans/../.hg/data abort: path contains illegal component: .hg/data (glob)
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tests/test-websub.t Thu Apr 18 23:46:26 2013 -0500 @@ -0,0 +1,36 @@ + $ "$TESTDIR/hghave" serve || exit 80 + + $ hg init test + $ cd test + + $ cat > .hg/hgrc <<EOF + > [extensions] + > # this is only necessary to check that the mapping from + > # interhg to websub works + > interhg = + > + > [websub] + > issues = s|Issue(\d+)|<a href="http://bts.example.org/issue\1">Issue\1</a>| + > + > [interhg] + > # check that we maintain some interhg backwards compatibility... + > # yes, 'x' is a weird delimiter... + > markbugs = sxbugx<i class="\x">bug</i>x + > EOF + + $ touch foo + $ hg add foo + $ hg commit -d '1 0' -m 'Issue123: fixed the bug!' + + $ hg serve -n test -p $HGPORT -d --pid-file=hg.pid -A access.log -E errors.log + $ cat hg.pid >> $DAEMON_PIDS + +log + + $ "$TESTDIR/get-with-headers.py" localhost:$HGPORT "rev/tip" | grep bts + <div class="description"><a href="http://bts.example.org/issue123">Issue123</a>: fixed the <i class="x">bug</i>!</div> +errors + + $ cat errors.log + + $ cd ..