Mercurial > hg-stable
changeset 6501:4f7feeb6d6ee
Merge
author | Bryan O'Sullivan <bos@serpentine.com> |
---|---|
date | Wed, 09 Apr 2008 15:28:30 -0700 |
parents | 315548fcc76b (diff) a3175cd7dbec (current diff) |
children | ba8a0338baf7 |
files | mercurial/util.py |
diffstat | 496 files changed, 20925 insertions(+), 6779 deletions(-) [+] |
line wrap: on
line diff
--- a/.hgignore Wed Apr 09 15:27:57 2008 -0700 +++ b/.hgignore Wed Apr 09 15:28:30 2008 -0700 @@ -24,6 +24,8 @@ mercurial/__version__.py Output/Mercurial-*.exe .DS_Store +tags +cscope.* syntax: regexp ^\.pc/
--- a/.hgsigs Wed Apr 09 15:27:57 2008 -0700 +++ b/.hgsigs Wed Apr 09 15:28:30 2008 -0700 @@ -4,3 +4,4 @@ 27230c29bfec36d5540fbe1c976810aefecfd1d2 0 iD8DBQBFheweywK+sNU5EO8RAt7VAKCrqJQWT2/uo2RWf0ZI4bLp6v82jACgjrMdsaTbxRsypcmEsdPhlG6/8F4= fb4b6d5fe100b0886f8bc3d6731ec0e5ed5c4694 0 iD8DBQBGgHicywK+sNU5EO8RAgNxAJ0VG8ixAaeudx4sZbhngI1syu49HQCeNUJQfWBgA8bkJ2pvsFpNxwYaX3I= 23889160905a1b09fffe1c07378e9fc1827606eb 0 iD8DBQBHGTzoywK+sNU5EO8RAr/UAJ0Y8s4jQtzgS+G9vM8z6CWBThZ8fwCcCT5XDj2XwxKkz/0s6UELwjsO3LU= +bae2e9c838e90a393bae3973a7850280413e091a 0 iD8DBQBH6DO5ywK+sNU5EO8RAsfrAJ0e4r9c9GF/MJsM7Xjd3NesLRC3+ACffj6+6HXdZf8cswAoFPO+DY00oD0=
--- a/.hgtags Wed Apr 09 15:27:57 2008 -0700 +++ b/.hgtags Wed Apr 09 15:28:30 2008 -0700 @@ -16,3 +16,4 @@ 27230c29bfec36d5540fbe1c976810aefecfd1d2 0.9.3 fb4b6d5fe100b0886f8bc3d6731ec0e5ed5c4694 0.9.4 23889160905a1b09fffe1c07378e9fc1827606eb 0.9.5 +bae2e9c838e90a393bae3973a7850280413e091a 1.0
--- a/contrib/bash_completion Wed Apr 09 15:27:57 2008 -0700 +++ b/contrib/bash_completion Wed Apr 09 15:28:30 2008 -0700 @@ -78,6 +78,7 @@ _hg_status() { local files="$("$hg" status -n$1 . 2>/dev/null)" + local IFS=$'\n' COMPREPLY=(${COMPREPLY[@]:-} $(compgen -W '$files' -- "$cur")) }
--- a/contrib/churn.py Wed Apr 09 15:27:57 2008 -0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,204 +0,0 @@ -# churn.py - create a graph showing who changed the most lines -# -# Copyright 2006 Josef "Jeff" Sipek <jeffpc@josefsipek.net> -# -# This software may be used and distributed according to the terms -# of the GNU General Public License, incorporated herein by reference. -# -# -# Aliases map file format is simple one alias per line in the following -# format: -# -# <alias email> <actual email> - -from mercurial.i18n import gettext as _ -from mercurial import hg, mdiff, cmdutil, ui, util, templater, node -import os, sys - -def get_tty_width(): - if 'COLUMNS' in os.environ: - try: - return int(os.environ['COLUMNS']) - except ValueError: - pass - try: - import termios, array, fcntl - for dev in (sys.stdout, sys.stdin): - try: - fd = dev.fileno() - if not os.isatty(fd): - continue - arri = fcntl.ioctl(fd, termios.TIOCGWINSZ, '\0' * 8) - return array.array('h', arri)[1] - except ValueError: - pass - except ImportError: - pass - return 80 - -def __gather(ui, repo, node1, node2): - def dirtywork(f, mmap1, mmap2): - lines = 0 - - to = mmap1 and repo.file(f).read(mmap1[f]) or None - tn = mmap2 and repo.file(f).read(mmap2[f]) or None - - diff = mdiff.unidiff(to, "", tn, "", f, f).split("\n") - - for line in diff: - if not line: - continue # skip EOF - if line.startswith(" "): - continue # context line - if line.startswith("--- ") or line.startswith("+++ "): - continue # begining of diff - if line.startswith("@@ "): - continue # info line - - # changed lines - lines += 1 - - return lines - - ## - - lines = 0 - - changes = repo.status(node1, node2, None, util.always)[:5] - - modified, added, removed, deleted, unknown = changes - - who = repo.changelog.read(node2)[1] - who = templater.email(who) # get the email of the person - - mmap1 = repo.manifest.read(repo.changelog.read(node1)[0]) - mmap2 = repo.manifest.read(repo.changelog.read(node2)[0]) - for f in modified: - lines += dirtywork(f, mmap1, mmap2) - - for f in added: - lines += dirtywork(f, None, mmap2) - - for f in removed: - lines += dirtywork(f, mmap1, None) - - for f in deleted: - lines += dirtywork(f, mmap1, mmap2) - - for f in unknown: - lines += dirtywork(f, mmap1, mmap2) - - return (who, lines) - -def gather_stats(ui, repo, amap, revs=None, progress=False): - stats = {} - - cl = repo.changelog - - if not revs: - revs = range(0, cl.count()) - - nr_revs = len(revs) - cur_rev = 0 - - for rev in revs: - cur_rev += 1 # next revision - - node2 = cl.node(rev) - node1 = cl.parents(node2)[0] - - if cl.parents(node2)[1] != node.nullid: - ui.note(_('Revision %d is a merge, ignoring...\n') % (rev,)) - continue - - who, lines = __gather(ui, repo, node1, node2) - - # remap the owner if possible - if amap.has_key(who): - ui.note("using '%s' alias for '%s'\n" % (amap[who], who)) - who = amap[who] - - if not stats.has_key(who): - stats[who] = 0 - stats[who] += lines - - ui.note("rev %d: %d lines by %s\n" % (rev, lines, who)) - - if progress: - nr_revs = max(nr_revs, 1) - if int(100.0*(cur_rev - 1)/nr_revs) < int(100.0*cur_rev/nr_revs): - ui.write("%d%%.." % (int(100.0*cur_rev/nr_revs),)) - sys.stdout.flush() - - if progress: - ui.write("done\n") - sys.stdout.flush() - - return stats - -def churn(ui, repo, **opts): - "Graphs the number of lines changed" - - def pad(s, l): - if len(s) < l: - return s + " " * (l-len(s)) - return s[0:l] - - def graph(n, maximum, width, char): - maximum = max(1, maximum) - n = int(n * width / float(maximum)) - - return char * (n) - - def get_aliases(f): - aliases = {} - - for l in f.readlines(): - l = l.strip() - alias, actual = l.split(" ") - aliases[alias] = actual - - return aliases - - amap = {} - aliases = opts.get('aliases') - if aliases: - try: - f = open(aliases,"r") - except OSError, e: - print "Error: " + e - return - - amap = get_aliases(f) - f.close() - - revs = [int(r) for r in cmdutil.revrange(repo, opts['rev'])] - revs.sort() - stats = gather_stats(ui, repo, amap, revs, opts.get('progress')) - - # make a list of tuples (name, lines) and sort it in descending order - ordered = stats.items() - ordered.sort(lambda x, y: cmp(y[1], x[1])) - - if not ordered: - return - maximum = ordered[0][1] - - width = get_tty_width() - ui.note(_("assuming %i character terminal\n") % width) - width -= 1 - - for i in ordered: - person = i[0] - lines = i[1] - print "%s %6d %s" % (pad(person, 20), lines, - graph(lines, maximum, width - 20 - 1 - 6 - 2 - 2, '*')) - -cmdtable = { - "churn": - (churn, - [('r', 'rev', [], _('limit statistics to the specified revisions')), - ('', 'aliases', '', _('file with email aliases')), - ('', 'progress', None, _('show progress'))], - 'hg churn [-r revision range] [-a file] [--progress]'), -}
--- a/contrib/convert-repo Wed Apr 09 15:27:57 2008 -0700 +++ b/contrib/convert-repo Wed Apr 09 15:28:30 2008 -0700 @@ -17,9 +17,11 @@ opts = {} args = [] try: - args = fancyopts.fancyopts(sys.argv[1:], options, opts) -except fancyopts.getopt.GetoptError, inst: - u.warn('Usage:\n%s' % help) + args = list(fancyopts.fancyopts(sys.argv[1:], options, opts)) + args += [None]*(3 - len(args)) + src, dest, revmapfile = args +except (fancyopts.getopt.GetoptError, ValueError), inst: + u.warn('Usage:\n%s\n' % help) sys.exit(-1) -convert._convert(u, *args, **opts) +convert.convert(u, src, dest, revmapfile, **opts)
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/contrib/dumprevlog Wed Apr 09 15:28:30 2008 -0700 @@ -0,0 +1,25 @@ +#!/usr/bin/env python +# Dump revlogs as raw data stream +# $ find .hg/store/ -name "*.i" | xargs dumprevlog > repo.dump + +import sys +from mercurial import revlog, node, util + +for fp in (sys.stdin, sys.stdout, sys.stderr): + util.set_binary(fp) + +for f in sys.argv[1:]: + binopen = lambda fn: open(fn, 'rb') + r = revlog.revlog(binopen, f) + print "file:", f + for i in xrange(r.count()): + n = r.node(i) + p = r.parents(n) + d = r.revision(n) + print "node:", node.hex(n) + print "linkrev:", r.linkrev(n) + print "parents:", node.hex(p[0]), node.hex(p[1]) + print "length:", len(d) + print "-start-" + print d + print "-end-"
--- a/contrib/hgk Wed Apr 09 15:27:57 2008 -0700 +++ b/contrib/hgk Wed Apr 09 15:28:30 2008 -0700 @@ -274,6 +274,7 @@ set comname {} set comdate {} set rev {} + set branch {} if {![info exists nchildren($id)]} { set children($id) {} set nchildren($id) 0 @@ -310,6 +311,8 @@ set comname [join [lrange $line 1 [expr {$x - 1}]]] } elseif {$tag == "revision"} { set rev [lindex $line 1] + } elseif {$tag == "branch"} { + set branch [join [lrange $line 1 end]] } } } else { @@ -334,17 +337,30 @@ set comdate [clock format $comdate -format "%Y-%m-%d %H:%M:%S"] } set commitinfo($id) [list $headline $auname $audate \ - $comname $comdate $comment $rev] + $comname $comdate $comment $rev $branch] } proc readrefs {} { global tagids idtags headids idheads tagcontents env curid - - set curid [exec $env(HG) --config ui.report_untrusted=false id] + + set status [catch {exec $env(HG) --config ui.report_untrusted=false id} curid] + if { $status != 0 } { + puts $::errorInfo + if { ![string equal $::errorCode NONE] } { + exit 2 + } + } regexp -- {[[:xdigit:]]+} $curid curid - set tags [exec $env(HG) --config ui.report_untrusted=false tags] + set status [catch {exec $env(HG) --config ui.report_untrusted=false tags} tags] + if { $status != 0 } { + puts $::errorInfo + if { ![string equal $::errorCode NONE] } { + exit 2 + } + } regsub -all "\r\n" $tags "\n" tags + set lines [split $tags "\n"] foreach f $lines { regexp {(\S+)$} $f full @@ -2286,6 +2302,9 @@ $ctext mark gravity fmark.0 left set info $commitinfo($id) $ctext insert end "Revision: [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" if {[info exists idtags($id)]} { @@ -2320,8 +2339,12 @@ $cflist delete 0 end $cflist insert end "Comments" + if {$nparents($id) <= 1} { + set parent "null" if {$nparents($id) == 1} { - startdiff [concat $id $parents($id)] + set parent $parents($id) + } + startdiff [concat $id $parent] } elseif {$nparents($id) > 1} { mergediff $id }
--- a/contrib/hgwebdir.fcgi Wed Apr 09 15:27:57 2008 -0700 +++ b/contrib/hgwebdir.fcgi Wed Apr 09 15:28:30 2008 -0700 @@ -9,9 +9,9 @@ # enable demandloading to reduce startup time from mercurial import demandimport; demandimport.enable() -# send python tracebacks to the browser if an error occurs: -import cgitb -cgitb.enable() +# Uncomment to send python tracebacks to the browser if an error occurs: +#import cgitb +#cgitb.enable() # If you'd like to serve pages with UTF-8 instead of your default # locale charset, you can do so by uncommenting the following lines.
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/contrib/logo-droplets.svg Wed Apr 09 15:28:30 2008 -0700 @@ -0,0 +1,624 @@ +<?xml version="1.0" encoding="UTF-8" standalone="no"?> +<!-- Created with Inkscape (http://www.inkscape.org/) --> +<svg + xmlns:dc="http://purl.org/dc/elements/1.1/" + xmlns:cc="http://web.resource.org/cc/" + xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" + xmlns:svg="http://www.w3.org/2000/svg" + xmlns="http://www.w3.org/2000/svg" + xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" + xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" + version="1.0" + width="100" + height="120" + viewBox="0 0 124.766 152.099" + id="Layer_1" + xml:space="preserve" + sodipodi:version="0.32" + inkscape:version="0.45.1" + sodipodi:docname="logo-droplets.svg" + sodipodi:docbase="/home/oxymoron/waste/selenic/public_html/hg-logo" + inkscape:output_extension="org.inkscape.output.svg.inkscape"><metadata + id="metadata6845"><rdf:RDF><cc:Work + rdf:about=""><dc:format>image/svg+xml</dc:format><dc:type + rdf:resource="http://purl.org/dc/dcmitype/StillImage" /><dc:title>Mercurial "droplets" logo</dc:title><dc:creator><cc:Agent><dc:title>Cali Mastny and Matt Mackall</dc:title></cc:Agent></dc:creator><cc:license + rdf:resource="http://creativecommons.org/licenses/GPL/2.0/" /><dc:date>Feb 12 2008</dc:date></cc:Work><cc:License + rdf:about="http://creativecommons.org/licenses/GPL/2.0/"><cc:permits + rdf:resource="http://web.resource.org/cc/Reproduction" /><cc:permits + rdf:resource="http://web.resource.org/cc/Distribution" /><cc:requires + rdf:resource="http://web.resource.org/cc/Notice" /><cc:permits + rdf:resource="http://web.resource.org/cc/DerivativeWorks" /><cc:requires + rdf:resource="http://web.resource.org/cc/ShareAlike" /><cc:requires + rdf:resource="http://web.resource.org/cc/SourceCode" /></cc:License></rdf:RDF></metadata><sodipodi:namedview + inkscape:window-height="576" + inkscape:window-width="746" + inkscape:pageshadow="2" + inkscape:pageopacity="0.0" + guidetolerance="10.0" + gridtolerance="10.0" + objecttolerance="10.0" + borderopacity="1.0" + bordercolor="#666666" + pagecolor="#ffffff" + id="base" + inkscape:zoom="2.3216673" + inkscape:cx="4.1210694" + inkscape:cy="65.759396" + inkscape:window-x="377" + inkscape:window-y="398" + inkscape:current-layer="Layer_1" + width="100px" + height="120px" + units="px" /><defs + id="defs261" /> +<pattern + overflow="visible" + viewBox="2.125 -70.896 69 69" + id="Polka_Dot_Pattern" + patternUnits="userSpaceOnUse" + height="69" + width="69" + y="736.415" + x="-316"> + <g + id="g4"> + <polygon + id="polygon6" + points="71.125,-1.896 2.125,-1.896 2.125,-70.896 71.125,-70.896 " + fill="none" /> + <polygon + id="polygon8" + points="71.125,-1.896 2.125,-1.896 2.125,-70.896 71.125,-70.896 " + fill="#F7BC60" /> + <g + id="g10"> + <path + id="path12" + d="M61.772-71.653c0.018,0.072,0.007,0.127-0.026,0.19c-0.053,0.101-0.112,0.062-0.165,0.128 c-0.05,0.062-0.099,0.276-0.079,0.362c-0.169,0.058-0.01,0.227-0.015,0.35c-0.002,0.05-0.041,0.105-0.045,0.161 c-0.01,0.119,0.017,0.266,0.068,0.37c0.097,0.198,0.268,0.413,0.435,0.544c0.19,0.148,0.365,0.572,0.608,0.631 c0.177,0.042,0.384-0.104,0.543-0.143c0.18-0.043,0.397,0.01,0.571-0.053c0.222-0.079,0.127-0.337,0.288-0.45 c0.104-0.074,0.287-0.01,0.406-0.051c0.2-0.069,0.339-0.263,0.376-0.46c0.016-0.082,0.01-0.145,0.039-0.221 c0.039-0.103,0.111-0.16,0.09-0.293c-0.01-0.062-0.052-0.12-0.064-0.187c-0.022-0.114,0.002-0.224,0-0.337 c-0.003-0.2,0.017-0.379-0.078-0.55c-0.38-0.688-1.236-0.929-1.975-0.789c-0.18,0.034-0.287,0.126-0.442,0.207 c-0.17,0.088-0.139,0.166-0.318,0.224c-0.081,0.026-0.216,0.124-0.215,0.224c0.001,0.115,0.005,0.051,0.012,0.169 c-0.021,0.011-0.021-0.005-0.03-0.025" + fill="#FFFFFF" /> + <path + id="path14" + d="M54.105-71.653c0.018,0.072,0.007,0.127-0.026,0.19c-0.053,0.101-0.112,0.062-0.165,0.128 c-0.05,0.062-0.099,0.276-0.079,0.362c-0.169,0.058-0.01,0.227-0.015,0.35c-0.002,0.05-0.041,0.105-0.045,0.161 c-0.01,0.119,0.017,0.266,0.068,0.37c0.097,0.198,0.268,0.413,0.435,0.544c0.19,0.148,0.365,0.572,0.608,0.631 c0.177,0.042,0.384-0.104,0.543-0.143c0.18-0.043,0.397,0.01,0.571-0.053c0.222-0.079,0.127-0.337,0.288-0.45 c0.104-0.074,0.287-0.01,0.406-0.051c0.2-0.069,0.339-0.263,0.376-0.46c0.016-0.082,0.01-0.145,0.039-0.221 c0.039-0.103,0.111-0.16,0.09-0.293c-0.01-0.062-0.052-0.12-0.064-0.187c-0.022-0.114,0.002-0.224,0-0.337 c-0.003-0.2,0.017-0.379-0.078-0.55c-0.38-0.688-1.236-0.929-1.975-0.789c-0.18,0.034-0.287,0.126-0.442,0.207 c-0.17,0.088-0.139,0.166-0.318,0.224c-0.081,0.026-0.216,0.124-0.215,0.224c0.001,0.115,0.005,0.051,0.012,0.169 c-0.021,0.011-0.021-0.005-0.03-0.025" + fill="#FFFFFF" /> + <path + id="path16" + d="M46.439-71.653c0.018,0.072,0.007,0.127-0.026,0.19c-0.053,0.101-0.112,0.062-0.165,0.128 c-0.05,0.062-0.099,0.276-0.079,0.362c-0.169,0.058-0.01,0.227-0.015,0.35c-0.002,0.05-0.041,0.105-0.045,0.161 c-0.01,0.119,0.017,0.266,0.068,0.37c0.097,0.198,0.268,0.413,0.435,0.544c0.19,0.148,0.365,0.572,0.608,0.631 c0.177,0.042,0.384-0.104,0.543-0.143c0.18-0.043,0.397,0.01,0.571-0.053c0.222-0.079,0.127-0.337,0.288-0.45 c0.104-0.074,0.287-0.01,0.406-0.051c0.2-0.069,0.339-0.263,0.376-0.46c0.016-0.082,0.01-0.145,0.039-0.221 c0.039-0.103,0.111-0.16,0.09-0.293c-0.01-0.062-0.052-0.12-0.064-0.187c-0.022-0.114,0.002-0.224,0-0.337 c-0.003-0.2,0.017-0.379-0.078-0.55c-0.38-0.688-1.236-0.929-1.975-0.789c-0.18,0.034-0.287,0.126-0.442,0.207 c-0.17,0.088-0.139,0.166-0.318,0.224c-0.081,0.026-0.216,0.124-0.215,0.224c0.001,0.115,0.005,0.051,0.012,0.169 c-0.021,0.011-0.021-0.005-0.03-0.025" + fill="#FFFFFF" /> + <path + id="path18" + d="M38.772-71.653c0.018,0.072,0.007,0.127-0.026,0.19c-0.053,0.101-0.112,0.062-0.165,0.128 c-0.05,0.062-0.099,0.276-0.079,0.362c-0.169,0.058-0.01,0.227-0.015,0.35c-0.002,0.05-0.041,0.105-0.045,0.161 c-0.01,0.119,0.017,0.266,0.068,0.37c0.097,0.198,0.268,0.413,0.435,0.544c0.19,0.148,0.365,0.572,0.608,0.631 c0.177,0.042,0.384-0.104,0.543-0.143c0.18-0.043,0.397,0.01,0.571-0.053c0.222-0.079,0.127-0.337,0.288-0.45 c0.104-0.074,0.287-0.01,0.406-0.051c0.2-0.069,0.339-0.263,0.376-0.46c0.016-0.082,0.01-0.145,0.039-0.221 c0.039-0.103,0.111-0.16,0.09-0.293c-0.01-0.062-0.052-0.12-0.064-0.187c-0.022-0.114,0.002-0.224,0-0.337 c-0.003-0.2,0.017-0.379-0.078-0.55c-0.38-0.688-1.236-0.929-1.975-0.789c-0.18,0.034-0.287,0.126-0.442,0.207 c-0.17,0.088-0.139,0.166-0.318,0.224c-0.081,0.026-0.216,0.124-0.215,0.224c0.001,0.115,0.005,0.051,0.012,0.169 c-0.021,0.011-0.021-0.005-0.03-0.025" + fill="#FFFFFF" /> + <path + id="path20" + d="M31.105-71.653c0.018,0.072,0.007,0.127-0.026,0.19c-0.053,0.101-0.112,0.062-0.165,0.128 c-0.05,0.062-0.099,0.276-0.079,0.362c-0.169,0.058-0.01,0.227-0.015,0.35c-0.002,0.05-0.041,0.105-0.045,0.161 c-0.01,0.119,0.017,0.266,0.068,0.37c0.097,0.198,0.268,0.413,0.435,0.544c0.19,0.148,0.365,0.572,0.608,0.631 c0.177,0.042,0.384-0.104,0.543-0.143c0.18-0.043,0.397,0.01,0.571-0.053c0.222-0.079,0.127-0.337,0.288-0.45 c0.104-0.074,0.287-0.01,0.406-0.051c0.2-0.069,0.339-0.263,0.376-0.46c0.016-0.082,0.01-0.145,0.039-0.221 c0.039-0.103,0.111-0.16,0.09-0.293c-0.01-0.062-0.052-0.12-0.064-0.187c-0.022-0.114,0.002-0.224,0-0.337 c-0.003-0.2,0.017-0.379-0.078-0.55c-0.38-0.688-1.236-0.929-1.975-0.789c-0.18,0.034-0.287,0.126-0.442,0.207 c-0.17,0.088-0.139,0.166-0.318,0.224c-0.081,0.026-0.216,0.124-0.215,0.224c0.001,0.115,0.005,0.051,0.012,0.169 c-0.021,0.011-0.021-0.005-0.03-0.025" + fill="#FFFFFF" /> + <path + id="path22" + d="M23.439-71.653c0.018,0.072,0.007,0.127-0.026,0.19c-0.053,0.101-0.112,0.062-0.165,0.128 c-0.05,0.062-0.099,0.276-0.079,0.362c-0.169,0.058-0.01,0.227-0.015,0.35c-0.002,0.05-0.041,0.105-0.045,0.161 c-0.01,0.119,0.017,0.266,0.068,0.37c0.097,0.198,0.268,0.413,0.435,0.544c0.19,0.148,0.365,0.572,0.608,0.631 c0.177,0.042,0.384-0.104,0.543-0.143c0.18-0.043,0.397,0.01,0.571-0.053c0.222-0.079,0.127-0.337,0.288-0.45 c0.104-0.074,0.287-0.01,0.406-0.051c0.2-0.069,0.339-0.263,0.376-0.46c0.016-0.082,0.01-0.145,0.039-0.221 c0.039-0.103,0.111-0.16,0.09-0.293c-0.01-0.062-0.052-0.12-0.064-0.187c-0.022-0.114,0.002-0.224,0-0.337 c-0.003-0.2,0.017-0.379-0.078-0.55c-0.38-0.688-1.236-0.929-1.975-0.789c-0.18,0.034-0.287,0.126-0.442,0.207 c-0.17,0.088-0.139,0.166-0.318,0.224c-0.081,0.026-0.216,0.124-0.215,0.224c0.001,0.115,0.005,0.051,0.012,0.169 c-0.021,0.011-0.021-0.005-0.03-0.025" + fill="#FFFFFF" /> + <path + id="path24" + d="M15.772-71.653c0.018,0.072,0.007,0.127-0.026,0.19c-0.053,0.101-0.112,0.062-0.165,0.128 c-0.05,0.062-0.099,0.276-0.079,0.362c-0.169,0.058-0.01,0.227-0.015,0.35c-0.002,0.05-0.041,0.105-0.045,0.161 c-0.01,0.119,0.017,0.266,0.068,0.37c0.097,0.198,0.268,0.413,0.435,0.544c0.19,0.148,0.365,0.572,0.608,0.631 c0.177,0.042,0.384-0.104,0.543-0.143c0.18-0.043,0.397,0.01,0.571-0.053c0.222-0.079,0.127-0.337,0.288-0.45 c0.104-0.074,0.287-0.01,0.406-0.051c0.2-0.069,0.339-0.263,0.376-0.46c0.016-0.082,0.01-0.145,0.039-0.221 c0.039-0.103,0.111-0.16,0.09-0.293c-0.01-0.062-0.052-0.12-0.064-0.187c-0.022-0.114,0.002-0.224,0-0.337 c-0.003-0.2,0.017-0.379-0.078-0.55c-0.38-0.688-1.236-0.929-1.975-0.789c-0.18,0.034-0.287,0.126-0.442,0.207 c-0.17,0.088-0.139,0.166-0.318,0.224c-0.081,0.026-0.216,0.124-0.215,0.224c0.001,0.115,0.005,0.051,0.012,0.169 c-0.021,0.011-0.021-0.005-0.03-0.025" + fill="#FFFFFF" /> + <path + id="path26" + d="M8.105-71.653c0.018,0.072,0.007,0.127-0.026,0.19c-0.053,0.101-0.112,0.062-0.165,0.128 c-0.05,0.062-0.099,0.276-0.079,0.362c-0.169,0.058-0.01,0.227-0.015,0.35c-0.002,0.05-0.041,0.105-0.045,0.161 c-0.01,0.119,0.017,0.266,0.068,0.37c0.097,0.198,0.268,0.413,0.435,0.544c0.19,0.148,0.365,0.572,0.608,0.631 c0.177,0.042,0.384-0.104,0.543-0.143c0.18-0.043,0.397,0.01,0.571-0.053c0.222-0.079,0.127-0.337,0.288-0.45 c0.104-0.074,0.287-0.01,0.406-0.051c0.2-0.069,0.339-0.263,0.376-0.46c0.016-0.082,0.01-0.145,0.039-0.221 c0.039-0.103,0.111-0.16,0.09-0.293c-0.01-0.062-0.052-0.12-0.064-0.187c-0.022-0.114,0.002-0.224,0-0.337 c-0.003-0.2,0.017-0.379-0.078-0.55c-0.38-0.688-1.236-0.929-1.975-0.789c-0.18,0.034-0.287,0.126-0.442,0.207 c-0.17,0.088-0.139,0.166-0.318,0.224c-0.081,0.026-0.216,0.124-0.215,0.224c0.001,0.115,0.005,0.051,0.012,0.169 c-0.021,0.011-0.021-0.005-0.03-0.025" + fill="#FFFFFF" /> + <path + id="path28" + d="M0.439-71.653c0.018,0.072,0.008,0.127-0.026,0.19C0.361-71.362,0.3-71.4,0.248-71.335 c-0.051,0.062-0.099,0.276-0.079,0.362c-0.169,0.058-0.01,0.227-0.015,0.35c-0.002,0.05-0.041,0.105-0.045,0.161 c-0.01,0.119,0.017,0.266,0.068,0.37c0.097,0.198,0.268,0.413,0.435,0.544c0.19,0.148,0.365,0.572,0.608,0.631 c0.177,0.042,0.384-0.104,0.543-0.143c0.18-0.043,0.397,0.01,0.571-0.053c0.222-0.079,0.127-0.337,0.288-0.45 c0.104-0.074,0.287-0.01,0.406-0.051c0.2-0.07,0.339-0.263,0.376-0.46c0.016-0.082,0.01-0.145,0.039-0.221 c0.038-0.103,0.111-0.16,0.09-0.293c-0.01-0.062-0.051-0.12-0.064-0.187c-0.021-0.114,0.002-0.224,0-0.337 c-0.003-0.2,0.017-0.379-0.078-0.55c-0.38-0.688-1.236-0.929-1.975-0.789c-0.18,0.034-0.287,0.126-0.442,0.207 c-0.17,0.088-0.139,0.166-0.318,0.224c-0.081,0.026-0.215,0.124-0.215,0.224c0.002,0.115,0.005,0.051,0.012,0.169 c-0.021,0.011-0.021-0.005-0.03-0.025" + fill="#FFFFFF" /> + </g> + <g + id="g30"> + <path + id="path32" + d="M69.439-71.653c0.018,0.072,0.008,0.127-0.026,0.19c-0.052,0.101-0.113,0.062-0.165,0.128 c-0.051,0.062-0.099,0.276-0.079,0.362c-0.169,0.058-0.01,0.227-0.015,0.35c-0.002,0.05-0.041,0.105-0.045,0.161 c-0.01,0.119,0.017,0.266,0.068,0.37c0.097,0.198,0.268,0.413,0.435,0.544c0.19,0.148,0.365,0.572,0.608,0.631 c0.177,0.042,0.384-0.104,0.543-0.143c0.18-0.043,0.397,0.01,0.571-0.053c0.222-0.079,0.127-0.337,0.288-0.45 c0.104-0.074,0.287-0.01,0.406-0.051c0.2-0.07,0.339-0.263,0.376-0.46c0.016-0.082,0.01-0.145,0.039-0.221 c0.038-0.103,0.111-0.16,0.09-0.293c-0.01-0.062-0.051-0.12-0.064-0.187c-0.021-0.114,0.002-0.224,0-0.337 c-0.003-0.2,0.017-0.379-0.078-0.55c-0.38-0.688-1.236-0.929-1.975-0.789c-0.18,0.034-0.287,0.126-0.442,0.207 c-0.17,0.088-0.139,0.166-0.318,0.224c-0.081,0.026-0.215,0.124-0.215,0.224c0.002,0.115,0.005,0.051,0.012,0.169 c-0.021,0.011-0.021-0.005-0.03-0.025" + fill="#FFFFFF" /> + </g> + <path + id="path34" + d="M0.495-71.653c0.018,0.072,0.008,0.127-0.026,0.19c-0.052,0.101-0.113,0.062-0.165,0.128 c-0.051,0.062-0.099,0.276-0.079,0.362c-0.169,0.058-0.01,0.227-0.015,0.35c-0.002,0.05-0.041,0.105-0.045,0.161 c-0.01,0.119,0.017,0.266,0.068,0.37c0.097,0.198,0.268,0.413,0.435,0.544c0.19,0.148,0.365,0.572,0.608,0.631 c0.177,0.042,0.384-0.104,0.543-0.143c0.18-0.043,0.397,0.01,0.571-0.053c0.222-0.079,0.127-0.337,0.288-0.45 c0.104-0.074,0.287-0.01,0.406-0.051c0.2-0.07,0.339-0.263,0.376-0.46c0.016-0.082,0.01-0.145,0.039-0.221 c0.038-0.103,0.111-0.16,0.09-0.293c-0.01-0.062-0.051-0.12-0.064-0.187c-0.021-0.114,0.002-0.224,0-0.337 c-0.003-0.2,0.017-0.379-0.078-0.55c-0.38-0.688-1.236-0.929-1.975-0.789c-0.18,0.034-0.287,0.126-0.442,0.207 c-0.17,0.088-0.139,0.166-0.318,0.224c-0.081,0.026-0.215,0.124-0.215,0.224C0.5-71.68,0.503-71.744,0.51-71.626 c-0.021,0.011-0.021-0.005-0.03-0.025" + fill="#FFFFFF" /> + <g + id="g36"> + <g + id="g38"> + <path + id="path40" + d="M69.439-64.001c0.018,0.072,0.007,0.127-0.026,0.19c-0.053,0.101-0.112,0.062-0.165,0.128 c-0.05,0.062-0.099,0.276-0.079,0.362c-0.169,0.058-0.01,0.227-0.015,0.35c-0.002,0.05-0.041,0.105-0.045,0.161 c-0.01,0.119,0.017,0.266,0.068,0.37c0.097,0.198,0.268,0.413,0.435,0.544c0.19,0.148,0.365,0.572,0.608,0.631 c0.177,0.042,0.384-0.104,0.543-0.143c0.18-0.043,0.397,0.01,0.571-0.053c0.222-0.079,0.127-0.337,0.288-0.45 c0.104-0.074,0.287-0.01,0.406-0.051c0.2-0.069,0.339-0.263,0.376-0.46c0.016-0.082,0.01-0.145,0.039-0.221 c0.039-0.103,0.111-0.16,0.09-0.293c-0.01-0.062-0.052-0.12-0.064-0.187c-0.022-0.114,0.002-0.224,0-0.337 c-0.003-0.2,0.017-0.379-0.078-0.55c-0.38-0.688-1.236-0.929-1.975-0.789c-0.18,0.034-0.287,0.126-0.442,0.207 c-0.17,0.088-0.139,0.166-0.318,0.224c-0.081,0.026-0.216,0.124-0.215,0.224c0.001,0.115,0.005,0.051,0.012,0.169 c-0.021,0.011-0.021-0.005-0.03-0.025" + fill="#FFFFFF" /> + <path + id="path42" + d="M61.778-64.001c0.018,0.072,0.007,0.127-0.026,0.19c-0.053,0.101-0.112,0.062-0.165,0.128 c-0.05,0.062-0.099,0.276-0.079,0.362c-0.169,0.058-0.009,0.227-0.015,0.35c-0.002,0.05-0.041,0.105-0.045,0.161 c-0.01,0.119,0.017,0.266,0.068,0.37c0.097,0.198,0.268,0.413,0.435,0.544c0.19,0.148,0.365,0.572,0.608,0.631 c0.177,0.042,0.384-0.104,0.543-0.143c0.18-0.043,0.397,0.01,0.571-0.053c0.222-0.079,0.127-0.337,0.288-0.45 c0.104-0.074,0.287-0.01,0.406-0.051c0.2-0.069,0.339-0.263,0.376-0.46c0.016-0.082,0.01-0.145,0.039-0.221 c0.039-0.103,0.111-0.16,0.09-0.293c-0.01-0.062-0.052-0.12-0.064-0.187c-0.022-0.114,0.002-0.224,0-0.337 c-0.003-0.2,0.017-0.379-0.078-0.55c-0.38-0.688-1.236-0.929-1.975-0.789c-0.18,0.034-0.287,0.126-0.442,0.207 c-0.17,0.088-0.139,0.166-0.318,0.224c-0.081,0.026-0.216,0.124-0.215,0.224c0.001,0.115,0.005,0.051,0.012,0.169 c-0.021,0.011-0.021-0.005-0.03-0.025" + fill="#FFFFFF" /> + <path + id="path44" + d="M54.118-64.001c0.018,0.072,0.007,0.127-0.026,0.19c-0.053,0.101-0.112,0.062-0.165,0.128 c-0.05,0.062-0.099,0.276-0.079,0.362c-0.169,0.058-0.009,0.227-0.015,0.35c-0.002,0.05-0.041,0.105-0.045,0.161 c-0.01,0.119,0.017,0.266,0.068,0.37c0.097,0.198,0.268,0.413,0.435,0.544c0.19,0.148,0.365,0.572,0.608,0.631 c0.177,0.042,0.384-0.104,0.543-0.143c0.18-0.043,0.397,0.01,0.571-0.053c0.222-0.079,0.127-0.337,0.288-0.45 c0.104-0.074,0.287-0.01,0.406-0.051c0.2-0.069,0.339-0.263,0.376-0.46c0.016-0.082,0.01-0.145,0.039-0.221 c0.039-0.103,0.111-0.16,0.09-0.293c-0.01-0.062-0.052-0.12-0.064-0.187c-0.022-0.114,0.002-0.224,0-0.337 c-0.003-0.2,0.017-0.379-0.078-0.55c-0.38-0.688-1.236-0.929-1.975-0.789c-0.18,0.034-0.287,0.126-0.442,0.207 c-0.17,0.088-0.139,0.166-0.318,0.224c-0.081,0.026-0.216,0.124-0.215,0.224c0.001,0.115,0.005,0.051,0.012,0.169 c-0.021,0.011-0.021-0.005-0.03-0.025" + fill="#FFFFFF" /> + <path + id="path46" + d="M46.458-64.001c0.018,0.072,0.007,0.127-0.026,0.19c-0.053,0.101-0.112,0.062-0.165,0.128 c-0.05,0.062-0.099,0.276-0.079,0.362c-0.169,0.058-0.009,0.227-0.015,0.35c-0.002,0.05-0.041,0.105-0.045,0.161 c-0.01,0.119,0.017,0.266,0.068,0.37c0.097,0.198,0.268,0.413,0.435,0.544c0.19,0.148,0.365,0.572,0.608,0.631 c0.177,0.042,0.384-0.104,0.543-0.143c0.18-0.043,0.397,0.01,0.571-0.053c0.222-0.079,0.127-0.337,0.288-0.45 c0.104-0.074,0.287-0.01,0.406-0.051c0.2-0.069,0.339-0.263,0.376-0.46c0.016-0.082,0.01-0.145,0.039-0.221 c0.039-0.103,0.111-0.16,0.09-0.293c-0.01-0.062-0.052-0.12-0.064-0.187c-0.022-0.114,0.002-0.224,0-0.337 c-0.003-0.2,0.017-0.379-0.078-0.55c-0.38-0.688-1.236-0.929-1.975-0.789c-0.18,0.034-0.287,0.126-0.442,0.207 c-0.17,0.088-0.139,0.166-0.318,0.224c-0.081,0.026-0.216,0.124-0.215,0.224c0.001,0.115,0.005,0.051,0.012,0.169 c-0.021,0.011-0.021-0.005-0.03-0.025" + fill="#FFFFFF" /> + <path + id="path48" + d="M38.797-64.001c0.018,0.072,0.007,0.127-0.026,0.19c-0.053,0.101-0.112,0.062-0.165,0.128 c-0.05,0.062-0.099,0.276-0.079,0.362c-0.169,0.058-0.009,0.227-0.015,0.35c-0.002,0.05-0.041,0.105-0.045,0.161 c-0.01,0.119,0.017,0.266,0.068,0.37c0.097,0.198,0.268,0.413,0.435,0.544c0.19,0.148,0.365,0.572,0.608,0.631 c0.177,0.042,0.384-0.104,0.543-0.143c0.18-0.043,0.397,0.01,0.571-0.053c0.222-0.079,0.127-0.337,0.288-0.45 c0.104-0.074,0.287-0.01,0.406-0.051c0.2-0.069,0.339-0.263,0.376-0.46c0.016-0.082,0.01-0.145,0.039-0.221 c0.039-0.103,0.111-0.16,0.09-0.293c-0.01-0.062-0.052-0.12-0.064-0.187c-0.022-0.114,0.002-0.224,0-0.337 c-0.003-0.2,0.017-0.379-0.078-0.55c-0.38-0.688-1.236-0.929-1.975-0.789c-0.18,0.034-0.287,0.126-0.442,0.207 c-0.17,0.088-0.139,0.166-0.318,0.224c-0.081,0.026-0.216,0.124-0.215,0.224c0.001,0.115,0.005,0.051,0.012,0.169 c-0.021,0.011-0.021-0.005-0.03-0.025" + fill="#FFFFFF" /> + <path + id="path50" + d="M31.137-64.001c0.018,0.072,0.007,0.127-0.026,0.19c-0.053,0.101-0.112,0.062-0.165,0.128 c-0.05,0.062-0.099,0.276-0.079,0.362c-0.169,0.058-0.009,0.227-0.015,0.35c-0.002,0.05-0.041,0.105-0.045,0.161 c-0.01,0.119,0.017,0.266,0.068,0.37c0.097,0.198,0.268,0.413,0.435,0.544c0.19,0.148,0.365,0.572,0.608,0.631 c0.177,0.042,0.384-0.104,0.543-0.143c0.18-0.043,0.397,0.01,0.571-0.053c0.222-0.079,0.127-0.337,0.288-0.45 c0.104-0.074,0.287-0.01,0.406-0.051c0.2-0.069,0.339-0.263,0.376-0.46c0.016-0.082,0.01-0.145,0.039-0.221 c0.039-0.103,0.111-0.16,0.09-0.293c-0.01-0.062-0.052-0.12-0.064-0.187c-0.022-0.114,0.002-0.224,0-0.337 c-0.003-0.2,0.017-0.379-0.078-0.55c-0.38-0.688-1.236-0.929-1.975-0.789c-0.18,0.034-0.287,0.126-0.442,0.207 c-0.17,0.088-0.139,0.166-0.318,0.224c-0.081,0.026-0.216,0.124-0.215,0.224c0.001,0.115,0.005,0.051,0.012,0.169 c-0.021,0.011-0.021-0.005-0.03-0.025" + fill="#FFFFFF" /> + <path + id="path52" + d="M23.477-64.001c0.018,0.072,0.007,0.127-0.026,0.19c-0.053,0.101-0.112,0.062-0.165,0.128 c-0.05,0.062-0.099,0.276-0.079,0.362c-0.169,0.058-0.009,0.227-0.015,0.35c-0.002,0.05-0.041,0.105-0.045,0.161 c-0.01,0.119,0.017,0.266,0.068,0.37c0.097,0.198,0.268,0.413,0.435,0.544c0.19,0.148,0.365,0.572,0.608,0.631 c0.177,0.042,0.384-0.104,0.543-0.143c0.18-0.043,0.397,0.01,0.571-0.053c0.222-0.079,0.127-0.337,0.288-0.45 c0.104-0.074,0.287-0.01,0.406-0.051c0.2-0.069,0.339-0.263,0.376-0.46c0.016-0.082,0.01-0.145,0.039-0.221 c0.039-0.103,0.111-0.16,0.09-0.293c-0.01-0.062-0.052-0.12-0.064-0.187c-0.022-0.114,0.002-0.224,0-0.337 c-0.003-0.2,0.017-0.379-0.078-0.55c-0.38-0.688-1.236-0.929-1.975-0.789c-0.18,0.034-0.287,0.126-0.442,0.207 c-0.17,0.088-0.139,0.166-0.318,0.224c-0.081,0.026-0.216,0.124-0.215,0.224c0.001,0.115,0.005,0.051,0.012,0.169 c-0.021,0.011-0.021-0.005-0.03-0.025" + fill="#FFFFFF" /> + <path + id="path54" + d="M15.816-64.001c0.018,0.072,0.007,0.127-0.026,0.19c-0.053,0.101-0.112,0.062-0.165,0.128 c-0.05,0.062-0.099,0.276-0.079,0.362c-0.169,0.058-0.009,0.227-0.015,0.35c-0.002,0.05-0.041,0.105-0.045,0.161 c-0.01,0.119,0.017,0.266,0.068,0.37c0.097,0.198,0.268,0.413,0.435,0.544c0.19,0.148,0.365,0.572,0.608,0.631 c0.177,0.042,0.384-0.104,0.543-0.143c0.18-0.043,0.397,0.01,0.571-0.053c0.222-0.079,0.127-0.337,0.288-0.45 c0.104-0.074,0.287-0.01,0.406-0.051c0.2-0.069,0.339-0.263,0.376-0.46c0.016-0.082,0.01-0.145,0.039-0.221 c0.039-0.103,0.111-0.16,0.09-0.293c-0.01-0.062-0.052-0.12-0.064-0.187c-0.022-0.114,0.002-0.224,0-0.337 c-0.003-0.2,0.017-0.379-0.078-0.55c-0.38-0.688-1.236-0.929-1.975-0.789c-0.18,0.034-0.287,0.126-0.442,0.207 c-0.17,0.088-0.139,0.166-0.318,0.224c-0.081,0.026-0.216,0.124-0.215,0.224c0.001,0.115,0.005,0.051,0.012,0.169 c-0.021,0.011-0.021-0.005-0.03-0.025" + fill="#FFFFFF" /> + <path + id="path56" + d="M8.156-64.001c0.018,0.072,0.007,0.127-0.026,0.19c-0.053,0.101-0.112,0.062-0.165,0.128 c-0.05,0.062-0.099,0.276-0.079,0.362c-0.169,0.058-0.009,0.227-0.015,0.35c-0.002,0.05-0.041,0.105-0.045,0.161 c-0.01,0.119,0.017,0.266,0.068,0.37c0.097,0.198,0.268,0.413,0.435,0.544c0.19,0.148,0.365,0.572,0.608,0.631 c0.177,0.042,0.384-0.104,0.543-0.143c0.18-0.043,0.397,0.01,0.571-0.053c0.222-0.079,0.127-0.337,0.288-0.45 c0.104-0.074,0.287-0.01,0.406-0.051c0.2-0.069,0.339-0.263,0.376-0.46c0.016-0.082,0.01-0.145,0.039-0.221 c0.039-0.103,0.111-0.16,0.09-0.293c-0.01-0.062-0.052-0.12-0.064-0.187c-0.022-0.114,0.002-0.224,0-0.337 c-0.003-0.2,0.017-0.379-0.078-0.55c-0.38-0.688-1.236-0.929-1.975-0.789c-0.18,0.034-0.287,0.126-0.442,0.207 c-0.17,0.088-0.139,0.166-0.318,0.224c-0.081,0.026-0.216,0.124-0.215,0.224c0.001,0.115,0.005,0.051,0.012,0.169 c-0.021,0.011-0.021-0.005-0.03-0.025" + fill="#FFFFFF" /> + <path + id="path58" + d="M0.495-64.001c0.018,0.072,0.007,0.127-0.026,0.19c-0.053,0.101-0.112,0.062-0.165,0.128 c-0.05,0.062-0.099,0.276-0.079,0.362c-0.169,0.058-0.01,0.227-0.015,0.35c-0.002,0.05-0.041,0.105-0.045,0.161 c-0.01,0.119,0.017,0.266,0.068,0.37c0.097,0.198,0.268,0.413,0.435,0.544c0.19,0.148,0.365,0.572,0.608,0.631 c0.177,0.042,0.384-0.104,0.543-0.143C2-61.45,2.217-61.397,2.391-61.46c0.222-0.079,0.127-0.337,0.288-0.45 c0.104-0.074,0.287-0.01,0.406-0.051c0.2-0.069,0.339-0.263,0.376-0.46c0.016-0.082,0.01-0.145,0.039-0.221 c0.039-0.103,0.111-0.16,0.09-0.293c-0.01-0.062-0.052-0.12-0.064-0.187c-0.022-0.114,0.002-0.224,0-0.337 c-0.003-0.2,0.017-0.379-0.078-0.55c-0.38-0.688-1.236-0.929-1.975-0.789c-0.18,0.034-0.287,0.126-0.442,0.207 c-0.17,0.088-0.139,0.166-0.318,0.224c-0.081,0.026-0.216,0.124-0.215,0.224c0.001,0.115,0.005,0.051,0.012,0.169 c-0.021,0.011-0.021-0.005-0.03-0.025" + fill="#FFFFFF" /> + </g> + <g + id="g60"> + <path + id="path62" + d="M69.439-56.348c0.018,0.072,0.007,0.127-0.026,0.19c-0.053,0.101-0.112,0.062-0.165,0.128 c-0.05,0.062-0.099,0.276-0.079,0.362c-0.169,0.058-0.01,0.227-0.015,0.35c-0.002,0.05-0.041,0.105-0.045,0.161 c-0.01,0.119,0.017,0.266,0.068,0.37c0.097,0.198,0.268,0.413,0.435,0.544c0.19,0.148,0.365,0.572,0.608,0.631 c0.177,0.042,0.384-0.104,0.543-0.143c0.18-0.043,0.397,0.01,0.571-0.053c0.222-0.079,0.127-0.337,0.288-0.45 c0.104-0.074,0.287-0.01,0.406-0.051c0.2-0.069,0.339-0.263,0.376-0.46c0.016-0.082,0.01-0.145,0.039-0.221 c0.039-0.103,0.111-0.16,0.09-0.293c-0.01-0.062-0.052-0.12-0.064-0.187c-0.022-0.114,0.002-0.224,0-0.337 c-0.003-0.2,0.017-0.379-0.078-0.55c-0.38-0.688-1.236-0.929-1.975-0.789c-0.18,0.034-0.287,0.126-0.442,0.207 c-0.17,0.088-0.139,0.166-0.318,0.224c-0.081,0.026-0.216,0.124-0.215,0.224c0.001,0.115,0.005,0.051,0.012,0.169 c-0.021,0.011-0.021-0.005-0.03-0.025" + fill="#FFFFFF" /> + <path + id="path64" + d="M61.778-56.348c0.018,0.072,0.007,0.127-0.026,0.19c-0.053,0.101-0.112,0.062-0.165,0.128 c-0.05,0.062-0.099,0.276-0.079,0.362c-0.169,0.058-0.009,0.227-0.015,0.35c-0.002,0.05-0.041,0.105-0.045,0.161 c-0.01,0.119,0.017,0.266,0.068,0.37c0.097,0.198,0.268,0.413,0.435,0.544c0.19,0.148,0.365,0.572,0.608,0.631 c0.177,0.042,0.384-0.104,0.543-0.143c0.18-0.043,0.397,0.01,0.571-0.053c0.222-0.079,0.127-0.337,0.288-0.45 c0.104-0.074,0.287-0.01,0.406-0.051c0.2-0.069,0.339-0.263,0.376-0.46c0.016-0.082,0.01-0.145,0.039-0.221 c0.039-0.103,0.111-0.16,0.09-0.293c-0.01-0.062-0.052-0.12-0.064-0.187c-0.022-0.114,0.002-0.224,0-0.337 c-0.003-0.2,0.017-0.379-0.078-0.55c-0.38-0.688-1.236-0.929-1.975-0.789c-0.18,0.034-0.287,0.126-0.442,0.207 c-0.17,0.088-0.139,0.166-0.318,0.224c-0.081,0.026-0.216,0.124-0.215,0.224c0.001,0.115,0.005,0.051,0.012,0.169 c-0.021,0.011-0.021-0.005-0.03-0.025" + fill="#FFFFFF" /> + <path + id="path66" + d="M54.118-56.348c0.018,0.072,0.007,0.127-0.026,0.19c-0.053,0.101-0.112,0.062-0.165,0.128 c-0.05,0.062-0.099,0.276-0.079,0.362c-0.169,0.058-0.009,0.227-0.015,0.35c-0.002,0.05-0.041,0.105-0.045,0.161 c-0.01,0.119,0.017,0.266,0.068,0.37c0.097,0.198,0.268,0.413,0.435,0.544c0.19,0.148,0.365,0.572,0.608,0.631 c0.177,0.042,0.384-0.104,0.543-0.143c0.18-0.043,0.397,0.01,0.571-0.053c0.222-0.079,0.127-0.337,0.288-0.45 c0.104-0.074,0.287-0.01,0.406-0.051c0.2-0.069,0.339-0.263,0.376-0.46c0.016-0.082,0.01-0.145,0.039-0.221 c0.039-0.103,0.111-0.16,0.09-0.293c-0.01-0.062-0.052-0.12-0.064-0.187c-0.022-0.114,0.002-0.224,0-0.337 c-0.003-0.2,0.017-0.379-0.078-0.55c-0.38-0.688-1.236-0.929-1.975-0.789c-0.18,0.034-0.287,0.126-0.442,0.207 c-0.17,0.088-0.139,0.166-0.318,0.224c-0.081,0.026-0.216,0.124-0.215,0.224c0.001,0.115,0.005,0.051,0.012,0.169 c-0.021,0.011-0.021-0.005-0.03-0.025" + fill="#FFFFFF" /> + <path + id="path68" + d="M46.458-56.348c0.018,0.072,0.007,0.127-0.026,0.19c-0.053,0.101-0.112,0.062-0.165,0.128 c-0.05,0.062-0.099,0.276-0.079,0.362c-0.169,0.058-0.009,0.227-0.015,0.35c-0.002,0.05-0.041,0.105-0.045,0.161 c-0.01,0.119,0.017,0.266,0.068,0.37c0.097,0.198,0.268,0.413,0.435,0.544c0.19,0.148,0.365,0.572,0.608,0.631 c0.177,0.042,0.384-0.104,0.543-0.143c0.18-0.043,0.397,0.01,0.571-0.053c0.222-0.079,0.127-0.337,0.288-0.45 c0.104-0.074,0.287-0.01,0.406-0.051c0.2-0.069,0.339-0.263,0.376-0.46c0.016-0.082,0.01-0.145,0.039-0.221 c0.039-0.103,0.111-0.16,0.09-0.293c-0.01-0.062-0.052-0.12-0.064-0.187c-0.022-0.114,0.002-0.224,0-0.337 c-0.003-0.2,0.017-0.379-0.078-0.55c-0.38-0.688-1.236-0.929-1.975-0.789c-0.18,0.034-0.287,0.126-0.442,0.207 c-0.17,0.088-0.139,0.166-0.318,0.224c-0.081,0.026-0.216,0.124-0.215,0.224c0.001,0.115,0.005,0.051,0.012,0.169 c-0.021,0.011-0.021-0.005-0.03-0.025" + fill="#FFFFFF" /> + <path + id="path70" + d="M38.797-56.348c0.018,0.072,0.007,0.127-0.026,0.19c-0.053,0.101-0.112,0.062-0.165,0.128 c-0.05,0.062-0.099,0.276-0.079,0.362c-0.169,0.058-0.009,0.227-0.015,0.35c-0.002,0.05-0.041,0.105-0.045,0.161 c-0.01,0.119,0.017,0.266,0.068,0.37c0.097,0.198,0.268,0.413,0.435,0.544c0.19,0.148,0.365,0.572,0.608,0.631 c0.177,0.042,0.384-0.104,0.543-0.143c0.18-0.043,0.397,0.01,0.571-0.053c0.222-0.079,0.127-0.337,0.288-0.45 c0.104-0.074,0.287-0.01,0.406-0.051c0.2-0.069,0.339-0.263,0.376-0.46c0.016-0.082,0.01-0.145,0.039-0.221 c0.039-0.103,0.111-0.16,0.09-0.293c-0.01-0.062-0.052-0.12-0.064-0.187c-0.022-0.114,0.002-0.224,0-0.337 c-0.003-0.2,0.017-0.379-0.078-0.55c-0.38-0.688-1.236-0.929-1.975-0.789c-0.18,0.034-0.287,0.126-0.442,0.207 c-0.17,0.088-0.139,0.166-0.318,0.224c-0.081,0.026-0.216,0.124-0.215,0.224c0.001,0.115,0.005,0.051,0.012,0.169 c-0.021,0.011-0.021-0.005-0.03-0.025" + fill="#FFFFFF" /> + <path + id="path72" + d="M31.137-56.348c0.018,0.072,0.007,0.127-0.026,0.19c-0.053,0.101-0.112,0.062-0.165,0.128 c-0.05,0.062-0.099,0.276-0.079,0.362c-0.169,0.058-0.009,0.227-0.015,0.35c-0.002,0.05-0.041,0.105-0.045,0.161 c-0.01,0.119,0.017,0.266,0.068,0.37c0.097,0.198,0.268,0.413,0.435,0.544c0.19,0.148,0.365,0.572,0.608,0.631 c0.177,0.042,0.384-0.104,0.543-0.143c0.18-0.043,0.397,0.01,0.571-0.053c0.222-0.079,0.127-0.337,0.288-0.45 c0.104-0.074,0.287-0.01,0.406-0.051c0.2-0.069,0.339-0.263,0.376-0.46c0.016-0.082,0.01-0.145,0.039-0.221 c0.039-0.103,0.111-0.16,0.09-0.293c-0.01-0.062-0.052-0.12-0.064-0.187c-0.022-0.114,0.002-0.224,0-0.337 c-0.003-0.2,0.017-0.379-0.078-0.55c-0.38-0.688-1.236-0.929-1.975-0.789c-0.18,0.034-0.287,0.126-0.442,0.207 c-0.17,0.088-0.139,0.166-0.318,0.224c-0.081,0.026-0.216,0.124-0.215,0.224c0.001,0.115,0.005,0.051,0.012,0.169 c-0.021,0.011-0.021-0.005-0.03-0.025" + fill="#FFFFFF" /> + <path + id="path74" + d="M23.477-56.348c0.018,0.072,0.007,0.127-0.026,0.19c-0.053,0.101-0.112,0.062-0.165,0.128 c-0.05,0.062-0.099,0.276-0.079,0.362c-0.169,0.058-0.009,0.227-0.015,0.35c-0.002,0.05-0.041,0.105-0.045,0.161 c-0.01,0.119,0.017,0.266,0.068,0.37c0.097,0.198,0.268,0.413,0.435,0.544c0.19,0.148,0.365,0.572,0.608,0.631 c0.177,0.042,0.384-0.104,0.543-0.143c0.18-0.043,0.397,0.01,0.571-0.053c0.222-0.079,0.127-0.337,0.288-0.45 c0.104-0.074,0.287-0.01,0.406-0.051c0.2-0.069,0.339-0.263,0.376-0.46c0.016-0.082,0.01-0.145,0.039-0.221 c0.039-0.103,0.111-0.16,0.09-0.293c-0.01-0.062-0.052-0.12-0.064-0.187c-0.022-0.114,0.002-0.224,0-0.337 c-0.003-0.2,0.017-0.379-0.078-0.55c-0.38-0.688-1.236-0.929-1.975-0.789c-0.18,0.034-0.287,0.126-0.442,0.207 c-0.17,0.088-0.139,0.166-0.318,0.224c-0.081,0.026-0.216,0.124-0.215,0.224c0.001,0.115,0.005,0.051,0.012,0.169 c-0.021,0.011-0.021-0.005-0.03-0.025" + fill="#FFFFFF" /> + <path + id="path76" + d="M15.816-56.348c0.018,0.072,0.007,0.127-0.026,0.19c-0.053,0.101-0.112,0.062-0.165,0.128 c-0.05,0.062-0.099,0.276-0.079,0.362c-0.169,0.058-0.009,0.227-0.015,0.35c-0.002,0.05-0.041,0.105-0.045,0.161 c-0.01,0.119,0.017,0.266,0.068,0.37c0.097,0.198,0.268,0.413,0.435,0.544c0.19,0.148,0.365,0.572,0.608,0.631 c0.177,0.042,0.384-0.104,0.543-0.143c0.18-0.043,0.397,0.01,0.571-0.053c0.222-0.079,0.127-0.337,0.288-0.45 c0.104-0.074,0.287-0.01,0.406-0.051c0.2-0.069,0.339-0.263,0.376-0.46c0.016-0.082,0.01-0.145,0.039-0.221 c0.039-0.103,0.111-0.16,0.09-0.293c-0.01-0.062-0.052-0.12-0.064-0.187c-0.022-0.114,0.002-0.224,0-0.337 c-0.003-0.2,0.017-0.379-0.078-0.55c-0.38-0.688-1.236-0.929-1.975-0.789c-0.18,0.034-0.287,0.126-0.442,0.207 c-0.17,0.088-0.139,0.166-0.318,0.224c-0.081,0.026-0.216,0.124-0.215,0.224c0.001,0.115,0.005,0.051,0.012,0.169 c-0.021,0.011-0.021-0.005-0.03-0.025" + fill="#FFFFFF" /> + <path + id="path78" + d="M8.156-56.348c0.018,0.072,0.007,0.127-0.026,0.19c-0.053,0.101-0.112,0.062-0.165,0.128 c-0.05,0.062-0.099,0.276-0.079,0.362c-0.169,0.058-0.009,0.227-0.015,0.35c-0.002,0.05-0.041,0.105-0.045,0.161 c-0.01,0.119,0.017,0.266,0.068,0.37c0.097,0.198,0.268,0.413,0.435,0.544c0.19,0.148,0.365,0.572,0.608,0.631 c0.177,0.042,0.384-0.104,0.543-0.143c0.18-0.043,0.397,0.01,0.571-0.053c0.222-0.079,0.127-0.337,0.288-0.45 c0.104-0.074,0.287-0.01,0.406-0.051c0.2-0.069,0.339-0.263,0.376-0.46c0.016-0.082,0.01-0.145,0.039-0.221 c0.039-0.103,0.111-0.16,0.09-0.293c-0.01-0.062-0.052-0.12-0.064-0.187c-0.022-0.114,0.002-0.224,0-0.337 c-0.003-0.2,0.017-0.379-0.078-0.55c-0.38-0.688-1.236-0.929-1.975-0.789c-0.18,0.034-0.287,0.126-0.442,0.207 c-0.17,0.088-0.139,0.166-0.318,0.224c-0.081,0.026-0.216,0.124-0.215,0.224c0.001,0.115,0.005,0.051,0.012,0.169 c-0.021,0.011-0.021-0.005-0.03-0.025" + fill="#FFFFFF" /> + <path + id="path80" + d="M0.495-56.348c0.018,0.072,0.007,0.127-0.026,0.19c-0.053,0.101-0.112,0.062-0.165,0.128 c-0.05,0.062-0.099,0.276-0.079,0.362c-0.169,0.058-0.01,0.227-0.015,0.35c-0.002,0.05-0.041,0.105-0.045,0.161 c-0.01,0.119,0.017,0.266,0.068,0.37c0.097,0.198,0.268,0.413,0.435,0.544c0.19,0.148,0.365,0.572,0.608,0.631 c0.177,0.042,0.384-0.104,0.543-0.143c0.18-0.043,0.397,0.01,0.571-0.053c0.222-0.079,0.127-0.337,0.288-0.45 c0.104-0.074,0.287-0.01,0.406-0.051c0.2-0.069,0.339-0.263,0.376-0.46c0.016-0.082,0.01-0.145,0.039-0.221 c0.039-0.103,0.111-0.16,0.09-0.293c-0.01-0.062-0.052-0.12-0.064-0.187c-0.022-0.114,0.002-0.224,0-0.337 c-0.003-0.2,0.017-0.379-0.078-0.55c-0.38-0.688-1.236-0.929-1.975-0.789c-0.18,0.034-0.287,0.126-0.442,0.207 c-0.17,0.088-0.139,0.166-0.318,0.224c-0.081,0.026-0.216,0.124-0.215,0.224C0.5-56.374,0.503-56.438,0.51-56.32 c-0.021,0.011-0.021-0.005-0.03-0.025" + fill="#FFFFFF" /> + </g> + <g + id="g82"> + <path + id="path84" + d="M69.439-48.695c0.018,0.072,0.007,0.127-0.026,0.19c-0.053,0.101-0.112,0.062-0.165,0.128 c-0.05,0.062-0.099,0.276-0.079,0.362c-0.169,0.058-0.01,0.227-0.015,0.35c-0.002,0.05-0.041,0.105-0.045,0.161 c-0.01,0.119,0.017,0.266,0.068,0.37c0.097,0.198,0.268,0.413,0.435,0.544c0.19,0.148,0.365,0.572,0.608,0.631 c0.177,0.042,0.384-0.104,0.543-0.143c0.18-0.043,0.397,0.01,0.571-0.053c0.222-0.079,0.127-0.337,0.288-0.45 c0.104-0.074,0.287-0.01,0.406-0.051c0.2-0.069,0.339-0.263,0.376-0.46c0.016-0.082,0.01-0.145,0.039-0.221 c0.039-0.103,0.111-0.16,0.09-0.293c-0.01-0.062-0.052-0.12-0.064-0.187c-0.022-0.114,0.002-0.224,0-0.337 c-0.003-0.2,0.017-0.379-0.078-0.55c-0.38-0.688-1.236-0.929-1.975-0.789c-0.18,0.034-0.287,0.126-0.442,0.207 c-0.17,0.088-0.139,0.166-0.318,0.224c-0.081,0.026-0.216,0.124-0.215,0.224c0.001,0.115,0.005,0.051,0.012,0.169 c-0.021,0.011-0.021-0.005-0.03-0.025" + fill="#FFFFFF" /> + <path + id="path86" + d="M61.778-48.695c0.018,0.072,0.007,0.127-0.026,0.19c-0.053,0.101-0.112,0.062-0.165,0.128 c-0.05,0.062-0.099,0.276-0.079,0.362c-0.169,0.058-0.009,0.227-0.015,0.35c-0.002,0.05-0.041,0.105-0.045,0.161 c-0.01,0.119,0.017,0.266,0.068,0.37c0.097,0.198,0.268,0.413,0.435,0.544c0.19,0.148,0.365,0.572,0.608,0.631 c0.177,0.042,0.384-0.104,0.543-0.143c0.18-0.043,0.397,0.01,0.571-0.053c0.222-0.079,0.127-0.337,0.288-0.45 c0.104-0.074,0.287-0.01,0.406-0.051c0.2-0.069,0.339-0.263,0.376-0.46c0.016-0.082,0.01-0.145,0.039-0.221 c0.039-0.103,0.111-0.16,0.09-0.293c-0.01-0.062-0.052-0.12-0.064-0.187c-0.022-0.114,0.002-0.224,0-0.337 c-0.003-0.2,0.017-0.379-0.078-0.55c-0.38-0.688-1.236-0.929-1.975-0.789c-0.18,0.034-0.287,0.126-0.442,0.207 c-0.17,0.088-0.139,0.166-0.318,0.224c-0.081,0.026-0.216,0.124-0.215,0.224c0.001,0.115,0.005,0.051,0.012,0.169 c-0.021,0.011-0.021-0.005-0.03-0.025" + fill="#FFFFFF" /> + <path + id="path88" + d="M54.118-48.695c0.018,0.072,0.007,0.127-0.026,0.19c-0.053,0.101-0.112,0.062-0.165,0.128 c-0.05,0.062-0.099,0.276-0.079,0.362c-0.169,0.058-0.009,0.227-0.015,0.35c-0.002,0.05-0.041,0.105-0.045,0.161 c-0.01,0.119,0.017,0.266,0.068,0.37c0.097,0.198,0.268,0.413,0.435,0.544c0.19,0.148,0.365,0.572,0.608,0.631 c0.177,0.042,0.384-0.104,0.543-0.143c0.18-0.043,0.397,0.01,0.571-0.053c0.222-0.079,0.127-0.337,0.288-0.45 c0.104-0.074,0.287-0.01,0.406-0.051c0.2-0.069,0.339-0.263,0.376-0.46c0.016-0.082,0.01-0.145,0.039-0.221 c0.039-0.103,0.111-0.16,0.09-0.293c-0.01-0.062-0.052-0.12-0.064-0.187c-0.022-0.114,0.002-0.224,0-0.337 c-0.003-0.2,0.017-0.379-0.078-0.55c-0.38-0.688-1.236-0.929-1.975-0.789c-0.18,0.034-0.287,0.126-0.442,0.207 c-0.17,0.088-0.139,0.166-0.318,0.224c-0.081,0.026-0.216,0.124-0.215,0.224c0.001,0.115,0.005,0.051,0.012,0.169 c-0.021,0.011-0.021-0.005-0.03-0.025" + fill="#FFFFFF" /> + <path + id="path90" + d="M46.458-48.695c0.018,0.072,0.007,0.127-0.026,0.19c-0.053,0.101-0.112,0.062-0.165,0.128 c-0.05,0.062-0.099,0.276-0.079,0.362c-0.169,0.058-0.009,0.227-0.015,0.35c-0.002,0.05-0.041,0.105-0.045,0.161 c-0.01,0.119,0.017,0.266,0.068,0.37c0.097,0.198,0.268,0.413,0.435,0.544c0.19,0.148,0.365,0.572,0.608,0.631 c0.177,0.042,0.384-0.104,0.543-0.143c0.18-0.043,0.397,0.01,0.571-0.053c0.222-0.079,0.127-0.337,0.288-0.45 c0.104-0.074,0.287-0.01,0.406-0.051c0.2-0.069,0.339-0.263,0.376-0.46c0.016-0.082,0.01-0.145,0.039-0.221 c0.039-0.103,0.111-0.16,0.09-0.293c-0.01-0.062-0.052-0.12-0.064-0.187c-0.022-0.114,0.002-0.224,0-0.337 c-0.003-0.2,0.017-0.379-0.078-0.55c-0.38-0.688-1.236-0.929-1.975-0.789c-0.18,0.034-0.287,0.126-0.442,0.207 c-0.17,0.088-0.139,0.166-0.318,0.224c-0.081,0.026-0.216,0.124-0.215,0.224c0.001,0.115,0.005,0.051,0.012,0.169 c-0.021,0.011-0.021-0.005-0.03-0.025" + fill="#FFFFFF" /> + <path + id="path92" + d="M38.797-48.695c0.018,0.072,0.007,0.127-0.026,0.19c-0.053,0.101-0.112,0.062-0.165,0.128 c-0.05,0.062-0.099,0.276-0.079,0.362c-0.169,0.058-0.009,0.227-0.015,0.35c-0.002,0.05-0.041,0.105-0.045,0.161 c-0.01,0.119,0.017,0.266,0.068,0.37c0.097,0.198,0.268,0.413,0.435,0.544c0.19,0.148,0.365,0.572,0.608,0.631 c0.177,0.042,0.384-0.104,0.543-0.143c0.18-0.043,0.397,0.01,0.571-0.053c0.222-0.079,0.127-0.337,0.288-0.45 c0.104-0.074,0.287-0.01,0.406-0.051c0.2-0.069,0.339-0.263,0.376-0.46c0.016-0.082,0.01-0.145,0.039-0.221 c0.039-0.103,0.111-0.16,0.09-0.293c-0.01-0.062-0.052-0.12-0.064-0.187c-0.022-0.114,0.002-0.224,0-0.337 c-0.003-0.2,0.017-0.379-0.078-0.55c-0.38-0.688-1.236-0.929-1.975-0.789c-0.18,0.034-0.287,0.126-0.442,0.207 c-0.17,0.088-0.139,0.166-0.318,0.224c-0.081,0.026-0.216,0.124-0.215,0.224c0.001,0.115,0.005,0.051,0.012,0.169 c-0.021,0.011-0.021-0.005-0.03-0.025" + fill="#FFFFFF" /> + <path + id="path94" + d="M31.137-48.695c0.018,0.072,0.007,0.127-0.026,0.19c-0.053,0.101-0.112,0.062-0.165,0.128 c-0.05,0.062-0.099,0.276-0.079,0.362c-0.169,0.058-0.009,0.227-0.015,0.35c-0.002,0.05-0.041,0.105-0.045,0.161 c-0.01,0.119,0.017,0.266,0.068,0.37c0.097,0.198,0.268,0.413,0.435,0.544c0.19,0.148,0.365,0.572,0.608,0.631 c0.177,0.042,0.384-0.104,0.543-0.143c0.18-0.043,0.397,0.01,0.571-0.053c0.222-0.079,0.127-0.337,0.288-0.45 c0.104-0.074,0.287-0.01,0.406-0.051c0.2-0.069,0.339-0.263,0.376-0.46c0.016-0.082,0.01-0.145,0.039-0.221 c0.039-0.103,0.111-0.16,0.09-0.293c-0.01-0.062-0.052-0.12-0.064-0.187c-0.022-0.114,0.002-0.224,0-0.337 c-0.003-0.2,0.017-0.379-0.078-0.55c-0.38-0.688-1.236-0.929-1.975-0.789c-0.18,0.034-0.287,0.126-0.442,0.207 c-0.17,0.088-0.139,0.166-0.318,0.224c-0.081,0.026-0.216,0.124-0.215,0.224c0.001,0.115,0.005,0.051,0.012,0.169 c-0.021,0.011-0.021-0.005-0.03-0.025" + fill="#FFFFFF" /> + <path + id="path96" + d="M23.477-48.695c0.018,0.072,0.007,0.127-0.026,0.19c-0.053,0.101-0.112,0.062-0.165,0.128 c-0.05,0.062-0.099,0.276-0.079,0.362c-0.169,0.058-0.009,0.227-0.015,0.35c-0.002,0.05-0.041,0.105-0.045,0.161 c-0.01,0.119,0.017,0.266,0.068,0.37c0.097,0.198,0.268,0.413,0.435,0.544c0.19,0.148,0.365,0.572,0.608,0.631 c0.177,0.042,0.384-0.104,0.543-0.143c0.18-0.043,0.397,0.01,0.571-0.053c0.222-0.079,0.127-0.337,0.288-0.45 c0.104-0.074,0.287-0.01,0.406-0.051c0.2-0.069,0.339-0.263,0.376-0.46c0.016-0.082,0.01-0.145,0.039-0.221 c0.039-0.103,0.111-0.16,0.09-0.293c-0.01-0.062-0.052-0.12-0.064-0.187c-0.022-0.114,0.002-0.224,0-0.337 c-0.003-0.2,0.017-0.379-0.078-0.55c-0.38-0.688-1.236-0.929-1.975-0.789c-0.18,0.034-0.287,0.126-0.442,0.207 c-0.17,0.088-0.139,0.166-0.318,0.224c-0.081,0.026-0.216,0.124-0.215,0.224c0.001,0.115,0.005,0.051,0.012,0.169 c-0.021,0.011-0.021-0.005-0.03-0.025" + fill="#FFFFFF" /> + <path + id="path98" + d="M15.816-48.695c0.018,0.072,0.007,0.127-0.026,0.19c-0.053,0.101-0.112,0.062-0.165,0.128 c-0.05,0.062-0.099,0.276-0.079,0.362c-0.169,0.058-0.009,0.227-0.015,0.35c-0.002,0.05-0.041,0.105-0.045,0.161 c-0.01,0.119,0.017,0.266,0.068,0.37c0.097,0.198,0.268,0.413,0.435,0.544c0.19,0.148,0.365,0.572,0.608,0.631 c0.177,0.042,0.384-0.104,0.543-0.143c0.18-0.043,0.397,0.01,0.571-0.053c0.222-0.079,0.127-0.337,0.288-0.45 c0.104-0.074,0.287-0.01,0.406-0.051c0.2-0.069,0.339-0.263,0.376-0.46c0.016-0.082,0.01-0.145,0.039-0.221 c0.039-0.103,0.111-0.16,0.09-0.293c-0.01-0.062-0.052-0.12-0.064-0.187c-0.022-0.114,0.002-0.224,0-0.337 c-0.003-0.2,0.017-0.379-0.078-0.55c-0.38-0.688-1.236-0.929-1.975-0.789c-0.18,0.034-0.287,0.126-0.442,0.207 c-0.17,0.088-0.139,0.166-0.318,0.224c-0.081,0.026-0.216,0.124-0.215,0.224c0.001,0.115,0.005,0.051,0.012,0.169 c-0.021,0.011-0.021-0.005-0.03-0.025" + fill="#FFFFFF" /> + <path + id="path100" + d="M8.156-48.695c0.018,0.072,0.007,0.127-0.026,0.19c-0.053,0.101-0.112,0.062-0.165,0.128 c-0.05,0.062-0.099,0.276-0.079,0.362c-0.169,0.058-0.009,0.227-0.015,0.35c-0.002,0.05-0.041,0.105-0.045,0.161 c-0.01,0.119,0.017,0.266,0.068,0.37c0.097,0.198,0.268,0.413,0.435,0.544c0.19,0.148,0.365,0.572,0.608,0.631 c0.177,0.042,0.384-0.104,0.543-0.143c0.18-0.043,0.397,0.01,0.571-0.053c0.222-0.079,0.127-0.337,0.288-0.45 c0.104-0.074,0.287-0.01,0.406-0.051c0.2-0.069,0.339-0.263,0.376-0.46c0.016-0.082,0.01-0.145,0.039-0.221 c0.039-0.103,0.111-0.16,0.09-0.293c-0.01-0.062-0.052-0.12-0.064-0.187c-0.022-0.114,0.002-0.224,0-0.337 c-0.003-0.2,0.017-0.379-0.078-0.55c-0.38-0.688-1.236-0.929-1.975-0.789c-0.18,0.034-0.287,0.126-0.442,0.207 c-0.17,0.088-0.139,0.166-0.318,0.224c-0.081,0.026-0.216,0.124-0.215,0.224c0.001,0.115,0.005,0.051,0.012,0.169 c-0.021,0.011-0.021-0.005-0.03-0.025" + fill="#FFFFFF" /> + <path + id="path102" + d="M0.495-48.695c0.018,0.072,0.007,0.127-0.026,0.19c-0.053,0.101-0.112,0.062-0.165,0.128 c-0.05,0.062-0.099,0.276-0.079,0.362c-0.169,0.058-0.01,0.227-0.015,0.35c-0.002,0.05-0.041,0.105-0.045,0.161 c-0.01,0.119,0.017,0.266,0.068,0.37c0.097,0.198,0.268,0.413,0.435,0.544c0.19,0.148,0.365,0.572,0.608,0.631 c0.177,0.042,0.384-0.104,0.543-0.143c0.18-0.043,0.397,0.01,0.571-0.053c0.222-0.079,0.127-0.337,0.288-0.45 c0.104-0.074,0.287-0.01,0.406-0.051c0.2-0.069,0.339-0.263,0.376-0.46c0.016-0.082,0.01-0.145,0.039-0.221 c0.039-0.103,0.111-0.16,0.09-0.293c-0.01-0.062-0.052-0.12-0.064-0.187c-0.022-0.114,0.002-0.224,0-0.337 c-0.003-0.2,0.017-0.379-0.078-0.55c-0.38-0.688-1.236-0.929-1.975-0.789c-0.18,0.034-0.287,0.126-0.442,0.207 c-0.17,0.088-0.139,0.166-0.318,0.224c-0.081,0.026-0.216,0.124-0.215,0.224c0.001,0.115,0.005,0.051,0.012,0.169 c-0.021,0.011-0.021-0.005-0.03-0.025" + fill="#FFFFFF" /> + </g> + <g + id="g104"> + <path + id="path106" + d="M69.439-41.042c0.018,0.072,0.007,0.127-0.026,0.19c-0.053,0.101-0.112,0.062-0.165,0.128 c-0.05,0.062-0.099,0.276-0.079,0.362c-0.169,0.058-0.01,0.227-0.015,0.35c-0.002,0.05-0.041,0.105-0.045,0.161 c-0.01,0.119,0.017,0.266,0.068,0.37c0.097,0.198,0.268,0.413,0.435,0.544c0.19,0.148,0.365,0.572,0.608,0.631 c0.177,0.042,0.384-0.104,0.543-0.143c0.18-0.043,0.397,0.01,0.571-0.053c0.222-0.079,0.127-0.337,0.288-0.45 c0.104-0.074,0.287-0.01,0.406-0.051c0.2-0.069,0.339-0.263,0.376-0.46c0.016-0.082,0.01-0.145,0.039-0.221 c0.039-0.103,0.111-0.16,0.09-0.293c-0.01-0.062-0.052-0.12-0.064-0.187c-0.022-0.114,0.002-0.224,0-0.337 c-0.003-0.2,0.017-0.379-0.078-0.55c-0.38-0.688-1.236-0.929-1.975-0.789c-0.18,0.034-0.287,0.126-0.442,0.207 c-0.17,0.088-0.139,0.166-0.318,0.224c-0.081,0.026-0.216,0.124-0.215,0.224c0.001,0.115,0.005,0.051,0.012,0.169 c-0.021,0.011-0.021-0.005-0.03-0.025" + fill="#FFFFFF" /> + <path + id="path108" + d="M61.778-41.042c0.018,0.072,0.007,0.127-0.026,0.19c-0.053,0.101-0.112,0.062-0.165,0.128 c-0.05,0.062-0.099,0.276-0.079,0.362c-0.169,0.058-0.009,0.227-0.015,0.35c-0.002,0.05-0.041,0.105-0.045,0.161 c-0.01,0.119,0.017,0.266,0.068,0.37c0.097,0.198,0.268,0.413,0.435,0.544c0.19,0.148,0.365,0.572,0.608,0.631 c0.177,0.042,0.384-0.104,0.543-0.143c0.18-0.043,0.397,0.01,0.571-0.053c0.222-0.079,0.127-0.337,0.288-0.45 c0.104-0.074,0.287-0.01,0.406-0.051c0.2-0.069,0.339-0.263,0.376-0.46c0.016-0.082,0.01-0.145,0.039-0.221 c0.039-0.103,0.111-0.16,0.09-0.293c-0.01-0.062-0.052-0.12-0.064-0.187c-0.022-0.114,0.002-0.224,0-0.337 c-0.003-0.2,0.017-0.379-0.078-0.55c-0.38-0.688-1.236-0.929-1.975-0.789c-0.18,0.034-0.287,0.126-0.442,0.207 c-0.17,0.088-0.139,0.166-0.318,0.224c-0.081,0.026-0.216,0.124-0.215,0.224c0.001,0.115,0.005,0.051,0.012,0.169 c-0.021,0.011-0.021-0.005-0.03-0.025" + fill="#FFFFFF" /> + <path + id="path110" + d="M54.118-41.042c0.018,0.072,0.007,0.127-0.026,0.19c-0.053,0.101-0.112,0.062-0.165,0.128 c-0.05,0.062-0.099,0.276-0.079,0.362c-0.169,0.058-0.009,0.227-0.015,0.35c-0.002,0.05-0.041,0.105-0.045,0.161 c-0.01,0.119,0.017,0.266,0.068,0.37c0.097,0.198,0.268,0.413,0.435,0.544c0.19,0.148,0.365,0.572,0.608,0.631 c0.177,0.042,0.384-0.104,0.543-0.143c0.18-0.043,0.397,0.01,0.571-0.053c0.222-0.079,0.127-0.337,0.288-0.45 c0.104-0.074,0.287-0.01,0.406-0.051c0.2-0.069,0.339-0.263,0.376-0.46c0.016-0.082,0.01-0.145,0.039-0.221 c0.039-0.103,0.111-0.16,0.09-0.293c-0.01-0.062-0.052-0.12-0.064-0.187c-0.022-0.114,0.002-0.224,0-0.337 c-0.003-0.2,0.017-0.379-0.078-0.55c-0.38-0.688-1.236-0.929-1.975-0.789c-0.18,0.034-0.287,0.126-0.442,0.207 c-0.17,0.088-0.139,0.166-0.318,0.224c-0.081,0.026-0.216,0.124-0.215,0.224c0.001,0.115,0.005,0.051,0.012,0.169 c-0.021,0.011-0.021-0.005-0.03-0.025" + fill="#FFFFFF" /> + <path + id="path112" + d="M46.458-41.042c0.018,0.072,0.007,0.127-0.026,0.19c-0.053,0.101-0.112,0.062-0.165,0.128 c-0.05,0.062-0.099,0.276-0.079,0.362c-0.169,0.058-0.009,0.227-0.015,0.35c-0.002,0.05-0.041,0.105-0.045,0.161 c-0.01,0.119,0.017,0.266,0.068,0.37c0.097,0.198,0.268,0.413,0.435,0.544c0.19,0.148,0.365,0.572,0.608,0.631 c0.177,0.042,0.384-0.104,0.543-0.143c0.18-0.043,0.397,0.01,0.571-0.053c0.222-0.079,0.127-0.337,0.288-0.45 c0.104-0.074,0.287-0.01,0.406-0.051c0.2-0.069,0.339-0.263,0.376-0.46c0.016-0.082,0.01-0.145,0.039-0.221 c0.039-0.103,0.111-0.16,0.09-0.293c-0.01-0.062-0.052-0.12-0.064-0.187c-0.022-0.114,0.002-0.224,0-0.337 c-0.003-0.2,0.017-0.379-0.078-0.55c-0.38-0.688-1.236-0.929-1.975-0.789c-0.18,0.034-0.287,0.126-0.442,0.207 c-0.17,0.088-0.139,0.166-0.318,0.224c-0.081,0.026-0.216,0.124-0.215,0.224c0.001,0.115,0.005,0.051,0.012,0.169 c-0.021,0.011-0.021-0.005-0.03-0.025" + fill="#FFFFFF" /> + <path + id="path114" + d="M38.797-41.042c0.018,0.072,0.007,0.127-0.026,0.19c-0.053,0.101-0.112,0.062-0.165,0.128 c-0.05,0.062-0.099,0.276-0.079,0.362c-0.169,0.058-0.009,0.227-0.015,0.35c-0.002,0.05-0.041,0.105-0.045,0.161 c-0.01,0.119,0.017,0.266,0.068,0.37c0.097,0.198,0.268,0.413,0.435,0.544c0.19,0.148,0.365,0.572,0.608,0.631 c0.177,0.042,0.384-0.104,0.543-0.143c0.18-0.043,0.397,0.01,0.571-0.053c0.222-0.079,0.127-0.337,0.288-0.45 c0.104-0.074,0.287-0.01,0.406-0.051c0.2-0.069,0.339-0.263,0.376-0.46c0.016-0.082,0.01-0.145,0.039-0.221 c0.039-0.103,0.111-0.16,0.09-0.293c-0.01-0.062-0.052-0.12-0.064-0.187c-0.022-0.114,0.002-0.224,0-0.337 c-0.003-0.2,0.017-0.379-0.078-0.55c-0.38-0.688-1.236-0.929-1.975-0.789c-0.18,0.034-0.287,0.126-0.442,0.207 c-0.17,0.088-0.139,0.166-0.318,0.224c-0.081,0.026-0.216,0.124-0.215,0.224c0.001,0.115,0.005,0.051,0.012,0.169 c-0.021,0.011-0.021-0.005-0.03-0.025" + fill="#FFFFFF" /> + <path + id="path116" + d="M31.137-41.042c0.018,0.072,0.007,0.127-0.026,0.19c-0.053,0.101-0.112,0.062-0.165,0.128 c-0.05,0.062-0.099,0.276-0.079,0.362c-0.169,0.058-0.009,0.227-0.015,0.35c-0.002,0.05-0.041,0.105-0.045,0.161 c-0.01,0.119,0.017,0.266,0.068,0.37c0.097,0.198,0.268,0.413,0.435,0.544c0.19,0.148,0.365,0.572,0.608,0.631 c0.177,0.042,0.384-0.104,0.543-0.143c0.18-0.043,0.397,0.01,0.571-0.053c0.222-0.079,0.127-0.337,0.288-0.45 c0.104-0.074,0.287-0.01,0.406-0.051c0.2-0.069,0.339-0.263,0.376-0.46c0.016-0.082,0.01-0.145,0.039-0.221 c0.039-0.103,0.111-0.16,0.09-0.293c-0.01-0.062-0.052-0.12-0.064-0.187c-0.022-0.114,0.002-0.224,0-0.337 c-0.003-0.2,0.017-0.379-0.078-0.55c-0.38-0.688-1.236-0.929-1.975-0.789c-0.18,0.034-0.287,0.126-0.442,0.207 c-0.17,0.088-0.139,0.166-0.318,0.224c-0.081,0.026-0.216,0.124-0.215,0.224c0.001,0.115,0.005,0.051,0.012,0.169 c-0.021,0.011-0.021-0.005-0.03-0.025" + fill="#FFFFFF" /> + <path + id="path118" + d="M23.477-41.042c0.018,0.072,0.007,0.127-0.026,0.19c-0.053,0.101-0.112,0.062-0.165,0.128 c-0.05,0.062-0.099,0.276-0.079,0.362c-0.169,0.058-0.009,0.227-0.015,0.35c-0.002,0.05-0.041,0.105-0.045,0.161 c-0.01,0.119,0.017,0.266,0.068,0.37c0.097,0.198,0.268,0.413,0.435,0.544c0.19,0.148,0.365,0.572,0.608,0.631 c0.177,0.042,0.384-0.104,0.543-0.143c0.18-0.043,0.397,0.01,0.571-0.053c0.222-0.079,0.127-0.337,0.288-0.45 c0.104-0.074,0.287-0.01,0.406-0.051c0.2-0.069,0.339-0.263,0.376-0.46c0.016-0.082,0.01-0.145,0.039-0.221 c0.039-0.103,0.111-0.16,0.09-0.293c-0.01-0.062-0.052-0.12-0.064-0.187c-0.022-0.114,0.002-0.224,0-0.337 c-0.003-0.2,0.017-0.379-0.078-0.55c-0.38-0.688-1.236-0.929-1.975-0.789c-0.18,0.034-0.287,0.126-0.442,0.207 c-0.17,0.088-0.139,0.166-0.318,0.224c-0.081,0.026-0.216,0.124-0.215,0.224c0.001,0.115,0.005,0.051,0.012,0.169 c-0.021,0.011-0.021-0.005-0.03-0.025" + fill="#FFFFFF" /> + <path + id="path120" + d="M15.816-41.042c0.018,0.072,0.007,0.127-0.026,0.19c-0.053,0.101-0.112,0.062-0.165,0.128 c-0.05,0.062-0.099,0.276-0.079,0.362c-0.169,0.058-0.009,0.227-0.015,0.35c-0.002,0.05-0.041,0.105-0.045,0.161 c-0.01,0.119,0.017,0.266,0.068,0.37c0.097,0.198,0.268,0.413,0.435,0.544c0.19,0.148,0.365,0.572,0.608,0.631 c0.177,0.042,0.384-0.104,0.543-0.143c0.18-0.043,0.397,0.01,0.571-0.053c0.222-0.079,0.127-0.337,0.288-0.45 c0.104-0.074,0.287-0.01,0.406-0.051c0.2-0.069,0.339-0.263,0.376-0.46c0.016-0.082,0.01-0.145,0.039-0.221 c0.039-0.103,0.111-0.16,0.09-0.293c-0.01-0.062-0.052-0.12-0.064-0.187c-0.022-0.114,0.002-0.224,0-0.337 c-0.003-0.2,0.017-0.379-0.078-0.55c-0.38-0.688-1.236-0.929-1.975-0.789c-0.18,0.034-0.287,0.126-0.442,0.207 c-0.17,0.088-0.139,0.166-0.318,0.224c-0.081,0.026-0.216,0.124-0.215,0.224c0.001,0.115,0.005,0.051,0.012,0.169 c-0.021,0.011-0.021-0.005-0.03-0.025" + fill="#FFFFFF" /> + <path + id="path122" + d="M8.156-41.042c0.018,0.072,0.007,0.127-0.026,0.19c-0.053,0.101-0.112,0.062-0.165,0.128 c-0.05,0.062-0.099,0.276-0.079,0.362c-0.169,0.058-0.009,0.227-0.015,0.35c-0.002,0.05-0.041,0.105-0.045,0.161 c-0.01,0.119,0.017,0.266,0.068,0.37c0.097,0.198,0.268,0.413,0.435,0.544c0.19,0.148,0.365,0.572,0.608,0.631 c0.177,0.042,0.384-0.104,0.543-0.143c0.18-0.043,0.397,0.01,0.571-0.053c0.222-0.079,0.127-0.337,0.288-0.45 c0.104-0.074,0.287-0.01,0.406-0.051c0.2-0.069,0.339-0.263,0.376-0.46c0.016-0.082,0.01-0.145,0.039-0.221 c0.039-0.103,0.111-0.16,0.09-0.293c-0.01-0.062-0.052-0.12-0.064-0.187c-0.022-0.114,0.002-0.224,0-0.337 c-0.003-0.2,0.017-0.379-0.078-0.55c-0.38-0.688-1.236-0.929-1.975-0.789c-0.18,0.034-0.287,0.126-0.442,0.207 c-0.17,0.088-0.139,0.166-0.318,0.224c-0.081,0.026-0.216,0.124-0.215,0.224c0.001,0.115,0.005,0.051,0.012,0.169 C8.15-41.004,8.149-41.02,8.14-41.04" + fill="#FFFFFF" /> + <path + id="path124" + d="M0.495-41.042c0.018,0.072,0.007,0.127-0.026,0.19c-0.053,0.101-0.112,0.062-0.165,0.128 c-0.05,0.062-0.099,0.276-0.079,0.362c-0.169,0.058-0.01,0.227-0.015,0.35c-0.002,0.05-0.041,0.105-0.045,0.161 c-0.01,0.119,0.017,0.266,0.068,0.37c0.097,0.198,0.268,0.413,0.435,0.544c0.19,0.148,0.365,0.572,0.608,0.631 c0.177,0.042,0.384-0.104,0.543-0.143c0.18-0.043,0.397,0.01,0.571-0.053c0.222-0.079,0.127-0.337,0.288-0.45 c0.104-0.074,0.287-0.01,0.406-0.051c0.2-0.069,0.339-0.263,0.376-0.46c0.016-0.082,0.01-0.145,0.039-0.221 c0.039-0.103,0.111-0.16,0.09-0.293c-0.01-0.062-0.052-0.12-0.064-0.187c-0.022-0.114,0.002-0.224,0-0.337 c-0.003-0.2,0.017-0.379-0.078-0.55c-0.38-0.688-1.236-0.929-1.975-0.789c-0.18,0.034-0.287,0.126-0.442,0.207 c-0.17,0.088-0.139,0.166-0.318,0.224c-0.081,0.026-0.216,0.124-0.215,0.224c0.001,0.115,0.005,0.051,0.012,0.169 c-0.021,0.011-0.021-0.005-0.03-0.025" + fill="#FFFFFF" /> + </g> + <g + id="g126"> + <path + id="path128" + d="M69.439-33.39c0.018,0.072,0.007,0.127-0.026,0.19c-0.053,0.101-0.112,0.062-0.165,0.128 c-0.05,0.062-0.099,0.276-0.079,0.362c-0.169,0.058-0.01,0.227-0.015,0.35c-0.002,0.05-0.041,0.105-0.045,0.161 c-0.01,0.119,0.017,0.266,0.068,0.37c0.097,0.198,0.268,0.413,0.435,0.544c0.19,0.148,0.365,0.572,0.608,0.631 c0.177,0.042,0.384-0.104,0.543-0.143c0.18-0.043,0.397,0.01,0.571-0.053c0.222-0.079,0.127-0.337,0.288-0.45 c0.104-0.074,0.287-0.01,0.406-0.051c0.2-0.069,0.339-0.263,0.376-0.46c0.016-0.082,0.01-0.145,0.039-0.221 c0.039-0.103,0.111-0.16,0.09-0.293c-0.01-0.062-0.052-0.12-0.064-0.187c-0.022-0.114,0.002-0.224,0-0.337 c-0.003-0.2,0.017-0.379-0.078-0.55c-0.38-0.688-1.236-0.929-1.975-0.789c-0.18,0.034-0.287,0.126-0.442,0.207 c-0.17,0.088-0.139,0.166-0.318,0.224c-0.081,0.026-0.216,0.124-0.215,0.224c0.001,0.115,0.005,0.051,0.012,0.169 c-0.021,0.011-0.021-0.005-0.03-0.025" + fill="#FFFFFF" /> + <path + id="path130" + d="M61.778-33.39c0.018,0.072,0.007,0.127-0.026,0.19c-0.053,0.101-0.112,0.062-0.165,0.128 c-0.05,0.062-0.099,0.276-0.079,0.362c-0.169,0.058-0.009,0.227-0.015,0.35c-0.002,0.05-0.041,0.105-0.045,0.161 c-0.01,0.119,0.017,0.266,0.068,0.37c0.097,0.198,0.268,0.413,0.435,0.544c0.19,0.148,0.365,0.572,0.608,0.631 c0.177,0.042,0.384-0.104,0.543-0.143c0.18-0.043,0.397,0.01,0.571-0.053c0.222-0.079,0.127-0.337,0.288-0.45 c0.104-0.074,0.287-0.01,0.406-0.051c0.2-0.069,0.339-0.263,0.376-0.46c0.016-0.082,0.01-0.145,0.039-0.221 c0.039-0.103,0.111-0.16,0.09-0.293c-0.01-0.062-0.052-0.12-0.064-0.187c-0.022-0.114,0.002-0.224,0-0.337 c-0.003-0.2,0.017-0.379-0.078-0.55c-0.38-0.688-1.236-0.929-1.975-0.789c-0.18,0.034-0.287,0.126-0.442,0.207 c-0.17,0.088-0.139,0.166-0.318,0.224c-0.081,0.026-0.216,0.124-0.215,0.224c0.001,0.115,0.005,0.051,0.012,0.169 c-0.021,0.011-0.021-0.005-0.03-0.025" + fill="#FFFFFF" /> + <path + id="path132" + d="M54.118-33.39c0.018,0.072,0.007,0.127-0.026,0.19c-0.053,0.101-0.112,0.062-0.165,0.128 c-0.05,0.062-0.099,0.276-0.079,0.362c-0.169,0.058-0.009,0.227-0.015,0.35c-0.002,0.05-0.041,0.105-0.045,0.161 c-0.01,0.119,0.017,0.266,0.068,0.37c0.097,0.198,0.268,0.413,0.435,0.544c0.19,0.148,0.365,0.572,0.608,0.631 c0.177,0.042,0.384-0.104,0.543-0.143c0.18-0.043,0.397,0.01,0.571-0.053c0.222-0.079,0.127-0.337,0.288-0.45 c0.104-0.074,0.287-0.01,0.406-0.051c0.2-0.069,0.339-0.263,0.376-0.46c0.016-0.082,0.01-0.145,0.039-0.221 c0.039-0.103,0.111-0.16,0.09-0.293c-0.01-0.062-0.052-0.12-0.064-0.187c-0.022-0.114,0.002-0.224,0-0.337 c-0.003-0.2,0.017-0.379-0.078-0.55c-0.38-0.688-1.236-0.929-1.975-0.789c-0.18,0.034-0.287,0.126-0.442,0.207 c-0.17,0.088-0.139,0.166-0.318,0.224c-0.081,0.026-0.216,0.124-0.215,0.224c0.001,0.115,0.005,0.051,0.012,0.169 c-0.021,0.011-0.021-0.005-0.03-0.025" + fill="#FFFFFF" /> + <path + id="path134" + d="M46.458-33.39c0.018,0.072,0.007,0.127-0.026,0.19c-0.053,0.101-0.112,0.062-0.165,0.128 c-0.05,0.062-0.099,0.276-0.079,0.362c-0.169,0.058-0.009,0.227-0.015,0.35c-0.002,0.05-0.041,0.105-0.045,0.161 c-0.01,0.119,0.017,0.266,0.068,0.37c0.097,0.198,0.268,0.413,0.435,0.544c0.19,0.148,0.365,0.572,0.608,0.631 c0.177,0.042,0.384-0.104,0.543-0.143c0.18-0.043,0.397,0.01,0.571-0.053c0.222-0.079,0.127-0.337,0.288-0.45 c0.104-0.074,0.287-0.01,0.406-0.051c0.2-0.069,0.339-0.263,0.376-0.46c0.016-0.082,0.01-0.145,0.039-0.221 c0.039-0.103,0.111-0.16,0.09-0.293c-0.01-0.062-0.052-0.12-0.064-0.187c-0.022-0.114,0.002-0.224,0-0.337 c-0.003-0.2,0.017-0.379-0.078-0.55c-0.38-0.688-1.236-0.929-1.975-0.789c-0.18,0.034-0.287,0.126-0.442,0.207 c-0.17,0.088-0.139,0.166-0.318,0.224c-0.081,0.026-0.216,0.124-0.215,0.224c0.001,0.115,0.005,0.051,0.012,0.169 c-0.021,0.011-0.021-0.005-0.03-0.025" + fill="#FFFFFF" /> + <path + id="path136" + d="M38.797-33.39c0.018,0.072,0.007,0.127-0.026,0.19c-0.053,0.101-0.112,0.062-0.165,0.128 c-0.05,0.062-0.099,0.276-0.079,0.362c-0.169,0.058-0.009,0.227-0.015,0.35c-0.002,0.05-0.041,0.105-0.045,0.161 c-0.01,0.119,0.017,0.266,0.068,0.37c0.097,0.198,0.268,0.413,0.435,0.544c0.19,0.148,0.365,0.572,0.608,0.631 c0.177,0.042,0.384-0.104,0.543-0.143c0.18-0.043,0.397,0.01,0.571-0.053c0.222-0.079,0.127-0.337,0.288-0.45 c0.104-0.074,0.287-0.01,0.406-0.051c0.2-0.069,0.339-0.263,0.376-0.46c0.016-0.082,0.01-0.145,0.039-0.221 c0.039-0.103,0.111-0.16,0.09-0.293c-0.01-0.062-0.052-0.12-0.064-0.187c-0.022-0.114,0.002-0.224,0-0.337 c-0.003-0.2,0.017-0.379-0.078-0.55c-0.38-0.688-1.236-0.929-1.975-0.789c-0.18,0.034-0.287,0.126-0.442,0.207 c-0.17,0.088-0.139,0.166-0.318,0.224c-0.081,0.026-0.216,0.124-0.215,0.224c0.001,0.115,0.005,0.051,0.012,0.169 c-0.021,0.011-0.021-0.005-0.03-0.025" + fill="#FFFFFF" /> + <path + id="path138" + d="M31.137-33.39c0.018,0.072,0.007,0.127-0.026,0.19c-0.053,0.101-0.112,0.062-0.165,0.128 c-0.05,0.062-0.099,0.276-0.079,0.362c-0.169,0.058-0.009,0.227-0.015,0.35c-0.002,0.05-0.041,0.105-0.045,0.161 c-0.01,0.119,0.017,0.266,0.068,0.37c0.097,0.198,0.268,0.413,0.435,0.544c0.19,0.148,0.365,0.572,0.608,0.631 c0.177,0.042,0.384-0.104,0.543-0.143c0.18-0.043,0.397,0.01,0.571-0.053c0.222-0.079,0.127-0.337,0.288-0.45 c0.104-0.074,0.287-0.01,0.406-0.051c0.2-0.069,0.339-0.263,0.376-0.46c0.016-0.082,0.01-0.145,0.039-0.221 c0.039-0.103,0.111-0.16,0.09-0.293c-0.01-0.062-0.052-0.12-0.064-0.187c-0.022-0.114,0.002-0.224,0-0.337 c-0.003-0.2,0.017-0.379-0.078-0.55c-0.38-0.688-1.236-0.929-1.975-0.789c-0.18,0.034-0.287,0.126-0.442,0.207 c-0.17,0.088-0.139,0.166-0.318,0.224c-0.081,0.026-0.216,0.124-0.215,0.224c0.001,0.115,0.005,0.051,0.012,0.169 c-0.021,0.011-0.021-0.005-0.03-0.025" + fill="#FFFFFF" /> + <path + id="path140" + d="M23.477-33.39c0.018,0.072,0.007,0.127-0.026,0.19c-0.053,0.101-0.112,0.062-0.165,0.128 c-0.05,0.062-0.099,0.276-0.079,0.362c-0.169,0.058-0.009,0.227-0.015,0.35c-0.002,0.05-0.041,0.105-0.045,0.161 c-0.01,0.119,0.017,0.266,0.068,0.37c0.097,0.198,0.268,0.413,0.435,0.544c0.19,0.148,0.365,0.572,0.608,0.631 c0.177,0.042,0.384-0.104,0.543-0.143c0.18-0.043,0.397,0.01,0.571-0.053c0.222-0.079,0.127-0.337,0.288-0.45 c0.104-0.074,0.287-0.01,0.406-0.051c0.2-0.069,0.339-0.263,0.376-0.46c0.016-0.082,0.01-0.145,0.039-0.221 c0.039-0.103,0.111-0.16,0.09-0.293c-0.01-0.062-0.052-0.12-0.064-0.187c-0.022-0.114,0.002-0.224,0-0.337 c-0.003-0.2,0.017-0.379-0.078-0.55c-0.38-0.688-1.236-0.929-1.975-0.789c-0.18,0.034-0.287,0.126-0.442,0.207 c-0.17,0.088-0.139,0.166-0.318,0.224c-0.081,0.026-0.216,0.124-0.215,0.224c0.001,0.115,0.005,0.051,0.012,0.169 c-0.021,0.011-0.021-0.005-0.03-0.025" + fill="#FFFFFF" /> + <path + id="path142" + d="M15.816-33.39c0.018,0.072,0.007,0.127-0.026,0.19c-0.053,0.101-0.112,0.062-0.165,0.128 c-0.05,0.062-0.099,0.276-0.079,0.362c-0.169,0.058-0.009,0.227-0.015,0.35c-0.002,0.05-0.041,0.105-0.045,0.161 c-0.01,0.119,0.017,0.266,0.068,0.37c0.097,0.198,0.268,0.413,0.435,0.544c0.19,0.148,0.365,0.572,0.608,0.631 c0.177,0.042,0.384-0.104,0.543-0.143c0.18-0.043,0.397,0.01,0.571-0.053c0.222-0.079,0.127-0.337,0.288-0.45 c0.104-0.074,0.287-0.01,0.406-0.051c0.2-0.069,0.339-0.263,0.376-0.46c0.016-0.082,0.01-0.145,0.039-0.221 c0.039-0.103,0.111-0.16,0.09-0.293c-0.01-0.062-0.052-0.12-0.064-0.187c-0.022-0.114,0.002-0.224,0-0.337 c-0.003-0.2,0.017-0.379-0.078-0.55c-0.38-0.688-1.236-0.929-1.975-0.789c-0.18,0.034-0.287,0.126-0.442,0.207 c-0.17,0.088-0.139,0.166-0.318,0.224c-0.081,0.026-0.216,0.124-0.215,0.224c0.001,0.115,0.005,0.051,0.012,0.169 c-0.021,0.011-0.021-0.005-0.03-0.025" + fill="#FFFFFF" /> + <path + id="path144" + d="M8.156-33.39c0.018,0.072,0.007,0.127-0.026,0.19c-0.053,0.101-0.112,0.062-0.165,0.128 c-0.05,0.062-0.099,0.276-0.079,0.362c-0.169,0.058-0.009,0.227-0.015,0.35c-0.002,0.05-0.041,0.105-0.045,0.161 c-0.01,0.119,0.017,0.266,0.068,0.37c0.097,0.198,0.268,0.413,0.435,0.544c0.19,0.148,0.365,0.572,0.608,0.631 c0.177,0.042,0.384-0.104,0.543-0.143c0.18-0.043,0.397,0.01,0.571-0.053c0.222-0.079,0.127-0.337,0.288-0.45 c0.104-0.074,0.287-0.01,0.406-0.051c0.2-0.069,0.339-0.263,0.376-0.46c0.016-0.082,0.01-0.145,0.039-0.221 c0.039-0.103,0.111-0.16,0.09-0.293c-0.01-0.062-0.052-0.12-0.064-0.187c-0.022-0.114,0.002-0.224,0-0.337 c-0.003-0.2,0.017-0.379-0.078-0.55c-0.38-0.688-1.236-0.929-1.975-0.789c-0.18,0.034-0.287,0.126-0.442,0.207 c-0.17,0.088-0.139,0.166-0.318,0.224c-0.081,0.026-0.216,0.124-0.215,0.224c0.001,0.115,0.005,0.051,0.012,0.169 c-0.021,0.011-0.021-0.005-0.03-0.025" + fill="#FFFFFF" /> + <path + id="path146" + d="M0.495-33.39c0.018,0.072,0.007,0.127-0.026,0.19c-0.053,0.101-0.112,0.062-0.165,0.128 c-0.05,0.062-0.099,0.276-0.079,0.362c-0.169,0.058-0.01,0.227-0.015,0.35c-0.002,0.05-0.041,0.105-0.045,0.161 c-0.01,0.119,0.017,0.266,0.068,0.37c0.097,0.198,0.268,0.413,0.435,0.544c0.19,0.148,0.365,0.572,0.608,0.631 c0.177,0.042,0.384-0.104,0.543-0.143c0.18-0.043,0.397,0.01,0.571-0.053c0.222-0.079,0.127-0.337,0.288-0.45 c0.104-0.074,0.287-0.01,0.406-0.051c0.2-0.069,0.339-0.263,0.376-0.46c0.016-0.082,0.01-0.145,0.039-0.221 c0.039-0.103,0.111-0.16,0.09-0.293c-0.01-0.062-0.052-0.12-0.064-0.187c-0.022-0.114,0.002-0.224,0-0.337 c-0.003-0.2,0.017-0.379-0.078-0.55c-0.38-0.688-1.236-0.929-1.975-0.789c-0.18,0.034-0.287,0.126-0.442,0.207 c-0.17,0.088-0.139,0.166-0.318,0.224c-0.081,0.026-0.216,0.124-0.215,0.224C0.5-33.416,0.503-33.48,0.51-33.362 c-0.021,0.011-0.021-0.005-0.03-0.025" + fill="#FFFFFF" /> + </g> + <g + id="g148"> + <path + id="path150" + d="M69.439-25.736c0.018,0.072,0.007,0.127-0.026,0.19c-0.053,0.101-0.112,0.062-0.165,0.128 c-0.05,0.062-0.099,0.276-0.079,0.362c-0.169,0.058-0.01,0.227-0.015,0.35c-0.002,0.05-0.041,0.105-0.045,0.161 c-0.01,0.119,0.017,0.266,0.068,0.37c0.097,0.198,0.268,0.413,0.435,0.544c0.19,0.148,0.365,0.572,0.608,0.631 c0.177,0.042,0.384-0.104,0.543-0.143c0.18-0.043,0.397,0.01,0.571-0.053c0.222-0.079,0.127-0.337,0.288-0.45 c0.104-0.074,0.287-0.01,0.406-0.051c0.2-0.069,0.339-0.263,0.376-0.46c0.016-0.082,0.01-0.145,0.039-0.221 c0.039-0.103,0.111-0.16,0.09-0.293c-0.01-0.062-0.052-0.12-0.064-0.187c-0.022-0.114,0.002-0.224,0-0.337 c-0.003-0.2,0.017-0.379-0.078-0.55c-0.38-0.688-1.236-0.929-1.975-0.789c-0.18,0.034-0.287,0.126-0.442,0.207 c-0.17,0.088-0.139,0.166-0.318,0.224c-0.081,0.026-0.216,0.124-0.215,0.224c0.001,0.115,0.005,0.051,0.012,0.169 c-0.021,0.011-0.021-0.005-0.03-0.025" + fill="#FFFFFF" /> + <path + id="path152" + d="M61.778-25.736c0.018,0.072,0.007,0.127-0.026,0.19c-0.053,0.101-0.112,0.062-0.165,0.128 c-0.05,0.062-0.099,0.276-0.079,0.362c-0.169,0.058-0.009,0.227-0.015,0.35c-0.002,0.05-0.041,0.105-0.045,0.161 c-0.01,0.119,0.017,0.266,0.068,0.37c0.097,0.198,0.268,0.413,0.435,0.544c0.19,0.148,0.365,0.572,0.608,0.631 c0.177,0.042,0.384-0.104,0.543-0.143c0.18-0.043,0.397,0.01,0.571-0.053c0.222-0.079,0.127-0.337,0.288-0.45 c0.104-0.074,0.287-0.01,0.406-0.051c0.2-0.069,0.339-0.263,0.376-0.46c0.016-0.082,0.01-0.145,0.039-0.221 c0.039-0.103,0.111-0.16,0.09-0.293c-0.01-0.062-0.052-0.12-0.064-0.187c-0.022-0.114,0.002-0.224,0-0.337 c-0.003-0.2,0.017-0.379-0.078-0.55c-0.38-0.688-1.236-0.929-1.975-0.789c-0.18,0.034-0.287,0.126-0.442,0.207 c-0.17,0.088-0.139,0.166-0.318,0.224c-0.081,0.026-0.216,0.124-0.215,0.224c0.001,0.115,0.005,0.051,0.012,0.169 c-0.021,0.011-0.021-0.005-0.03-0.025" + fill="#FFFFFF" /> + <path + id="path154" + d="M54.118-25.736c0.018,0.072,0.007,0.127-0.026,0.19c-0.053,0.101-0.112,0.062-0.165,0.128 c-0.05,0.062-0.099,0.276-0.079,0.362c-0.169,0.058-0.009,0.227-0.015,0.35c-0.002,0.05-0.041,0.105-0.045,0.161 c-0.01,0.119,0.017,0.266,0.068,0.37c0.097,0.198,0.268,0.413,0.435,0.544c0.19,0.148,0.365,0.572,0.608,0.631 c0.177,0.042,0.384-0.104,0.543-0.143c0.18-0.043,0.397,0.01,0.571-0.053c0.222-0.079,0.127-0.337,0.288-0.45 c0.104-0.074,0.287-0.01,0.406-0.051c0.2-0.069,0.339-0.263,0.376-0.46c0.016-0.082,0.01-0.145,0.039-0.221 c0.039-0.103,0.111-0.16,0.09-0.293c-0.01-0.062-0.052-0.12-0.064-0.187c-0.022-0.114,0.002-0.224,0-0.337 c-0.003-0.2,0.017-0.379-0.078-0.55c-0.38-0.688-1.236-0.929-1.975-0.789c-0.18,0.034-0.287,0.126-0.442,0.207 c-0.17,0.088-0.139,0.166-0.318,0.224c-0.081,0.026-0.216,0.124-0.215,0.224c0.001,0.115,0.005,0.051,0.012,0.169 c-0.021,0.011-0.021-0.005-0.03-0.025" + fill="#FFFFFF" /> + <path + id="path156" + d="M46.458-25.736c0.018,0.072,0.007,0.127-0.026,0.19c-0.053,0.101-0.112,0.062-0.165,0.128 c-0.05,0.062-0.099,0.276-0.079,0.362c-0.169,0.058-0.009,0.227-0.015,0.35c-0.002,0.05-0.041,0.105-0.045,0.161 c-0.01,0.119,0.017,0.266,0.068,0.37c0.097,0.198,0.268,0.413,0.435,0.544c0.19,0.148,0.365,0.572,0.608,0.631 c0.177,0.042,0.384-0.104,0.543-0.143c0.18-0.043,0.397,0.01,0.571-0.053c0.222-0.079,0.127-0.337,0.288-0.45 c0.104-0.074,0.287-0.01,0.406-0.051c0.2-0.069,0.339-0.263,0.376-0.46c0.016-0.082,0.01-0.145,0.039-0.221 c0.039-0.103,0.111-0.16,0.09-0.293c-0.01-0.062-0.052-0.12-0.064-0.187c-0.022-0.114,0.002-0.224,0-0.337 c-0.003-0.2,0.017-0.379-0.078-0.55c-0.38-0.688-1.236-0.929-1.975-0.789c-0.18,0.034-0.287,0.126-0.442,0.207 c-0.17,0.088-0.139,0.166-0.318,0.224c-0.081,0.026-0.216,0.124-0.215,0.224c0.001,0.115,0.005,0.051,0.012,0.169 c-0.021,0.011-0.021-0.005-0.03-0.025" + fill="#FFFFFF" /> + <path + id="path158" + d="M38.797-25.736c0.018,0.072,0.007,0.127-0.026,0.19c-0.053,0.101-0.112,0.062-0.165,0.128 c-0.05,0.062-0.099,0.276-0.079,0.362c-0.169,0.058-0.009,0.227-0.015,0.35c-0.002,0.05-0.041,0.105-0.045,0.161 c-0.01,0.119,0.017,0.266,0.068,0.37c0.097,0.198,0.268,0.413,0.435,0.544c0.19,0.148,0.365,0.572,0.608,0.631 c0.177,0.042,0.384-0.104,0.543-0.143c0.18-0.043,0.397,0.01,0.571-0.053c0.222-0.079,0.127-0.337,0.288-0.45 c0.104-0.074,0.287-0.01,0.406-0.051c0.2-0.069,0.339-0.263,0.376-0.46c0.016-0.082,0.01-0.145,0.039-0.221 c0.039-0.103,0.111-0.16,0.09-0.293c-0.01-0.062-0.052-0.12-0.064-0.187c-0.022-0.114,0.002-0.224,0-0.337 c-0.003-0.2,0.017-0.379-0.078-0.55c-0.38-0.688-1.236-0.929-1.975-0.789c-0.18,0.034-0.287,0.126-0.442,0.207 c-0.17,0.088-0.139,0.166-0.318,0.224c-0.081,0.026-0.216,0.124-0.215,0.224c0.001,0.115,0.005,0.051,0.012,0.169 c-0.021,0.011-0.021-0.005-0.03-0.025" + fill="#FFFFFF" /> + <path + id="path160" + d="M31.137-25.736c0.018,0.072,0.007,0.127-0.026,0.19c-0.053,0.101-0.112,0.062-0.165,0.128 c-0.05,0.062-0.099,0.276-0.079,0.362c-0.169,0.058-0.009,0.227-0.015,0.35c-0.002,0.05-0.041,0.105-0.045,0.161 c-0.01,0.119,0.017,0.266,0.068,0.37c0.097,0.198,0.268,0.413,0.435,0.544c0.19,0.148,0.365,0.572,0.608,0.631 c0.177,0.042,0.384-0.104,0.543-0.143c0.18-0.043,0.397,0.01,0.571-0.053c0.222-0.079,0.127-0.337,0.288-0.45 c0.104-0.074,0.287-0.01,0.406-0.051c0.2-0.069,0.339-0.263,0.376-0.46c0.016-0.082,0.01-0.145,0.039-0.221 c0.039-0.103,0.111-0.16,0.09-0.293c-0.01-0.062-0.052-0.12-0.064-0.187c-0.022-0.114,0.002-0.224,0-0.337 c-0.003-0.2,0.017-0.379-0.078-0.55c-0.38-0.688-1.236-0.929-1.975-0.789c-0.18,0.034-0.287,0.126-0.442,0.207 c-0.17,0.088-0.139,0.166-0.318,0.224c-0.081,0.026-0.216,0.124-0.215,0.224c0.001,0.115,0.005,0.051,0.012,0.169 c-0.021,0.011-0.021-0.005-0.03-0.025" + fill="#FFFFFF" /> + <path + id="path162" + d="M23.477-25.736c0.018,0.072,0.007,0.127-0.026,0.19c-0.053,0.101-0.112,0.062-0.165,0.128 c-0.05,0.062-0.099,0.276-0.079,0.362c-0.169,0.058-0.009,0.227-0.015,0.35c-0.002,0.05-0.041,0.105-0.045,0.161 c-0.01,0.119,0.017,0.266,0.068,0.37c0.097,0.198,0.268,0.413,0.435,0.544c0.19,0.148,0.365,0.572,0.608,0.631 c0.177,0.042,0.384-0.104,0.543-0.143c0.18-0.043,0.397,0.01,0.571-0.053c0.222-0.079,0.127-0.337,0.288-0.45 c0.104-0.074,0.287-0.01,0.406-0.051c0.2-0.069,0.339-0.263,0.376-0.46c0.016-0.082,0.01-0.145,0.039-0.221 c0.039-0.103,0.111-0.16,0.09-0.293c-0.01-0.062-0.052-0.12-0.064-0.187c-0.022-0.114,0.002-0.224,0-0.337 c-0.003-0.2,0.017-0.379-0.078-0.55c-0.38-0.688-1.236-0.929-1.975-0.789c-0.18,0.034-0.287,0.126-0.442,0.207 c-0.17,0.088-0.139,0.166-0.318,0.224c-0.081,0.026-0.216,0.124-0.215,0.224c0.001,0.115,0.005,0.051,0.012,0.169 c-0.021,0.011-0.021-0.005-0.03-0.025" + fill="#FFFFFF" /> + <path + id="path164" + d="M15.816-25.736c0.018,0.072,0.007,0.127-0.026,0.19c-0.053,0.101-0.112,0.062-0.165,0.128 c-0.05,0.062-0.099,0.276-0.079,0.362c-0.169,0.058-0.009,0.227-0.015,0.35c-0.002,0.05-0.041,0.105-0.045,0.161 c-0.01,0.119,0.017,0.266,0.068,0.37c0.097,0.198,0.268,0.413,0.435,0.544c0.19,0.148,0.365,0.572,0.608,0.631 c0.177,0.042,0.384-0.104,0.543-0.143c0.18-0.043,0.397,0.01,0.571-0.053c0.222-0.079,0.127-0.337,0.288-0.45 c0.104-0.074,0.287-0.01,0.406-0.051c0.2-0.069,0.339-0.263,0.376-0.46c0.016-0.082,0.01-0.145,0.039-0.221 c0.039-0.103,0.111-0.16,0.09-0.293c-0.01-0.062-0.052-0.12-0.064-0.187c-0.022-0.114,0.002-0.224,0-0.337 c-0.003-0.2,0.017-0.379-0.078-0.55c-0.38-0.688-1.236-0.929-1.975-0.789c-0.18,0.034-0.287,0.126-0.442,0.207 c-0.17,0.088-0.139,0.166-0.318,0.224c-0.081,0.026-0.216,0.124-0.215,0.224c0.001,0.115,0.005,0.051,0.012,0.169 c-0.021,0.011-0.021-0.005-0.03-0.025" + fill="#FFFFFF" /> + <path + id="path166" + d="M8.156-25.736c0.018,0.072,0.007,0.127-0.026,0.19c-0.053,0.101-0.112,0.062-0.165,0.128 c-0.05,0.062-0.099,0.276-0.079,0.362c-0.169,0.058-0.009,0.227-0.015,0.35c-0.002,0.05-0.041,0.105-0.045,0.161 c-0.01,0.119,0.017,0.266,0.068,0.37c0.097,0.198,0.268,0.413,0.435,0.544c0.19,0.148,0.365,0.572,0.608,0.631 c0.177,0.042,0.384-0.104,0.543-0.143c0.18-0.043,0.397,0.01,0.571-0.053c0.222-0.079,0.127-0.337,0.288-0.45 c0.104-0.074,0.287-0.01,0.406-0.051c0.2-0.069,0.339-0.263,0.376-0.46c0.016-0.082,0.01-0.145,0.039-0.221 c0.039-0.103,0.111-0.16,0.09-0.293c-0.01-0.062-0.052-0.12-0.064-0.187c-0.022-0.114,0.002-0.224,0-0.337 c-0.003-0.2,0.017-0.379-0.078-0.55c-0.38-0.688-1.236-0.929-1.975-0.789c-0.18,0.034-0.287,0.126-0.442,0.207 c-0.17,0.088-0.139,0.166-0.318,0.224c-0.081,0.026-0.216,0.124-0.215,0.224c0.001,0.115,0.005,0.051,0.012,0.169 c-0.021,0.011-0.021-0.005-0.03-0.025" + fill="#FFFFFF" /> + <path + id="path168" + d="M0.495-25.736c0.018,0.072,0.007,0.127-0.026,0.19c-0.053,0.101-0.112,0.062-0.165,0.128 c-0.05,0.062-0.099,0.276-0.079,0.362c-0.169,0.058-0.01,0.227-0.015,0.35c-0.002,0.05-0.041,0.105-0.045,0.161 c-0.01,0.119,0.017,0.266,0.068,0.37c0.097,0.198,0.268,0.413,0.435,0.544c0.19,0.148,0.365,0.572,0.608,0.631 c0.177,0.042,0.384-0.104,0.543-0.143c0.18-0.043,0.397,0.01,0.571-0.053c0.222-0.079,0.127-0.337,0.288-0.45 c0.104-0.074,0.287-0.01,0.406-0.051c0.2-0.069,0.339-0.263,0.376-0.46c0.016-0.082,0.01-0.145,0.039-0.221 c0.039-0.103,0.111-0.16,0.09-0.293c-0.01-0.062-0.052-0.12-0.064-0.187c-0.022-0.114,0.002-0.224,0-0.337 c-0.003-0.2,0.017-0.379-0.078-0.55c-0.38-0.688-1.236-0.929-1.975-0.789c-0.18,0.034-0.287,0.126-0.442,0.207 c-0.17,0.088-0.139,0.166-0.318,0.224c-0.081,0.026-0.216,0.124-0.215,0.224c0.001,0.115,0.005,0.051,0.012,0.169 c-0.021,0.011-0.021-0.005-0.03-0.025" + fill="#FFFFFF" /> + </g> + <g + id="g170"> + <path + id="path172" + d="M69.439-18.084c0.018,0.072,0.007,0.127-0.026,0.19c-0.053,0.101-0.112,0.062-0.165,0.128 c-0.05,0.062-0.099,0.276-0.079,0.362c-0.169,0.058-0.01,0.227-0.015,0.35c-0.002,0.05-0.041,0.105-0.045,0.161 c-0.01,0.119,0.017,0.266,0.068,0.37c0.097,0.198,0.268,0.413,0.435,0.544c0.19,0.148,0.365,0.572,0.608,0.631 c0.177,0.042,0.384-0.104,0.543-0.143c0.18-0.043,0.397,0.01,0.571-0.053c0.222-0.079,0.127-0.337,0.288-0.45 c0.104-0.074,0.287-0.01,0.406-0.051c0.2-0.069,0.339-0.263,0.376-0.46c0.016-0.082,0.01-0.145,0.039-0.221 c0.039-0.103,0.111-0.16,0.09-0.293c-0.01-0.062-0.052-0.12-0.064-0.187c-0.022-0.114,0.002-0.224,0-0.337 c-0.003-0.2,0.017-0.379-0.078-0.55c-0.38-0.688-1.236-0.929-1.975-0.789c-0.18,0.034-0.287,0.126-0.442,0.207 c-0.17,0.088-0.139,0.166-0.318,0.224c-0.081,0.026-0.216,0.124-0.215,0.224c0.001,0.115,0.005,0.051,0.012,0.169 c-0.021,0.011-0.021-0.005-0.03-0.025" + fill="#FFFFFF" /> + <path + id="path174" + d="M61.778-18.084c0.018,0.072,0.007,0.127-0.026,0.19c-0.053,0.101-0.112,0.062-0.165,0.128 c-0.05,0.062-0.099,0.276-0.079,0.362c-0.169,0.058-0.009,0.227-0.015,0.35c-0.002,0.05-0.041,0.105-0.045,0.161 c-0.01,0.119,0.017,0.266,0.068,0.37c0.097,0.198,0.268,0.413,0.435,0.544c0.19,0.148,0.365,0.572,0.608,0.631 c0.177,0.042,0.384-0.104,0.543-0.143c0.18-0.043,0.397,0.01,0.571-0.053c0.222-0.079,0.127-0.337,0.288-0.45 c0.104-0.074,0.287-0.01,0.406-0.051c0.2-0.069,0.339-0.263,0.376-0.46c0.016-0.082,0.01-0.145,0.039-0.221 c0.039-0.103,0.111-0.16,0.09-0.293c-0.01-0.062-0.052-0.12-0.064-0.187c-0.022-0.114,0.002-0.224,0-0.337 c-0.003-0.2,0.017-0.379-0.078-0.55c-0.38-0.688-1.236-0.929-1.975-0.789c-0.18,0.034-0.287,0.126-0.442,0.207 c-0.17,0.088-0.139,0.166-0.318,0.224c-0.081,0.026-0.216,0.124-0.215,0.224c0.001,0.115,0.005,0.051,0.012,0.169 c-0.021,0.011-0.021-0.005-0.03-0.025" + fill="#FFFFFF" /> + <path + id="path176" + d="M54.118-18.084c0.018,0.072,0.007,0.127-0.026,0.19c-0.053,0.101-0.112,0.062-0.165,0.128 c-0.05,0.062-0.099,0.276-0.079,0.362c-0.169,0.058-0.009,0.227-0.015,0.35c-0.002,0.05-0.041,0.105-0.045,0.161 c-0.01,0.119,0.017,0.266,0.068,0.37c0.097,0.198,0.268,0.413,0.435,0.544c0.19,0.148,0.365,0.572,0.608,0.631 c0.177,0.042,0.384-0.104,0.543-0.143c0.18-0.043,0.397,0.01,0.571-0.053c0.222-0.079,0.127-0.337,0.288-0.45 c0.104-0.074,0.287-0.01,0.406-0.051c0.2-0.069,0.339-0.263,0.376-0.46c0.016-0.082,0.01-0.145,0.039-0.221 c0.039-0.103,0.111-0.16,0.09-0.293c-0.01-0.062-0.052-0.12-0.064-0.187c-0.022-0.114,0.002-0.224,0-0.337 c-0.003-0.2,0.017-0.379-0.078-0.55c-0.38-0.688-1.236-0.929-1.975-0.789c-0.18,0.034-0.287,0.126-0.442,0.207 c-0.17,0.088-0.139,0.166-0.318,0.224c-0.081,0.026-0.216,0.124-0.215,0.224c0.001,0.115,0.005,0.051,0.012,0.169 c-0.021,0.011-0.021-0.005-0.03-0.025" + fill="#FFFFFF" /> + <path + id="path178" + d="M46.458-18.084c0.018,0.072,0.007,0.127-0.026,0.19c-0.053,0.101-0.112,0.062-0.165,0.128 c-0.05,0.062-0.099,0.276-0.079,0.362c-0.169,0.058-0.009,0.227-0.015,0.35c-0.002,0.05-0.041,0.105-0.045,0.161 c-0.01,0.119,0.017,0.266,0.068,0.37c0.097,0.198,0.268,0.413,0.435,0.544c0.19,0.148,0.365,0.572,0.608,0.631 c0.177,0.042,0.384-0.104,0.543-0.143c0.18-0.043,0.397,0.01,0.571-0.053c0.222-0.079,0.127-0.337,0.288-0.45 c0.104-0.074,0.287-0.01,0.406-0.051c0.2-0.069,0.339-0.263,0.376-0.46c0.016-0.082,0.01-0.145,0.039-0.221 c0.039-0.103,0.111-0.16,0.09-0.293c-0.01-0.062-0.052-0.12-0.064-0.187c-0.022-0.114,0.002-0.224,0-0.337 c-0.003-0.2,0.017-0.379-0.078-0.55c-0.38-0.688-1.236-0.929-1.975-0.789c-0.18,0.034-0.287,0.126-0.442,0.207 c-0.17,0.088-0.139,0.166-0.318,0.224c-0.081,0.026-0.216,0.124-0.215,0.224c0.001,0.115,0.005,0.051,0.012,0.169 c-0.021,0.011-0.021-0.005-0.03-0.025" + fill="#FFFFFF" /> + <path + id="path180" + d="M38.797-18.084c0.018,0.072,0.007,0.127-0.026,0.19c-0.053,0.101-0.112,0.062-0.165,0.128 c-0.05,0.062-0.099,0.276-0.079,0.362c-0.169,0.058-0.009,0.227-0.015,0.35c-0.002,0.05-0.041,0.105-0.045,0.161 c-0.01,0.119,0.017,0.266,0.068,0.37c0.097,0.198,0.268,0.413,0.435,0.544c0.19,0.148,0.365,0.572,0.608,0.631 c0.177,0.042,0.384-0.104,0.543-0.143c0.18-0.043,0.397,0.01,0.571-0.053c0.222-0.079,0.127-0.337,0.288-0.45 c0.104-0.074,0.287-0.01,0.406-0.051c0.2-0.069,0.339-0.263,0.376-0.46c0.016-0.082,0.01-0.145,0.039-0.221 c0.039-0.103,0.111-0.16,0.09-0.293c-0.01-0.062-0.052-0.12-0.064-0.187c-0.022-0.114,0.002-0.224,0-0.337 c-0.003-0.2,0.017-0.379-0.078-0.55c-0.38-0.688-1.236-0.929-1.975-0.789c-0.18,0.034-0.287,0.126-0.442,0.207 c-0.17,0.088-0.139,0.166-0.318,0.224c-0.081,0.026-0.216,0.124-0.215,0.224c0.001,0.115,0.005,0.051,0.012,0.169 c-0.021,0.011-0.021-0.005-0.03-0.025" + fill="#FFFFFF" /> + <path + id="path182" + d="M31.137-18.084c0.018,0.072,0.007,0.127-0.026,0.19c-0.053,0.101-0.112,0.062-0.165,0.128 c-0.05,0.062-0.099,0.276-0.079,0.362c-0.169,0.058-0.009,0.227-0.015,0.35c-0.002,0.05-0.041,0.105-0.045,0.161 c-0.01,0.119,0.017,0.266,0.068,0.37c0.097,0.198,0.268,0.413,0.435,0.544c0.19,0.148,0.365,0.572,0.608,0.631 c0.177,0.042,0.384-0.104,0.543-0.143c0.18-0.043,0.397,0.01,0.571-0.053c0.222-0.079,0.127-0.337,0.288-0.45 c0.104-0.074,0.287-0.01,0.406-0.051c0.2-0.069,0.339-0.263,0.376-0.46c0.016-0.082,0.01-0.145,0.039-0.221 c0.039-0.103,0.111-0.16,0.09-0.293c-0.01-0.062-0.052-0.12-0.064-0.187c-0.022-0.114,0.002-0.224,0-0.337 c-0.003-0.2,0.017-0.379-0.078-0.55c-0.38-0.688-1.236-0.929-1.975-0.789c-0.18,0.034-0.287,0.126-0.442,0.207 c-0.17,0.088-0.139,0.166-0.318,0.224c-0.081,0.026-0.216,0.124-0.215,0.224c0.001,0.115,0.005,0.051,0.012,0.169 c-0.021,0.011-0.021-0.005-0.03-0.025" + fill="#FFFFFF" /> + <path + id="path184" + d="M23.477-18.084c0.018,0.072,0.007,0.127-0.026,0.19c-0.053,0.101-0.112,0.062-0.165,0.128 c-0.05,0.062-0.099,0.276-0.079,0.362c-0.169,0.058-0.009,0.227-0.015,0.35c-0.002,0.05-0.041,0.105-0.045,0.161 c-0.01,0.119,0.017,0.266,0.068,0.37c0.097,0.198,0.268,0.413,0.435,0.544c0.19,0.148,0.365,0.572,0.608,0.631 c0.177,0.042,0.384-0.104,0.543-0.143c0.18-0.043,0.397,0.01,0.571-0.053c0.222-0.079,0.127-0.337,0.288-0.45 c0.104-0.074,0.287-0.01,0.406-0.051c0.2-0.069,0.339-0.263,0.376-0.46c0.016-0.082,0.01-0.145,0.039-0.221 c0.039-0.103,0.111-0.16,0.09-0.293c-0.01-0.062-0.052-0.12-0.064-0.187c-0.022-0.114,0.002-0.224,0-0.337 c-0.003-0.2,0.017-0.379-0.078-0.55c-0.38-0.688-1.236-0.929-1.975-0.789c-0.18,0.034-0.287,0.126-0.442,0.207 c-0.17,0.088-0.139,0.166-0.318,0.224c-0.081,0.026-0.216,0.124-0.215,0.224c0.001,0.115,0.005,0.051,0.012,0.169 c-0.021,0.011-0.021-0.005-0.03-0.025" + fill="#FFFFFF" /> + <path + id="path186" + d="M15.816-18.084c0.018,0.072,0.007,0.127-0.026,0.19c-0.053,0.101-0.112,0.062-0.165,0.128 c-0.05,0.062-0.099,0.276-0.079,0.362c-0.169,0.058-0.009,0.227-0.015,0.35c-0.002,0.05-0.041,0.105-0.045,0.161 c-0.01,0.119,0.017,0.266,0.068,0.37c0.097,0.198,0.268,0.413,0.435,0.544c0.19,0.148,0.365,0.572,0.608,0.631 c0.177,0.042,0.384-0.104,0.543-0.143c0.18-0.043,0.397,0.01,0.571-0.053c0.222-0.079,0.127-0.337,0.288-0.45 c0.104-0.074,0.287-0.01,0.406-0.051c0.2-0.069,0.339-0.263,0.376-0.46c0.016-0.082,0.01-0.145,0.039-0.221 c0.039-0.103,0.111-0.16,0.09-0.293c-0.01-0.062-0.052-0.12-0.064-0.187c-0.022-0.114,0.002-0.224,0-0.337 c-0.003-0.2,0.017-0.379-0.078-0.55c-0.38-0.688-1.236-0.929-1.975-0.789c-0.18,0.034-0.287,0.126-0.442,0.207 c-0.17,0.088-0.139,0.166-0.318,0.224c-0.081,0.026-0.216,0.124-0.215,0.224c0.001,0.115,0.005,0.051,0.012,0.169 c-0.021,0.011-0.021-0.005-0.03-0.025" + fill="#FFFFFF" /> + <path + id="path188" + d="M8.156-18.084c0.018,0.072,0.007,0.127-0.026,0.19c-0.053,0.101-0.112,0.062-0.165,0.128 c-0.05,0.062-0.099,0.276-0.079,0.362c-0.169,0.058-0.009,0.227-0.015,0.35c-0.002,0.05-0.041,0.105-0.045,0.161 c-0.01,0.119,0.017,0.266,0.068,0.37c0.097,0.198,0.268,0.413,0.435,0.544c0.19,0.148,0.365,0.572,0.608,0.631 c0.177,0.042,0.384-0.104,0.543-0.143c0.18-0.043,0.397,0.01,0.571-0.053c0.222-0.079,0.127-0.337,0.288-0.45 c0.104-0.074,0.287-0.01,0.406-0.051c0.2-0.069,0.339-0.263,0.376-0.46c0.016-0.082,0.01-0.145,0.039-0.221 c0.039-0.103,0.111-0.16,0.09-0.293c-0.01-0.062-0.052-0.12-0.064-0.187c-0.022-0.114,0.002-0.224,0-0.337 c-0.003-0.2,0.017-0.379-0.078-0.55c-0.38-0.688-1.236-0.929-1.975-0.789c-0.18,0.034-0.287,0.126-0.442,0.207 c-0.17,0.088-0.139,0.166-0.318,0.224c-0.081,0.026-0.216,0.124-0.215,0.224c0.001,0.115,0.005,0.051,0.012,0.169 c-0.021,0.011-0.021-0.005-0.03-0.025" + fill="#FFFFFF" /> + <path + id="path190" + d="M0.495-18.084c0.018,0.072,0.007,0.127-0.026,0.19c-0.053,0.101-0.112,0.062-0.165,0.128 c-0.05,0.062-0.099,0.276-0.079,0.362c-0.169,0.058-0.01,0.227-0.015,0.35c-0.002,0.05-0.041,0.105-0.045,0.161 c-0.01,0.119,0.017,0.266,0.068,0.37c0.097,0.198,0.268,0.413,0.435,0.544c0.19,0.148,0.365,0.572,0.608,0.631 c0.177,0.042,0.384-0.104,0.543-0.143c0.18-0.043,0.397,0.01,0.571-0.053c0.222-0.079,0.127-0.337,0.288-0.45 c0.104-0.074,0.287-0.01,0.406-0.051c0.2-0.069,0.339-0.263,0.376-0.46c0.016-0.082,0.01-0.145,0.039-0.221 c0.039-0.103,0.111-0.16,0.09-0.293c-0.01-0.062-0.052-0.12-0.064-0.187c-0.022-0.114,0.002-0.224,0-0.337 c-0.003-0.2,0.017-0.379-0.078-0.55c-0.38-0.688-1.236-0.929-1.975-0.789c-0.18,0.034-0.287,0.126-0.442,0.207 c-0.17,0.088-0.139,0.166-0.318,0.224c-0.081,0.026-0.216,0.124-0.215,0.224C0.5-18.11,0.503-18.175,0.51-18.057 c-0.021,0.011-0.021-0.005-0.03-0.025" + fill="#FFFFFF" /> + </g> + <g + id="g192"> + <path + id="path194" + d="M69.439-10.431c0.018,0.072,0.007,0.127-0.026,0.19c-0.053,0.101-0.112,0.062-0.165,0.128 c-0.05,0.062-0.099,0.276-0.079,0.362C69-9.692,69.159-9.523,69.154-9.4c-0.002,0.05-0.041,0.105-0.045,0.161 c-0.01,0.119,0.017,0.266,0.068,0.37c0.097,0.198,0.268,0.413,0.435,0.544c0.19,0.148,0.365,0.572,0.608,0.631 c0.177,0.042,0.384-0.104,0.543-0.143c0.18-0.043,0.397,0.01,0.571-0.053c0.222-0.079,0.127-0.337,0.288-0.45 c0.104-0.074,0.287-0.01,0.406-0.051c0.2-0.069,0.339-0.263,0.376-0.46c0.016-0.082,0.01-0.145,0.039-0.221 c0.039-0.103,0.111-0.16,0.09-0.293c-0.01-0.062-0.052-0.12-0.064-0.187c-0.022-0.114,0.002-0.224,0-0.337 c-0.003-0.2,0.017-0.379-0.078-0.55c-0.38-0.688-1.236-0.929-1.975-0.789c-0.18,0.034-0.287,0.126-0.442,0.207 c-0.17,0.088-0.139,0.166-0.318,0.224c-0.081,0.026-0.216,0.124-0.215,0.224c0.001,0.115,0.005,0.051,0.012,0.169 c-0.021,0.011-0.021-0.005-0.03-0.025" + fill="#FFFFFF" /> + <path + id="path196" + d="M61.778-10.431c0.018,0.072,0.007,0.127-0.026,0.19c-0.053,0.101-0.112,0.062-0.165,0.128 c-0.05,0.062-0.099,0.276-0.079,0.362c-0.169,0.058-0.009,0.227-0.015,0.35c-0.002,0.05-0.041,0.105-0.045,0.161 c-0.01,0.119,0.017,0.266,0.068,0.37c0.097,0.198,0.268,0.413,0.435,0.544c0.19,0.148,0.365,0.572,0.608,0.631 c0.177,0.042,0.384-0.104,0.543-0.143c0.18-0.043,0.397,0.01,0.571-0.053c0.222-0.079,0.127-0.337,0.288-0.45 c0.104-0.074,0.287-0.01,0.406-0.051c0.2-0.069,0.339-0.263,0.376-0.46c0.016-0.082,0.01-0.145,0.039-0.221 c0.039-0.103,0.111-0.16,0.09-0.293c-0.01-0.062-0.052-0.12-0.064-0.187c-0.022-0.114,0.002-0.224,0-0.337 c-0.003-0.2,0.017-0.379-0.078-0.55c-0.38-0.688-1.236-0.929-1.975-0.789c-0.18,0.034-0.287,0.126-0.442,0.207 c-0.17,0.088-0.139,0.166-0.318,0.224c-0.081,0.026-0.216,0.124-0.215,0.224c0.001,0.115,0.005,0.051,0.012,0.169 c-0.021,0.011-0.021-0.005-0.03-0.025" + fill="#FFFFFF" /> + <path + id="path198" + d="M54.118-10.431c0.018,0.072,0.007,0.127-0.026,0.19c-0.053,0.101-0.112,0.062-0.165,0.128 c-0.05,0.062-0.099,0.276-0.079,0.362c-0.169,0.058-0.009,0.227-0.015,0.35c-0.002,0.05-0.041,0.105-0.045,0.161 c-0.01,0.119,0.017,0.266,0.068,0.37c0.097,0.198,0.268,0.413,0.435,0.544c0.19,0.148,0.365,0.572,0.608,0.631 c0.177,0.042,0.384-0.104,0.543-0.143c0.18-0.043,0.397,0.01,0.571-0.053c0.222-0.079,0.127-0.337,0.288-0.45 c0.104-0.074,0.287-0.01,0.406-0.051c0.2-0.069,0.339-0.263,0.376-0.46c0.016-0.082,0.01-0.145,0.039-0.221 c0.039-0.103,0.111-0.16,0.09-0.293c-0.01-0.062-0.052-0.12-0.064-0.187c-0.022-0.114,0.002-0.224,0-0.337 c-0.003-0.2,0.017-0.379-0.078-0.55c-0.38-0.688-1.236-0.929-1.975-0.789c-0.18,0.034-0.287,0.126-0.442,0.207 c-0.17,0.088-0.139,0.166-0.318,0.224c-0.081,0.026-0.216,0.124-0.215,0.224c0.001,0.115,0.005,0.051,0.012,0.169 c-0.021,0.011-0.021-0.005-0.03-0.025" + fill="#FFFFFF" /> + <path + id="path200" + d="M46.458-10.431c0.018,0.072,0.007,0.127-0.026,0.19c-0.053,0.101-0.112,0.062-0.165,0.128 c-0.05,0.062-0.099,0.276-0.079,0.362c-0.169,0.058-0.009,0.227-0.015,0.35c-0.002,0.05-0.041,0.105-0.045,0.161 c-0.01,0.119,0.017,0.266,0.068,0.37c0.097,0.198,0.268,0.413,0.435,0.544c0.19,0.148,0.365,0.572,0.608,0.631 c0.177,0.042,0.384-0.104,0.543-0.143c0.18-0.043,0.397,0.01,0.571-0.053c0.222-0.079,0.127-0.337,0.288-0.45 c0.104-0.074,0.287-0.01,0.406-0.051c0.2-0.069,0.339-0.263,0.376-0.46c0.016-0.082,0.01-0.145,0.039-0.221 c0.039-0.103,0.111-0.16,0.09-0.293c-0.01-0.062-0.052-0.12-0.064-0.187c-0.022-0.114,0.002-0.224,0-0.337 c-0.003-0.2,0.017-0.379-0.078-0.55c-0.38-0.688-1.236-0.929-1.975-0.789c-0.18,0.034-0.287,0.126-0.442,0.207 c-0.17,0.088-0.139,0.166-0.318,0.224c-0.081,0.026-0.216,0.124-0.215,0.224c0.001,0.115,0.005,0.051,0.012,0.169 c-0.021,0.011-0.021-0.005-0.03-0.025" + fill="#FFFFFF" /> + <path + id="path202" + d="M38.797-10.431c0.018,0.072,0.007,0.127-0.026,0.19c-0.053,0.101-0.112,0.062-0.165,0.128 c-0.05,0.062-0.099,0.276-0.079,0.362c-0.169,0.058-0.009,0.227-0.015,0.35c-0.002,0.05-0.041,0.105-0.045,0.161 c-0.01,0.119,0.017,0.266,0.068,0.37c0.097,0.198,0.268,0.413,0.435,0.544c0.19,0.148,0.365,0.572,0.608,0.631 c0.177,0.042,0.384-0.104,0.543-0.143c0.18-0.043,0.397,0.01,0.571-0.053c0.222-0.079,0.127-0.337,0.288-0.45 c0.104-0.074,0.287-0.01,0.406-0.051c0.2-0.069,0.339-0.263,0.376-0.46c0.016-0.082,0.01-0.145,0.039-0.221 c0.039-0.103,0.111-0.16,0.09-0.293c-0.01-0.062-0.052-0.12-0.064-0.187c-0.022-0.114,0.002-0.224,0-0.337 c-0.003-0.2,0.017-0.379-0.078-0.55c-0.38-0.688-1.236-0.929-1.975-0.789c-0.18,0.034-0.287,0.126-0.442,0.207 c-0.17,0.088-0.139,0.166-0.318,0.224c-0.081,0.026-0.216,0.124-0.215,0.224c0.001,0.115,0.005,0.051,0.012,0.169 c-0.021,0.011-0.021-0.005-0.03-0.025" + fill="#FFFFFF" /> + <path + id="path204" + d="M31.137-10.431c0.018,0.072,0.007,0.127-0.026,0.19c-0.053,0.101-0.112,0.062-0.165,0.128 c-0.05,0.062-0.099,0.276-0.079,0.362c-0.169,0.058-0.009,0.227-0.015,0.35c-0.002,0.05-0.041,0.105-0.045,0.161 c-0.01,0.119,0.017,0.266,0.068,0.37c0.097,0.198,0.268,0.413,0.435,0.544c0.19,0.148,0.365,0.572,0.608,0.631 c0.177,0.042,0.384-0.104,0.543-0.143c0.18-0.043,0.397,0.01,0.571-0.053c0.222-0.079,0.127-0.337,0.288-0.45 c0.104-0.074,0.287-0.01,0.406-0.051c0.2-0.069,0.339-0.263,0.376-0.46c0.016-0.082,0.01-0.145,0.039-0.221 c0.039-0.103,0.111-0.16,0.09-0.293c-0.01-0.062-0.052-0.12-0.064-0.187c-0.022-0.114,0.002-0.224,0-0.337 c-0.003-0.2,0.017-0.379-0.078-0.55c-0.38-0.688-1.236-0.929-1.975-0.789c-0.18,0.034-0.287,0.126-0.442,0.207 c-0.17,0.088-0.139,0.166-0.318,0.224c-0.081,0.026-0.216,0.124-0.215,0.224c0.001,0.115,0.005,0.051,0.012,0.169 c-0.021,0.011-0.021-0.005-0.03-0.025" + fill="#FFFFFF" /> + <path + id="path206" + d="M23.477-10.431c0.018,0.072,0.007,0.127-0.026,0.19c-0.053,0.101-0.112,0.062-0.165,0.128 c-0.05,0.062-0.099,0.276-0.079,0.362c-0.169,0.058-0.009,0.227-0.015,0.35c-0.002,0.05-0.041,0.105-0.045,0.161 c-0.01,0.119,0.017,0.266,0.068,0.37c0.097,0.198,0.268,0.413,0.435,0.544c0.19,0.148,0.365,0.572,0.608,0.631 c0.177,0.042,0.384-0.104,0.543-0.143c0.18-0.043,0.397,0.01,0.571-0.053c0.222-0.079,0.127-0.337,0.288-0.45 c0.104-0.074,0.287-0.01,0.406-0.051c0.2-0.069,0.339-0.263,0.376-0.46c0.016-0.082,0.01-0.145,0.039-0.221 c0.039-0.103,0.111-0.16,0.09-0.293c-0.01-0.062-0.052-0.12-0.064-0.187c-0.022-0.114,0.002-0.224,0-0.337 c-0.003-0.2,0.017-0.379-0.078-0.55c-0.38-0.688-1.236-0.929-1.975-0.789c-0.18,0.034-0.287,0.126-0.442,0.207 c-0.17,0.088-0.139,0.166-0.318,0.224c-0.081,0.026-0.216,0.124-0.215,0.224c0.001,0.115,0.005,0.051,0.012,0.169 c-0.021,0.011-0.021-0.005-0.03-0.025" + fill="#FFFFFF" /> + <path + id="path208" + d="M15.816-10.431c0.018,0.072,0.007,0.127-0.026,0.19c-0.053,0.101-0.112,0.062-0.165,0.128 c-0.05,0.062-0.099,0.276-0.079,0.362c-0.169,0.058-0.009,0.227-0.015,0.35c-0.002,0.05-0.041,0.105-0.045,0.161 c-0.01,0.119,0.017,0.266,0.068,0.37c0.097,0.198,0.268,0.413,0.435,0.544c0.19,0.148,0.365,0.572,0.608,0.631 c0.177,0.042,0.384-0.104,0.543-0.143c0.18-0.043,0.397,0.01,0.571-0.053C17.933-7.969,17.839-8.227,18-8.34 c0.104-0.074,0.287-0.01,0.406-0.051c0.2-0.069,0.339-0.263,0.376-0.46c0.016-0.082,0.01-0.145,0.039-0.221 c0.039-0.103,0.111-0.16,0.09-0.293c-0.01-0.062-0.052-0.12-0.064-0.187c-0.022-0.114,0.002-0.224,0-0.337 c-0.003-0.2,0.017-0.379-0.078-0.55c-0.38-0.688-1.236-0.929-1.975-0.789c-0.18,0.034-0.287,0.126-0.442,0.207 c-0.17,0.088-0.139,0.166-0.318,0.224c-0.081,0.026-0.216,0.124-0.215,0.224c0.001,0.115,0.005,0.051,0.012,0.169 c-0.021,0.011-0.021-0.005-0.03-0.025" + fill="#FFFFFF" /> + <path + id="path210" + d="M8.156-10.431c0.018,0.072,0.007,0.127-0.026,0.19c-0.053,0.101-0.112,0.062-0.165,0.128 C7.915-10.05,7.866-9.836,7.886-9.75C7.717-9.692,7.876-9.523,7.871-9.4C7.868-9.351,7.83-9.295,7.826-9.239 c-0.01,0.119,0.017,0.266,0.068,0.37c0.097,0.198,0.268,0.413,0.435,0.544c0.19,0.148,0.365,0.572,0.608,0.631 C9.114-7.652,9.321-7.799,9.48-7.837c0.18-0.043,0.397,0.01,0.571-0.053c0.222-0.079,0.127-0.337,0.288-0.45 c0.104-0.074,0.287-0.01,0.406-0.051c0.2-0.069,0.339-0.263,0.376-0.46c0.016-0.082,0.01-0.145,0.039-0.221 c0.039-0.103,0.111-0.16,0.09-0.293c-0.01-0.062-0.052-0.12-0.064-0.187c-0.022-0.114,0.002-0.224,0-0.337 c-0.003-0.2,0.017-0.379-0.078-0.55c-0.38-0.688-1.236-0.929-1.975-0.789c-0.18,0.034-0.287,0.126-0.442,0.207 c-0.17,0.088-0.139,0.166-0.318,0.224c-0.081,0.026-0.216,0.124-0.215,0.224c0.001,0.115,0.005,0.051,0.012,0.169 c-0.021,0.011-0.021-0.005-0.03-0.025" + fill="#FFFFFF" /> + <path + id="path212" + d="M0.495-10.431c0.018,0.072,0.007,0.127-0.026,0.19c-0.053,0.101-0.112,0.062-0.165,0.128 C0.254-10.05,0.205-9.836,0.225-9.75C0.056-9.692,0.215-9.523,0.21-9.4c-0.002,0.05-0.041,0.105-0.045,0.161 c-0.01,0.119,0.017,0.266,0.068,0.37C0.33-8.671,0.501-8.456,0.668-8.325c0.19,0.148,0.365,0.572,0.608,0.631 C1.454-7.652,1.66-7.799,1.819-7.837C2-7.88,2.217-7.827,2.391-7.89c0.222-0.079,0.127-0.337,0.288-0.45 c0.104-0.074,0.287-0.01,0.406-0.051c0.2-0.069,0.339-0.263,0.376-0.46C3.477-8.933,3.471-8.995,3.5-9.071 c0.039-0.103,0.111-0.16,0.09-0.293c-0.01-0.062-0.052-0.12-0.064-0.187c-0.022-0.114,0.002-0.224,0-0.337 c-0.003-0.2,0.017-0.379-0.078-0.55c-0.38-0.688-1.236-0.929-1.975-0.789c-0.18,0.034-0.287,0.126-0.442,0.207 c-0.17,0.088-0.139,0.166-0.318,0.224c-0.081,0.026-0.216,0.124-0.215,0.224c0.001,0.115,0.005,0.051,0.012,0.169 c-0.021,0.011-0.021-0.005-0.03-0.025" + fill="#FFFFFF" /> + </g> + </g> + <g + id="g214"> + <path + id="path216" + d="M69.439-2.778c0.018,0.072,0.008,0.127-0.026,0.19C69.361-2.487,69.3-2.525,69.248-2.46 c-0.051,0.062-0.099,0.276-0.079,0.362C69-2.04,69.159-1.871,69.154-1.748c-0.002,0.05-0.041,0.105-0.045,0.161 c-0.01,0.119,0.017,0.266,0.068,0.37c0.097,0.198,0.268,0.413,0.435,0.544c0.19,0.148,0.365,0.572,0.608,0.631 C70.397,0,70.604-0.146,70.763-0.185c0.18-0.043,0.397,0.01,0.571-0.053c0.222-0.079,0.127-0.337,0.288-0.45 c0.104-0.074,0.287-0.01,0.406-0.051c0.2-0.07,0.339-0.263,0.376-0.46c0.016-0.082,0.01-0.145,0.039-0.221 c0.038-0.103,0.111-0.16,0.09-0.293c-0.01-0.062-0.051-0.12-0.064-0.187c-0.021-0.114,0.002-0.224,0-0.337 c-0.003-0.2,0.017-0.379-0.078-0.55c-0.38-0.688-1.236-0.929-1.975-0.789c-0.18,0.034-0.287,0.126-0.442,0.207 c-0.17,0.088-0.139,0.166-0.318,0.224c-0.081,0.026-0.215,0.124-0.215,0.224c0.002,0.115,0.005,0.051,0.012,0.169 c-0.021,0.011-0.021-0.005-0.03-0.025" + fill="#FFFFFF" /> + <path + id="path218" + d="M61.778-2.778c0.018,0.072,0.007,0.127-0.026,0.19C61.7-2.487,61.64-2.525,61.587-2.46 c-0.05,0.062-0.099,0.276-0.079,0.362c-0.169,0.058-0.01,0.227-0.015,0.35c-0.002,0.05-0.041,0.105-0.045,0.161 c-0.01,0.119,0.017,0.266,0.068,0.37c0.097,0.198,0.268,0.413,0.435,0.544c0.19,0.148,0.365,0.572,0.608,0.631 C62.737,0,62.943-0.146,63.103-0.185c0.18-0.043,0.397,0.01,0.571-0.053c0.222-0.079,0.127-0.337,0.288-0.45 c0.104-0.074,0.287-0.01,0.406-0.051c0.2-0.069,0.339-0.263,0.376-0.46c0.016-0.082,0.01-0.145,0.039-0.221 c0.039-0.103,0.111-0.16,0.09-0.293c-0.01-0.062-0.052-0.12-0.064-0.187c-0.022-0.114,0.002-0.224,0-0.337 c-0.003-0.2,0.017-0.379-0.078-0.55c-0.38-0.688-1.236-0.929-1.975-0.789c-0.18,0.034-0.287,0.126-0.442,0.207 c-0.17,0.088-0.139,0.166-0.318,0.224C61.915-3.117,61.78-3.02,61.781-2.92c0.001,0.115,0.005,0.051,0.012,0.169 c-0.021,0.011-0.021-0.005-0.03-0.025" + fill="#FFFFFF" /> + <path + id="path220" + d="M54.118-2.778c0.018,0.072,0.007,0.127-0.026,0.19C54.04-2.487,53.98-2.525,53.927-2.46 c-0.05,0.062-0.099,0.276-0.079,0.362c-0.169,0.058-0.01,0.227-0.015,0.35c-0.002,0.05-0.041,0.105-0.045,0.161 c-0.01,0.119,0.017,0.266,0.068,0.37c0.097,0.198,0.268,0.413,0.435,0.544c0.19,0.148,0.365,0.572,0.608,0.631 C55.077,0,55.283-0.146,55.442-0.185c0.18-0.043,0.397,0.01,0.571-0.053c0.222-0.079,0.127-0.337,0.288-0.45 c0.104-0.074,0.287-0.01,0.406-0.051c0.2-0.069,0.339-0.263,0.376-0.46c0.016-0.082,0.01-0.145,0.039-0.221 c0.039-0.103,0.111-0.16,0.09-0.293c-0.01-0.062-0.052-0.12-0.064-0.187c-0.022-0.114,0.002-0.224,0-0.337 c-0.003-0.2,0.017-0.379-0.078-0.55c-0.38-0.688-1.236-0.929-1.975-0.789c-0.18,0.034-0.287,0.126-0.442,0.207 c-0.17,0.088-0.139,0.166-0.318,0.224C54.255-3.117,54.12-3.02,54.121-2.92c0.001,0.115,0.005,0.051,0.012,0.169 c-0.021,0.011-0.021-0.005-0.03-0.025" + fill="#FFFFFF" /> + <path + id="path222" + d="M46.458-2.778c0.018,0.072,0.007,0.127-0.026,0.19c-0.053,0.101-0.112,0.062-0.165,0.128 c-0.05,0.062-0.099,0.276-0.079,0.362c-0.169,0.058-0.01,0.227-0.015,0.35c-0.002,0.05-0.041,0.105-0.045,0.161 c-0.01,0.119,0.017,0.266,0.068,0.37c0.097,0.198,0.268,0.413,0.435,0.544c0.19,0.148,0.365,0.572,0.608,0.631 C47.416,0,47.623-0.146,47.782-0.185c0.18-0.043,0.397,0.01,0.571-0.053c0.222-0.079,0.127-0.337,0.288-0.45 c0.104-0.074,0.287-0.01,0.406-0.051c0.2-0.069,0.339-0.263,0.376-0.46c0.016-0.082,0.01-0.145,0.039-0.221 c0.039-0.103,0.111-0.16,0.09-0.293c-0.01-0.062-0.052-0.12-0.064-0.187c-0.022-0.114,0.002-0.224,0-0.337 c-0.003-0.2,0.017-0.379-0.078-0.55c-0.38-0.688-1.236-0.929-1.975-0.789c-0.18,0.034-0.287,0.126-0.442,0.207 c-0.17,0.088-0.139,0.166-0.318,0.224C46.594-3.117,46.459-3.02,46.46-2.92c0.001,0.115,0.005,0.051,0.012,0.169 c-0.021,0.011-0.021-0.005-0.03-0.025" + fill="#FFFFFF" /> + <path + id="path224" + d="M38.797-2.778c0.018,0.072,0.007,0.127-0.026,0.19c-0.053,0.101-0.112,0.062-0.165,0.128 c-0.05,0.062-0.099,0.276-0.079,0.362c-0.169,0.058-0.01,0.227-0.015,0.35c-0.002,0.05-0.041,0.105-0.045,0.161 c-0.01,0.119,0.017,0.266,0.068,0.37c0.097,0.198,0.268,0.413,0.435,0.544c0.19,0.148,0.365,0.572,0.608,0.631 C39.756,0,39.962-0.146,40.122-0.185c0.18-0.043,0.397,0.01,0.571-0.053c0.222-0.079,0.127-0.337,0.288-0.45 c0.104-0.074,0.287-0.01,0.406-0.051c0.2-0.069,0.339-0.263,0.376-0.46c0.016-0.082,0.01-0.145,0.039-0.221 c0.039-0.103,0.111-0.16,0.09-0.293c-0.01-0.062-0.052-0.12-0.064-0.187c-0.022-0.114,0.002-0.224,0-0.337 c-0.003-0.2,0.017-0.379-0.078-0.55c-0.38-0.688-1.236-0.929-1.975-0.789c-0.18,0.034-0.287,0.126-0.442,0.207 c-0.17,0.088-0.139,0.166-0.318,0.224C38.934-3.117,38.799-3.02,38.8-2.92c0.001,0.115,0.005,0.051,0.012,0.169 c-0.021,0.011-0.021-0.005-0.03-0.025" + fill="#FFFFFF" /> + <path + id="path226" + d="M31.137-2.778c0.018,0.072,0.007,0.127-0.026,0.19c-0.053,0.101-0.112,0.062-0.165,0.128 c-0.05,0.062-0.099,0.276-0.079,0.362c-0.169,0.058-0.01,0.227-0.015,0.35c-0.002,0.05-0.041,0.105-0.045,0.161 c-0.01,0.119,0.017,0.266,0.068,0.37c0.097,0.198,0.268,0.413,0.435,0.544c0.19,0.148,0.365,0.572,0.608,0.631 C32.095,0,32.302-0.146,32.461-0.185c0.18-0.043,0.397,0.01,0.571-0.053c0.222-0.079,0.127-0.337,0.288-0.45 c0.104-0.074,0.287-0.01,0.406-0.051c0.2-0.069,0.339-0.263,0.376-0.46c0.016-0.082,0.01-0.145,0.039-0.221 c0.039-0.103,0.111-0.16,0.09-0.293c-0.01-0.062-0.052-0.12-0.064-0.187c-0.022-0.114,0.002-0.224,0-0.337 c-0.003-0.2,0.017-0.379-0.078-0.55c-0.38-0.688-1.236-0.929-1.975-0.789c-0.18,0.034-0.287,0.126-0.442,0.207 c-0.17,0.088-0.139,0.166-0.318,0.224C31.273-3.117,31.139-3.02,31.14-2.92c0.001,0.115,0.005,0.051,0.012,0.169 c-0.021,0.011-0.021-0.005-0.03-0.025" + fill="#FFFFFF" /> + <path + id="path228" + d="M23.477-2.778c0.018,0.072,0.007,0.127-0.026,0.19c-0.053,0.101-0.112,0.062-0.165,0.128 c-0.05,0.062-0.099,0.276-0.079,0.362c-0.169,0.058-0.01,0.227-0.015,0.35c-0.002,0.05-0.041,0.105-0.045,0.161 c-0.01,0.119,0.017,0.266,0.068,0.37c0.097,0.198,0.268,0.413,0.435,0.544c0.19,0.148,0.365,0.572,0.608,0.631 C24.435,0,24.642-0.146,24.801-0.185c0.18-0.043,0.397,0.01,0.571-0.053c0.222-0.079,0.127-0.337,0.288-0.45 c0.104-0.074,0.287-0.01,0.406-0.051c0.2-0.069,0.339-0.263,0.376-0.46c0.016-0.082,0.01-0.145,0.039-0.221 c0.039-0.103,0.111-0.16,0.09-0.293c-0.01-0.062-0.052-0.12-0.064-0.187c-0.022-0.114,0.002-0.224,0-0.337 c-0.003-0.2,0.017-0.379-0.078-0.55c-0.38-0.688-1.236-0.929-1.975-0.789c-0.18,0.034-0.287,0.126-0.442,0.207 c-0.17,0.088-0.139,0.166-0.318,0.224c-0.081,0.026-0.216,0.124-0.215,0.224c0.001,0.115,0.005,0.051,0.012,0.169 c-0.021,0.011-0.021-0.005-0.03-0.025" + fill="#FFFFFF" /> + <path + id="path230" + d="M15.816-2.778c0.018,0.072,0.007,0.127-0.026,0.19c-0.053,0.101-0.112,0.062-0.165,0.128 c-0.05,0.062-0.099,0.276-0.079,0.362c-0.169,0.058-0.01,0.227-0.015,0.35c-0.002,0.05-0.041,0.105-0.045,0.161 c-0.01,0.119,0.017,0.266,0.068,0.37c0.097,0.198,0.268,0.413,0.435,0.544c0.19,0.148,0.365,0.572,0.608,0.631 C16.774,0,16.981-0.146,17.14-0.185c0.18-0.043,0.397,0.01,0.571-0.053c0.222-0.079,0.127-0.337,0.288-0.45 c0.104-0.074,0.287-0.01,0.406-0.051c0.2-0.069,0.339-0.263,0.376-0.46c0.016-0.082,0.01-0.145,0.039-0.221 c0.039-0.103,0.111-0.16,0.09-0.293c-0.01-0.062-0.052-0.12-0.064-0.187c-0.022-0.114,0.002-0.224,0-0.337 c-0.003-0.2,0.017-0.379-0.078-0.55c-0.38-0.688-1.236-0.929-1.975-0.789c-0.18,0.034-0.287,0.126-0.442,0.207 c-0.17,0.088-0.139,0.166-0.318,0.224c-0.081,0.026-0.216,0.124-0.215,0.224c0.001,0.115,0.005,0.051,0.012,0.169 C15.81-2.74,15.809-2.756,15.8-2.776" + fill="#FFFFFF" /> + <path + id="path232" + d="M8.156-2.778c0.018,0.072,0.007,0.127-0.026,0.19C8.077-2.487,8.018-2.525,7.965-2.46 c-0.05,0.062-0.099,0.276-0.079,0.362c-0.169,0.058-0.01,0.227-0.015,0.35C7.868-1.698,7.83-1.643,7.826-1.587 c-0.01,0.119,0.017,0.266,0.068,0.37c0.097,0.198,0.268,0.413,0.435,0.544c0.19,0.148,0.365,0.572,0.608,0.631 C9.114,0,9.321-0.146,9.48-0.185c0.18-0.043,0.397,0.01,0.571-0.053c0.222-0.079,0.127-0.337,0.288-0.45 c0.104-0.074,0.287-0.01,0.406-0.051c0.2-0.069,0.339-0.263,0.376-0.46c0.016-0.082,0.01-0.145,0.039-0.221 c0.039-0.103,0.111-0.16,0.09-0.293c-0.01-0.062-0.052-0.12-0.064-0.187c-0.022-0.114,0.002-0.224,0-0.337 c-0.003-0.2,0.017-0.379-0.078-0.55c-0.38-0.688-1.236-0.929-1.975-0.789C8.954-3.54,8.847-3.448,8.692-3.367 c-0.17,0.088-0.139,0.166-0.318,0.224C8.292-3.117,8.158-3.02,8.159-2.92C8.16-2.805,8.164-2.869,8.17-2.751 C8.15-2.74,8.149-2.756,8.14-2.776" + fill="#FFFFFF" /> + <path + id="path234" + d="M0.495-2.778c0.018,0.072,0.008,0.127-0.026,0.19C0.417-2.487,0.356-2.525,0.304-2.46 C0.253-2.397,0.205-2.184,0.225-2.098C0.056-2.04,0.215-1.871,0.21-1.748c-0.002,0.05-0.041,0.105-0.045,0.161 c-0.01,0.119,0.017,0.266,0.068,0.37C0.33-1.019,0.501-0.804,0.668-0.673c0.19,0.148,0.365,0.572,0.608,0.631 C1.454,0,1.66-0.146,1.819-0.185C2-0.228,2.217-0.175,2.391-0.237c0.222-0.079,0.127-0.337,0.288-0.45 c0.104-0.074,0.287-0.01,0.406-0.051c0.2-0.07,0.339-0.263,0.376-0.46C3.477-1.28,3.471-1.343,3.5-1.419 c0.038-0.103,0.111-0.16,0.09-0.293c-0.01-0.062-0.051-0.12-0.064-0.187c-0.021-0.114,0.002-0.224,0-0.337 c-0.003-0.2,0.017-0.379-0.078-0.55c-0.38-0.688-1.236-0.929-1.975-0.789C1.293-3.54,1.187-3.448,1.031-3.367 c-0.17,0.088-0.139,0.166-0.318,0.224C0.632-3.117,0.498-3.02,0.498-2.92C0.5-2.805,0.503-2.869,0.51-2.751 C0.489-2.74,0.488-2.756,0.479-2.776" + fill="#FFFFFF" /> + </g> + </g> +</pattern> + + + + + + + + + + + + + + + + + + + + + + +<rect + style="fill:#ffffff;fill-opacity:1;stroke:#000000;stroke-width:1.97552931;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" + id="rect6847" + width="124.77364" + height="150.12347" + x="0.31690097" + y="0.98776293" /><path + d="M 9.8480335,124.60683 C 11.62496,123.82337 13.513211,123.43203 15.327243,123.43203 C 17.067063,123.43203 18.177759,123.85879 18.806894,124.74937 C 20.139374,123.8596 21.953416,123.43203 23.360116,123.43203 C 27.581053,123.43203 27.728648,125.14068 27.728648,130.16028 L 27.728648,141.26547 C 27.728648,141.76382 27.802857,141.76382 25.692811,141.76382 L 25.692811,129.94606 C 25.692811,126.31544 25.618592,125.21154 23.213365,125.21154 C 22.139794,125.21154 21.029108,125.4603 19.844204,126.24379 L 19.844204,141.5142 C 19.807099,141.65677 19.732887,141.69215 19.474821,141.72758 C 19.436863,141.72758 19.400602,141.763 19.362653,141.763 L 17.807522,141.763 L 17.807522,129.94606 C 17.807522,126.45791 17.807522,125.17607 15.29098,125.17607 C 14.2174,125.17607 13.143818,125.38944 11.884705,125.99501 L 11.884705,141.26547 C 11.884705,141.76382 11.958925,141.76382 9.8488776,141.76382 L 9.8488776,124.60683 M 37.680118,123.43203 C 34.533596,123.43203 31.053954,124.32176 31.053954,133.8263 C 31.053954,141.15915 33.607611,142.29771 37.125192,142.29771 C 39.826435,142.29771 42.159131,141.40799 42.159131,140.98039 C 42.159131,140.44659 42.084921,139.62768 41.900221,139.16468 C 40.75243,139.94814 39.123081,140.37486 37.309893,140.37486 C 34.829612,140.37486 33.164001,139.66309 33.126052,134.14592 C 34.755402,134.14592 38.902128,134.11044 41.97444,133.50498 C 42.233351,132.33022 42.343821,130.62155 42.343821,129.12711 C 42.343821,125.56743 40.900016,123.43203 37.680118,123.43203 M 37.494584,125.21154 C 39.715955,125.21154 40.307995,126.67048 40.3451,129.51849 C 40.3451,130.26565 40.307995,131.15541 40.19667,132.00972 C 38.123729,132.50815 34.60612,132.50815 33.125209,132.50815 C 33.385806,126.0304 35.606333,125.21154 37.494584,125.21154 M 45.565397,124.99816 C 47.304384,123.85879 48.897464,123.43203 50.525969,123.43203 C 52.34,123.43203 53.191776,123.93046 53.191776,124.53602 C 53.191776,124.89187 53.079617,125.49742 52.894917,125.85331 C 52.376261,125.56823 51.785075,125.31945 50.821131,125.31945 C 49.637079,125.31945 48.526385,125.63909 47.638339,126.42255 L 47.638339,141.26632 C 47.638339,141.7647 47.675453,141.7647 45.565397,141.7647 L 45.565397,124.99816 M 64.254794,124.60683 C 64.254794,124.14383 62.700508,123.43203 61.145377,123.43203 C 58.145598,123.43203 54.481256,124.4643 54.481256,133.25617 C 54.481256,141.58507 56.70347,142.3331 60.589608,142.3331 C 62.514121,142.3331 64.254794,141.30089 64.254794,140.73161 C 64.254794,140.4111 64.181418,139.91269 63.99504,139.48515 C 63.217475,140.05441 62.033423,140.58905 60.775152,140.58905 C 58.11018,140.58905 56.55504,139.84185 56.55504,133.3633 C 56.55504,126.20837 59.108698,125.21154 61.330069,125.21154 C 62.58834,125.21154 63.291694,125.56743 63.99504,126.0304 C 64.181418,125.60367 64.254794,124.99816 64.254794,124.60683 M 78.435657,141.15915 C 76.806308,141.97803 74.659991,142.29851 72.808845,142.29851 C 68.070088,142.29851 67.366733,140.30571 67.366733,135.57114 L 67.366733,124.42971 C 67.366733,123.96757 67.330471,123.96757 69.440527,123.96757 L 69.440527,135.7854 C 69.440527,139.34513 69.958338,140.55538 72.734626,140.55538 C 73.808215,140.55538 75.289126,140.34199 76.399811,139.70105 L 76.399811,124.43056 C 76.399811,123.96839 76.325602,123.96839 78.435657,123.96839 L 78.435657,141.15915 M 82.657438,124.99816 C 84.396406,123.85879 85.98865,123.43203 87.617156,123.43203 C 89.431178,123.43203 90.282962,123.93046 90.282962,124.53602 C 90.282962,124.89187 90.171639,125.49742 89.986938,125.85331 C 89.468283,125.56823 88.876272,125.31945 87.913163,125.31945 C 86.729111,125.31945 85.618415,125.63909 84.729535,126.42255 L 84.729535,141.26632 C 84.729535,141.7647 84.767484,141.7647 82.657438,141.7647 L 82.657438,124.99816 M 95.036045,123.9659 C 93.406714,123.9659 92.926008,123.9659 92.926008,124.92729 L 92.926008,141.76382 C 94.99895,141.76382 95.036045,141.76382 95.036045,141.26547 L 95.036045,123.9659 M 92.851787,117.70149 C 92.851787,118.87629 93.222023,119.30304 93.961631,119.33843 C 94.813415,119.33843 95.220746,118.73376 95.220746,117.66526 C 95.257851,116.56214 94.960991,116.06374 94.11006,116.06374 C 93.296243,116.06374 92.888893,116.66926 92.851787,117.70149 M 98.547748,124.99816 C 98.547748,124.60683 98.62196,124.39264 98.770389,124.28635 C 99.473743,123.89502 102.17666,123.43203 105.24898,123.43203 C 107.58166,123.43203 109.06174,124.53602 109.06174,127.73899 L 109.06174,130.05231 C 109.06174,136.38835 108.87704,141.12293 108.87704,141.12293 C 108.02528,141.58507 106.43387,142.29771 103.84143,142.29771 C 101.17646,142.3331 98.511478,142.0843 98.511478,136.81596 C 98.511478,131.7972 101.25067,131.01375 103.98986,131.01375 C 105.02633,131.01375 106.24834,131.12082 107.06301,131.4413 C 107.06301,131.4413 107.06301,129.12711 107.06301,128.13033 C 107.06301,125.81704 105.87895,125.31862 104.47141,125.31862 C 102.58399,125.31862 99.956127,125.67451 98.808337,126.20837 C 98.585707,125.81704 98.547748,125.21154 98.547748,124.99816 M 107.06216,132.9011 C 106.35882,132.65147 105.35945,132.54522 104.65609,132.54522 C 102.54604,132.54522 100.62069,132.97198 100.62069,136.88763 C 100.62069,140.55363 102.21293,140.58991 104.10032,140.58991 C 105.28522,140.58991 106.47014,140.26946 106.87663,139.84271 C 106.87747,139.84185 107.06216,135.57029 107.06216,132.9011 M 114.91792,141.26547 C 114.91792,141.76382 114.95503,141.76382 112.88124,141.76382 L 112.88124,116.56214 C 112.88124,115.60073 113.28857,115.60073 114.91792,115.60073 L 114.91792,141.26547" + style="fill:#010101;stroke-width:2.02999997;stroke-miterlimit:4;stroke-dasharray:none" + id="text2611" /><g + transform="matrix(0.9351326,0,0,0.9351326,150.39508,-1.251766)" + id="g4503" + style="opacity:1"><path + d="M -45.749655,92.691592 C -25.709638,59.370739 -49.98206,5.3291313 -94.363693,10.819389 C -134.46337,15.776665 -135.10949,57.983708 -99.76917,68.010455 C -69.186498,76.695132 -93.451029,96.093536 -92.742037,109.01138 C -92.030055,121.92728 -66.155038,126.61324 -45.749655,92.691592 z " + style="fill:#1b1a1b" + id="path2339" /><circle + cx="33.728001" + cy="85.363998" + r="15.414" + transform="matrix(1.0917947,-0.2858168,0.2858168,1.0917947,-180.30817,13.494135)" + style="fill:#1b1a1b" + id="circle2341" + sodipodi:cx="33.728001" + sodipodi:cy="85.363998" + sodipodi:rx="15.414" + sodipodi:ry="15.414" /><path + d="M -140.06215,48.935849 C -146.31997,49.541603 -150.90082,55.100456 -150.29507,61.358275 C -149.68817,67.620461 -144.12955,72.20487 -137.87064,71.59883 C -131.61373,70.985148 -127.02904,65.427621 -127.63726,59.169282 C -128.24543,52.915596 -133.80324,48.329809 -140.06215,48.935849 z " + style="fill:#1b1a1b" + id="path2343" /><path + d="M -44.99294,91.339709 C -24.951831,58.018571 -49.224253,3.976963 -93.605885,9.4672202 C -133.70556,14.424496 -134.35249,56.632918 -99.012168,66.659664 C -68.429497,75.344341 -92.694028,94.742745 -91.984749,107.66168 C -91.271961,120.5762 -65.398322,125.26135 -44.99294,91.339709 z " + style="fill:#bfbfbf" + id="path2561" /><path + d="M -86.84228,112.75985 C -88.056751,110.79004 -86.19955,108.60176 -84.290569,108.76815 C -81.251858,109.03428 -74.635637,108.73252 -69.415044,105.77341 C -56.372412,98.379694 -36.300952,62.803704 -46.395841,40.365295 C -50.915249,30.320886 -53.115898,27.444964 -57.770162,22.531645 C -58.719625,21.529587 -58.174556,21.584053 -57.531623,21.923221 C -55.014762,23.244092 -50.592026,28.36035 -46.055478,36.687677 C -38.390628,50.757116 -38.788117,67.483141 -41.638835,77.975343 C -43.624548,85.27439 -50.464117,101.78644 -60.480639,108.92577 C -70.5197,116.0815 -82.266433,120.18559 -86.84228,112.75985 z " + style="fill:#000000" + id="path2563" /><path + d="M -95.930347,66.591355 C -102.76341,64.562985 -111.57238,61.738267 -116.66758,55.073789 C -120.42371,50.15984 -122.3305,44.796759 -122.81745,41.755703 C -122.99069,40.670602 -123.13785,39.765332 -122.82526,39.515509 C -122.68064,39.399486 -120.02045,45.412302 -116.04367,50.451645 C -112.06769,55.492366 -106.51047,58.440379 -101.88092,59.511496 C -97.763206,60.46345 -89.233623,62.555175 -86.347769,65.013729 C -83.380949,67.540918 -83.133309,73.00119 -84.131664,73.617197 C -85.138469,74.236583 -87.180025,69.187603 -95.930347,66.591355 z " + style="fill:#000000" + id="path2565" /><path + d="M -81.840812,113.72311 C -81.972699,115.28707 -80.176315,115.59377 -77.75828,115.23141 C -74.658947,114.76654 -72.037923,114.41754 -68.470623,112.62971 C -63.63582,110.20674 -58.742752,106.74072 -55.159223,102.06476 C -44.467444,88.115271 -40.681354,71.610444 -41.264404,69.236185 C -41.459242,71.196944 -44.040349,81.489071 -49.943268,90.767882 C -57.52457,102.68631 -63.022197,109.03464 -75.701416,112.1124 C -79.230011,112.96964 -81.668137,111.66432 -81.840812,113.72311 z " + style="fill:#ffffff" + id="path2567" /><path + d="M -109.96233,59.479354 C -108.51822,60.704238 -105.55938,62.336389 -99.737455,64.245644 C -92.705873,66.551032 -89.282274,68.550326 -87.848506,69.508429 C -86.329222,70.525809 -85.366279,72.795951 -85.27115,70.779631 C -85.17194,68.761076 -86.416123,67.025373 -89.192166,66.104839 C -91.070345,65.481234 -94.229847,63.996111 -97.258539,63.398373 C -99.204694,63.014221 -102.37098,62.251845 -105.08636,61.420426 C -106.57454,60.963046 -108.09089,60.161888 -109.96233,59.479354 z " + style="fill:#ffffff" + id="path2569" /><circle + cx="34.681" + cy="84.375" + r="15.414" + transform="matrix(1.0917947,-0.2858168,0.2858168,1.0917947,-180.30817,13.494135)" + style="fill:#bfbfbf" + id="circle2577" + sodipodi:cx="34.681" + sodipodi:cy="84.375" + sodipodi:rx="15.414" + sodipodi:ry="15.414" /><path + d="M -128.68413,108.37945 C -115.15301,120.91784 -94.786007,103.69471 -103.75445,88.482597 C -104.76154,86.774656 -106.06907,85.474351 -105.63906,86.782721 C -102.77288,95.529828 -105.42141,102.44941 -110.3632,106.01451 C -115.20857,109.5112 -121.86847,110.09622 -127.20028,107.33186 C -128.76601,106.5203 -129.41538,107.70291 -128.68413,108.37945 z " + style="fill:#000000" + id="path2579" /><path + d="M -118.06686,110.95477 C -116.34413,110.59244 -106.32442,107.99742 -103.97055,99.756195 C -103.23743,97.186709 -103.1058,97.702893 -103.31295,99.095232 C -104.37035,106.20143 -111.08741,111.44338 -116.80312,111.63773 C -117.963,111.75704 -119.48484,111.25131 -118.06686,110.95477 z " + style="fill:#ffffff" + id="path2585" /><path + d="M -139.30435,47.583681 C -145.56216,48.189435 -150.14301,53.748288 -149.53726,60.006106 C -148.93065,66.2672 -143.37174,70.852702 -137.11392,70.246948 C -130.85592,69.632979 -126.27151,64.074361 -126.88083,57.816308 C -127.48791,51.562336 -133.04544,46.977641 -139.30435,47.583681 z " + style="fill:#bfbfbf" + id="path2589" /><path + d="M -144.46878,67.571208 C -144.39939,68.375508 -143.29781,69.408789 -141.56718,69.883196 C -140.08038,70.290771 -136.24758,71.332594 -131.32372,68.224839 C -126.39986,65.117084 -125.8321,56.804464 -128.07041,54.35955 C -128.76326,53.121154 -129.66426,52.21957 -128.94737,54.195974 C -127.13695,59.186468 -130.65487,63.854586 -133.68917,66.0162 C -136.72238,68.177528 -140.56932,67.154692 -142.14014,66.675779 C -143.71095,66.196867 -144.53929,66.740369 -144.46878,67.571208 z " + style="fill:#000000" + id="path2591" /><path + d="M -138.11472,68.687851 C -137.66344,68.281557 -135.37889,68.447629 -133.31622,67.338341 C -131.25464,66.229338 -128.80419,63.798254 -128.36692,60.343756 C -128.10933,58.315237 -128.03197,58.824631 -127.92942,59.929403 C -128.24939,65.67243 -133.53086,68.844638 -136.55132,69.263202 C -137.36636,69.376239 -138.8007,69.307247 -138.11472,68.687851 z " + style="fill:#ffffff" + id="path2597" /><path + d="M -47.767489,69.693822 C -39.234739,45.099506 -57.090457,7.9576459 -93.212919,12.425552 C -125.85191,16.461012 -126.37823,50.814524 -97.613495,58.976486 C -65.031338,63.908526 -84.650966,88.487524 -87.434101,100.88229 C -89.929232,111.99304 -61.102889,113.82164 -47.767489,69.693822 z " + style="fill:#999999" + id="path2561_1_" /><path + d="M -70.093288,88.904346 C -78.920045,87.812046 -91.622267,107.74061 -79.645446,105.40671 C -67.670523,103.07448 -91.622267,107.74061 -79.645446,105.40671 C -73.888849,104.55302 -69.119803,102.52058 -64.850547,97.64761 C -59.283982,91.295233 -50.968477,77.5735 -48.563483,68.707586 C -46.537563,61.232354 -47.555881,49.650767 -49.644305,60.532553 C -51.786232,71.700167 -61.266532,89.996647 -70.093288,88.904346 z " + style="fill:#f3f3f3" + id="path2571" /><path + d="M -129.3854,104.84502 C -127.34184,104.87935 -126.10573,105.16706 -124.03635,106.61908 C -119.94568,108.31891 -112.42648,107.24179 -108.9543,102.67081 C -105.48212,98.099823 -105.36811,91.801741 -106.69103,87.996073 C -109.92728,78.682039 -123.67593,78.846722 -129.81795,86.579362 C -136.46216,95.2146 -131.42897,104.81069 -129.3854,104.84502 z " + style="fill:#999999" + id="path2581" /><path + d="M -147.63565,61.683628 C -147.22833,62.966318 -146.18754,64.837882 -143.9897,65.149887 C -141.05481,65.566524 -140.45479,66.892551 -136.9892,66.204631 C -133.52361,65.516711 -130.89674,62.676625 -129.84557,59.535064 C -128.64212,55.188187 -130.44406,52.944024 -133.15599,50.940416 C -135.86791,48.936808 -141.83359,49.152263 -145.3938,52.39768 C -147.92393,54.702631 -148.62733,58.560726 -147.63565,61.683628 z " + style="fill:#999999" + id="path2593_2_" /><path + d="M -136.11009,64.55822 C -133.44721,63.861113 -129.92545,60.232613 -131.67381,57.462279 C -133.83086,54.048798 -139.84051,56.970651 -140.04374,60.77103 C -140.24777,64.572786 -138.93238,65.297057 -136.11009,64.55822 z " + style="fill:#f3f3f3" + id="path256" /><path + d="M -116.11512,105.50904 C -113.8431,104.91425 -106.88259,102.0818 -108.18994,91.962983 C -108.85161,86.83742 -111.64725,98.324328 -116.82409,100.04237 C -124.66721,102.64507 -123.78607,107.51719 -116.11512,105.50904 z " + style="fill:#f3f3f3" + id="path258" /></g> +</svg> \ No newline at end of file
--- a/contrib/mercurial.el Wed Apr 09 15:27:57 2008 -0700 +++ b/contrib/mercurial.el Wed Apr 09 15:28:30 2008 -0700 @@ -35,8 +35,10 @@ ;; This code has been developed under XEmacs 21.5, and may not work as ;; well under GNU Emacs (albeit tested under 21.4). Patches to ;; enhance the portability of this code, fix bugs, and add features -;; are most welcome. You can clone a Mercurial repository for this -;; package from http://www.serpentine.com/hg/hg-emacs +;; are most welcome. + +;; As of version 22.3, GNU Emacs's VC mode has direct support for +;; Mercurial, so this package may not prove as useful there. ;; Please send problem reports and suggestions to bos@serpentine.com.
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/contrib/mergetools.hgrc Wed Apr 09 15:28:30 2008 -0700 @@ -0,0 +1,50 @@ +# Some default global settings for common merge tools + +[merge-tools] +kdiff3.args=--auto -L1 base --L2 local --L3 other $base $local $other -o $output +kdiff3.regkey=Software\KDiff3 +kdiff3.regappend=\kdiff3.exe +kdiff3.fixeol=True +kdiff3.gui=True + +gvimdiff.args=--nofork -d -g -O $local $other $base +gvimdiff.regkey=Software\Vim\GVim +gvimdiff.regname=path +gvimdiff.priority=-9 + +merge.checkconflicts=True +merge.priority=-10 + +gpyfm.gui=True + +meld.gui=True + +tkdiff.args=$local $other -a $base -o $output +tkdiff.gui=True +tkdiff.priority=-8 + +xxdiff.args=--show-merged-pane --exit-with-merge-status --title1 local --title2 base --title3 other --merged-filename $output --merge $local $base $other +xxdiff.gui=True +xxdiff.priority=-8 + +diffmerge.args=--nosplash --merge --title1=base --title2=local --title3=other $base $local $other +diffmerge.checkchanged=True +diffmerge.gui=True + +p4merge.args=$base $local $other $output +p4merge.regkey=Software\Perforce\Environment +p4merge.regname=P4INSTROOT +p4merge.regappend=\p4merge.exe +p4merge.gui=True +p4merge.priority=-8 + +tortoisemerge.args=/base: $output /mine:$local /theirs:$other /merged:$output +tortoisemerge.regkey=Software\TortoiseSVN +tortoisemerge.gui=True + +ecmerge.args=$base $local $other --mode=merge3 --title0=base --title1=local --title2=other --to=$output +ecmerge.regkey=Software\Elli\xc3\xa9 Computing\Merge +ecmerge.gui=True + +filemerge.args=-left $other -right $local -ancestor $base -merge $output +filemerge.gui=True
--- a/contrib/simplemerge Wed Apr 09 15:27:57 2008 -0700 +++ b/contrib/simplemerge Wed Apr 09 15:28:30 2008 -0700 @@ -1,503 +1,11 @@ #!/usr/bin/env python -# Copyright (C) 2004, 2005 Canonical Ltd -# -# This program is free software; you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation; either version 2 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program; if not, write to the Free Software -# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - - -# mbp: "you know that thing where cvs gives you conflict markers?" -# s: "i hate that." from mercurial import demandimport demandimport.enable() -from mercurial import util, mdiff, fancyopts +import os, sys from mercurial.i18n import _ - - -class CantReprocessAndShowBase(Exception): - pass - - -def warn(message): - sys.stdout.flush() - sys.stderr.write(message) - sys.stderr.flush() - - -def intersect(ra, rb): - """Given two ranges return the range where they intersect or None. - - >>> intersect((0, 10), (0, 6)) - (0, 6) - >>> intersect((0, 10), (5, 15)) - (5, 10) - >>> intersect((0, 10), (10, 15)) - >>> intersect((0, 9), (10, 15)) - >>> intersect((0, 9), (7, 15)) - (7, 9) - """ - assert ra[0] <= ra[1] - assert rb[0] <= rb[1] - - sa = max(ra[0], rb[0]) - sb = min(ra[1], rb[1]) - if sa < sb: - return sa, sb - else: - return None - - -def compare_range(a, astart, aend, b, bstart, bend): - """Compare a[astart:aend] == b[bstart:bend], without slicing. - """ - if (aend-astart) != (bend-bstart): - return False - for ia, ib in zip(xrange(astart, aend), xrange(bstart, bend)): - if a[ia] != b[ib]: - return False - else: - return True - - - - -class Merge3Text(object): - """3-way merge of texts. - - Given strings BASE, OTHER, THIS, tries to produce a combined text - incorporating the changes from both BASE->OTHER and BASE->THIS.""" - def __init__(self, basetext, atext, btext, base=None, a=None, b=None): - self.basetext = basetext - self.atext = atext - self.btext = btext - if base is None: - base = mdiff.splitnewlines(basetext) - if a is None: - a = mdiff.splitnewlines(atext) - if b is None: - b = mdiff.splitnewlines(btext) - self.base = base - self.a = a - self.b = b - - - - def merge_lines(self, - name_a=None, - name_b=None, - name_base=None, - start_marker='<<<<<<<', - mid_marker='=======', - end_marker='>>>>>>>', - base_marker=None, - reprocess=False): - """Return merge in cvs-like form. - """ - self.conflicts = False - newline = '\n' - if len(self.a) > 0: - if self.a[0].endswith('\r\n'): - newline = '\r\n' - elif self.a[0].endswith('\r'): - newline = '\r' - if base_marker and reprocess: - raise CantReprocessAndShowBase() - if name_a: - start_marker = start_marker + ' ' + name_a - if name_b: - end_marker = end_marker + ' ' + name_b - if name_base and base_marker: - base_marker = base_marker + ' ' + name_base - merge_regions = self.merge_regions() - if reprocess is True: - merge_regions = self.reprocess_merge_regions(merge_regions) - for t in merge_regions: - what = t[0] - if what == 'unchanged': - for i in range(t[1], t[2]): - yield self.base[i] - elif what == 'a' or what == 'same': - for i in range(t[1], t[2]): - yield self.a[i] - elif what == 'b': - for i in range(t[1], t[2]): - yield self.b[i] - elif what == 'conflict': - self.conflicts = True - yield start_marker + newline - for i in range(t[3], t[4]): - yield self.a[i] - if base_marker is not None: - yield base_marker + newline - for i in range(t[1], t[2]): - yield self.base[i] - yield mid_marker + newline - for i in range(t[5], t[6]): - yield self.b[i] - yield end_marker + newline - else: - raise ValueError(what) - - - - - - def merge_annotated(self): - """Return merge with conflicts, showing origin of lines. - - Most useful for debugging merge. - """ - for t in self.merge_regions(): - what = t[0] - if what == 'unchanged': - for i in range(t[1], t[2]): - yield 'u | ' + self.base[i] - elif what == 'a' or what == 'same': - for i in range(t[1], t[2]): - yield what[0] + ' | ' + self.a[i] - elif what == 'b': - for i in range(t[1], t[2]): - yield 'b | ' + self.b[i] - elif what == 'conflict': - yield '<<<<\n' - for i in range(t[3], t[4]): - yield 'A | ' + self.a[i] - yield '----\n' - for i in range(t[5], t[6]): - yield 'B | ' + self.b[i] - yield '>>>>\n' - else: - raise ValueError(what) - - - - - - def merge_groups(self): - """Yield sequence of line groups. Each one is a tuple: - - 'unchanged', lines - Lines unchanged from base - - 'a', lines - Lines taken from a - - 'same', lines - Lines taken from a (and equal to b) - - 'b', lines - Lines taken from b - - 'conflict', base_lines, a_lines, b_lines - Lines from base were changed to either a or b and conflict. - """ - for t in self.merge_regions(): - what = t[0] - if what == 'unchanged': - yield what, self.base[t[1]:t[2]] - elif what == 'a' or what == 'same': - yield what, self.a[t[1]:t[2]] - elif what == 'b': - yield what, self.b[t[1]:t[2]] - elif what == 'conflict': - yield (what, - self.base[t[1]:t[2]], - self.a[t[3]:t[4]], - self.b[t[5]:t[6]]) - else: - raise ValueError(what) - - - def merge_regions(self): - """Return sequences of matching and conflicting regions. - - This returns tuples, where the first value says what kind we - have: - - 'unchanged', start, end - Take a region of base[start:end] - - 'same', astart, aend - b and a are different from base but give the same result - - 'a', start, end - Non-clashing insertion from a[start:end] - - Method is as follows: - - The two sequences align only on regions which match the base - and both descendents. These are found by doing a two-way diff - of each one against the base, and then finding the - intersections between those regions. These "sync regions" - are by definition unchanged in both and easily dealt with. - - The regions in between can be in any of three cases: - conflicted, or changed on only one side. - """ - - # section a[0:ia] has been disposed of, etc - iz = ia = ib = 0 - - for zmatch, zend, amatch, aend, bmatch, bend in self.find_sync_regions(): - #print 'match base [%d:%d]' % (zmatch, zend) - - matchlen = zend - zmatch - assert matchlen >= 0 - assert matchlen == (aend - amatch) - assert matchlen == (bend - bmatch) - - len_a = amatch - ia - len_b = bmatch - ib - len_base = zmatch - iz - assert len_a >= 0 - assert len_b >= 0 - assert len_base >= 0 - - #print 'unmatched a=%d, b=%d' % (len_a, len_b) - - if len_a or len_b: - # try to avoid actually slicing the lists - equal_a = compare_range(self.a, ia, amatch, - self.base, iz, zmatch) - equal_b = compare_range(self.b, ib, bmatch, - self.base, iz, zmatch) - same = compare_range(self.a, ia, amatch, - self.b, ib, bmatch) - - if same: - yield 'same', ia, amatch - elif equal_a and not equal_b: - yield 'b', ib, bmatch - elif equal_b and not equal_a: - yield 'a', ia, amatch - elif not equal_a and not equal_b: - yield 'conflict', iz, zmatch, ia, amatch, ib, bmatch - else: - raise AssertionError("can't handle a=b=base but unmatched") - - ia = amatch - ib = bmatch - iz = zmatch - - # if the same part of the base was deleted on both sides - # that's OK, we can just skip it. - - - if matchlen > 0: - assert ia == amatch - assert ib == bmatch - assert iz == zmatch - - yield 'unchanged', zmatch, zend - iz = zend - ia = aend - ib = bend - - - def reprocess_merge_regions(self, merge_regions): - """Where there are conflict regions, remove the agreed lines. - - Lines where both A and B have made the same changes are - eliminated. - """ - for region in merge_regions: - if region[0] != "conflict": - yield region - continue - type, iz, zmatch, ia, amatch, ib, bmatch = region - a_region = self.a[ia:amatch] - b_region = self.b[ib:bmatch] - matches = mdiff.get_matching_blocks(''.join(a_region), - ''.join(b_region)) - next_a = ia - next_b = ib - for region_ia, region_ib, region_len in matches[:-1]: - region_ia += ia - region_ib += ib - reg = self.mismatch_region(next_a, region_ia, next_b, - region_ib) - if reg is not None: - yield reg - yield 'same', region_ia, region_len+region_ia - next_a = region_ia + region_len - next_b = region_ib + region_len - reg = self.mismatch_region(next_a, amatch, next_b, bmatch) - if reg is not None: - yield reg - - - def mismatch_region(next_a, region_ia, next_b, region_ib): - if next_a < region_ia or next_b < region_ib: - return 'conflict', None, None, next_a, region_ia, next_b, region_ib - mismatch_region = staticmethod(mismatch_region) - - - def find_sync_regions(self): - """Return a list of sync regions, where both descendents match the base. - - Generates a list of (base1, base2, a1, a2, b1, b2). There is - always a zero-length sync region at the end of all the files. - """ - - ia = ib = 0 - amatches = mdiff.get_matching_blocks(self.basetext, self.atext) - bmatches = mdiff.get_matching_blocks(self.basetext, self.btext) - len_a = len(amatches) - len_b = len(bmatches) - - sl = [] - - while ia < len_a and ib < len_b: - abase, amatch, alen = amatches[ia] - bbase, bmatch, blen = bmatches[ib] - - # there is an unconflicted block at i; how long does it - # extend? until whichever one ends earlier. - i = intersect((abase, abase+alen), (bbase, bbase+blen)) - if i: - intbase = i[0] - intend = i[1] - intlen = intend - intbase - - # found a match of base[i[0], i[1]]; this may be less than - # the region that matches in either one - assert intlen <= alen - assert intlen <= blen - assert abase <= intbase - assert bbase <= intbase - - asub = amatch + (intbase - abase) - bsub = bmatch + (intbase - bbase) - aend = asub + intlen - bend = bsub + intlen - - assert self.base[intbase:intend] == self.a[asub:aend], \ - (self.base[intbase:intend], self.a[asub:aend]) - - assert self.base[intbase:intend] == self.b[bsub:bend] - - sl.append((intbase, intend, - asub, aend, - bsub, bend)) - - # advance whichever one ends first in the base text - if (abase + alen) < (bbase + blen): - ia += 1 - else: - ib += 1 - - intbase = len(self.base) - abase = len(self.a) - bbase = len(self.b) - sl.append((intbase, intbase, abase, abase, bbase, bbase)) - - return sl - - - - def find_unconflicted(self): - """Return a list of ranges in base that are not conflicted.""" - am = mdiff.get_matching_blocks(self.basetext, self.atext) - bm = mdiff.get_matching_blocks(self.basetext, self.btext) - - unc = [] - - while am and bm: - # there is an unconflicted block at i; how long does it - # extend? until whichever one ends earlier. - a1 = am[0][0] - a2 = a1 + am[0][2] - b1 = bm[0][0] - b2 = b1 + bm[0][2] - i = intersect((a1, a2), (b1, b2)) - if i: - unc.append(i) - - if a2 < b2: - del am[0] - else: - del bm[0] - - return unc - - -# bzr compatible interface, for the tests -class Merge3(Merge3Text): - """3-way merge of texts. - - Given BASE, OTHER, THIS, tries to produce a combined text - incorporating the changes from both BASE->OTHER and BASE->THIS. - All three will typically be sequences of lines.""" - def __init__(self, base, a, b): - basetext = '\n'.join([i.strip('\n') for i in base] + ['']) - atext = '\n'.join([i.strip('\n') for i in a] + ['']) - btext = '\n'.join([i.strip('\n') for i in b] + ['']) - if util.binary(basetext) or util.binary(atext) or util.binary(btext): - raise util.Abort(_("don't know how to merge binary files")) - Merge3Text.__init__(self, basetext, atext, btext, base, a, b) - - -def simplemerge(local, base, other, **opts): - def readfile(filename): - f = open(filename, "rb") - text = f.read() - f.close() - if util.binary(text): - msg = _("%s looks like a binary file.") % filename - if not opts.get('text'): - raise util.Abort(msg) - elif not opts.get('quiet'): - warn(_('warning: %s\n') % msg) - return text - - name_a = local - name_b = other - labels = opts.get('label', []) - if labels: - name_a = labels.pop(0) - if labels: - name_b = labels.pop(0) - if labels: - raise util.Abort(_("can only specify two labels.")) - - localtext = readfile(local) - basetext = readfile(base) - othertext = readfile(other) - - orig = local - local = os.path.realpath(local) - if not opts.get('print'): - opener = util.opener(os.path.dirname(local)) - out = opener(os.path.basename(local), "w", atomictemp=True) - else: - out = sys.stdout - - reprocess = not opts.get('no_minimal') - - m3 = Merge3Text(basetext, localtext, othertext) - for line in m3.merge_lines(name_a=name_a, name_b=name_b, - reprocess=reprocess): - out.write(line) - - if not opts.get('print'): - out.rename() - - if m3.conflicts: - if not opts.get('quiet'): - warn(_("warning: conflicts during merge.\n")) - return 1 +from mercurial import simplemerge, fancyopts, util options = [('L', 'label', [], _('labels to use on conflict markers')), ('a', 'text', None, _('treat all files as text')), @@ -517,6 +25,9 @@ By default, LOCAL is overwritten with the results of this operation. ''') +class ParseError(Exception): + """Exception raised on errors in parsing the command line.""" + def showhelp(): sys.stdout.write(usage) sys.stdout.write('\noptions:\n') @@ -530,33 +41,24 @@ for first, second in out_opts: sys.stdout.write(' %-*s %s\n' % (opts_len, first, second)) -class ParseError(Exception): - """Exception raised on errors in parsing the command line.""" - -def main(argv): +try: + opts = {} try: - opts = {} - try: - args = fancyopts.fancyopts(argv[1:], options, opts) - except fancyopts.getopt.GetoptError, e: - raise ParseError(e) - if opts['help']: - showhelp() - return 0 - if len(args) != 3: - raise ParseError(_('wrong number of arguments')) - return simplemerge(*args, **opts) - except ParseError, e: - sys.stdout.write("%s: %s\n" % (sys.argv[0], e)) + args = fancyopts.fancyopts(sys.argv[1:], options, opts) + except fancyopts.getopt.GetoptError, e: + raise ParseError(e) + if opts['help']: showhelp() - return 1 - except util.Abort, e: - sys.stderr.write("abort: %s\n" % e) - return 255 - except KeyboardInterrupt: - return 255 - -if __name__ == '__main__': - import sys - import os - sys.exit(main(sys.argv)) + sys.exit(0) + if len(args) != 3: + raise ParseError(_('wrong number of arguments')) + sys.exit(simplemerge.simplemerge(*args, **opts)) +except ParseError, e: + sys.stdout.write("%s: %s\n" % (sys.argv[0], e)) + showhelp() + sys.exit(1) +except util.Abort, e: + sys.stderr.write("abort: %s\n" % e) + sys.exit(255) +except KeyboardInterrupt: + sys.exit(255)
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/contrib/undumprevlog Wed Apr 09 15:28:30 2008 -0700 @@ -0,0 +1,37 @@ +#!/usr/bin/env python +# Undump a dump from dumprevlog +# $ hg init +# $ undumprevlog < repo.dump + +import sys +from mercurial import revlog, node, util, transaction + +for fp in (sys.stdin, sys.stdout, sys.stderr): + util.set_binary(fp) + +opener = util.opener('.', False) +tr = transaction.transaction(sys.stderr.write, opener, "undump.journal") +while 1: + l = sys.stdin.readline() + if not l: + break + if l.startswith("file:"): + f = l[6:-1] + r = revlog.revlog(opener, f) + print f + elif l.startswith("node:"): + n = node.bin(l[6:-1]) + elif l.startswith("linkrev:"): + lr = int(l[9:-1]) + elif l.startswith("parents:"): + p = l[9:-1].split() + p1 = node.bin(p[0]) + p2 = node.bin(p[1]) + elif l.startswith("length:"): + length = int(l[8:-1]) + sys.stdin.readline() # start marker + d = sys.stdin.read(length) + sys.stdin.readline() # end marker + r.addrevision(d, tr, lr, p1, p2) + +tr.close()
--- a/contrib/zsh_completion Wed Apr 09 15:27:57 2008 -0700 +++ b/contrib/zsh_completion Wed Apr 09 15:28:30 2008 -0700 @@ -13,6 +13,9 @@ # option) any later version. # +emulate -LR zsh +setopt extendedglob + local curcontext="$curcontext" state line typeset -A _hg_cmd_globals @@ -153,9 +156,9 @@ typeset -a tags local tag rev - _hg_cmd tags 2> /dev/null | while read tag rev + _hg_cmd tags 2> /dev/null | while read tag do - tags+=($tag) + tags+=(${tag/ # [0-9]#:*}) done (( $#tags )) && _describe -t tags 'tags' tags } @@ -674,13 +677,13 @@ # MQ _hg_qseries() { typeset -a patches - patches=($(_hg_cmd qseries 2>/dev/null)) + patches=(${(f)"$(_hg_cmd qseries 2>/dev/null)"}) (( $#patches )) && _describe -t hg-patches 'patches' patches } _hg_qapplied() { typeset -a patches - patches=($(_hg_cmd qapplied 2>/dev/null)) + patches=(${(f)"$(_hg_cmd qapplied 2>/dev/null)"}) if (( $#patches )) then patches+=(qbase qtip) @@ -690,7 +693,7 @@ _hg_qunapplied() { typeset -a patches - patches=($(_hg_cmd qunapplied 2>/dev/null)) + patches=(${(f)"$(_hg_cmd qunapplied 2>/dev/null)"}) (( $#patches )) && _describe -t hg-unapplied-patches 'unapplied patches' patches } @@ -730,6 +733,12 @@ '*:unapplied patch:_hg_qunapplied' } +_hg_cmd_qgoto() { + _arguments -s -w : $_hg_global_opts \ + '(--force -f)'{-f,--force}'[overwrite any local changes]' \ + ':patch:_hg_qseries' +} + _hg_cmd_qguard() { _arguments -s -w : $_hg_global_opts \ '(--list -l)'{-l,--list}'[list all patches and guards]' \
--- a/doc/Makefile Wed Apr 09 15:27:57 2008 -0700 +++ b/doc/Makefile Wed Apr 09 15:28:30 2008 -0700 @@ -18,7 +18,9 @@ python gendoc.py > $@ %: %.xml - xmlto man $*.xml + xmlto man $*.xml ; \ + sed -e 's/^\.hg/\\\&.hg/' $* > $*~ ; \ + mv $*~ $* %.xml: %.txt asciidoc -d manpage -b docbook $*.txt
--- a/doc/gendoc.py Wed Apr 09 15:27:57 2008 -0700 +++ b/doc/gendoc.py Wed Apr 09 15:28:30 2008 -0700 @@ -59,36 +59,36 @@ underlined(_("COMMANDS")) h = {} for c, attr in table.items(): - f = c.split("|")[0] - f = f.lstrip("^") - h[f] = c + f = c.split("|")[0] + f = f.lstrip("^") + h[f] = c cmds = h.keys() cmds.sort() for f in cmds: - if f.startswith("debug"): continue - d = get_cmd(h[f]) - # synopsis - ui.write("%s::\n" % d['synopsis'].replace("hg ","", 1)) - # description - ui.write("%s\n\n" % d['desc'][1]) - # options - opt_output = list(d['opts']) - if opt_output: - opts_len = max([len(line[0]) for line in opt_output]) - ui.write(_(" options:\n")) - for optstr, desc in opt_output: - if desc: - s = "%-*s %s" % (opts_len, optstr, desc) - else: - s = optstr - s = textwrap.fill(s, initial_indent=4 * " ", - subsequent_indent=(6 + opts_len) * " ") - ui.write("%s\n" % s) - ui.write("\n") - # aliases - if d['aliases']: - ui.write(_(" aliases: %s\n\n") % " ".join(d['aliases'])) + if f.startswith("debug"): continue + d = get_cmd(h[f]) + # synopsis + ui.write("%s::\n" % d['synopsis'].replace("hg ","", 1)) + # description + ui.write("%s\n\n" % d['desc'][1]) + # options + opt_output = list(d['opts']) + if opt_output: + opts_len = max([len(line[0]) for line in opt_output]) + ui.write(_(" options:\n")) + for optstr, desc in opt_output: + if desc: + s = "%-*s %s" % (opts_len, optstr, desc) + else: + s = optstr + s = textwrap.fill(s, initial_indent=4 * " ", + subsequent_indent=(6 + opts_len) * " ") + ui.write("%s\n" % s) + ui.write("\n") + # aliases + if d['aliases']: + ui.write(_(" aliases: %s\n\n") % " ".join(d['aliases'])) # print topics for t in helptable:
--- a/doc/hg.1.txt Wed Apr 09 15:27:57 2008 -0700 +++ b/doc/hg.1.txt Wed Apr 09 15:28:30 2008 -0700 @@ -30,7 +30,7 @@ repository path:: either the pathname of a local repository or the URI of a remote - repository. There are two available URI protocols, http:// which is + repository. There are two available URI protocols, http:// which is fast and the static-http:// protocol which is much slower but does not require a special server on the web host. @@ -43,7 +43,7 @@ Mercurial accepts several notations for identifying individual revisions. - A plain integer is treated as a revision number. Negative + A plain integer is treated as a revision number. Negative integers are treated as offsets from the tip, with -1 denoting the tip. @@ -52,11 +52,11 @@ A hexadecimal string less than 40 characters long is treated as a unique revision identifier, and referred to as a short-form - identifier. A short-form identifier is only valid if it is the + identifier. A short-form identifier is only valid if it is the prefix of one full-length identifier. Any other string is treated as a tag name, which is a symbolic - name associated with a revision identifier. Tag names may not + name associated with a revision identifier. Tag names may not contain the ":" character. The reserved name "tip" is a special tag that always identifies @@ -78,24 +78,24 @@ separated by the ":" character. The syntax of range notation is [BEGIN]:[END], where BEGIN and END - are revision identifiers. Both BEGIN and END are optional. If - BEGIN is not specified, it defaults to revision number 0. If END - is not specified, it defaults to the tip. The range ":" thus + are revision identifiers. Both BEGIN and END are optional. If + BEGIN is not specified, it defaults to revision number 0. If END + is not specified, it defaults to the tip. The range ":" thus means "all revisions". If BEGIN is greater than END, revisions are treated in reverse order. - A range acts as a closed interval. This means that a range of 3:5 - gives 3, 4 and 5. Similarly, a range of 4:2 gives 4, 3, and 2. + A range acts as a closed interval. This means that a range of 3:5 + gives 3, 4 and 5. Similarly, a range of 4:2 gives 4, 3, and 2. FILES ----- - repo/.hgignore:: + .hgignore:: This file contains regular expressions (one per line) that describe file names that should be ignored by hg. For details, see hgignore(5). - repo/.hgtags:: + .hgtags:: This file contains changeset hash values and text tag names (one of each separated by spaces) that correspond to tagged versions of the repository contents. @@ -103,7 +103,7 @@ /etc/mercurial/hgrc, $HOME/.hgrc, .hg/hgrc:: This file contains defaults and configuration. Values in .hg/hgrc override those in $HOME/.hgrc, and these override settings made in the - global /etc/mercurial/hgrc configuration. See hgrc(5) for details of + global /etc/mercurial/hgrc configuration. See hgrc(5) for details of the contents and format of these files. Some commands (e.g. revert) produce backup files ending in .orig, if
--- a/doc/hgignore.5.txt Wed Apr 09 15:27:57 2008 -0700 +++ b/doc/hgignore.5.txt Wed Apr 09 15:28:30 2008 -0700 @@ -17,25 +17,25 @@ ----------- Mercurial ignores every unmanaged file that matches any pattern in an -ignore file. The patterns in an ignore file do not apply to files -managed by Mercurial. To control Mercurial's handling of files that -it manages, see the hg(1) man page. Look for the "-I" and "-X" +ignore file. The patterns in an ignore file do not apply to files +managed by Mercurial. To control Mercurial's handling of files that +it manages, see the hg(1) man page. Look for the "-I" and "-X" options. In addition, a Mercurial configuration file can point to a set of -per-user or global ignore files. See the hgrc(5) man page for details -of how to configure these files. Look for the "ignore" entry in the +per-user or global ignore files. See the hgrc(5) man page for details +of how to configure these files. Look for the "ignore" entry in the "ui" section. SYNTAX ------ An ignore file is a plain text file consisting of a list of patterns, -with one pattern per line. Empty lines are skipped. The "#" +with one pattern per line. Empty lines are skipped. The "#" character is treated as a comment character, and the "\" character is treated as an escape character. -Mercurial supports several pattern syntaxes. The default syntax used +Mercurial supports several pattern syntaxes. The default syntax used is Python/Perl-style regular expressions. To change the syntax used, use a line of the following form: @@ -52,9 +52,9 @@ The chosen syntax stays in effect when parsing all patterns that follow, until another syntax is selected. -Neither glob nor regexp patterns are rooted. A glob-syntax pattern of +Neither glob nor regexp patterns are rooted. A glob-syntax pattern of the form "*.c" will match a file ending in ".c" in any directory, and -a regexp pattern of the form "\.c$" will do the same. To root a +a regexp pattern of the form "\.c$" will do the same. To root a regexp pattern, start it with "^". EXAMPLE @@ -68,7 +68,6 @@ *.elc *.pyc *~ - .*.swp # switch to regexp syntax. syntax: regexp
--- a/doc/hgmerge.1.txt Wed Apr 09 15:27:57 2008 -0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,35 +0,0 @@ -HGMERGE(1) -========== -Matt Mackall <mpm@selenic.com> -v0.1, 27 May 2005 - -NAME ----- -hgmerge - default wrapper to merge files in Mercurial SCM system - -SYNOPSIS --------- -'hgmerge' local ancestor remote - -DESCRIPTION ------------ -The hgmerge(1) command provides a graphical interface to merge files in the -Mercurial system. It is a simple wrapper around kdiff3, merge(1) and tkdiff(1), -or simply diff(1) and patch(1) depending on what is present on the system. - -hgmerge(1) is used by the Mercurial SCM if the environment variable HGMERGE is -not set. - -AUTHOR ------- -Written by Vincent Danjean <Vincent.Danjean@free.fr> - -SEE ALSO --------- -hg(1) - the command line interface to Mercurial SCM - -COPYING -------- -Copyright \(C) 2005-2007 Matt Mackall. -Free use of this software is granted under the terms of the GNU General -Public License (GPL).
--- a/doc/hgrc.5.txt Wed Apr 09 15:27:57 2008 -0700 +++ b/doc/hgrc.5.txt Wed Apr 09 15:28:30 2008 -0700 @@ -17,45 +17,58 @@ Mercurial reads configuration data from several files, if they exist. The names of these files depend on the system on which Mercurial is -installed. Windows registry keys contain PATH-like strings, every -part must reference a Mercurial.ini file or be a directory where *.rc -files will be read. +installed. *.rc files from a single directory are read in +alphabetical order, later ones overriding earlier ones. Where +multiple paths are given below, settings from later paths override +earlier ones. -(Unix) <install-root>/etc/mercurial/hgrc.d/*.rc:: -(Unix) <install-root>/etc/mercurial/hgrc:: +(Unix) <install-root>/etc/mercurial/hgrc.d/*.rc:: +(Unix) <install-root>/etc/mercurial/hgrc:: Per-installation configuration files, searched for in the - directory where Mercurial is installed. For example, if installed - in /shared/tools, Mercurial will look in - /shared/tools/etc/mercurial/hgrc. Options in these files apply to - all Mercurial commands executed by any user in any directory. + directory where Mercurial is installed. <install-root> is the + parent directory of the hg executable (or symlink) being run. + For example, if installed in /shared/tools/bin/hg, Mercurial will + look in /shared/tools/etc/mercurial/hgrc. Options in these files + apply to all Mercurial commands executed by any user in any + directory. -(Unix) /etc/mercurial/hgrc.d/*.rc:: -(Unix) /etc/mercurial/hgrc:: -(Windows) HKEY_LOCAL_MACHINE\SOFTWARE\Mercurial:: - or:: -(Windows) C:\Mercurial\Mercurial.ini:: +(Unix) /etc/mercurial/hgrc.d/*.rc:: +(Unix) /etc/mercurial/hgrc:: Per-system configuration files, for the system on which Mercurial - is running. Options in these files apply to all Mercurial - commands executed by any user in any directory. Options in these + is running. Options in these files apply to all Mercurial + commands executed by any user in any directory. Options in these files override per-installation options. -(Unix) $HOME/.hgrc:: -(Windows) C:\Documents and Settings\USERNAME\Mercurial.ini:: -(Windows) $HOME\Mercurial.ini:: - Per-user configuration file, for the user running Mercurial. - Options in this file apply to all Mercurial commands executed by - any user in any directory. Options in this file override +(Windows) <install-dir>\Mercurial.ini:: + or else:: +(Windows) HKEY_LOCAL_MACHINE\SOFTWARE\Mercurial:: + or else:: +(Windows) C:\Mercurial\Mercurial.ini:: + Per-installation/system configuration files, for the system on + which Mercurial is running. Options in these files apply to all + Mercurial commands executed by any user in any directory. + Registry keys contain PATH-like strings, every part of which must + reference a Mercurial.ini file or be a directory where *.rc files + will be read. + +(Unix) $HOME/.hgrc:: +(Windows) %HOME%\Mercurial.ini:: +(Windows) %HOME%\.hgrc:: +(Windows) %USERPROFILE%\Mercurial.ini:: +(Windows) %USERPROFILE%\.hgrc:: + Per-user configuration file(s), for the user running Mercurial. + On Windows 9x, %HOME% is replaced by %APPDATA%. + Options in these files apply to all Mercurial commands executed + by this user in any directory. Options in thes files override per-installation and per-system options. - On Windows system, one of these is chosen exclusively according - to definition of HOME environment variable. (Unix, Windows) <repo>/.hg/hgrc:: Per-repository configuration options that only apply in a - particular repository. This file is not version-controlled, and - will not get transferred during a "clone" operation. Options in + particular repository. This file is not version-controlled, and + will not get transferred during a "clone" operation. Options in this file override options in all other configuration files. On Unix, most of this file will be ignored if it doesn't belong - to a trusted user or to a trusted group. See the documentation + to a trusted user or to a trusted group. See the documentation for the trusted section below for more details. SYNTAX @@ -69,10 +82,10 @@ green= eggs -Each line contains one entry. If the lines that follow are indented, +Each line contains one entry. If the lines that follow are indented, they are treated as continuations of that entry. -Leading whitespace is removed from values. Empty lines are skipped. +Leading whitespace is removed from values. Empty lines are skipped. The optional values can contain format strings which refer to other values in the same section, or values in a special DEFAULT section. @@ -94,12 +107,12 @@ Filters consist of a filter pattern followed by a filter command. Filter patterns are globs by default, rooted at the repository - root. For example, to match any file ending in ".txt" in the root - directory only, use the pattern "*.txt". To match any file ending + root. For example, to match any file ending in ".txt" in the root + directory only, use the pattern "*.txt". To match any file ending in ".c" anywhere in the repository, use the pattern "**.c". The filter command can start with a specifier, either "pipe:" or - "tempfile:". If no specifier is given, "pipe:" is used by default. + "tempfile:". If no specifier is given, "pipe:" is used by default. A "pipe:" command must accept data on stdin and return the transformed data on stdout. @@ -116,29 +129,34 @@ # can safely omit "pipe:", because it's the default) *.gz = gzip - A "tempfile:" command is a template. The string INFILE is replaced + A "tempfile:" command is a template. The string INFILE is replaced with the name of a temporary file that contains the data to be - filtered by the command. The string OUTFILE is replaced with the + filtered by the command. The string OUTFILE is replaced with the name of an empty temporary file, where the filtered data must be written by the command. NOTE: the tempfile mechanism is recommended for Windows systems, where the standard shell I/O redirection operators often have - strange effects. In particular, if you are doing line ending - conversion on Windows using the popular dos2unix and unix2dos - programs, you *must* use the tempfile mechanism, as using pipes will - corrupt the contents of your files. + strange effects and may corrupt the contents of your files. - Tempfile example: + The most common usage is for LF <-> CRLF translation on Windows. + For this, use the "smart" convertors which check for binary files: + [extensions] + hgext.win32text = [encode] - # convert files to unix line ending conventions on checkin - **.txt = tempfile: dos2unix -n INFILE OUTFILE - + ** = cleverencode: [decode] - # convert files to windows line ending conventions when writing - # them to the working dir - **.txt = tempfile: unix2dos -n INFILE OUTFILE + ** = cleverdecode: + + or if you only want to translate certain files: + + [extensions] + hgext.win32text = + [encode] + **.txt = dumbencode: + [decode] + **.txt = dumbdecode: defaults:: Use the [defaults] section to define command defaults, i.e. the @@ -174,22 +192,22 @@ email:: Settings for extensions that send email messages. from;; - Optional. Email address to use in "From" header and SMTP envelope + Optional. Email address to use in "From" header and SMTP envelope of outgoing messages. to;; - Optional. Comma-separated list of recipients' email addresses. + Optional. Comma-separated list of recipients' email addresses. cc;; - Optional. Comma-separated list of carbon copy recipients' + Optional. Comma-separated list of carbon copy recipients' email addresses. bcc;; - Optional. Comma-separated list of blind carbon copy - recipients' email addresses. Cannot be set interactively. + Optional. Comma-separated list of blind carbon copy + recipients' email addresses. Cannot be set interactively. method;; - Optional. Method to use to send email messages. If value is + Optional. Method to use to send email messages. If value is "smtp" (default), use SMTP (see section "[smtp]" for - configuration). Otherwise, use as name of program to run that + configuration). Otherwise, use as name of program to run that acts like sendmail (takes "-f" option for sender, list of - recipients on command line, message on stdin). Normally, setting + recipients on command line, message on stdin). Normally, setting this to "sendmail" or "/usr/sbin/sendmail" is enough to use sendmail to send messages. @@ -211,6 +229,10 @@ the path to the ".py" file (including the file name extension) that defines the extension. + To explicitly disable an extension that is enabled in an hgrc of + broader scope, prepend its path with '!', as in + 'hgext.foo = !/ext/path' or 'hgext.foo = !' when no path is supplied. + Example for ~/.hgrc: [extensions] @@ -228,6 +250,83 @@ you to store longer filenames in some situations at the expense of compatibility. +merge-patterns:: + This section specifies merge tools to associate with particular file + patterns. Tools matched here will take precedence over the default + merge tool. Patterns are globs by default, rooted at the repository root. + + Example: + + [merge-patterns] + **.c = kdiff3 + **.jpg = myimgmerge + +merge-tools:: + This section configures external merge tools to use for file-level + merges. + + Example ~/.hgrc: + + [merge-tools] + # Override stock tool location + kdiff3.executable = ~/bin/kdiff3 + # Specify command line + kdiff3.args = $base $local $other -o $output + # Give higher priority + kdiff3.priority = 1 + + # Define new tool + myHtmlTool.args = -m $local $other $base $output + myHtmlTool.regkey = Software\FooSoftware\HtmlMerge + myHtmlTool.priority = 1 + + Supported arguments: + + priority;; + The priority in which to evaluate this tool. + Default: 0. + executable;; + Either just the name of the executable or its pathname. + Default: the tool name. + args;; + The arguments to pass to the tool executable. You can refer to the files + being merged as well as the output file through these variables: $base, + $local, $other, $output. + Default: $local $base $other + premerge;; + Attempt to run internal non-interactive 3-way merge tool before + launching external tool. + Default: True + binary;; + This tool can merge binary files. Defaults to False, unless tool + was selected by file pattern match. + symlink;; + This tool can merge symlinks. Defaults to False, even if tool was + selected by file pattern match. + checkconflicts;; + Check whether there are conflicts even though the tool reported + success. + Default: False + checkchanged;; + Check whether outputs were written even though the tool reported + success. + Default: False + fixeol;; + Attempt to fix up EOL changes caused by the merge tool. + Default: False + gui;; + This tool requires a graphical interface to run. Default: False + regkey;; + Windows registry key which describes install location of this tool. + Mercurial will search for this key first under HKEY_CURRENT_USER and + then under HKEY_LOCAL_MACHINE. Default: None + regname;; + Name of value to read from specified registry key. Defaults to the + unnamed (default) value. + regappend;; + String to append to the value read from the registry, typically the + executable name of the tool. Default: None + hooks:: Commands or Python functions that get automatically executed by various actions such as starting or finishing a commit. Multiple @@ -244,24 +343,24 @@ incoming.autobuild = /my/build/hook Most hooks are run with environment variables set that give added - useful information. For each hook below, the environment variables + useful information. For each hook below, the environment variables it is passed are listed with names of the form "$HG_foo". changegroup;; Run after a changegroup has been added via push, pull or - unbundle. ID of the first new changeset is in $HG_NODE. URL from + unbundle. ID of the first new changeset is in $HG_NODE. URL from which changes came is in $HG_URL. commit;; Run after a changeset has been created in the local repository. - ID of the newly created changeset is in $HG_NODE. Parent + ID of the newly created changeset is in $HG_NODE. Parent changeset IDs are in $HG_PARENT1 and $HG_PARENT2. incoming;; Run after a changeset has been pulled, pushed, or unbundled into - the local repository. The ID of the newly arrived changeset is in - $HG_NODE. URL that was source of changes came is in $HG_URL. + the local repository. The ID of the newly arrived changeset is in + $HG_NODE. URL that was source of changes came is in $HG_URL. outgoing;; - Run after sending changes from local repository to another. ID of - first changeset sent is in $HG_NODE. Source of operation is in + Run after sending changes from local repository to another. ID of + first changeset sent is in $HG_NODE. Source of operation is in $HG_SOURCE; see "preoutgoing" hook for description. post-<command>;; Run after successful invocations of the associated command. The @@ -273,56 +372,56 @@ the command doesn't execute and Mercurial returns the failure code. prechangegroup;; Run before a changegroup is added via push, pull or unbundle. - Exit status 0 allows the changegroup to proceed. Non-zero status - will cause the push, pull or unbundle to fail. URL from which + Exit status 0 allows the changegroup to proceed. Non-zero status + will cause the push, pull or unbundle to fail. URL from which changes will come is in $HG_URL. precommit;; - Run before starting a local commit. Exit status 0 allows the - commit to proceed. Non-zero status will cause the commit to fail. + Run before starting a local commit. Exit status 0 allows the + commit to proceed. Non-zero status will cause the commit to fail. Parent changeset IDs are in $HG_PARENT1 and $HG_PARENT2. preoutgoing;; Run before collecting changes to send from the local repository to - another. Non-zero status will cause failure. This lets you - prevent pull over http or ssh. Also prevents against local pull, + another. Non-zero status will cause failure. This lets you + prevent pull over http or ssh. Also prevents against local pull, push (outbound) or bundle commands, but not effective, since you - can just copy files instead then. Source of operation is in - $HG_SOURCE. If "serve", operation is happening on behalf of - remote ssh or http repository. If "push", "pull" or "bundle", + can just copy files instead then. Source of operation is in + $HG_SOURCE. If "serve", operation is happening on behalf of + remote ssh or http repository. If "push", "pull" or "bundle", operation is happening on behalf of repository on same system. pretag;; - Run before creating a tag. Exit status 0 allows the tag to be - created. Non-zero status will cause the tag to fail. ID of - changeset to tag is in $HG_NODE. Name of tag is in $HG_TAG. Tag + Run before creating a tag. Exit status 0 allows the tag to be + created. Non-zero status will cause the tag to fail. ID of + changeset to tag is in $HG_NODE. Name of tag is in $HG_TAG. Tag is local if $HG_LOCAL=1, in repo if $HG_LOCAL=0. pretxnchangegroup;; Run after a changegroup has been added via push, pull or unbundle, - but before the transaction has been committed. Changegroup is - visible to hook program. This lets you validate incoming changes - before accepting them. Passed the ID of the first new changeset - in $HG_NODE. Exit status 0 allows the transaction to commit. + but before the transaction has been committed. Changegroup is + visible to hook program. This lets you validate incoming changes + before accepting them. Passed the ID of the first new changeset + in $HG_NODE. Exit status 0 allows the transaction to commit. Non-zero status will cause the transaction to be rolled back and - the push, pull or unbundle will fail. URL that was source of + the push, pull or unbundle will fail. URL that was source of changes is in $HG_URL. pretxncommit;; Run after a changeset has been created but the transaction not yet - committed. Changeset is visible to hook program. This lets you - validate commit message and changes. Exit status 0 allows the - commit to proceed. Non-zero status will cause the transaction to - be rolled back. ID of changeset is in $HG_NODE. Parent changeset + committed. Changeset is visible to hook program. This lets you + validate commit message and changes. Exit status 0 allows the + commit to proceed. Non-zero status will cause the transaction to + be rolled back. ID of changeset is in $HG_NODE. Parent changeset IDs are in $HG_PARENT1 and $HG_PARENT2. preupdate;; - Run before updating the working directory. Exit status 0 allows - the update to proceed. Non-zero status will prevent the update. - Changeset ID of first new parent is in $HG_PARENT1. If merge, ID + Run before updating the working directory. Exit status 0 allows + the update to proceed. Non-zero status will prevent the update. + Changeset ID of first new parent is in $HG_PARENT1. If merge, ID of second new parent is in $HG_PARENT2. tag;; - Run after a tag is created. ID of tagged changeset is in - $HG_NODE. Name of tag is in $HG_TAG. Tag is local if + Run after a tag is created. ID of tagged changeset is in + $HG_NODE. Name of tag is in $HG_TAG. Tag is local if $HG_LOCAL=1, in repo if $HG_LOCAL=0. update;; - Run after updating the working directory. Changeset ID of first - new parent is in $HG_PARENT1. If merge, ID of second new parent - is in $HG_PARENT2. If update succeeded, $HG_ERROR=0. If update + Run after updating the working directory. Changeset ID of first + new parent is in $HG_PARENT1. If merge, ID of second new parent + is in $HG_PARENT2. If update succeeded, $HG_ERROR=0. If update failed (e.g. because conflicts not resolved), $HG_ERROR=1. Note: it is generally better to use standard hooks rather than the @@ -340,10 +439,10 @@ hookname = python:modulename.submodule.callable - Python hooks are run within the Mercurial process. Each hook is + Python hooks are run within the Mercurial process. Each hook is called with at least three keyword arguments: a ui object (keyword "ui"), a repository object (keyword "repo"), and a "hooktype" - keyword that tells what kind of hook is used. Arguments listed as + keyword that tells what kind of hook is used. Arguments listed as environment variables above are passed as keyword arguments, with no "HG_" prefix, and names in lower case. @@ -357,68 +456,68 @@ Host name and (optional) port of the proxy server, for example "myproxy:8000". no;; - Optional. Comma-separated list of host names that should bypass + Optional. Comma-separated list of host names that should bypass the proxy. passwd;; - Optional. Password to authenticate with at the proxy server. + Optional. Password to authenticate with at the proxy server. user;; - Optional. User name to authenticate with at the proxy server. + Optional. User name to authenticate with at the proxy server. smtp:: Configuration for extensions that need to send email messages. host;; 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: 25. tls;; - Optional. Whether to connect to mail server using TLS. True or - False. Default: False. + Optional. Whether to connect to mail server using TLS. True or + False. Default: False. username;; - Optional. User name to authenticate to SMTP server with. + Optional. User name to authenticate to SMTP server with. If username is specified, password must also be specified. Default: none. password;; - Optional. Password to authenticate to SMTP server with. + Optional. Password to authenticate to SMTP server with. If username is specified, password must also be specified. Default: none. local_hostname;; - Optional. It's the hostname that the sender can use to identify itself + Optional. It's the hostname that the sender can use to identify itself to the MTA. paths:: - Assigns symbolic names to repositories. The left side is the + Assigns symbolic names to repositories. The left side is the symbolic name, and the right gives the directory or URL that is the - location of the repository. Default paths can be declared by + location of the repository. Default paths can be declared by setting the following entries. default;; Directory or URL to use when pulling if no source is specified. Default is set to repository from which the current repository was cloned. default-push;; - Optional. Directory or URL to use when pushing if no destination + Optional. Directory or URL to use when pushing if no destination is specified. server:: Controls generic server settings. uncompressed;; Whether to allow clients to clone a repo using the uncompressed - streaming protocol. This transfers about 40% more data than a + streaming protocol. This transfers about 40% more data than a regular clone, but uses less memory and CPU on both server and - client. Over a LAN (100Mbps or better) or a very fast WAN, an + client. Over a LAN (100Mbps or better) or a very fast WAN, an uncompressed streaming clone is a lot faster (~10x) than a regular - clone. Over most WAN connections (anything slower than about + clone. Over most WAN connections (anything slower than about 6Mbps), uncompressed streaming is slower, because of the extra - data transfer overhead. Default is False. + data transfer overhead. Default is False. trusted:: For security reasons, Mercurial will not use the settings in the .hg/hgrc file from a repository if it doesn't belong to a - trusted user or to a trusted group. The main exception is the + trusted user or to a trusted group. The main exception is the web interface, which automatically uses some safe settings, since it's common to serve repositories from different users. - This section specifies what users and groups are trusted. The - current user is always trusted. To trust everybody, list a user + This section specifies what users and groups are trusted. The + current user is always trusted. To trust everybody, list a user or a group with name "*". users;; @@ -428,13 +527,18 @@ ui:: User interface controls. + archivemeta;; + Whether to include the .hg_archival.txt file containing metadata + (hashes for the repository base and for tip) in archives created by + the hg archive command or downloaded via hgweb. + Default is true. debug;; - Print debugging information. True or False. Default is False. + Print debugging information. True or False. Default is False. editor;; - The editor to use during a commit. Default is $EDITOR or "vi". + The editor to use during a commit. Default is $EDITOR or "vi". fallbackencoding;; Encoding to try if it's not possible to decode the changelog using - UTF-8. Default is ISO-8859-1. + UTF-8. Default is ISO-8859-1. ignore;; A file to read per-user ignore patterns from. This file should be in the same format as a repository-wide .hgignore file. This option @@ -443,24 +547,36 @@ "ignore.other = ~/.hgignore2". For details of the ignore file format, see the hgignore(5) man page. interactive;; - Allow to prompt the user. True or False. Default is True. + Allow to prompt the user. True or False. Default is True. logtemplate;; Template string for commands that print changesets. merge;; The conflict resolution program to use during a manual merge. - Default is "hgmerge". + There are some internal tools available: + + internal:local;; + keep the local version + internal:other;; + use the other version + internal:merge;; + use the internal non-interactive merge tool + internal:fail;; + fail to merge + + See the merge-tools section for more information on configuring tools. + patch;; command to use to apply patches. Look for 'gpatch' or 'patch' in PATH if unset. quiet;; - Reduce the amount of output printed. True or False. Default is False. + Reduce the amount of output printed. True or False. Default is False. remotecmd;; remote command to use for clone/push/pull operations. Default is 'hg'. report_untrusted;; Warn if a .hg/hgrc file is ignored due to not being owned by a - trusted user or group. True or False. Default is True. + trusted user or group. True or False. Default is True. slash;; - Display paths using a slash ("/") as the path separator. This only + Display paths using a slash ("/") as the path separator. This only makes a difference on systems where the default path separator is not the slash character (e.g. Windows uses the backslash character ("\")). Default is False. @@ -468,7 +584,7 @@ command to use for SSH connections. Default is 'ssh'. strict;; Require exact command names, instead of allowing unambiguous - abbreviations. True or False. Default is False. + abbreviations. True or False. Default is False. style;; Name of style to use for command output. timeout;; @@ -477,12 +593,12 @@ username;; The committer of a changeset created when running "commit". Typically a person's name and email address, e.g. "Fred Widget - <fred@example.com>". Default is $EMAIL or username@hostname. + <fred@example.com>". Default is $EMAIL or username@hostname. If the username in hgrc is empty, it has to be specified manually or in a different hgrc file (e.g. $HOME/.hgrc, if the admin set "username =" in the system hgrc). verbose;; - Increase the amount of output printed. True or False. Default is False. + Increase the amount of output printed. True or False. Default is False. web:: @@ -503,9 +619,9 @@ allowpull;; Whether to allow pulling from the repository. Default is true. allow_push;; - Whether to allow pushing to the repository. If empty or not set, - push is not allowed. If the special value "*", any remote user - can push, including unauthenticated users. Otherwise, the remote + Whether to allow pushing to the repository. If empty or not set, + push is not allowed. If the special value "*", any remote user + can push, including unauthenticated users. Otherwise, the remote user must have been authenticated, and the authenticated user name must be present in this list (separated by whitespace or ","). The contents of the allow_push list are examined after the @@ -519,13 +635,13 @@ Example: "http://hgserver/repos/" contact;; Name or email address of the person in charge of the repository. - Default is "unknown". + Defaults to ui.username or $EMAIL or "unknown" if unset or empty. deny_push;; - Whether to deny pushing to the repository. If empty or not set, - push is not denied. If the special value "*", all remote users - are denied push. Otherwise, unauthenticated users are all denied, + Whether to deny pushing to the repository. If empty or not set, + push is not denied. If the special value "*", all remote users + are denied push. Otherwise, unauthenticated users are all denied, and any authenticated user name present in this list (separated by - whitespace or ",") is also denied. The contents of the deny_push + whitespace or ",") is also denied. The contents of the deny_push list are examined before the allow_push list. description;; Textual description of the repository's purpose or contents. @@ -548,9 +664,11 @@ Maximum number of files to list per changeset. Default is 10. port;; Port to listen on. Default is 8000. + prefix;; + Prefix path to serve from. Default is '' (server root). push_ssl;; Whether to require that inbound pushes be transported over SSL to - prevent password sniffing. Default is true. + prevent password sniffing. Default is true. staticurl;; Base URL to use for static files. If unset, static files (e.g. the hgicon.png favicon) will be served by the CGI script itself.
--- a/doc/ja/hgmerge.1.ja.txt Wed Apr 09 15:27:57 2008 -0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,37 +0,0 @@ -HGMERGE(1) -========== -Matt Mackall <mpm@selenic.com> -v0.1, 27 May 2005 - -名前 --- -hgmerge - Mercurial ソースコード管理システムでファイルをマージする -のに使われるデフォルトのラッパー - -書式 --- -'hgmerge' local ancestor remote - -説明 --- -hgmerge(1) コマンドは Mercurial システムでファイルをマージするため -のグラフィカルなインターフェイスを提供します。これは kdiff3, -merge(1), tkdiff(1), または単純に diff(1) と patch(1) のラッパーで、 -どれがシステム上にあるかに依存します。 - -hgmerge(1) は Mercurial ソースコード管理システムで環境変数 -HGMERGE が設定されていない場合に使われます。 - -著者 --- -Vincent Danjean <Vincent.Danjean@free.fr> によって書かれました。 - -関連情報 --- -hg(1) - Mercurial システムへのコマンドラインインターフェイス - -著作権情報 ----- -Copyright (C) 2005-2007 Matt Mackall. -このソフトウェアの自由な使用は GNU 一般公有使用許諾 (GPL) のもとで -認められます。
--- a/hgext/acl.py Wed Apr 09 15:27:57 2008 -0700 +++ b/hgext/acl.py Wed Apr 09 15:28:30 2008 -0700 @@ -46,7 +46,7 @@ # ** = user6 from mercurial.i18n import _ -from mercurial.node import * +from mercurial.node import bin, short from mercurial import util import getpass
--- a/hgext/bugzilla.py Wed Apr 09 15:27:57 2008 -0700 +++ b/hgext/bugzilla.py Wed Apr 09 15:28:30 2008 -0700 @@ -53,7 +53,7 @@ # committer_email = bugzilla_user_name from mercurial.i18n import _ -from mercurial.node import * +from mercurial.node import short from mercurial import cmdutil, templater, util import os, re, time @@ -282,7 +282,7 @@ root=self.repo.root, webroot=webroot(self.repo.root)) data = self.ui.popbuffer() - self.add_comment(bugid, data, templater.email(ctx.user())) + self.add_comment(bugid, data, util.email(ctx.user())) def hook(ui, repo, hooktype, node=None, **kwargs): '''add comment to bugzilla for each changeset that refers to a
--- a/hgext/children.py Wed Apr 09 15:27:57 2008 -0700 +++ b/hgext/children.py Wed Apr 09 15:28:30 2008 -0700 @@ -8,6 +8,7 @@ # of the GNU General Public License, incorporated herein by reference. from mercurial import cmdutil +from mercurial.commands import templateopts from mercurial.i18n import _ @@ -35,7 +36,6 @@ "children": (children, [('r', 'rev', '', _('show children of the specified rev')), - ('', 'style', '', _('display using template map file')), - ('', 'template', '', _('display with template'))], + ] + templateopts, _('hg children [-r REV] [FILE]')), }
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/hgext/churn.py Wed Apr 09 15:28:30 2008 -0700 @@ -0,0 +1,206 @@ +# churn.py - create a graph showing who changed the most lines +# +# Copyright 2006 Josef "Jeff" Sipek <jeffpc@josefsipek.net> +# +# This software may be used and distributed according to the terms +# of the GNU General Public License, incorporated herein by reference. +# +# +# Aliases map file format is simple one alias per line in the following +# format: +# +# <alias email> <actual email> + +from mercurial.i18n import gettext as _ +from mercurial import mdiff, cmdutil, util, node +import os, sys + +def get_tty_width(): + if 'COLUMNS' in os.environ: + try: + return int(os.environ['COLUMNS']) + except ValueError: + pass + try: + import termios, array, fcntl + for dev in (sys.stdout, sys.stdin): + try: + fd = dev.fileno() + if not os.isatty(fd): + continue + arri = fcntl.ioctl(fd, termios.TIOCGWINSZ, '\0' * 8) + return array.array('h', arri)[1] + except ValueError: + pass + except ImportError: + pass + return 80 + +def __gather(ui, repo, node1, node2): + def dirtywork(f, mmap1, mmap2): + lines = 0 + + to = mmap1 and repo.file(f).read(mmap1[f]) or None + tn = mmap2 and repo.file(f).read(mmap2[f]) or None + + diff = mdiff.unidiff(to, "", tn, "", f, f).split("\n") + + for line in diff: + if not line: + continue # skip EOF + if line.startswith(" "): + continue # context line + if line.startswith("--- ") or line.startswith("+++ "): + continue # begining of diff + if line.startswith("@@ "): + continue # info line + + # changed lines + lines += 1 + + return lines + + ## + + lines = 0 + + changes = repo.status(node1, node2, None, util.always)[:5] + + modified, added, removed, deleted, unknown = changes + + who = repo.changelog.read(node2)[1] + who = util.email(who) # get the email of the person + + mmap1 = repo.manifest.read(repo.changelog.read(node1)[0]) + mmap2 = repo.manifest.read(repo.changelog.read(node2)[0]) + for f in modified: + lines += dirtywork(f, mmap1, mmap2) + + for f in added: + lines += dirtywork(f, None, mmap2) + + for f in removed: + lines += dirtywork(f, mmap1, None) + + for f in deleted: + lines += dirtywork(f, mmap1, mmap2) + + for f in unknown: + lines += dirtywork(f, mmap1, mmap2) + + return (who, lines) + +def gather_stats(ui, repo, amap, revs=None, progress=False): + stats = {} + + cl = repo.changelog + + if not revs: + revs = range(0, cl.count()) + + nr_revs = len(revs) + cur_rev = 0 + + for rev in revs: + cur_rev += 1 # next revision + + node2 = cl.node(rev) + node1 = cl.parents(node2)[0] + + if cl.parents(node2)[1] != node.nullid: + ui.note(_('Revision %d is a merge, ignoring...\n') % (rev,)) + continue + + who, lines = __gather(ui, repo, node1, node2) + + # remap the owner if possible + if who in amap: + ui.note("using '%s' alias for '%s'\n" % (amap[who], who)) + who = amap[who] + + if not who in stats: + stats[who] = 0 + stats[who] += lines + + ui.note("rev %d: %d lines by %s\n" % (rev, lines, who)) + + if progress: + nr_revs = max(nr_revs, 1) + if int(100.0*(cur_rev - 1)/nr_revs) < int(100.0*cur_rev/nr_revs): + ui.write("\rGenerating stats: %d%%" % (int(100.0*cur_rev/nr_revs),)) + sys.stdout.flush() + + if progress: + ui.write("\r") + sys.stdout.flush() + + return stats + +def churn(ui, repo, **opts): + "Graphs the number of lines changed" + + def pad(s, l): + if len(s) < l: + return s + " " * (l-len(s)) + return s[0:l] + + def graph(n, maximum, width, char): + maximum = max(1, maximum) + n = int(n * width / float(maximum)) + + return char * (n) + + def get_aliases(f): + aliases = {} + + for l in f.readlines(): + l = l.strip() + alias, actual = l.split() + aliases[alias] = actual + + return aliases + + amap = {} + aliases = opts.get('aliases') + if aliases: + try: + f = open(aliases,"r") + except OSError, e: + print "Error: " + e + return + + amap = get_aliases(f) + f.close() + + revs = [int(r) for r in cmdutil.revrange(repo, opts['rev'])] + revs.sort() + stats = gather_stats(ui, repo, amap, revs, opts.get('progress')) + + # make a list of tuples (name, lines) and sort it in descending order + ordered = stats.items() + if not ordered: + return + ordered.sort(lambda x, y: cmp(y[1], x[1])) + max_churn = ordered[0][1] + + tty_width = get_tty_width() + ui.note(_("assuming %i character terminal\n") % tty_width) + tty_width -= 1 + + max_user_width = max([len(user) for user, churn in ordered]) + + graph_width = tty_width - max_user_width - 1 - 6 - 2 - 2 + + for user, churn in ordered: + print "%s %6d %s" % (pad(user, max_user_width), + churn, + graph(churn, max_churn, graph_width, '*')) + +cmdtable = { + "churn": + (churn, + [('r', 'rev', [], _('limit statistics to the specified revisions')), + ('', 'aliases', '', _('file with email aliases')), + ('', 'progress', None, _('show progress'))], + 'hg churn [-r revision range] [-a file] [--progress]'), +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/hgext/color.py Wed Apr 09 15:28:30 2008 -0700 @@ -0,0 +1,219 @@ +# color.py color output for the status and qseries commands +# +# Copyright (C) 2007 Kevin Christen <kevin.christen@gmail.com> +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License as published by the +# Free Software Foundation; either version 2 of the License, or (at your +# option) any later version. +# +# This program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General +# Public License for more details. +# +# You should have received a copy of the GNU General Public License along +# with this program; if not, write to the Free Software Foundation, Inc., +# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + +'''add color output to the status and qseries commands + +This extension modifies the status command to add color to its output to +reflect file status, and the qseries command to add color to reflect patch +status (applied, unapplied, missing). Other effects in addition to color, +like bold and underlined text, are also available. Effects are rendered +with the ECMA-48 SGR control function (aka ANSI escape codes). This module +also provides the render_text function, which can be used to add effects to +any text. + +To enable this extension, add this to your .hgrc file: +[extensions] +color = + +Default effects my be overriden from the .hgrc file: + +[color] +status.modified = blue bold underline red_background +status.added = green bold +status.removed = red bold blue_background +status.deleted = cyan bold underline +status.unknown = magenta bold underline +status.ignored = black bold + + 'none' turns off all effects +status.clean = none +status.copied = none + +qseries.applied = blue bold underline +qseries.unapplied = black bold +qseries.missing = red bold +''' + +import re, sys + +from mercurial import commands, cmdutil +from mercurial.i18n import _ + +# start and stop parameters for effects +_effect_params = { 'none': (0, 0), + 'black': (30, 39), + 'red': (31, 39), + 'green': (32, 39), + 'yellow': (33, 39), + 'blue': (34, 39), + 'magenta': (35, 39), + 'cyan': (36, 39), + 'white': (37, 39), + 'bold': (1, 22), + 'italic': (3, 23), + 'underline': (4, 24), + 'inverse': (7, 27), + 'black_background': (40, 49), + 'red_background': (41, 49), + 'green_background': (42, 49), + 'yellow_background': (43, 49), + 'blue_background': (44, 49), + 'purple_background': (45, 49), + 'cyan_background': (46, 49), + 'white_background': (47, 49), } + +def render_effects(text, *effects): + 'Wrap text in commands to turn on each effect.' + start = [] + stop = [] + for effect in effects: + start.append(str(_effect_params[effect][0])) + stop.append(str(_effect_params[effect][1])) + start = '\033[' + ';'.join(start) + 'm' + stop = '\033[' + ';'.join(stop) + 'm' + return start + text + stop + +def colorstatus(statusfunc, ui, repo, *pats, **opts): + '''run the status command with colored output''' + + delimiter = opts['print0'] and '\0' or '\n' + + # run status and capture it's output + ui.pushbuffer() + retval = statusfunc(ui, repo, *pats, **opts) + # filter out empty strings + lines = [ line for line in ui.popbuffer().split(delimiter) if line ] + + if opts['no_status']: + # if --no-status, run the command again without that option to get + # output with status abbreviations + opts['no_status'] = False + ui.pushbuffer() + statusfunc(ui, repo, *pats, **opts) + # filter out empty strings + lines_with_status = [ line for + line in ui.popbuffer().split(delimiter) if line ] + else: + lines_with_status = lines + + # apply color to output and display it + for i in xrange(0, len(lines)): + status = _status_abbreviations[lines_with_status[i][0]] + effects = _status_effects[status] + if effects: + lines[i] = render_effects(lines[i], *effects) + sys.stdout.write(lines[i] + delimiter) + return retval + +_status_abbreviations = { 'M': 'modified', + 'A': 'added', + 'R': 'removed', + '!': 'deleted', + '?': 'unknown', + 'I': 'ignored', + 'C': 'clean', + ' ': 'copied', } + +_status_effects = { 'modified': ('blue', 'bold'), + 'added': ('green', 'bold'), + 'removed': ('red', 'bold'), + 'deleted': ('cyan', 'bold', 'underline'), + 'unknown': ('magenta', 'bold', 'underline'), + 'ignored': ('black', 'bold'), + 'clean': ('none', ), + 'copied': ('none', ), } + +def colorqseries(qseriesfunc, ui, repo, *dummy, **opts): + '''run the qseries command with colored output''' + ui.pushbuffer() + retval = qseriesfunc(ui, repo, **opts) + patches = ui.popbuffer().splitlines() + for patch in patches: + if opts['missing']: + effects = _patch_effects['missing'] + # Determine if patch is applied. Search for beginning of output + # line in the applied patch list, in case --summary has been used + # and output line isn't just the patch name. + elif [ applied for applied in repo.mq.applied + if patch.startswith(applied.name) ]: + effects = _patch_effects['applied'] + else: + effects = _patch_effects['unapplied'] + sys.stdout.write(render_effects(patch, *effects) + '\n') + return retval + +_patch_effects = { 'applied': ('blue', 'bold', 'underline'), + 'missing': ('red', 'bold'), + 'unapplied': ('black', 'bold'), } + +def uisetup(ui): + '''Initialize the extension.''' + nocoloropt = ('', 'no-color', None, _("don't colorize output")) + _decoratecmd(ui, 'status', commands.table, colorstatus, nocoloropt) + _configcmdeffects(ui, 'status', _status_effects); + if ui.config('extensions', 'hgext.mq', default=None) is not None: + from hgext import mq + _decoratecmd(ui, 'qseries', mq.cmdtable, colorqseries, nocoloropt) + _configcmdeffects(ui, 'qseries', _patch_effects); + +def _decoratecmd(ui, cmd, table, delegate, *delegateoptions): + '''Replace the function that implements cmd in table with a decorator. + + The decorator that becomes the new implementation of cmd calls + delegate. The delegate's first argument is the replaced function, + followed by the normal Mercurial command arguments (ui, repo, ...). If + the delegate adds command options, supply them as delegateoptions. + ''' + cmdkey, cmdentry = _cmdtableitem(ui, cmd, table) + decorator = lambda ui, repo, *args, **opts: \ + _colordecorator(delegate, cmdentry[0], + ui, repo, *args, **opts) + # make sure 'hg help cmd' still works + decorator.__doc__ = cmdentry[0].__doc__ + decoratorentry = (decorator,) + cmdentry[1:] + for option in delegateoptions: + decoratorentry[1].append(option) + table[cmdkey] = decoratorentry + +def _cmdtableitem(ui, cmd, table): + '''Return key, value from table for cmd, or None if not found.''' + aliases, entry = cmdutil.findcmd(ui, cmd, table) + for candidatekey, candidateentry in table.iteritems(): + if candidateentry is entry: + return candidatekey, entry + +def _colordecorator(colorfunc, nocolorfunc, ui, repo, *args, **opts): + '''Delegate to colorfunc or nocolorfunc, depending on conditions. + + Delegate to colorfunc unless --no-color option is set or output is not + to a tty. + ''' + if opts['no_color'] or not sys.stdout.isatty(): + return nocolorfunc(ui, repo, *args, **opts) + return colorfunc(nocolorfunc, ui, repo, *args, **opts) + +def _configcmdeffects(ui, cmdname, effectsmap): + '''Override default effects for cmdname with those from .hgrc file. + + Entries in the .hgrc file are in the [color] section, and look like + 'cmdname'.'status' (for instance, 'status.modified = blue bold inverse'). + ''' + for status in effectsmap: + effects = ui.config('color', cmdname + '.' + status) + if effects: + effectsmap[status] = re.split('\W+', effects)
--- a/hgext/convert/__init__.py Wed Apr 09 15:27:57 2008 -0700 +++ b/hgext/convert/__init__.py Wed Apr 09 15:28:30 2008 -0700 @@ -19,6 +19,8 @@ - Darcs - git - Subversion + - Monotone + - GNU Arch Accepted destination formats: - Mercurial @@ -32,8 +34,8 @@ basename of the source with '-hg' appended. If the destination repository doesn't exist, it will be created. - If <MAPFILE> isn't given, it will be put in a default location - (<dest>/.hg/shamap by default). The <MAPFILE> is a simple text + 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 for that revision, like so: <source ID> <destination ID> @@ -57,7 +59,7 @@ exclude path/to/file rename from/file to/file - + The 'include' directive causes a file, or all files under a directory, to be included in the destination repository, and the exclusion of all other files and dirs not explicitely included. @@ -66,23 +68,61 @@ subdirectory into the root of the repository, use '.' as the path to rename to. - Back end options: + The splicemap is a file that allows insertion of synthetic + history, letting you specify the parents of a revision. This is + useful if you want to e.g. give a Subversion merge two parents, or + graft two disconnected series of history together. Each entry + contains a key, followed by a space, followed by one or two + values, separated by spaces. The key is the revision ID in the + source revision control system whose parents should be modified + (same format as a key in .hg/shamap). The values are the revision + IDs (in either the source or destination revision control system) + that should be used as the new parents for that node. + + Mercurial Source + ----------------- + + --config convert.hg.saverev=True (boolean) + allow target to preserve source revision ID + + Subversion Source + ----------------- + + Subversion source detects classical trunk/branches/tags layouts. + By default, the supplied "svn://repo/path/" source URL is + converted as a single branch. If "svn://repo/path/trunk" exists + it replaces the default branch. If "svn://repo/path/branches" + exists, its subdirectories are listed as possible branches. If + "svn://repo/path/tags" exists, it is looked for tags referencing + converted branches. Default "trunk", "branches" and "tags" values + can be overriden with following options. Set them to paths + relative to the source URL, or leave them blank to disable + autodetection. + + --config convert.svn.branches=branches (directory name) + specify the directory containing branches + --config convert.svn.tags=tags (directory name) + specify the directory containing tags + --config convert.svn.trunk=trunk (directory name) + specify the name of the trunk branch + + Source history can be retrieved starting at a specific revision, + instead of being integrally converted. Only single branch + conversions are supported. + + --config convert.svn.startrev=0 (svn revision number) + specify start Subversion revision. + + Mercurial Destination + --------------------- --config convert.hg.clonebranches=False (boolean) - hg target: XXX not documented - --config convert.hg.saverev=True (boolean) - hg source: allow target to preserve source revision ID + dispatch source branches in separate clones. --config convert.hg.tagsbranch=default (branch name) - hg target: XXX not documented + tag revisions branch name --config convert.hg.usebranchnames=True (boolean) - hg target: preserve branch names + preserve branch names - --config convert.svn.branches=branches (directory name) - svn source: specify the directory containing branches - --config convert.svn.tags=tags (directory name) - svn source: specify the directory containing tags - --config convert.svn.trunk=trunk (directory name) - svn source: specify the name of the trunk branch """ return convcmd.convert(ui, src, dest, revmapfile, **opts) @@ -99,8 +139,9 @@ ('', 'filemap', '', 'remap file names using contents of file'), ('r', 'rev', '', 'import up to target revision REV'), ('s', 'source-type', '', 'source repository type'), + ('', 'splicemap', '', 'splice synthesized history into place'), ('', 'datesort', None, 'try to sort changesets by date')], - 'hg convert [OPTION]... SOURCE [DEST [MAPFILE]]'), + 'hg convert [OPTION]... SOURCE [DEST [REVMAP]]'), "debugsvnlog": (debugsvnlog, [],
--- a/hgext/convert/common.py Wed Apr 09 15:27:57 2008 -0700 +++ b/hgext/convert/common.py Wed Apr 09 15:28:30 2008 -0700 @@ -1,5 +1,6 @@ # common code for the convert extension import base64, errno +import os import cPickle as pickle from mercurial import util from mercurial.i18n import _ @@ -17,10 +18,13 @@ s = base64.decodestring(s) return pickle.loads(s) -def checktool(exe, name=None): +class MissingTool(Exception): pass + +def checktool(exe, name=None, abort=True): name = name or exe if not util.find_exe(exe): - raise util.Abort('cannot find required "%s" tool' % name) + exc = abort and util.Abort or MissingTool + raise exc(_('cannot find required "%s" tool') % name) class NoRepo(Exception): pass @@ -29,8 +33,8 @@ class commit(object): def __init__(self, author, date, desc, parents, branch=None, rev=None, extra={}): - self.author = author - self.date = date + self.author = author or 'unknown' + self.date = date or '0 0' self.desc = desc self.parents = parents self.branch = branch @@ -104,13 +108,13 @@ def getchangedfiles(self, rev, i): """Return the files changed by rev compared to parent[i]. - + i is an index selecting one of the parents of rev. The return value should be the list of files that are different in rev and this parent. If rev has no parents, i is None. - + This function is only needed to support --filemap """ raise NotImplementedError() @@ -176,12 +180,11 @@ tags: {tagname: sink_rev_id, ...}""" raise NotImplementedError() - def setbranch(self, branch, pbranch, parents): + def setbranch(self, branch, pbranches): """Set the current branch name. Called before the first putfile on the branch. branch: branch name for subsequent commits - pbranch: branch name of parent commit - parents: destination revisions of parent""" + pbranches: (converted parent revision, parent branch) tuples""" pass def setfilemapmode(self, active): @@ -212,7 +215,7 @@ def postrun(self): pass - def _run(self, cmd, *args, **kwargs): + def _cmdline(self, cmd, *args, **kwargs): cmdline = [self.command, cmd] + list(args) for k, v in kwargs.iteritems(): if len(k) == 1: @@ -227,10 +230,13 @@ except TypeError: pass cmdline = [util.shellquote(arg) for arg in cmdline] - cmdline += ['<', util.nulldev] + cmdline += ['2>', util.nulldev, '<', util.nulldev] cmdline = ' '.join(cmdline) self.ui.debug(cmdline, '\n') + return cmdline + def _run(self, cmd, *args, **kwargs): + cmdline = self._cmdline(cmd, *args, **kwargs) self.prerun() try: return util.popen(cmdline) @@ -243,6 +249,12 @@ self.ui.debug(output) return output, fp.close() + def runlines(self, cmd, *args, **kwargs): + fp = self._run(cmd, *args, **kwargs) + output = fp.readlines() + self.ui.debug(''.join(output)) + return output, fp.close() + def checkexit(self, status, output=''): if status: if output: @@ -256,6 +268,52 @@ self.checkexit(status, output) return output + def runlines0(self, cmd, *args, **kwargs): + output, status = self.runlines(cmd, *args, **kwargs) + self.checkexit(status, ''.join(output)) + return output + + def getargmax(self): + if '_argmax' in self.__dict__: + return self._argmax + + # POSIX requires at least 4096 bytes for ARG_MAX + self._argmax = 4096 + try: + self._argmax = os.sysconf("SC_ARG_MAX") + except: + pass + + # Windows shells impose their own limits on command line length, + # down to 2047 bytes for cmd.exe under Windows NT/2k and 2500 bytes + # for older 4nt.exe. See http://support.microsoft.com/kb/830473 for + # details about cmd.exe limitations. + + # Since ARG_MAX is for command line _and_ environment, lower our limit + # (and make happy Windows shells while doing this). + + self._argmax = self._argmax/2 - 1 + return self._argmax + + def limit_arglist(self, arglist, cmd, *args, **kwargs): + limit = self.getargmax() - len(self._cmdline(cmd, *args, **kwargs)) + bytes = 0 + fl = [] + for fn in arglist: + b = len(fn) + 3 + if bytes + b < limit or len(fl) == 0: + fl.append(fn) + bytes += b + else: + yield fl + fl = [fn] + bytes = b + if fl: + yield fl + + def xargs(self, arglist, cmd, *args, **kwargs): + for l in self.limit_arglist(arglist, cmd, *args, **kwargs): + self.run0(cmd, *(list(args) + l), **kwargs) class mapfile(dict): def __init__(self, ui, path): @@ -267,6 +325,8 @@ self._read() def _read(self): + if self.path is None: + return try: fp = open(self.path, 'r') except IOError, err: @@ -279,7 +339,7 @@ self.order.append(key) super(mapfile, self).__setitem__(key, value) fp.close() - + def __setitem__(self, key, value): if self.fp is None: try:
--- a/hgext/convert/convcmd.py Wed Apr 09 15:27:57 2008 -0700 +++ b/hgext/convert/convcmd.py Wed Apr 09 15:28:30 2008 -0700 @@ -5,24 +5,36 @@ # This software may be used and distributed according to the terms # of the GNU General Public License, incorporated herein by reference. -from common import NoRepo, SKIPREV, converter_source, converter_sink, mapfile +from common import NoRepo, MissingTool, SKIPREV, mapfile from cvs import convert_cvs from darcs import darcs_source from git import convert_git from hg import mercurial_source, mercurial_sink from subversion import debugsvnlog, svn_source, svn_sink +from monotone import monotone_source +from gnuarch import gnuarch_source import filemap import os, shutil from mercurial import hg, util from mercurial.i18n import _ +orig_encoding = 'ascii' + +def recode(s): + if isinstance(s, unicode): + return s.encode(orig_encoding, 'replace') + else: + return s.decode('utf-8').encode(orig_encoding, 'replace') + source_converters = [ ('cvs', convert_cvs), ('git', convert_git), ('svn', svn_source), ('hg', mercurial_source), ('darcs', darcs_source), + ('mtn', monotone_source), + ('gnuarch', gnuarch_source), ] sink_converters = [ @@ -36,7 +48,7 @@ try: if not type or name == type: return source(ui, path, rev) - except NoRepo, inst: + except (NoRepo, MissingTool), inst: exceptions.append(inst) if not ui.quiet: for inst in exceptions: @@ -74,6 +86,8 @@ self.readauthormap(opts.get('authors')) self.authorfile = self.dest.authorfile() + self.splicemap = mapfile(ui, opts.get('splicemap')) + def walktree(self, heads): '''Return a mapping that identifies the uncommitted parents of every uncommitted changeset.''' @@ -98,6 +112,7 @@ visit = parents.keys() seen = {} children = {} + actives = [] while visit: n = visit.pop(0) @@ -106,49 +121,63 @@ # Ensure that nodes without parents are present in the 'children' # mapping. children.setdefault(n, []) + hasparent = False for p in parents[n]: if not p in self.map: visit.append(p) + hasparent = True children.setdefault(p, []).append(n) + if not hasparent: + actives.append(n) + + del seen + del visit + + if self.opts.get('datesort'): + dates = {} + def getdate(n): + if n not in dates: + dates[n] = util.parsedate(self.commitcache[n].date) + return dates[n] + + def picknext(nodes): + return min([(getdate(n), n) for n in nodes])[1] + else: + prev = [None] + def picknext(nodes): + # Return the first eligible child of the previously converted + # revision, or any of them. + next = nodes[0] + for n in nodes: + if prev[0] in parents[n]: + next = n + break + prev[0] = next + return next s = [] - removed = {} - visit = children.keys() - while visit: - n = visit.pop(0) - if n in removed: continue - dep = 0 - if n in parents: - for p in parents[n]: - if p in self.map: continue - if p not in removed: - # we're still dependent - visit.append(n) - dep = 1 - break + pendings = {} + while actives: + n = picknext(actives) + actives.remove(n) + s.append(n) - if not dep: - # all n's parents are in the list - removed[n] = 1 - if n not in self.map: - s.append(n) - if n in children: - for c in children[n]: - visit.insert(0, c) + # Update dependents list + for c in children.get(n, []): + if c not in pendings: + pendings[c] = [p for p in parents[c] if p not in self.map] + try: + pendings[c].remove(n) + except ValueError: + raise util.Abort(_('cycle detected between %s and %s') + % (recode(c), recode(n))) + if not pendings[c]: + # Parents are converted, node is eligible + actives.insert(0, c) + pendings[c] = None - if self.opts.get('datesort'): - depth = {} - for n in s: - depth[n] = 0 - pl = [p for p in self.commitcache[n].parents - if p not in self.map] - if pl: - depth[n] = max([depth[p] for p in pl]) + 1 - - s = [(depth[n], util.parsedate(self.commitcache[n].date), n) - for n in s] - s.sort() - s = [e[2] for e in s] + if len(s) != len(parents): + raise util.Abort(_("not all revisions were sorted")) return s @@ -164,9 +193,12 @@ def readauthormap(self, authorfile): afile = open(authorfile, 'r') for line in afile: + if line.strip() == '': + continue try: - srcauthor = line.split('=')[0].strip() - dstauthor = line.split('=')[1].strip() + srcauthor, dstauthor = line.split('=', 1) + srcauthor = srcauthor.strip() + dstauthor = dstauthor.strip() if srcauthor in self.authors and dstauthor != self.authors[srcauthor]: self.ui.status( 'Overriding mapping for author %s, was %s, will be %s\n' @@ -177,8 +209,8 @@ self.authors[srcauthor] = dstauthor except IndexError: self.ui.warn( - 'Ignoring bad line in author file map %s: %s\n' - % (authorfile, line)) + 'Ignoring bad line in author map file %s: %s\n' + % (authorfile, line.rstrip())) afile.close() def cachecommit(self, rev): @@ -201,15 +233,14 @@ self.map[rev] = dest return files, copies = changes - parents = [self.map[r] for r in commit.parents] + pbranches = [] if commit.parents: - prev = commit.parents[0] - if prev not in self.commitcache: - self.cachecommit(prev) - pbranch = self.commitcache[prev].branch - else: - pbranch = None - self.dest.setbranch(commit.branch, pbranch, parents) + for prev in commit.parents: + if prev not in self.commitcache: + self.cachecommit(prev) + pbranches.append((self.map[prev], + self.commitcache[prev].branch)) + self.dest.setbranch(commit.branch, pbranches) for f, v in files: filenames.append(f) try: @@ -225,11 +256,19 @@ # Merely marks that a copy happened. self.dest.copyfile(copyf, f) + try: + parents = self.splicemap[rev].replace(',', ' ').split() + self.ui.status('spliced in %s as parents of %s\n' % + (parents, rev)) + parents = [self.map.get(p, p) for p in parents] + except KeyError: + parents = [b[0] for b in pbranches] newnode = self.dest.putcommit(filenames, parents, commit) self.source.converted(rev, newnode) self.map[rev] = newnode def convert(self): + try: self.source.before() self.dest.before() @@ -248,7 +287,11 @@ desc = self.commitcache[c].desc if "\n" in desc: desc = desc.splitlines()[0] - self.ui.status("%d %s\n" % (num, desc)) + # convert log message to local encoding without using + # tolocal() because util._encoding conver() use it as + # 'utf-8' + self.ui.status("%d %s\n" % (num, recode(desc))) + self.ui.note(_("source: %s\n" % recode(c))) self.copy(c) tags = self.source.gettags() @@ -277,6 +320,8 @@ self.map.close() def convert(ui, src, dest=None, revmapfile=None, **opts): + global orig_encoding + orig_encoding = util._encoding util._encoding = 'UTF-8' if not dest:
--- a/hgext/convert/cvs.py Wed Apr 09 15:27:57 2008 -0700 +++ b/hgext/convert/cvs.py Wed Apr 09 15:28:30 2008 -0700 @@ -14,7 +14,9 @@ if not os.path.exists(cvs): raise NoRepo("%s does not look like a CVS checkout" % path) - for tool in ('cvsps', 'cvs'): + self.cmd = ui.config('convert', 'cvsps', 'cvsps -A -u --cvs-direct -q') + cvspsexe = self.cmd.split(None, 1)[0] + for tool in (cvspsexe, 'cvs'): checktool(tool) self.changeset = {} @@ -34,7 +36,7 @@ return maxrev = 0 - cmd = 'cvsps -A -u --cvs-direct -q' + cmd = self.cmd if self.rev: # TODO: handle tags try: @@ -53,11 +55,13 @@ os.chdir(self.path) id = None state = 0 + filerevids = {} for l in util.popen(cmd): if state == 0: # header if l.startswith("PatchSet"): id = l[9:-2] if maxrev and int(id) > maxrev: + # ignore everything state = 3 elif l.startswith("Date"): date = util.parsedate(l[6:-1], ["%Y/%m/%d %H:%M:%S"]) @@ -68,6 +72,7 @@ self.lastbranch[branch] = id elif l.startswith("Ancestor branch"): ancestor = l[17:-1] + # figure out the parent later self.parent[id] = self.lastbranch[ancestor] elif l.startswith("Author"): author = self.recode(l[8:-1]) @@ -77,23 +82,37 @@ if (len(t) > 1) or (t[0] and (t[0] != "(none)")): self.tags.update(dict.fromkeys(t, id)) elif l.startswith("Log:"): + # switch to gathering log state = 1 log = "" elif state == 1: # log if l == "Members: \n": + # switch to gathering members files = {} + oldrevs = [] log = self.recode(log[:-1]) state = 2 else: + # gather log log += l - elif state == 2: - if l == "\n": # + elif state == 2: # members + if l == "\n": # start of next entry state = 0 p = [self.parent[id]] if id == "1": p = [] if branch == "HEAD": branch = "" + if branch: + latest = None + # the last changeset that contains a base + # file is our parent + for r in oldrevs: + latest = max(filerevids.get(r, None), latest) + if latest: + p = [latest] + + # add current commit to set c = commit(author=author, date=date, parents=p, desc=log, branch=branch) self.changeset[id] = c @@ -102,9 +121,14 @@ colon = l.rfind(':') file = l[1:colon] rev = l[colon+1:-2] - rev = rev.split("->")[1] + oldrev, rev = rev.split("->") files[file] = rev + + # save some information for identifying branch points + oldrevs.append("%s:%s" % (oldrev, file)) + filerevids["%s:%s" % (rev, file)] = id elif state == 3: + # swallow all input continue self.heads = self.lastbranch.values() @@ -179,7 +203,7 @@ if conntype != "pserver": if conntype == "rsh": - rsh = os.environ.get("CVS_RSH" or "rsh") + rsh = os.environ.get("CVS_RSH") or "ssh" if user: cmd = [rsh, '-l', user, host] + cmd else:
--- a/hgext/convert/filemap.py Wed Apr 09 15:27:57 2008 -0700 +++ b/hgext/convert/filemap.py Wed Apr 09 15:28:30 2008 -0700 @@ -10,6 +10,7 @@ from common import SKIPREV, converter_source def rpairs(name): + yield '.', name e = len(name) while e != -1: yield name[:e], name[e+1:] @@ -128,6 +129,12 @@ self.children = {} self.seenchildren = {} + def before(self): + self.base.before() + + def after(self): + self.base.after() + def setrevmap(self, revmap): # rebuild our state to make things restartable #
--- a/hgext/convert/git.py Wed Apr 09 15:27:57 2008 -0700 +++ b/hgext/convert/git.py Wed Apr 09 15:28:30 2008 -0700 @@ -102,7 +102,6 @@ tzs, tzh, tzm = tz[-5:-4] + "1", tz[-4:-2], tz[-2:] tz = -int(tzs) * (int(tzh) * 3600 + int(tzm)) date = tm + " " + str(tz) - author = author or "unknown" c = commit(parents=parents, date=date, author=author, desc=message, rev=version)
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/hgext/convert/gnuarch.py Wed Apr 09 15:28:30 2008 -0700 @@ -0,0 +1,301 @@ +# GNU Arch support for the convert extension + +from common import NoRepo, commandline, commit, converter_source +from mercurial.i18n import _ +from mercurial import util +import os, shutil, tempfile, stat + +class gnuarch_source(converter_source, commandline): + + class gnuarch_rev: + def __init__(self, rev): + self.rev = rev + self.summary = '' + self.date = None + self.author = '' + self.add_files = [] + self.mod_files = [] + self.del_files = [] + self.ren_files = {} + self.ren_dirs = {} + + def __init__(self, ui, path, rev=None): + super(gnuarch_source, self).__init__(ui, path, rev=rev) + + if not os.path.exists(os.path.join(path, '{arch}')): + raise NoRepo(_("%s does not look like a GNU Arch repo" % path)) + + # Could use checktool, but we want to check for baz or tla. + self.execmd = None + if util.find_exe('baz'): + self.execmd = 'baz' + else: + if util.find_exe('tla'): + self.execmd = 'tla' + else: + raise util.Abort(_('cannot find a GNU Arch tool')) + + commandline.__init__(self, ui, self.execmd) + + self.path = os.path.realpath(path) + self.tmppath = None + + self.treeversion = None + self.lastrev = None + self.changes = {} + self.parents = {} + self.tags = {} + self.modecache = {} + + def before(self): + if self.execmd == 'tla': + output = self.run0('tree-version', self.path) + else: + output = self.run0('tree-version', '-d', self.path) + self.treeversion = output.strip() + + self.ui.status(_('analyzing tree version %s...\n' % self.treeversion)) + + # Get name of temporary directory + version = self.treeversion.split('/') + self.tmppath = os.path.join(tempfile.gettempdir(), + 'hg-%s' % version[1]) + + # Generate parents dictionary + child = [] + output, status = self.runlines('revisions', self.treeversion) + self.checkexit(status, 'archive registered?') + for l in output: + rev = l.strip() + self.changes[rev] = self.gnuarch_rev(rev) + + # Read author, date and summary + catlog = self.runlines0('cat-log', '-d', self.path, rev) + self._parsecatlog(catlog, rev) + + self.parents[rev] = child + child = [rev] + if rev == self.rev: + break + self.parents[None] = child + + def after(self): + self.ui.debug(_('cleaning up %s\n' % self.tmppath)) + shutil.rmtree(self.tmppath, ignore_errors=True) + + def getheads(self): + return self.parents[None] + + def getfile(self, name, rev): + if rev != self.lastrev: + raise util.Abort(_('internal calling inconsistency')) + + # Raise IOError if necessary (i.e. deleted files). + if not os.path.exists(os.path.join(self.tmppath, name)): + raise IOError + + data, mode = self._getfile(name, rev) + self.modecache[(name, rev)] = mode + + return data + + def getmode(self, name, rev): + return self.modecache[(name, rev)] + + def getchanges(self, rev): + self.modecache = {} + self._update(rev) + changes = [] + copies = {} + + for f in self.changes[rev].add_files: + changes.append((f, rev)) + + for f in self.changes[rev].mod_files: + changes.append((f, rev)) + + for f in self.changes[rev].del_files: + changes.append((f, rev)) + + for src in self.changes[rev].ren_files: + to = self.changes[rev].ren_files[src] + changes.append((src, rev)) + changes.append((to, rev)) + copies[src] = to + + for src in self.changes[rev].ren_dirs: + to = self.changes[rev].ren_dirs[src] + chgs, cps = self._rendirchanges(src, to); + changes += [(f, rev) for f in chgs] + for c in cps: + copies[c] = cps[c] + + changes.sort() + self.lastrev = rev + + return changes, copies + + def getcommit(self, rev): + changes = self.changes[rev] + return commit(author = changes.author, date = changes.date, + desc = changes.summary, parents = self.parents[rev]) + + def gettags(self): + return self.tags + + def _execute(self, cmd, *args, **kwargs): + cmdline = [self.execmd, cmd] + cmdline += args + cmdline = [util.shellquote(arg) for arg in cmdline] + cmdline += ['>', util.nulldev, '2>', util.nulldev] + cmdline = util.quotecommand(' '.join(cmdline)) + self.ui.debug(cmdline, '\n') + return os.system(cmdline) + + def _update(self, rev): + if rev == 'base-0': + # Initialise 'base-0' revision + self._obtainrevision(rev) + else: + self.ui.debug(_('applying revision %s...\n' % rev)) + revision = '%s--%s' % (self.treeversion, rev) + changeset, status = self.runlines('replay', '-d', self.tmppath, + revision) + if status: + # Something went wrong while merging (baz or tla + # issue?), get latest revision and try from there + shutil.rmtree(self.tmppath, ignore_errors=True) + self._obtainrevision(rev) + else: + old_rev = self.parents[rev][0] + self.ui.debug(_('computing changeset between %s and %s...\n' \ + % (old_rev, rev))) + rev_a = '%s--%s' % (self.treeversion, old_rev) + rev_b = '%s--%s' % (self.treeversion, rev) + self._parsechangeset(changeset, rev) + + def _getfile(self, name, rev): + mode = os.lstat(os.path.join(self.tmppath, name)).st_mode + if stat.S_ISLNK(mode): + data = os.readlink(os.path.join(self.tmppath, name)) + mode = mode and 'l' or '' + else: + data = open(os.path.join(self.tmppath, name), 'rb').read() + mode = (mode & 0111) and 'x' or '' + return data, mode + + def _exclude(self, name): + exclude = [ '{arch}', '.arch-ids', '.arch-inventory' ] + for exc in exclude: + if name.find(exc) != -1: + return True + return False + + def _readcontents(self, path): + files = [] + contents = os.listdir(path) + while len(contents) > 0: + c = contents.pop() + p = os.path.join(path, c) + # os.walk could be used, but here we avoid internal GNU + # Arch files and directories, thus saving a lot time. + if not self._exclude(p): + if os.path.isdir(p): + contents += [os.path.join(c, f) for f in os.listdir(p)] + else: + files.append(c) + return files + + def _rendirchanges(self, src, dest): + changes = [] + copies = {} + files = self._readcontents(os.path.join(self.tmppath, dest)) + for f in files: + s = os.path.join(src, f) + d = os.path.join(dest, f) + changes.append(s) + changes.append(d) + copies[s] = d + return changes, copies + + def _obtainrevision(self, rev): + self.ui.debug(_('obtaining revision %s...\n' % rev)) + revision = '%s--%s' % (self.treeversion, rev) + output = self._execute('get', revision, self.tmppath) + self.checkexit(output) + self.ui.debug(_('analysing revision %s...\n' % rev)) + files = self._readcontents(self.tmppath) + self.changes[rev].add_files += files + + def _stripbasepath(self, path): + if path.startswith('./'): + return path[2:] + return path + + def _parsecatlog(self, data, rev): + summary = [] + for l in data: + l = l.strip() + if summary: + summary.append(l) + elif l.startswith('Summary:'): + summary.append(l[len('Summary: '):]) + elif l.startswith('Standard-date:'): + date = l[len('Standard-date: '):] + strdate = util.strdate(date, '%Y-%m-%d %H:%M:%S') + self.changes[rev].date = util.datestr(strdate) + elif l.startswith('Creator:'): + self.changes[rev].author = l[len('Creator: '):] + self.changes[rev].summary = '\n'.join(summary) + + def _parsechangeset(self, data, rev): + for l in data: + l = l.strip() + # Added file (ignore added directory) + if l.startswith('A') and not l.startswith('A/'): + file = self._stripbasepath(l[1:].strip()) + if not self._exclude(file): + self.changes[rev].add_files.append(file) + # Deleted file (ignore deleted directory) + elif l.startswith('D') and not l.startswith('D/'): + file = self._stripbasepath(l[1:].strip()) + if not self._exclude(file): + self.changes[rev].del_files.append(file) + # Modified binary file + elif l.startswith('Mb'): + file = self._stripbasepath(l[2:].strip()) + if not self._exclude(file): + self.changes[rev].mod_files.append(file) + # Modified link + elif l.startswith('M->'): + file = self._stripbasepath(l[3:].strip()) + if not self._exclude(file): + self.changes[rev].mod_files.append(file) + # Modified file + elif l.startswith('M'): + file = self._stripbasepath(l[1:].strip()) + if not self._exclude(file): + self.changes[rev].mod_files.append(file) + # Renamed file (or link) + elif l.startswith('=>'): + files = l[2:].strip().split(' ') + if len(files) == 1: + files = l[2:].strip().split('\t') + src = self._stripbasepath(files[0]) + dst = self._stripbasepath(files[1]) + if not self._exclude(src) and not self._exclude(dst): + self.changes[rev].ren_files[src] = dst + # Conversion from file to link or from link to file (modified) + elif l.startswith('ch'): + file = self._stripbasepath(l[2:].strip()) + if not self._exclude(file): + self.changes[rev].mod_files.append(file) + # Renamed directory + elif l.startswith('/>'): + dirs = l[2:].strip().split(' ') + if len(dirs) == 1: + dirs = l[2:].strip().split('\t') + src = self._stripbasepath(dirs[0]) + dst = self._stripbasepath(dirs[1]) + if not self._exclude(src) and not self._exclude(dst): + self.changes[rev].ren_dirs[src] = dst
--- a/hgext/convert/hg.py Wed Apr 09 15:27:57 2008 -0700 +++ b/hgext/convert/hg.py Wed Apr 09 15:28:30 2008 -0700 @@ -15,8 +15,9 @@ import os, time from mercurial.i18n import _ -from mercurial.node import * -from mercurial import hg, lock, revlog, util +from mercurial.repo import RepoError +from mercurial.node import bin, hex, nullid +from mercurial import hg, revlog, util from common import NoRepo, commit, converter_source, converter_sink @@ -30,15 +31,19 @@ if os.path.isdir(path) and len(os.listdir(path)) > 0: try: self.repo = hg.repository(self.ui, path) - except hg.RepoError, err: + if not self.repo.local(): + raise NoRepo(_('%s is not a local Mercurial repo') % path) + except RepoError, err: ui.print_exc() raise NoRepo(err.args[0]) else: try: ui.status(_('initializing destination %s repository\n') % path) self.repo = hg.repository(self.ui, path, create=True) + if not self.repo.local(): + raise NoRepo(_('%s is not a local Mercurial repo') % path) self.created.append(path) - except hg.RepoError, err: + except RepoError, err: ui.print_exc() raise NoRepo("could not create hg repo %s as sink" % path) self.lock = None @@ -46,11 +51,13 @@ self.filemapmode = False def before(self): + self.ui.debug(_('run hg sink pre-conversion action\n')) self.wlock = self.repo.wlock() self.lock = self.repo.lock() self.repo.dirstate.clear() def after(self): + self.ui.debug(_('run hg sink post-conversion action\n')) self.repo.dirstate.invalidate() self.lock = None self.wlock = None @@ -80,30 +87,43 @@ except OSError: pass - def setbranch(self, branch, pbranch, parents): - if (not self.clonebranches) or (branch == self.lastbranch): + def setbranch(self, branch, pbranches): + if not self.clonebranches: return + setbranch = (branch != self.lastbranch) self.lastbranch = branch - self.after() if not branch: branch = 'default' - if not pbranch: - pbranch = 'default' + pbranches = [(b[0], b[1] and b[1] or 'default') for b in pbranches] + pbranch = pbranches and pbranches[0][1] or 'default' branchpath = os.path.join(self.path, branch) - try: - self.repo = hg.repository(self.ui, branchpath) - except: - if not parents: + if setbranch: + self.after() + try: + self.repo = hg.repository(self.ui, branchpath) + except: self.repo = hg.repository(self.ui, branchpath, create=True) - else: - self.ui.note(_('cloning branch %s to %s\n') % (pbranch, branch)) - hg.clone(self.ui, os.path.join(self.path, pbranch), - branchpath, rev=parents, update=False, - stream=True) - self.repo = hg.repository(self.ui, branchpath) - self.before() + self.before() + + # pbranches may bring revisions from other branches (merge parents) + # Make sure we have them, or pull them. + missings = {} + for b in pbranches: + try: + self.repo.lookup(b[0]) + except: + missings.setdefault(b[1], []).append(b[0]) + + if missings: + self.after() + for pbranch, heads in missings.iteritems(): + pbranchpath = os.path.join(self.path, pbranch) + prepo = hg.repository(self.ui, pbranchpath) + self.ui.note(_('pulling from %s into %s\n') % (pbranch, branch)) + self.repo.pull(prepo, [prepo.lookup(h) for h in heads]) + self.before() def putcommit(self, files, parents, commit): seen = {} @@ -136,7 +156,7 @@ bin(p1), bin(p2), extra=extra) self.repo.dirstate.clear() text = "(octopus merge fixup)\n" - p2 = hg.hex(self.repo.changelog.tip()) + p2 = hex(self.repo.changelog.tip()) if self.filemapmode and nparents == 1: man = self.repo.manifest @@ -175,10 +195,10 @@ extra['branch'] = self.tagsbranch try: tagparent = self.repo.changectx(self.tagsbranch).node() - except hg.RepoError, inst: + except RepoError, inst: tagparent = nullid self.repo.rawcommit([".hgtags"], "update tags", "convert-repo", - date, tagparent, nullid) + date, tagparent, nullid, extra=extra) return hex(self.repo.changelog.tip()) def setfilemapmode(self, active): @@ -193,8 +213,8 @@ # try to provoke an exception if this isn't really a hg # repo, but some other bogus compatible-looking url if not self.repo.local(): - raise hg.RepoError() - except hg.RepoError: + raise RepoError() + except RepoError: ui.print_exc() raise NoRepo("%s is not a local Mercurial repo" % path) self.lastrev = None @@ -274,3 +294,9 @@ 'a') self.convertfp.write('%s %s\n' % (destrev, rev)) self.convertfp.flush() + + def before(self): + self.ui.debug(_('run hg source pre-conversion action\n')) + + def after(self): + self.ui.debug(_('run hg source post-conversion action\n'))
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/hgext/convert/monotone.py Wed Apr 09 15:28:30 2008 -0700 @@ -0,0 +1,198 @@ +# monotone support for the convert extension + +import os, re, time +from mercurial import util +from common import NoRepo, MissingTool, commit, converter_source, checktool +from common import commandline +from mercurial.i18n import _ + +class monotone_source(converter_source, commandline): + def __init__(self, ui, path=None, rev=None): + converter_source.__init__(self, ui, path, rev) + commandline.__init__(self, ui, 'mtn') + + self.ui = ui + self.path = path + + # regular expressions for parsing monotone output + space = r'\s*' + name = r'\s+"((?:[^"]|\\")*)"\s*' + value = name + revision = r'\s+\[(\w+)\]\s*' + lines = r'(?:.|\n)+' + + self.dir_re = re.compile(space + "dir" + name) + self.file_re = re.compile(space + "file" + name + "content" + revision) + self.add_file_re = re.compile(space + "add_file" + name + "content" + revision) + self.patch_re = re.compile(space + "patch" + name + "from" + revision + "to" + revision) + self.rename_re = re.compile(space + "rename" + name + "to" + name) + self.delete_re = re.compile(space + "delete" + name) + self.tag_re = re.compile(space + "tag" + name + "revision" + revision) + self.cert_re = re.compile(lines + space + "name" + name + "value" + value) + + attr = space + "file" + lines + space + "attr" + space + self.attr_execute_re = re.compile(attr + '"mtn:execute"' + space + '"true"') + + # cached data + self.manifest_rev = None + self.manifest = None + self.files = None + self.dirs = None + + norepo = NoRepo (_("%s does not look like a monotone repo") % path) + if not os.path.exists(path): + raise norepo + + checktool('mtn', abort=False) + + # test if there are any revisions + self.rev = None + try: + self.getheads() + except: + raise norepo + self.rev = rev + + def mtnrun(self, *args, **kwargs): + kwargs['d'] = self.path + return self.run0('automate', *args, **kwargs) + + def mtnloadmanifest(self, rev): + if self.manifest_rev == rev: + return + self.manifest = self.mtnrun("get_manifest_of", rev).split("\n\n") + self.manifest_rev = rev + self.files = {} + self.dirs = {} + + for e in self.manifest: + m = self.file_re.match(e) + if m: + attr = "" + name = m.group(1) + node = m.group(2) + if self.attr_execute_re.match(e): + attr += "x" + self.files[name] = (node, attr) + m = self.dir_re.match(e) + if m: + self.dirs[m.group(1)] = True + + def mtnisfile(self, name, rev): + # a non-file could be a directory or a deleted or renamed file + self.mtnloadmanifest(rev) + try: + self.files[name] + return True + except KeyError: + return False + + def mtnisdir(self, name, rev): + self.mtnloadmanifest(rev) + try: + self.dirs[name] + return True + except KeyError: + return False + + def mtngetcerts(self, rev): + certs = {"author":"<missing>", "date":"<missing>", + "changelog":"<missing>", "branch":"<missing>"} + cert_list = self.mtnrun("certs", rev).split("\n\n") + for e in cert_list: + m = self.cert_re.match(e) + if m: + certs[m.group(1)] = m.group(2) + return certs + + def mtnrenamefiles(self, files, fromdir, todir): + renamed = {} + for tofile in files: + suffix = tofile.lstrip(todir) + if todir + suffix == tofile: + renamed[tofile] = (fromdir + suffix).lstrip("/") + return renamed + + + # implement the converter_source interface: + + def getheads(self): + if not self.rev: + return self.mtnrun("leaves").splitlines() + else: + return [self.rev] + + def getchanges(self, rev): + #revision = self.mtncmd("get_revision %s" % rev).split("\n\n") + revision = self.mtnrun("get_revision", rev).split("\n\n") + files = {} + copies = {} + for e in revision: + m = self.add_file_re.match(e) + if m: + files[m.group(1)] = rev + m = self.patch_re.match(e) + if m: + files[m.group(1)] = rev + + # Delete/rename is handled later when the convert engine + # discovers an IOError exception from getfile, + # but only if we add the "from" file to the list of changes. + m = self.delete_re.match(e) + if m: + files[m.group(1)] = rev + m = self.rename_re.match(e) + if m: + toname = m.group(2) + fromname = m.group(1) + if self.mtnisfile(toname, rev): + copies[toname] = fromname + files[toname] = rev + files[fromname] = rev + if self.mtnisdir(toname, rev): + renamed = self.mtnrenamefiles(self.files, fromname, toname) + for tofile, fromfile in renamed.items(): + self.ui.debug (_("copying file in renamed dir from '%s' to '%s'") % (fromfile, tofile), '\n') + files[tofile] = rev + for fromfile in renamed.values(): + files[fromfile] = rev + return (files.items(), copies) + + def getmode(self, name, rev): + self.mtnloadmanifest(rev) + try: + node, attr = self.files[name] + return attr + except KeyError: + return "" + + def getfile(self, name, rev): + if not self.mtnisfile(name, rev): + raise IOError() # file was deleted or renamed + try: + return self.mtnrun("get_file_of", name, r=rev) + except: + raise IOError() # file was deleted or renamed + + def getcommit(self, rev): + certs = self.mtngetcerts(rev) + return commit( + author=certs["author"], + date=util.datestr(util.strdate(certs["date"], "%Y-%m-%dT%H:%M:%S")), + desc=certs["changelog"], + rev=rev, + parents=self.mtnrun("parents", rev).splitlines(), + branch=certs["branch"]) + + def gettags(self): + tags = {} + for e in self.mtnrun("tags").split("\n\n"): + m = self.tag_re.match(e) + if m: + tags[m.group(1)] = m.group(2) + return tags + + def getchangedfiles(self, rev, i): + # This function is only needed to support --filemap + # ... and we don't support that + raise NotImplementedError()
--- a/hgext/convert/subversion.py Wed Apr 09 15:27:57 2008 -0700 +++ b/hgext/convert/subversion.py Wed Apr 09 15:28:30 2008 -0700 @@ -51,7 +51,10 @@ except SubversionException: pass if os.path.isdir(path): - return 'file://%s' % os.path.normpath(os.path.abspath(path)) + path = os.path.normpath(os.path.abspath(path)) + if os.name == 'nt': + path = '/' + util.normpath(path) + return 'file://%s' % path return path def optrev(number): @@ -86,9 +89,16 @@ receiver) except SubversionException, (inst, num): pickle.dump(num, fp, protocol) + except IOError: + # Caller may interrupt the iteration + pickle.dump(None, fp, protocol) else: pickle.dump(None, fp, protocol) fp.close() + # With large history, cleanup process goes crazy and suddenly + # consumes *huge* amount of memory. The output file being closed, + # there is no need for clean termination. + os._exit(0) def debugsvnlog(ui, **opts): """Fetch SVN log in a subprocess and channel them back to parent to @@ -99,7 +109,53 @@ args = decodeargs(sys.stdin.read()) get_log_child(sys.stdout, *args) +class logstream: + """Interruptible revision log iterator.""" + def __init__(self, stdout): + self._stdout = stdout + + def __iter__(self): + while True: + entry = pickle.load(self._stdout) + try: + orig_paths, revnum, author, date, message = entry + except: + if entry is None: + break + raise SubversionException("child raised exception", entry) + yield entry + + def close(self): + if self._stdout: + self._stdout.close() + self._stdout = None + +def get_log(url, paths, start, end, limit=0, discover_changed_paths=True, + strict_node_history=False): + args = [url, paths, start, end, limit, discover_changed_paths, + strict_node_history] + arg = encodeargs(args) + hgexe = util.hgexecutable() + cmd = '%s debugsvnlog' % util.shellquote(hgexe) + stdin, stdout = os.popen2(cmd, 'b') + stdin.write(arg) + stdin.close() + return logstream(stdout) + # SVN conversion code stolen from bzr-svn and tailor +# +# Subversion looks like a versioned filesystem, branches structures +# are defined by conventions and not enforced by the tool. First, +# we define the potential branches (modules) as "trunk" and "branches" +# children directories. Revisions are then identified by their +# module and revision number (and a repository identifier). +# +# The revision graph is really a tree (or a forest). By default, a +# revision parent is the previous revision in the same module. If the +# module directory is copied/moved from another module then the +# revision is the module root and its parent the source revision in +# the parent module. A revision has at most one parent. +# class svn_source(converter_source): def __init__(self, ui, url, rev=None): super(svn_source, self).__init__(ui, url, rev=rev) @@ -130,7 +186,7 @@ self.ctx = self.transport.client self.base = svn.ra.get_repos_root(self.ra) self.module = self.url[len(self.base):] - self.modulemap = {} # revision, module + self.rootmodule = self.module self.commits = {} self.paths = {} self.uuid = svn.ra.get_uuid(self.ra).decode(self.encoding) @@ -144,14 +200,26 @@ except ValueError: raise util.Abort('svn: revision %s is not an integer' % rev) + self.startrev = self.ui.config('convert', 'svn.startrev', default=0) + try: + self.startrev = int(self.startrev) + if self.startrev < 0: + self.startrev = 0 + except ValueError: + raise util.Abort(_('svn: start revision %s is not an integer') + % self.startrev) + try: self.get_blacklist() except IOError, e: pass - self.last_changed = self.latest(self.module, latest) + self.head = self.latest(self.module, latest) + if not self.head: + raise util.Abort(_('no revision found in module %s') % + self.module.encode(self.encoding)) + self.last_changed = self.revnum(self.head) - self.head = self.revid(self.last_changed) self._changescache = None if os.path.exists(os.path.join(url, '.svn/entries')): @@ -178,46 +246,72 @@ return False def getheads(self): - # detect standard /branches, /tags, /trunk layout + + def isdir(path, revnum): + kind = svn.ra.check_path(self.ra, path, revnum) + return kind == svn.core.svn_node_dir + + def getcfgpath(name, rev): + cfgpath = self.ui.config('convert', 'svn.' + name) + if cfgpath is not None and cfgpath.strip() == '': + return None + path = (cfgpath or name).strip('/') + if not self.exists(path, rev): + if cfgpath: + raise util.Abort(_('expected %s to be at %r, but not found') + % (name, path)) + return None + self.ui.note(_('found %s at %r\n') % (name, path)) + return path + rev = optrev(self.last_changed) - rpath = self.url.strip('/') - cfgtrunk = self.ui.config('convert', 'svn.trunk') - cfgbranches = self.ui.config('convert', 'svn.branches') - cfgtags = self.ui.config('convert', 'svn.tags') - trunk = (cfgtrunk or 'trunk').strip('/') - branches = (cfgbranches or 'branches').strip('/') - tags = (cfgtags or 'tags').strip('/') - if self.exists(trunk, rev) and self.exists(branches, rev) and self.exists(tags, rev): - self.ui.note('found trunk at %r, branches at %r and tags at %r\n' % - (trunk, branches, tags)) - oldmodule = self.module + oldmodule = '' + trunk = getcfgpath('trunk', rev) + self.tags = getcfgpath('tags', rev) + branches = getcfgpath('branches', rev) + + # If the project has a trunk or branches, we will extract heads + # from them. We keep the project root otherwise. + if trunk: + oldmodule = self.module or '' self.module += '/' + trunk - lt = self.latest(self.module, self.last_changed) - self.head = self.revid(lt) - self.heads = [self.head] + self.head = self.latest(self.module, self.last_changed) + if not self.head: + raise util.Abort(_('no revision found in module %s') % + self.module.encode(self.encoding)) + + # First head in the list is the module's head + self.heads = [self.head] + if self.tags is not None: + self.tags = '%s/%s' % (oldmodule , (self.tags or 'tags')) + + # Check if branches bring a few more heads to the list + if branches: + rpath = self.url.strip('/') branchnames = svn.client.ls(rpath + '/' + branches, rev, False, self.ctx) for branch in branchnames.keys(): - if oldmodule: - module = oldmodule + '/' + branches + '/' + branch - else: - module = '/' + branches + '/' + branch - brevnum = self.latest(module, self.last_changed) - brev = self.revid(brevnum, module) - self.ui.note('found branch %s at %d\n' % (branch, brevnum)) - self.heads.append(brev) + module = '%s/%s/%s' % (oldmodule, branches, branch) + if not isdir(module, self.last_changed): + continue + brevid = self.latest(module, self.last_changed) + if not brevid: + self.ui.note(_('ignoring empty branch %s\n') % + branch.encode(self.encoding)) + continue + self.ui.note('found branch %s at %d\n' % + (branch, self.revnum(brevid))) + self.heads.append(brevid) - if oldmodule: - self.tags = '%s/%s' % (oldmodule, tags) - else: - self.tags = '/%s' % tags + if self.startrev and self.heads: + if len(self.heads) > 1: + raise util.Abort(_('svn: start revision is not supported with ' + 'with more than one branch')) + revnum = self.revnum(self.heads[0]) + if revnum < self.startrev: + raise util.Abort(_('svn: no revision found after start revision %d') + % self.startrev) - elif cfgtrunk or cfgbranches or cfgtags: - raise util.Abort('trunk/branch/tags layout expected, but not found') - else: - self.ui.note('working with one branch\n') - self.heads = [self.head] - self.tags = tags return self.heads def getfile(self, file, rev): @@ -234,7 +328,17 @@ self._changescache = None self.modecache = {} (paths, parents) = self.paths[rev] - files, copies = self.expandpaths(rev, paths, parents) + if parents: + files, copies = self.expandpaths(rev, paths, parents) + else: + # Perform a full checkout on roots + uuid, module, revnum = self.revsplit(rev) + entries = svn.client.ls(self.base + module, optrev(revnum), + True, self.ctx) + files = [n for n,e in entries.iteritems() + if e.kind == svn.core.svn_node_file] + copies = {} + files.sort() files = zip(files, [rev] * len(files)) @@ -252,54 +356,78 @@ uuid, module, revnum = self.revsplit(rev) self.module = module self.reparent(module) + # We assume that: + # - requests for revisions after "stop" come from the + # revision graph backward traversal. Cache all of them + # down to stop, they will be used eventually. + # - requests for revisions before "stop" come to get + # isolated branches parents. Just fetch what is needed. stop = self.lastrevs.get(module, 0) - self._fetch_revisions(from_revnum=revnum, to_revnum=stop) + if revnum < stop: + stop = revnum + 1 + self._fetch_revisions(revnum, stop) commit = self.commits[rev] # caller caches the result, so free it here to release memory del self.commits[rev] return commit - def get_log(self, paths, start, end, limit=0, discover_changed_paths=True, - strict_node_history=False): - - def parent(fp): - while True: - entry = pickle.load(fp) - try: - orig_paths, revnum, author, date, message = entry - except: - if entry is None: - break - raise SubversionException("child raised exception", entry) - yield entry - - args = [self.url, paths, start, end, limit, discover_changed_paths, - strict_node_history] - arg = encodeargs(args) - hgexe = util.hgexecutable() - cmd = '%s debugsvnlog' % util.shellquote(hgexe) - stdin, stdout = os.popen2(cmd, 'b') - - stdin.write(arg) - stdin.close() - - for p in parent(stdout): - yield p - def gettags(self): tags = {} - start = self.revnum(self.head) + if self.tags is None: + return tags + + # svn tags are just a convention, project branches left in a + # 'tags' directory. There is no other relationship than + # ancestry, which is expensive to discover and makes them hard + # to update incrementally. Worse, past revisions may be + # referenced by tags far away in the future, requiring a deep + # history traversal on every calculation. Current code + # performs a single backward traversal, tracking moves within + # the tags directory (tag renaming) and recording a new tag + # everytime a project is copied from outside the tags + # directory. It also lists deleted tags, this behaviour may + # change in the future. + pendings = [] + tagspath = self.tags + start = svn.ra.get_latest_revnum(self.ra) try: - for entry in self.get_log([self.tags], 0, start): - orig_paths, revnum, author, date, message = entry - for path in orig_paths: - if not path.startswith(self.tags+'/'): + for entry in get_log(self.url, [self.tags], start, self.startrev): + origpaths, revnum, author, date, message = entry + copies = [(e.copyfrom_path, e.copyfrom_rev, p) for p, e + in origpaths.iteritems() if e.copyfrom_path] + copies.sort() + # Apply moves/copies from more specific to general + copies.reverse() + + srctagspath = tagspath + if copies and copies[-1][2] == tagspath: + # Track tags directory moves + srctagspath = copies.pop()[0] + + for source, sourcerev, dest in copies: + if not dest.startswith(tagspath + '/'): continue - ent = orig_paths[path] - source = ent.copyfrom_path - rev = ent.copyfrom_rev - tag = path.split('/')[-1] - tags[tag] = self.revid(rev, module=source) + for tag in pendings: + if tag[0].startswith(dest): + tagpath = source + tag[0][len(dest):] + tag[:2] = [tagpath, sourcerev] + break + else: + pendings.append([source, sourcerev, dest.split('/')[-1]]) + + # Tell tag renamings from tag creations + remainings = [] + for source, sourcerev, tagname in pendings: + if source.startswith(srctagspath): + remainings.append([source, sourcerev, tagname]) + continue + # From revision may be fake, get one with changes + tagid = self.latest(source, sourcerev) + if tagid: + tags[tagname] = tagid + pendings = remainings + tagspath = srctagspath + except SubversionException, (inst, num): self.ui.note('no tags found at revision %d\n' % start) return tags @@ -335,7 +463,16 @@ return uuid, mod, revnum def latest(self, path, stop=0): - 'find the latest revision affecting path, up to stop' + """Find the latest revid affecting path, up to stop. It may return + a revision in a different module, since a branch may be moved without + a change being reported. Return None if computed module does not + belong to rootmodule subtree. + """ + if not path.startswith(self.rootmodule): + # Requests on foreign branches may be forbidden at server level + self.ui.debug(_('ignoring foreign branch %r\n') % path) + return None + if not stop: stop = svn.ra.get_latest_revnum(self.ra) try: @@ -347,7 +484,31 @@ if not dirent: raise util.Abort('%s not found up to revision %d' % (path, stop)) - return dirent.created_rev + # stat() gives us the previous revision on this line of development, but + # it might be in *another module*. Fetch the log and detect renames down + # to the latest revision. + stream = get_log(self.url, [path], stop, dirent.created_rev) + try: + for entry in stream: + paths, revnum, author, date, message = entry + if revnum <= dirent.created_rev: + break + + for p in paths: + if not path.startswith(p) or not paths[p].copyfrom_path: + continue + newpath = paths[p].copyfrom_path + path[len(p):] + self.ui.debug("branch renamed from %s to %s at %d\n" % + (path, newpath, revnum)) + path = newpath + break + finally: + stream.close() + + if not path.startswith(self.rootmodule): + self.ui.debug(_('ignoring foreign branch %r\n') % path) + return None + return self.revid(dirent.created_rev, path) def get_blacklist(self): """Avoid certain revision numbers. @@ -395,13 +556,11 @@ entries = [] copyfrom = {} # Map of entrypath, revision for finding source of deleted revisions. copies = {} - revnum = self.revnum(rev) - if revnum in self.modulemap: - new_module = self.modulemap[revnum] - if new_module != self.module: - self.module = new_module - self.reparent(self.module) + new_module, revnum = self.revsplit(rev)[1:] + if new_module != self.module: + self.module = new_module + self.reparent(self.module) for path, ent in paths: entrypath = get_entry_from_path(path, module=self.module) @@ -412,7 +571,9 @@ if ent.copyfrom_path: copyfrom_path = get_entry_from_path(ent.copyfrom_path) if copyfrom_path: - self.ui.debug("Copied to %s from %s@%s\n" % (entry, copyfrom_path, ent.copyfrom_rev)) + self.ui.debug("Copied to %s from %s@%s\n" % + (entrypath, copyfrom_path, + ent.copyfrom_rev)) # It's probably important for hg that the source # exists in the revision's parent, not just the # ent.copyfrom_rev @@ -425,12 +586,9 @@ # if a branch is created but entries are removed in the same # changeset, get the right fromrev - if parents: - uuid, old_module, fromrev = self.revsplit(parents[0]) - else: - fromrev = revnum - 1 - # might always need to be revnum - 1 in these 3 lines? - old_module = self.modulemap.get(fromrev, self.module) + # parents cannot be empty here, you cannot remove things from + # a root revision. + uuid, old_module, fromrev = self.revsplit(parents[0]) basepath = old_module + "/" + get_entry_from_path(path, module=self.module) entrypath = old_module + "/" + get_entry_from_path(path, module=self.module) @@ -459,7 +617,12 @@ fromrev = froment.copyfrom_rev self.ui.debug("Info: %s %s %s %s\n" % (frompath, froment, ent, entrypath)) - fromkind = svn.ra.check_path(self.ra, entrypath, fromrev) + # We can avoid the reparent calls if the module has not changed + # but it probably does not worth the pain. + self.reparent('') + fromkind = svn.ra.check_path(self.ra, entrypath.strip('/'), fromrev) + self.reparent(self.module) + if fromkind == svn.core.svn_node_file: # a deleted file entries.append(self.recode(entry)) elif fromkind == svn.core.svn_node_dir: @@ -501,6 +664,9 @@ # If the directory just had a prop change, # then we shouldn't need to look for its children. + if ent.action == 'M': + continue + # Also this could create duplicate entries. Not sure # whether this will matter. Maybe should make entries a set. # print "Changed directory", revnum, path, ent.action, ent.copyfrom_path, ent.copyfrom_rev @@ -559,44 +725,49 @@ copies[self.recode(copyto_entry)] = self.recode(entry) # copy from quux splort/quuxfile - return (entries, copies) + return (util.unique(entries), copies) - def _fetch_revisions(self, from_revnum = 0, to_revnum = 347): + def _fetch_revisions(self, from_revnum, to_revnum): + if from_revnum < to_revnum: + from_revnum, to_revnum = to_revnum, from_revnum + self.child_cset = None def parselogentry(orig_paths, revnum, author, date, message): + """Return the parsed commit object or None, and True if + the revision is a branch root. + """ self.ui.debug("parsing revision %d (%d changes)\n" % (revnum, len(orig_paths))) - if revnum in self.modulemap: - new_module = self.modulemap[revnum] - if new_module != self.module: - self.module = new_module - self.reparent(self.module) - + branched = False rev = self.revid(revnum) # branch log might return entries for a parent we already have - if (rev in self.commits or - (revnum < self.lastrevs.get(self.module, 0))): - return + + if (rev in self.commits or revnum < to_revnum): + return None, branched parents = [] - # check whether this revision is the start of a branch - if self.module in orig_paths: - ent = orig_paths[self.module] + # check whether this revision is the start of a branch or part + # of a branch renaming + orig_paths = orig_paths.items() + orig_paths.sort() + root_paths = [(p,e) for p,e in orig_paths if self.module.startswith(p)] + if root_paths: + path, ent = root_paths[-1] if ent.copyfrom_path: + branched = True + newpath = ent.copyfrom_path + self.module[len(path):] # ent.copyfrom_rev may not be the actual last revision - prev = self.latest(ent.copyfrom_path, ent.copyfrom_rev) - self.modulemap[prev] = ent.copyfrom_path - parents = [self.revid(prev, ent.copyfrom_path)] - self.ui.note('found parent of branch %s at %d: %s\n' % \ - (self.module, prev, ent.copyfrom_path)) + previd = self.latest(newpath, ent.copyfrom_rev) + if previd is not None: + prevmodule, prevnum = self.revsplit(previd)[1:] + if prevnum >= self.startrev: + parents = [previd] + self.ui.note('found parent of branch %s at %d: %s\n' % + (self.module, prevnum, prevmodule)) else: self.ui.debug("No copyfrom path, don't know what to do.\n") - self.modulemap[revnum] = self.module # track backwards in time - - orig_paths = orig_paths.items() - orig_paths.sort() paths = [] # filter out unrelated paths for path, ent in orig_paths: @@ -605,14 +776,12 @@ continue paths.append((path, ent)) - self.paths[rev] = (paths, parents) - # Example SVN datetime. Includes microseconds. # ISO-8601 conformant # '2007-01-04T17:35:00.902377Z' date = util.parsedate(date[:19] + " UTC", ["%Y-%m-%dT%H:%M:%S"]) - log = message and self.recode(message) + log = message and self.recode(message) or '' author = author and self.recode(author) or '' try: branch = self.module.split("/")[-1] @@ -629,27 +798,58 @@ rev=rev.encode('utf-8')) self.commits[rev] = cset + # The parents list is *shared* among self.paths and the + # commit object. Both will be updated below. + self.paths[rev] = (paths, cset.parents) if self.child_cset and not self.child_cset.parents: - self.child_cset.parents = [rev] + self.child_cset.parents[:] = [rev] self.child_cset = cset + return cset, branched self.ui.note('fetching revision log for "%s" from %d to %d\n' % (self.module, from_revnum, to_revnum)) try: - for entry in self.get_log([self.module], from_revnum, to_revnum): - orig_paths, revnum, author, date, message = entry - if self.is_blacklisted(revnum): - self.ui.note('skipping blacklisted revision %d\n' % revnum) - continue - if orig_paths is None: - self.ui.debug('revision %d has no entries\n' % revnum) - continue - parselogentry(orig_paths, revnum, author, date, message) + firstcset = None + lastonbranch = False + stream = get_log(self.url, [self.module], from_revnum, to_revnum) + try: + for entry in stream: + paths, revnum, author, date, message = entry + if revnum < self.startrev: + lastonbranch = True + break + if self.is_blacklisted(revnum): + self.ui.note('skipping blacklisted revision %d\n' + % revnum) + continue + if paths is None: + self.ui.debug('revision %d has no entries\n' % revnum) + continue + cset, lastonbranch = parselogentry(paths, revnum, author, + date, message) + if cset: + firstcset = cset + if lastonbranch: + break + finally: + stream.close() + + if not lastonbranch and firstcset and not firstcset.parents: + # The first revision of the sequence (the last fetched one) + # has invalid parents if not a branch root. Find the parent + # revision now, if any. + try: + firstrevnum = self.revnum(firstcset.rev) + if firstrevnum > 1: + latest = self.latest(self.module, firstrevnum - 1) + if latest: + firstcset.parents.append(latest) + except util.Abort: + pass except SubversionException, (inst, num): if num == svn.core.SVN_ERR_FS_NO_SUCH_REVISION: - raise NoSuchRevision(branch=self, - revision="Revision number %d" % to_revnum) + raise util.Abort('svn: branch has no revision %s' % to_revnum) raise def _getfile(self, file, rev): @@ -657,9 +857,9 @@ # TODO: ra.get_file transmits the whole file instead of diffs. mode = '' try: - revnum = self.revnum(rev) - if self.module != self.modulemap[revnum]: - self.module = self.modulemap[revnum] + new_module, revnum = self.revsplit(rev)[1:] + if self.module != new_module: + self.module = new_module self.reparent(self.module) info = svn.ra.get_file(self.ra, file, revnum, io) if isinstance(info, list): @@ -714,7 +914,7 @@ def join(self, name): return os.path.join(self.wc, '.svn', name) - + def revmapfile(self): return self.join('hg-shamap') @@ -725,6 +925,9 @@ converter_sink.__init__(self, ui, path) commandline.__init__(self, ui, 'svn') self.delete = [] + self.setexec = [] + self.delexec = [] + self.copies = [] self.wc = None self.cwd = os.getcwd() @@ -743,11 +946,11 @@ os.path.basename(path)) commandline(ui, 'svnadmin').run0('create', path) created = path - path = path.replace('\\', '/') + path = util.normpath(path) if not path.startswith('/'): path = '/' + path path = 'file://' + path - + ui.status(_('initializing svn wc %r\n') % os.path.basename(wcpath)) self.run0('checkout', path, wcpath) @@ -762,7 +965,7 @@ fp = open(hook, 'w') fp.write(pre_revprop_change) fp.close() - util.set_exec(hook, True) + util.set_flags(hook, "x") xport = transport.SvnRaTransport(url=geturl(path)) self.uuid = svn.ra.get_uuid(xport.ra) @@ -789,18 +992,21 @@ # systematically is just as expensive and much simpler. was_exec = 'x' not in flags - util.set_exec(self.wjoin(filename), 'x' in flags) + util.set_flags(self.wjoin(filename), flags) if was_exec: if 'x' not in flags: - self.run0('propdel', 'svn:executable', filename) + self.delexec.append(filename) else: if 'x' in flags: - self.run0('propset', 'svn:executable', '*', filename) - + self.setexec.append(filename) + def delfile(self, name): self.delete.append(name) def copyfile(self, source, dest): + self.copies.append([source, dest]) + + def _copyfile(self, source, dest): # SVN's copy command pukes if the destination file exists, but # our copyfile method expects to record a copy that has # already occurred. Cross the semantic gap. @@ -823,7 +1029,7 @@ os.rename(tempname, wdest) def dirs_of(self, files): - dirs = set() + dirs = util.set() for f in files: if os.path.isdir(self.wjoin(f)): dirs.add(f) @@ -831,19 +1037,23 @@ dirs.add(f[:i]) return dirs - def add_files(self, files): + def add_dirs(self, files): add_dirs = [d for d in self.dirs_of(files) if not os.path.exists(self.wjoin(d, '.svn', 'entries'))] if add_dirs: add_dirs.sort() - self.run('add', non_recursive=True, quiet=True, *add_dirs) + self.xargs(add_dirs, 'add', non_recursive=True, quiet=True) + return add_dirs + + def add_files(self, files): if files: - self.run('add', quiet=True, *files) - return files.union(add_dirs) - + self.xargs(files, 'add', quiet=True) + return files + def tidy_dirs(self, names): dirs = list(self.dirs_of(names)) - dirs.sort(reverse=True) + dirs.sort() + dirs.reverse() deleted = [] for d in dirs: wd = self.wjoin(d) @@ -857,20 +1067,32 @@ def revid(self, rev): return u"svn:%s@%s" % (self.uuid, rev) - + def putcommit(self, files, parents, commit): for parent in parents: try: return self.revid(self.childmap[parent]) except KeyError: pass - entries = set(self.delete) + entries = util.set(self.delete) + files = util.frozenset(files) + entries.update(self.add_dirs(files.difference(entries))) + if self.copies: + for s, d in self.copies: + self._copyfile(s, d) + self.copies = [] if self.delete: - self.run0('delete', *self.delete) + self.xargs(self.delete, 'delete') self.delete = [] - files = util.frozenset(files) entries.update(self.add_files(files.difference(entries))) entries.update(self.tidy_dirs(entries)) + if self.delexec: + self.xargs(self.delexec, 'propdel', 'svn:executable') + self.delexec = [] + if self.setexec: + self.xargs(self.setexec, 'propset', 'svn:executable', '*') + self.setexec = [] + fd, messagefile = tempfile.mkstemp(prefix='hg-convert-') fp = os.fdopen(fd, 'w') fp.write(commit.desc) @@ -879,7 +1101,7 @@ output = self.run0('commit', username=util.shortuser(commit.author), file=messagefile, - *list(entries)) + encoding='utf-8') try: rev = self.commit_re.search(output).group(1) except AttributeError:
--- a/hgext/convert/transport.py Wed Apr 09 15:27:57 2008 -0700 +++ b/hgext/convert/transport.py Wed Apr 09 15:28:30 2008 -0700 @@ -18,10 +18,6 @@ # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA -from cStringIO import StringIO -import os -from tempfile import mktemp - from svn.core import SubversionException, Pool import svn.ra import svn.client @@ -120,10 +116,3 @@ def do_update(self, revnum, path, *args, **kwargs): return self.Reporter(svn.ra.do_update(self.ra, revnum, path, *args, **kwargs)) - - def clone(self, offset=None): - """See Transport.clone().""" - if offset is None: - return self.__class__(self.base) - - return SvnRaTransport(urlutils.join(self.base, offset), ra=self.ra)
--- a/hgext/extdiff.py Wed Apr 09 15:27:57 2008 -0700 +++ b/hgext/extdiff.py Wed Apr 09 15:28:30 2008 -0700 @@ -46,7 +46,7 @@ ''' from mercurial.i18n import _ -from mercurial.node import * +from mercurial.node import short from mercurial import cmdutil, util, commands import os, shlex, shutil, tempfile @@ -80,13 +80,18 @@ '''snapshot files from working directory. if not using snapshot, -I/-X does not work and recursive diff in tools like kdiff3 and meld displays too many files.''' - dirname = os.path.basename(repo.root) + repo_root = repo.root + + dirname = os.path.basename(repo_root) if dirname == "": dirname = "root" base = os.path.join(tmproot, dirname) os.mkdir(base) ui.note(_('making snapshot of %d files from working dir\n') % (len(files))) + + fns_and_mtime = [] + for fn in files: wfn = util.pconvert(fn) ui.note(' %s\n' % wfn) @@ -94,13 +99,27 @@ destdir = os.path.dirname(dest) if not os.path.isdir(destdir): os.makedirs(destdir) + fp = open(dest, 'wb') for chunk in util.filechunkiter(repo.wopener(wfn)): fp.write(chunk) - return dirname + fp.close() + + fns_and_mtime.append((dest, os.path.join(repo_root, fn), + os.path.getmtime(dest))) + + + return dirname, fns_and_mtime def dodiff(ui, repo, diffcmd, diffopts, pats, opts): + '''Do the actuall diff: + + - copy to a temp structure if diffing 2 internal revisions + - copy to a temp structure if diffing working revision with + another one and more than 1 file is changed + - just invoke the diff for a single file in the working dir + ''' node1, node2 = cmdutil.revpair(repo, opts['rev']) files, matchfn, anypats = cmdutil.matchpats(repo, pats, opts) modified, added, removed, deleted, unknown = repo.status( @@ -115,11 +134,17 @@ dir1 = snapshot_node(ui, repo, modified + removed, node1, tmproot) changes = len(modified) + len(removed) + len(added) + fns_and_mtime = [] + # If node2 in not the wc or there is >1 change, copy it if node2: dir2 = snapshot_node(ui, repo, modified + added, node2, tmproot) elif changes > 1: - dir2 = snapshot_wdir(ui, repo, modified + added, tmproot) + #we only actually need to get the files to copy back to the working + #dir in this case (because the other cases are: diffing 2 revisions + #or single file -- in which case the file is already directly passed + #to the diff tool). + dir2, fns_and_mtime = snapshot_wdir(ui, repo, modified + added, tmproot) else: # This lets the diff tool open the changed file directly dir2 = '' @@ -142,6 +167,13 @@ util.shellquote(dir1), util.shellquote(dir2))) ui.debug('running %r in %s\n' % (cmdline, tmproot)) util.system(cmdline, cwd=tmproot) + + for copy_fn, working_fn, mtime in fns_and_mtime: + if os.path.getmtime(copy_fn) != mtime: + ui.debug('File changed while diffing. ' + 'Overwriting: %s (src: %s)\n' % (working_fn, copy_fn)) + util.copyfile(copy_fn, working_fn) + return 1 finally: ui.note(_('cleaning up temp directory\n'))
--- a/hgext/fetch.py Wed Apr 09 15:27:57 2008 -0700 +++ b/hgext/fetch.py Wed Apr 09 15:28:30 2008 -0700 @@ -6,8 +6,8 @@ # of the GNU General Public License, incorporated herein by reference. from mercurial.i18n import _ -from mercurial.node import * -from mercurial import commands, cmdutil, hg, node, util +from mercurial.node import nullid, short +from mercurial import commands, cmdutil, hg, util def fetch(ui, repo, source='default', **opts): '''Pull changes from a remote repository, merge new changes if needed. @@ -17,7 +17,15 @@ If the pulled changes add a new head, the head is automatically merged, and the result of the merge is committed. Otherwise, the - working directory is updated.''' + working directory is updated to include the new changes. + + When a merge occurs, the newly pulled changes are assumed to be + "authoritative". The head of the new changes is used as the first + parent, with local changes as the second. To switch the merge + order, use --switch-parent. + + See 'hg help dates' for a list of formats valid for -d/--date. + ''' def postincoming(other, modheads): if modheads == 0: @@ -31,38 +39,60 @@ newparent = newchildren[0] hg.clean(repo, newparent) newheads = [n for n in repo.heads() if n != newparent] - err = False - if newheads: - ui.status(_('merging with new head %d:%s\n') % - (repo.changelog.rev(newheads[0]), short(newheads[0]))) - err = hg.merge(repo, newheads[0], remind=False) - if not err and len(newheads) > 1: + if len(newheads) > 1: ui.status(_('not merging with %d other new heads ' '(use "hg heads" and "hg merge" to merge them)') % (len(newheads) - 1)) + return + err = False + if newheads: + # By default, we consider the repository we're pulling + # *from* as authoritative, so we merge our changes into + # theirs. + if opts['switch_parent']: + firstparent, secondparent = newparent, newheads[0] + else: + firstparent, secondparent = newheads[0], newparent + ui.status(_('updating to %d:%s\n') % + (repo.changelog.rev(firstparent), + short(firstparent))) + hg.clean(repo, firstparent) + ui.status(_('merging with %d:%s\n') % + (repo.changelog.rev(secondparent), short(secondparent))) + err = hg.merge(repo, secondparent, remind=False) if not err: mod, add, rem = repo.status()[:3] message = (cmdutil.logmessage(opts) or - (_('Automated merge with %s') % other.url())) + (_('Automated merge with %s') % + util.removeauth(other.url()))) + force_editor = opts.get('force_editor') or opts.get('edit') n = repo.commit(mod + add + rem, message, - opts['user'], opts['date'], - force_editor=opts.get('force_editor')) + opts['user'], opts['date'], force=True, + force_editor=force_editor) ui.status(_('new changeset %d:%s merges remote changes ' 'with local\n') % (repo.changelog.rev(n), short(n))) + def pull(): cmdutil.setremoteconfig(ui, opts) other = hg.repository(ui, ui.expandpath(source)) - ui.status(_('pulling from %s\n') % ui.expandpath(source)) + ui.status(_('pulling from %s\n') % + util.hidepassword(ui.expandpath(source))) revs = None - if opts['rev'] and not other.local(): - raise util.Abort(_("fetch -r doesn't work for remote repositories yet")) - elif opts['rev']: - revs = [other.lookup(rev) for rev in opts['rev']] + if opts['rev']: + if not other.local(): + raise util.Abort(_("fetch -r doesn't work for remote " + "repositories yet")) + else: + revs = [other.lookup(rev) for rev in opts['rev']] modheads = repo.pull(other, heads=revs) return postincoming(other, modheads) + date = opts.get('date') + if date: + opts['date'] = util.parsedate(date) + parent, p2 = repo.dirstate.parents() if parent != repo.changelog.tip(): raise util.Abort(_('working dir not at tip ' @@ -73,9 +103,11 @@ try: wlock = repo.wlock() lock = repo.lock() - mod, add, rem = repo.status()[:3] + mod, add, rem, del_ = repo.status()[:4] if mod or add or rem: raise util.Abort(_('outstanding uncommitted changes')) + if del_: + raise util.Abort(_('working directory is missing some files')) if len(repo.heads()) > 1: raise util.Abort(_('multiple heads in this repository ' '(use "hg heads" and "hg merge" to merge)')) @@ -87,7 +119,9 @@ 'fetch': (fetch, [('r', 'rev', [], _('a specific revision you would like to pull')), - ('f', 'force-editor', None, _('edit commit message')), + ('e', 'edit', None, _('edit commit message')), + ('', 'force-editor', None, _('edit commit message (DEPRECATED)')), + ('', 'switch-parent', None, _('switch parents when merging')), ] + commands.commitopts + commands.commitopts2 + commands.remoteopts, _('hg fetch [SOURCE]')), }
--- a/hgext/gpg.py Wed Apr 09 15:27:57 2008 -0700 +++ b/hgext/gpg.py Wed Apr 09 15:28:30 2008 -0700 @@ -198,11 +198,18 @@ If no revision is given, the parent of the working directory is used, or tip if no revision is checked out. + + See 'hg help dates' for a list of formats valid for -d/--date. """ mygpg = newgpg(ui, **opts) sigver = "0" sigmessage = "" + + date = opts.get('date') + if date: + opts['date'] = util.parsedate(date) + if revs: nodes = [repo.lookup(n) for n in revs] else:
--- a/hgext/graphlog.py Wed Apr 09 15:27:57 2008 -0700 +++ b/hgext/graphlog.py Wed Apr 09 15:28:30 2008 -0700 @@ -5,11 +5,13 @@ # This software may be used and distributed according to the terms of # the GNU General Public License, incorporated herein by reference. +import os import sys from mercurial.cmdutil import revrange, show_changeset +from mercurial.commands import templateopts from mercurial.i18n import _ -from mercurial.node import nullid, nullrev -from mercurial.util import Abort +from mercurial.node import nullrev +from mercurial.util import Abort, canonpath def revision_grapher(repo, start_rev, stop_rev): """incremental revision grapher @@ -63,6 +65,62 @@ revs = next_revs curr_rev -= 1 +def filelog_grapher(repo, path, start_rev, stop_rev): + """incremental file log grapher + + This generator function walks through the revision history of a + single file from revision start_rev to revision stop_rev (which must + be less than or equal to start_rev) and for each revision emits + tuples with the following elements: + + - Current revision. + - Current node. + - Column of the current node in the set of ongoing edges. + - Edges; a list of (col, next_col) indicating the edges between + the current node and its parents. + - Number of columns (ongoing edges) in the current revision. + - The difference between the number of columns (ongoing edges) + in the next revision and the number of columns (ongoing edges) + in the current revision. That is: -1 means one column removed; + 0 means no columns added or removed; 1 means one column added. + """ + + assert start_rev >= stop_rev + curr_rev = start_rev + revs = [] + filerev = repo.file(path).count() - 1 + while filerev >= 0: + fctx = repo.filectx(path, fileid=filerev) + + # Compute revs and next_revs. + if filerev not in revs: + revs.append(filerev) + rev_index = revs.index(filerev) + next_revs = revs[:] + + # Add parents to next_revs. + parents = [f.filerev() for f in fctx.parents() if f.path() == path] + parents_to_add = [] + for parent in parents: + if parent not in next_revs: + parents_to_add.append(parent) + parents_to_add.sort() + next_revs[rev_index:rev_index + 1] = parents_to_add + + edges = [] + for parent in parents: + edges.append((rev_index, next_revs.index(parent))) + + changerev = fctx.linkrev() + if changerev <= start_rev: + node = repo.changelog.node(changerev) + n_columns_diff = len(next_revs) - len(revs) + yield (changerev, node, rev_index, edges, len(revs), n_columns_diff) + if changerev <= stop_rev: + break + revs = next_revs + filerev -= 1 + def get_rev_parents(repo, rev): return [x for x in repo.changelog.parentrevs(rev) if x != nullrev] @@ -141,7 +199,7 @@ else: return (repo.changelog.count() - 1, 0) -def graphlog(ui, repo, **opts): +def graphlog(ui, repo, path=None, **opts): """show revision history alongside an ASCII revision graph Print a revision history alongside a revision graph drawn with @@ -157,7 +215,11 @@ if start_rev == nullrev: return cs_printer = show_changeset(ui, repo, opts) - grapher = revision_grapher(repo, start_rev, stop_rev) + if path: + cpath = canonpath(repo.root, os.getcwd(), path) + grapher = filelog_grapher(repo, cpath, start_rev, stop_rev) + else: + grapher = revision_grapher(repo, start_rev, stop_rev) repo_parents = repo.dirstate.parents() prev_n_columns_diff = 0 prev_node_index = 0 @@ -259,7 +321,6 @@ [('l', 'limit', '', _('limit number of changes displayed')), ('p', 'patch', False, _('show patch')), ('r', 'rev', [], _('show the specified revision or range')), - ('', 'style', '', _('display using template map file')), - ('', 'template', '', _('display with template'))], - _('hg glog [OPTION]...')), + ] + templateopts, + _('hg glog [OPTION]... [FILE]')), }
--- a/hgext/hbisect.py Wed Apr 09 15:27:57 2008 -0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,313 +0,0 @@ -# bisect extension for mercurial -# -# Copyright 2005, 2006 Benoit Boissinot <benoit.boissinot@ens-lyon.org> -# Inspired by git bisect, extension skeleton taken from mq.py. -# -# This software may be used and distributed according to the terms -# of the GNU General Public License, incorporated herein by reference. - -from mercurial.i18n import _ -from mercurial import hg, util, commands, cmdutil -import os, sys, sets - -versionstr = "0.0.3" - -def lookup_rev(ui, repo, rev=None): - """returns rev or the checked-out revision if rev is None""" - if not rev is None: - return repo.lookup(rev) - parents = [p for p in repo.dirstate.parents() if p != hg.nullid] - if len(parents) != 1: - raise util.Abort(_("unexpected number of parents, " - "please commit or revert")) - return parents.pop() - -def check_clean(ui, repo): - modified, added, removed, deleted, unknown = repo.status()[:5] - if modified or added or removed: - ui.warn("Repository is not clean, please commit or revert\n") - sys.exit(1) - -class bisect(object): - """dichotomic search in the DAG of changesets""" - def __init__(self, ui, repo): - self.repo = repo - self.path = repo.join("bisect") - self.opener = util.opener(self.path) - self.ui = ui - self.goodrevs = [] - self.badrev = None - self.good_path = "good" - self.bad_path = "bad" - self.is_reset = False - - if os.path.exists(os.path.join(self.path, self.good_path)): - self.goodrevs = self.opener(self.good_path).read().splitlines() - self.goodrevs = [hg.bin(x) for x in self.goodrevs] - if os.path.exists(os.path.join(self.path, self.bad_path)): - r = self.opener(self.bad_path).read().splitlines() - if r: - self.badrev = hg.bin(r.pop(0)) - - def write(self): - if self.is_reset: - return - if not os.path.isdir(self.path): - os.mkdir(self.path) - f = self.opener(self.good_path, "w") - f.write("\n".join([hg.hex(r) for r in self.goodrevs])) - if len(self.goodrevs) > 0: - f.write("\n") - f = self.opener(self.bad_path, "w") - if self.badrev: - f.write(hg.hex(self.badrev) + "\n") - - def init(self): - """start a new bisection""" - if os.path.isdir(self.path): - raise util.Abort(_("bisect directory already exists\n")) - os.mkdir(self.path) - check_clean(self.ui, self.repo) - return 0 - - def reset(self): - """finish a bisection""" - if os.path.isdir(self.path): - sl = [os.path.join(self.path, p) - for p in [self.bad_path, self.good_path]] - for s in sl: - if os.path.exists(s): - os.unlink(s) - os.rmdir(self.path) - # Not sure about this - #self.ui.write("Going back to tip\n") - #self.repo.update(self.repo.changelog.tip()) - self.is_reset = True - return 0 - - def num_ancestors(self, head=None, stop=None): - """ - returns a dict with the mapping: - node -> number of ancestors (self included) - for all nodes who are ancestor of head and - not in stop. - """ - if head is None: - head = self.badrev - return self.__ancestors_and_nb_ancestors(head, stop)[1] - - def ancestors(self, head=None, stop=None): - """ - returns the set of the ancestors of head (self included) - who are not in stop. - """ - if head is None: - head = self.badrev - return self.__ancestors_and_nb_ancestors(head, stop)[0] - - def __ancestors_and_nb_ancestors(self, head, stop=None): - """ - if stop is None then ancestors of goodrevs are used as - lower limit. - - returns (anc, n_child) where anc is the set of the ancestors of head - and n_child is a dictionary with the following mapping: - node -> number of ancestors (self included) - """ - cl = self.repo.changelog - if not stop: - stop = sets.Set([]) - for i in xrange(len(self.goodrevs)-1, -1, -1): - g = self.goodrevs[i] - if g in stop: - continue - stop.update(cl.reachable(g)) - def num_children(a): - """ - returns a dictionnary with the following mapping - node -> [number of children, empty set] - """ - d = {a: [0, sets.Set([])]} - for i in xrange(cl.rev(a)+1): - n = cl.node(i) - if not d.has_key(n): - d[n] = [0, sets.Set([])] - parents = [p for p in cl.parents(n) if p != hg.nullid] - for p in parents: - d[p][0] += 1 - return d - - if head in stop: - raise util.Abort(_("Inconsistent state, %s:%s is good and bad") - % (cl.rev(head), hg.short(head))) - n_child = num_children(head) - for i in xrange(cl.rev(head)+1): - n = cl.node(i) - parents = [p for p in cl.parents(n) if p != hg.nullid] - for p in parents: - n_child[p][0] -= 1 - if not n in stop: - n_child[n][1].union_update(n_child[p][1]) - if n_child[p][0] == 0: - n_child[p] = len(n_child[p][1]) - if not n in stop: - n_child[n][1].add(n) - if n_child[n][0] == 0: - if n == head: - anc = n_child[n][1] - n_child[n] = len(n_child[n][1]) - return anc, n_child - - def next(self): - if not self.badrev: - raise util.Abort(_("You should give at least one bad revision")) - if not self.goodrevs: - self.ui.warn(_("No good revision given\n")) - self.ui.warn(_("Marking the first revision as good\n")) - ancestors, num_ancestors = self.__ancestors_and_nb_ancestors( - self.badrev) - tot = len(ancestors) - if tot == 1: - if ancestors.pop() != self.badrev: - raise util.Abort(_("Could not find the first bad revision")) - self.ui.write(_("The first bad revision is:\n")) - displayer = cmdutil.show_changeset(self.ui, self.repo, {}) - displayer.show(changenode=self.badrev) - return None - best_rev = None - best_len = -1 - for n in ancestors: - l = num_ancestors[n] - l = min(l, tot - l) - if l > best_len: - best_len = l - best_rev = n - assert best_rev is not None - nb_tests = 0 - q, r = divmod(tot, 2) - while q: - nb_tests += 1 - q, r = divmod(q, 2) - msg = _("Testing changeset %s:%s (%s changesets remaining, " - "~%s tests)\n") % (self.repo.changelog.rev(best_rev), - hg.short(best_rev), tot, nb_tests) - self.ui.write(msg) - return best_rev - - def autonext(self): - """find and update to the next revision to test""" - check_clean(self.ui, self.repo) - rev = self.next() - if rev is not None: - return hg.clean(self.repo, rev) - - def good(self, rev): - self.goodrevs.append(rev) - - def autogood(self, rev=None): - """mark revision as good and update to the next revision to test""" - check_clean(self.ui, self.repo) - rev = lookup_rev(self.ui, self.repo, rev) - self.good(rev) - if self.badrev: - return self.autonext() - - def bad(self, rev): - self.badrev = rev - - def autobad(self, rev=None): - """mark revision as bad and update to the next revision to test""" - check_clean(self.ui, self.repo) - rev = lookup_rev(self.ui, self.repo, rev) - self.bad(rev) - if self.goodrevs: - self.autonext() - -# should we put it in the class ? -def test(ui, repo, rev): - """test the bisection code""" - b = bisect(ui, repo) - rev = repo.lookup(rev) - ui.write("testing with rev %s\n" % hg.hex(rev)) - anc = b.ancestors() - while len(anc) > 1: - if not rev in anc: - ui.warn("failure while bisecting\n") - sys.exit(1) - ui.write("it worked :)\n") - new_rev = b.next() - ui.write("choosing if good or bad\n") - if rev in b.ancestors(head=new_rev): - b.bad(new_rev) - ui.write("it is bad\n") - else: - b.good(new_rev) - ui.write("it is good\n") - anc = b.ancestors() - #repo.update(new_rev, force=True) - for v in anc: - if v != rev: - ui.warn("fail to found cset! :(\n") - return 1 - ui.write("Found bad cset: %s\n" % hg.hex(b.badrev)) - ui.write("Everything is ok :)\n") - return 0 - -def bisect_run(ui, repo, cmd=None, *args): - """Dichotomic search in the DAG of changesets - -This extension helps to find changesets which cause problems. -To use, mark the earliest changeset you know introduces the problem -as bad, then mark the latest changeset which is free from the problem -as good. Bisect will update your working directory to a revision for -testing. Once you have performed tests, mark the working directory -as bad or good and bisect will either update to another candidate -changeset or announce that it has found the bad revision. - -Note: bisect expects bad revisions to be descendants of good revisions. -If you are looking for the point at which a problem was fixed, then make -the problem-free state "bad" and the problematic state "good." - -For subcommands see "hg bisect help\" - """ - def help_(cmd=None, *args): - """show help for a given bisect subcommand or all subcommands""" - cmdtable = bisectcmdtable - if cmd: - doc = cmdtable[cmd][0].__doc__ - synopsis = cmdtable[cmd][2] - ui.write(synopsis + "\n") - ui.write("\n" + doc + "\n") - return - ui.write(_("list of subcommands for the bisect extension\n\n")) - cmds = cmdtable.keys() - cmds.sort() - m = max([len(c) for c in cmds]) - for cmd in cmds: - doc = cmdtable[cmd][0].__doc__.splitlines(0)[0].rstrip() - ui.write(" %-*s %s\n" % (m, cmd, doc)) - - b = bisect(ui, repo) - bisectcmdtable = { - "init": (b.init, 0, _("hg bisect init")), - "bad": (b.autobad, 1, _("hg bisect bad [<rev>]")), - "good": (b.autogood, 1, _("hg bisect good [<rev>]")), - "next": (b.autonext, 0, _("hg bisect next")), - "reset": (b.reset, 0, _("hg bisect reset")), - "help": (help_, 1, _("hg bisect help [<subcommand>]")), - } - - if not bisectcmdtable.has_key(cmd): - ui.warn(_("bisect: Unknown sub-command\n")) - return help_() - if len(args) > bisectcmdtable[cmd][1]: - ui.warn(_("bisect: Too many arguments\n")) - return help_() - ret = bisectcmdtable[cmd][0](*args) - b.write() - return ret - -cmdtable = { - "bisect": (bisect_run, [], _("hg bisect [help|init|reset|next|good|bad]")), - #"bisect-test": (test, [], "hg bisect-test rev"), -}
--- a/hgext/hgk.py Wed Apr 09 15:27:57 2008 -0700 +++ b/hgext/hgk.py Wed Apr 09 15:28:30 2008 -0700 @@ -13,7 +13,7 @@ # querying of information, and an extension to mercurial named hgk.py, # which provides hooks for hgk to get information. hgk can be found in # the contrib directory, and hgk.py can be found in the hgext -# directory. +# directory. # # To load the hgext.py extension, add it to your .hgrc file (you have # to use your global $HOME/.hgrc file, not one in a repository). You @@ -45,8 +45,9 @@ # Revisions context menu will now display additional entries to fire # vdiff on hovered and selected revisions. -import sys, os -from mercurial import hg, fancyopts, commands, ui, util, patch, revlog +import os +from mercurial import commands, util, patch, revlog +from mercurial.node import nullid, nullrev, short def difftree(ui, repo, node1=None, node2=None, *files, **opts): """diff trees from two commits""" @@ -57,21 +58,18 @@ status = repo.status(node1, node2, files=files)[:5] modified, added, removed, deleted, unknown = status - empty = hg.short(hg.nullid) + empty = short(nullid) for f in modified: # TODO get file permissions - print ":100664 100664 %s %s M\t%s\t%s" % (hg.short(mmap[f]), - hg.short(mmap2[f]), - f, f) + ui.write(":100664 100664 %s %s M\t%s\t%s\n" % + (short(mmap[f]), short(mmap2[f]), f, f)) for f in added: - print ":000000 100664 %s %s N\t%s\t%s" % (empty, - hg.short(mmap2[f]), - f, f) + ui.write(":000000 100664 %s %s N\t%s\t%s\n" % + (empty, short(mmap2[f]), f, f)) for f in removed: - print ":100664 000000 %s %s D\t%s\t%s" % (hg.short(mmap[f]), - empty, - f, f) + ui.write(":100664 000000 %s %s D\t%s\t%s\n" % + (short(mmap[f]), empty, f, f)) ## while True: @@ -93,7 +91,7 @@ node1 = repo.changelog.parents(node1)[0] if opts['patch']: if opts['pretty']: - catcommit(repo, node2, "") + catcommit(ui, repo, node2, "") patch.diff(repo, node1, node2, files=files, opts=patch.diffopts(ui, {'git': True})) @@ -102,14 +100,14 @@ if not opts['stdin']: break -def catcommit(repo, n, prefix, ctx=None): +def catcommit(ui, repo, n, prefix, ctx=None): nlprefix = '\n' + prefix; if ctx is None: ctx = repo.changectx(n) (p1, p2) = ctx.parents() - print "tree %s" % (hg.short(ctx.changeset()[0])) # use ctx.node() instead ?? - if p1: print "parent %s" % (hg.short(p1.node())) - if p2: print "parent %s" % (hg.short(p2.node())) + ui.write("tree %s\n" % short(ctx.changeset()[0])) # use ctx.node() instead ?? + if p1: ui.write("parent %s\n" % short(p1.node())) + if p2: ui.write("parent %s\n" % short(p2.node())) date = ctx.date() description = ctx.description().replace("\0", "") lines = description.splitlines() @@ -118,23 +116,24 @@ else: committer = ctx.user() - print "author %s %s %s" % (ctx.user(), int(date[0]), date[1]) - print "committer %s %s %s" % (committer, int(date[0]), date[1]) - print "revision %d" % ctx.rev() - print "" + ui.write("author %s %s %s\n" % (ctx.user(), int(date[0]), date[1])) + ui.write("committer %s %s %s\n" % (committer, int(date[0]), date[1])) + ui.write("revision %d\n" % ctx.rev()) + ui.write("branch %s\n\n" % ctx.branch()) + if prefix != "": - print "%s%s" % (prefix, description.replace('\n', nlprefix).strip()) + ui.write("%s%s\n" % (prefix, description.replace('\n', nlprefix).strip())) else: - print description + ui.write(description + "\n") if prefix: - sys.stdout.write('\0') + ui.write('\0') def base(ui, repo, node1, node2): """Output common ancestor information""" node1 = repo.lookup(node1) node2 = repo.lookup(node2) n = repo.changelog.ancestor(node1, node2) - print hg.short(n) + ui.write(short(n) + "\n") def catfile(ui, repo, type=None, r=None, **opts): """cat a specific revision""" @@ -157,10 +156,10 @@ while r: if type != "commit": - sys.stderr.write("aborting hg cat-file only understands commits\n") - sys.exit(1); + ui.warn("aborting hg cat-file only understands commits\n") + return 1; n = repo.lookup(r) - catcommit(repo, n, prefix) + catcommit(ui, repo, n, prefix) if opts['stdin']: try: (type, r) = raw_input().split(' '); @@ -174,7 +173,7 @@ # telling you which commits are reachable from the supplied ones via # a bitmask based on arg position. # you can specify a commit to stop at by starting the sha1 with ^ -def revtree(args, repo, full="tree", maxnr=0, parents=False): +def revtree(ui, args, repo, full="tree", maxnr=0, parents=False): def chlogwalk(): count = repo.changelog.count() i = count @@ -254,29 +253,29 @@ parentstr = "" if parents: pp = repo.changelog.parents(n) - if pp[0] != hg.nullid: - parentstr += " " + hg.short(pp[0]) - if pp[1] != hg.nullid: - parentstr += " " + hg.short(pp[1]) + if pp[0] != nullid: + parentstr += " " + short(pp[0]) + if pp[1] != nullid: + parentstr += " " + short(pp[1]) if not full: - print hg.short(n) + parentstr + ui.write("%s%s\n" % (short(n), parentstr)) elif full == "commit": - print hg.short(n) + parentstr - catcommit(repo, n, ' ', ctx) + ui.write("%s%s\n" % (short(n), parentstr)) + catcommit(ui, repo, n, ' ', ctx) else: (p1, p2) = repo.changelog.parents(n) - (h, h1, h2) = map(hg.short, (n, p1, p2)) + (h, h1, h2) = map(short, (n, p1, p2)) (i1, i2) = map(repo.changelog.rev, (p1, p2)) date = ctx.date()[0] - print "%s %s:%s" % (date, h, mask), + ui.write("%s %s:%s" % (date, h, mask)) mask = is_reachable(want_sha1, reachable, p1) - if i1 != hg.nullrev and mask > 0: - print "%s:%s " % (h1, mask), + if i1 != nullrev and mask > 0: + ui.write("%s:%s " % (h1, mask)), mask = is_reachable(want_sha1, reachable, p2) - if i2 != hg.nullrev and mask > 0: - print "%s:%s " % (h2, mask), - print "" + if i2 != nullrev and mask > 0: + ui.write("%s:%s " % (h2, mask)) + ui.write("\n") if maxnr and count >= maxnr: break count += 1 @@ -304,15 +303,15 @@ else: full = None copy = [x for x in revs] - revtree(copy, repo, full, opts['max_count'], opts['parents']) + revtree(ui, copy, repo, full, opts['max_count'], opts['parents']) def config(ui, repo, **opts): """print extension options""" def writeopt(name, value): - ui.write('k=%s\nv=%s\n' % (name, value)) + ui.write('k=%s\nv=%s\n' % (name, value)) writeopt('vdiff', ui.config('hgk', 'vdiff', '')) - + def view(ui, repo, *etc, **opts): "start interactive history viewer"
--- a/hgext/highlight.py Wed Apr 09 15:27:57 2008 -0700 +++ b/hgext/highlight.py Wed Apr 09 15:28:30 2008 -0700 @@ -15,108 +15,27 @@ [web] pygments_style = <style> -The default is 'colorful'. If this is changed the corresponding CSS -file should be re-generated by running - -# pygmentize -f html -S <newstyle> - +The default is 'colorful'. -- Adam Hupp <adam@hupp.org> - - """ from mercurial import demandimport -demandimport.ignore.extend(['pkgutil', - 'pkg_resources', - '__main__',]) - -import mimetypes +demandimport.ignore.extend(['pkgutil', 'pkg_resources', '__main__',]) -from mercurial.hgweb import hgweb_mod -from mercurial.hgweb.hgweb_mod import hgweb +from mercurial.hgweb import webcommands, webutil, common from mercurial import util -from mercurial.hgweb.common import paritygen -from mercurial.node import hex +from mercurial.templatefilters import filters from pygments import highlight from pygments.util import ClassNotFound -from pygments.lexers import guess_lexer_for_filename, TextLexer +from pygments.lexers import guess_lexer, guess_lexer_for_filename, TextLexer from pygments.formatters import HtmlFormatter -SYNTAX_CSS = ('\n<link rel="stylesheet" href="#staticurl#highlight.css" ' +SYNTAX_CSS = ('\n<link rel="stylesheet" href="{url}highlightcss" ' 'type="text/css" />') -class StripedHtmlFormatter(HtmlFormatter): - def __init__(self, stripecount, *args, **kwargs): - super(StripedHtmlFormatter, self).__init__(*args, **kwargs) - self.stripecount = stripecount - - def wrap(self, source, outfile): - yield 0, "<div class='highlight'>" - yield 0, "<pre>" - parity = paritygen(self.stripecount) - - for n, i in source: - if n == 1: - i = "<div class='parity%s'>%s</div>" % (parity.next(), i) - yield n, i - - yield 0, "</pre>" - yield 0, "</div>" - - -def pygments_format(filename, rawtext, forcetext, encoding, - stripecount, style): - etext = util.tolocal(rawtext) - if not forcetext: - try: - lexer = guess_lexer_for_filename(filename, etext, - encoding=util._encoding) - except ClassNotFound: - lexer = TextLexer(encoding=util._encoding) - else: - lexer = TextLexer(encoding=util._encoding) - - formatter = StripedHtmlFormatter(stripecount, style=style, - linenos='inline', encoding=encoding) - - return highlight(etext, lexer, formatter) - - -def filerevision_pygments(self, tmpl, fctx): - """Reimplement hgweb.filerevision to use syntax highlighting""" - f = fctx.path() - - rawtext = fctx.data() - text = rawtext - - fl = fctx.filelog() - n = fctx.filenode() - - mt = mimetypes.guess_type(f)[0] - - if util.binary(text): - mt = mt or 'application/octet-stream' - text = "(binary:%s)" % mt - - # don't parse (binary:...) as anything - forcetext = True - else: - mt = mt or 'text/plain' - forcetext = False - - def lines(text): - for line in text.splitlines(True): - yield {"line": line} - - style = self.config("web", "pygments_style", "colorful") - - text_formatted = lines(pygments_format(f, text, forcetext, self.encoding, - self.stripecount, style)) - - # override per-line template - tmpl.cache['fileline'] = '#line#' +def pygmentize(field, fctx, style, tmpl): # append a <link ...> to the syntax highlighting css old_header = ''.join(tmpl('header')) @@ -124,24 +43,58 @@ new_header = old_header + SYNTAX_CSS tmpl.cache['header'] = new_header - yield tmpl("filerevision", - file=f, - path=hgweb_mod._up(f), # fixme: make public - text=text_formatted, - raw=rawtext, - mimetype=mt, - rev=fctx.rev(), - node=hex(fctx.node()), - author=fctx.user(), - date=fctx.date(), - desc=fctx.description(), - parent=self.siblings(fctx.parents()), - child=self.siblings(fctx.children()), - rename=self.renamelink(fl, n), - permissions=fctx.manifest().flags(f)) + text = fctx.data() + if util.binary(text): + return + + # To get multi-line strings right, we can't format line-by-line + try: + lexer = guess_lexer_for_filename(fctx.path(), text, + encoding=util._encoding) + except ClassNotFound: + try: + lexer = guess_lexer(text, encoding=util._encoding) + except ClassNotFound: + lexer = TextLexer(encoding=util._encoding) + + formatter = HtmlFormatter(style=style, encoding=util._encoding) + + colorized = highlight(text, lexer, formatter) + # strip wrapping div + colorized = colorized[:colorized.find('\n</pre>')] + colorized = colorized[colorized.find('<pre>')+5:] + coloriter = iter(colorized.splitlines()) + + filters['colorize'] = lambda x: coloriter.next() + + oldl = tmpl.cache[field] + newl = oldl.replace('line|escape', 'line|colorize') + tmpl.cache[field] = newl + +web_filerevision = webcommands._filerevision +web_annotate = webcommands.annotate + +def filerevision_highlight(web, tmpl, fctx): + style = web.config('web', 'pygments_style', 'colorful') + pygmentize('fileline', fctx, style, tmpl) + return web_filerevision(web, tmpl, fctx) + +def annotate_highlight(web, req, tmpl): + fctx = webutil.filectx(web.repo, req) + style = web.config('web', 'pygments_style', 'colorful') + pygmentize('annotateline', fctx, style, tmpl) + return web_annotate(web, req, tmpl) + +def generate_css(web, req, tmpl): + pg_style = web.config('web', 'pygments_style', 'colorful') + fmter = HtmlFormatter(style = pg_style) + req.respond(common.HTTP_OK, 'text/css') + return ['/* pygments_style = %s */\n\n' % pg_style, fmter.get_style_defs('')] # monkeypatch in the new version -# should be safer than overriding the method in a derived class -# and then patching the class -hgweb.filerevision = filerevision_pygments + +webcommands._filerevision = filerevision_highlight +webcommands.annotate = annotate_highlight +webcommands.highlightcss = generate_css +webcommands.__all__.append('highlightcss')
--- a/hgext/imerge.py Wed Apr 09 15:27:57 2008 -0700 +++ b/hgext/imerge.py Wed Apr 09 15:28:30 2008 -0700 @@ -6,8 +6,9 @@ ''' from mercurial.i18n import _ -from mercurial.node import * -from mercurial import commands, cmdutil, dispatch, fancyopts, hg, merge, util +from mercurial.node import hex, short +from mercurial import commands, cmdutil, dispatch, fancyopts +from mercurial import hg, filemerge, util, revlog import os, tarfile class InvalidStateFileException(Exception): pass @@ -77,8 +78,9 @@ try: parents = [self.repo.changectx(n) for n in status[:2]] - except LookupError: - raise util.Abort('merge parent %s not in repository' % short(p)) + except revlog.LookupError, e: + raise util.Abort(_('merge parent %s not in repository') % + short(e.name)) status = status[2:] conflicts = int(status.pop(0)) * 3 @@ -118,7 +120,7 @@ # this could be greatly improved realmerge = os.environ.get('HGMERGE') if not interactive: - os.environ['HGMERGE'] = 'merge' + os.environ['HGMERGE'] = 'internal:merge' # The filemerge ancestor algorithm does not work if self.wctx # already has two parents (in normal merge it doesn't yet). But @@ -126,7 +128,7 @@ self.wctx._parents.pop() try: # TODO: we should probably revert the file if merge fails - return merge.filemerge(self.repo, fn, fd, fo, self.wctx, p2) + return filemerge.filemerge(self.repo, fn, fd, fo, self.wctx, p2) finally: self.wctx._parents.append(p2) if realmerge: @@ -135,13 +137,13 @@ del os.environ['HGMERGE'] def start(self, rev=None): - _filemerge = merge.filemerge - def filemerge(repo, fw, fd, fo, wctx, mctx): + _filemerge = filemerge.filemerge + def filemerge_(repo, fw, fd, fo, wctx, mctx): self.conflicts[fw] = (fd, fo) - merge.filemerge = filemerge + filemerge.filemerge = filemerge_ commands.merge(self.ui, self.repo, rev=rev) - merge.filemerge = _filemerge + filemerge.filemerge = _filemerge self.wctx = self.repo.workingctx() self.save()
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/hgext/inotify/__init__.py Wed Apr 09 15:28:30 2008 -0700 @@ -0,0 +1,104 @@ +# __init__.py - inotify-based status acceleration for Linux +# +# Copyright 2006, 2007, 2008 Bryan O'Sullivan <bos@serpentine.com> +# Copyright 2007, 2008 Brendan Cully <brendan@kublai.com> +# +# This software may be used and distributed according to the terms +# of the GNU General Public License, incorporated herein by reference. + +'''inotify-based status acceleration for Linux systems +''' + +# todo: socket permissions + +from mercurial.i18n import gettext as _ +from mercurial import cmdutil, util +import client, errno, os, server, socket +from weakref import proxy + +def serve(ui, repo, **opts): + '''start an inotify server for this repository''' + timeout = opts.get('timeout') + if timeout: + timeout = float(timeout) * 1e3 + + class service: + def init(self): + self.master = server.Master(ui, repo, timeout) + + def run(self): + try: + self.master.run() + finally: + self.master.shutdown() + + service = service() + cmdutil.service(opts, initfn=service.init, runfn=service.run) + +def reposetup(ui, repo): + if not repo.local(): + return + + # XXX: weakref until hg stops relying on __del__ + repo = proxy(repo) + + class inotifydirstate(repo.dirstate.__class__): + # Set to True if we're the inotify server, so we don't attempt + # to recurse. + inotifyserver = False + + def status(self, files, match, list_ignored, list_clean, + list_unknown=True): + try: + if not list_ignored and not self.inotifyserver: + result = client.query(ui, repo, files, match, False, + list_clean, list_unknown) + if result is not None: + return result + except socket.error, err: + if err[0] == errno.ECONNREFUSED: + ui.warn(_('(found dead inotify server socket; ' + 'removing it)\n')) + os.unlink(repo.join('inotify.sock')) + elif err[0] != errno.ENOENT: + raise + if ui.configbool('inotify', 'autostart'): + query = None + ui.debug(_('(starting inotify server)\n')) + try: + server.start(ui, repo) + query = client.query + except server.AlreadyStartedException, inst: + # another process may have started its own + # inotify server while this one was starting. + ui.debug(str(inst)) + query = client.query + except Exception, inst: + ui.warn(_('could not start inotify server: ' + '%s\n') % inst) + ui.print_exc() + + if query: + try: + return query(ui, repo, files or [], match, + list_ignored, list_clean, list_unknown) + except socket.error, err: + ui.warn(_('could not talk to new inotify ' + 'server: %s\n') % err[1]) + ui.print_exc() + + return super(inotifydirstate, self).status( + files, match or util.always, list_ignored, list_clean, + list_unknown) + + repo.dirstate.__class__ = inotifydirstate + +cmdtable = { + '^inserve': + (serve, + [('d', 'daemon', None, _('run server in background')), + ('', 'daemon-pipefds', '', _('used internally by daemon mode')), + ('t', 'idle-timeout', '', _('minutes to sit idle before exiting')), + ('', 'pid-file', '', _('name of file to write process ID to'))], + _('hg inserve [OPT]...')), + }
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/hgext/inotify/client.py Wed Apr 09 15:28:30 2008 -0700 @@ -0,0 +1,55 @@ +# client.py - inotify status client +# +# Copyright 2006, 2007, 2008 Bryan O'Sullivan <bos@serpentine.com> +# Copyright 2007, 2008 Brendan Cully <brendan@kublai.com> +# +# This software may be used and distributed according to the terms +# of the GNU General Public License, incorporated herein by reference. + +from mercurial.i18n import gettext as _ +from mercurial import ui +import common +import os, select, socket, stat, struct, sys + +def query(ui, repo, names, match, list_ignored, list_clean, list_unknown=True): + sock = socket.socket(socket.AF_UNIX) + sockpath = repo.join('inotify.sock') + sock.connect(sockpath) + + def genquery(): + for n in names or []: + yield n + states = 'almrx!' + if list_ignored: + raise ValueError('this is insanity') + if list_clean: states += 'n' + if list_unknown: states += '?' + yield states + + req = '\0'.join(genquery()) + + sock.sendall(chr(common.version)) + sock.sendall(req) + sock.shutdown(socket.SHUT_WR) + + cs = common.recvcs(sock) + version = ord(cs.read(1)) + + if version != common.version: + ui.warn(_('(inotify: received response from incompatible server ' + 'version %d)\n') % version) + return None + + try: + resphdr = struct.unpack(common.resphdrfmt, cs.read(common.resphdrsize)) + except struct.error: + return None + + def readnames(nbytes): + if nbytes: + names = cs.read(nbytes) + if names: + return filter(match, names.split('\0')) + return [] + + return map(readnames, resphdr)
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/hgext/inotify/common.py Wed Apr 09 15:28:30 2008 -0700 @@ -0,0 +1,26 @@ +# server.py - inotify common protocol code +# +# Copyright 2006, 2007, 2008 Bryan O'Sullivan <bos@serpentine.com> +# Copyright 2007, 2008 Brendan Cully <brendan@kublai.com> +# +# This software may be used and distributed according to the terms +# of the GNU General Public License, incorporated herein by reference. + +import cStringIO, socket, struct + +version = 1 + +resphdrfmt = '>llllllll' +resphdrsize = struct.calcsize(resphdrfmt) + +def recvcs(sock): + cs = cStringIO.StringIO() + s = True + try: + while s: + s = sock.recv(65536) + cs.write(s) + finally: + sock.shutdown(socket.SHUT_RD) + cs.seek(0) + return cs
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/hgext/inotify/linux/__init__.py Wed Apr 09 15:28:30 2008 -0700 @@ -0,0 +1,41 @@ +# __init__.py - low-level interfaces to the Linux inotify subsystem + +# Copyright 2006 Bryan O'Sullivan <bos@serpentine.com> + +# This library is free software; you can redistribute it and/or modify +# it under the terms of version 2.1 of the GNU Lesser General Public +# License, incorporated herein by reference. + +'''Low-level interface to the Linux inotify subsystem. + +The inotify subsystem provides an efficient mechanism for file status +monitoring and change notification. + +This package provides the low-level inotify system call interface and +associated constants and helper functions. + +For a higher-level interface that remains highly efficient, use the +inotify.watcher package.''' + +__author__ = "Bryan O'Sullivan <bos@serpentine.com>" + +from _inotify import * + +procfs_path = '/proc/sys/fs/inotify' + +def _read_procfs_value(name): + def read_value(): + try: + return int(open(procfs_path + '/' + name).read()) + except OSError, err: + return None + + read_value.__doc__ = '''Return the value of the %s setting from /proc. + + If inotify is not enabled on this system, return None.''' % name + + return read_value + +max_queued_events = _read_procfs_value('max_queued_events') +max_user_instances = _read_procfs_value('max_user_instances') +max_user_watches = _read_procfs_value('max_user_watches')
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/hgext/inotify/linux/_inotify.c Wed Apr 09 15:28:30 2008 -0700 @@ -0,0 +1,608 @@ +/* + * _inotify.c - Python extension interfacing to the Linux inotify subsystem + * + * Copyright 2006 Bryan O'Sullivan <bos@serpentine.com> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of version 2.1 of the GNU Lesser General + * Public License, incorporated herein by reference. + */ + +#include <Python.h> +#include <alloca.h> +#include <sys/inotify.h> +#include <stdint.h> +#include <sys/ioctl.h> +#include <unistd.h> + +static PyObject *init(PyObject *self, PyObject *args) +{ + PyObject *ret = NULL; + int fd = -1; + + if (!PyArg_ParseTuple(args, ":init")) + goto bail; + + Py_BEGIN_ALLOW_THREADS + fd = inotify_init(); + Py_END_ALLOW_THREADS + + if (fd == -1) { + PyErr_SetFromErrno(PyExc_OSError); + goto bail; + } + + ret = PyInt_FromLong(fd); + if (ret == NULL) + goto bail; + + goto done; + +bail: + if (fd != -1) + close(fd); + + Py_CLEAR(ret); + +done: + return ret; +} + +PyDoc_STRVAR( + init_doc, + "init() -> fd\n" + "\n" + "Initialise an inotify instance.\n" + "Return a file descriptor associated with a new inotify event queue."); + +static PyObject *add_watch(PyObject *self, PyObject *args) +{ + PyObject *ret = NULL; + uint32_t mask; + int wd = -1; + char *path; + int fd; + + if (!PyArg_ParseTuple(args, "isI:add_watch", &fd, &path, &mask)) + goto bail; + + Py_BEGIN_ALLOW_THREADS + wd = inotify_add_watch(fd, path, mask); + Py_END_ALLOW_THREADS + + if (wd == -1) { + PyErr_SetFromErrnoWithFilename(PyExc_OSError, path); + goto bail; + } + + ret = PyInt_FromLong(wd); + if (ret == NULL) + goto bail; + + goto done; + +bail: + if (wd != -1) + inotify_rm_watch(fd, wd); + + Py_CLEAR(ret); + +done: + return ret; +} + +PyDoc_STRVAR( + add_watch_doc, + "add_watch(fd, path, mask) -> wd\n" + "\n" + "Add a watch to an inotify instance, or modify an existing watch.\n" + "\n" + " fd: file descriptor returned by init()\n" + " path: path to watch\n" + " mask: mask of events to watch for\n" + "\n" + "Return a unique numeric watch descriptor for the inotify instance\n" + "mapped by the file descriptor."); + +static PyObject *remove_watch(PyObject *self, PyObject *args) +{ + PyObject *ret = NULL; + uint32_t wd; + int fd; + int r; + + if (!PyArg_ParseTuple(args, "iI:remove_watch", &fd, &wd)) + goto bail; + + Py_BEGIN_ALLOW_THREADS + r = inotify_rm_watch(fd, wd); + Py_END_ALLOW_THREADS + + if (r == -1) { + PyErr_SetFromErrno(PyExc_OSError); + goto bail; + } + + Py_INCREF(Py_None); + + goto done; + +bail: + Py_CLEAR(ret); + +done: + return ret; +} + +PyDoc_STRVAR( + remove_watch_doc, + "remove_watch(fd, wd)\n" + "\n" + " fd: file descriptor returned by init()\n" + " wd: watch descriptor returned by add_watch()\n" + "\n" + "Remove a watch associated with the watch descriptor wd from the\n" + "inotify instance associated with the file descriptor fd.\n" + "\n" + "Removing a watch causes an IN_IGNORED event to be generated for this\n" + "watch descriptor."); + +#define bit_name(x) {x, #x} + +static struct { + int bit; + const char *name; + PyObject *pyname; +} bit_names[] = { + bit_name(IN_ACCESS), + bit_name(IN_MODIFY), + bit_name(IN_ATTRIB), + bit_name(IN_CLOSE_WRITE), + bit_name(IN_CLOSE_NOWRITE), + bit_name(IN_OPEN), + bit_name(IN_MOVED_FROM), + bit_name(IN_MOVED_TO), + bit_name(IN_CREATE), + bit_name(IN_DELETE), + bit_name(IN_DELETE_SELF), + bit_name(IN_MOVE_SELF), + bit_name(IN_UNMOUNT), + bit_name(IN_Q_OVERFLOW), + bit_name(IN_IGNORED), + bit_name(IN_ONLYDIR), + bit_name(IN_DONT_FOLLOW), + bit_name(IN_MASK_ADD), + bit_name(IN_ISDIR), + bit_name(IN_ONESHOT), + {0} +}; + +static PyObject *decode_mask(int mask) +{ + PyObject *ret = PyList_New(0); + int i; + + if (ret == NULL) + goto bail; + + for (i = 0; bit_names[i].bit; i++) { + if (mask & bit_names[i].bit) { + if (bit_names[i].pyname == NULL) { + bit_names[i].pyname = PyString_FromString(bit_names[i].name); + if (bit_names[i].pyname == NULL) + goto bail; + } + Py_INCREF(bit_names[i].pyname); + if (PyList_Append(ret, bit_names[i].pyname) == -1) + goto bail; + } + } + + goto done; + +bail: + Py_CLEAR(ret); + +done: + return ret; +} + +static PyObject *pydecode_mask(PyObject *self, PyObject *args) +{ + int mask; + + if (!PyArg_ParseTuple(args, "i:decode_mask", &mask)) + return NULL; + + return decode_mask(mask); +} + +PyDoc_STRVAR( + decode_mask_doc, + "decode_mask(mask) -> list_of_strings\n" + "\n" + "Decode an inotify mask value into a list of strings that give the\n" + "name of each bit set in the mask."); + +static char doc[] = "Low-level inotify interface wrappers."; + +static void define_const(PyObject *dict, const char *name, uint32_t val) +{ + PyObject *pyval = PyInt_FromLong(val); + PyObject *pyname = PyString_FromString(name); + + if (!pyname || !pyval) + goto bail; + + PyDict_SetItem(dict, pyname, pyval); + +bail: + Py_XDECREF(pyname); + Py_XDECREF(pyval); +} + +static void define_consts(PyObject *dict) +{ + define_const(dict, "IN_ACCESS", IN_ACCESS); + define_const(dict, "IN_MODIFY", IN_MODIFY); + define_const(dict, "IN_ATTRIB", IN_ATTRIB); + define_const(dict, "IN_CLOSE_WRITE", IN_CLOSE_WRITE); + define_const(dict, "IN_CLOSE_NOWRITE", IN_CLOSE_NOWRITE); + define_const(dict, "IN_OPEN", IN_OPEN); + define_const(dict, "IN_MOVED_FROM", IN_MOVED_FROM); + define_const(dict, "IN_MOVED_TO", IN_MOVED_TO); + + define_const(dict, "IN_CLOSE", IN_CLOSE); + define_const(dict, "IN_MOVE", IN_MOVE); + + define_const(dict, "IN_CREATE", IN_CREATE); + define_const(dict, "IN_DELETE", IN_DELETE); + define_const(dict, "IN_DELETE_SELF", IN_DELETE_SELF); + define_const(dict, "IN_MOVE_SELF", IN_MOVE_SELF); + define_const(dict, "IN_UNMOUNT", IN_UNMOUNT); + define_const(dict, "IN_Q_OVERFLOW", IN_Q_OVERFLOW); + define_const(dict, "IN_IGNORED", IN_IGNORED); + + define_const(dict, "IN_ONLYDIR", IN_ONLYDIR); + define_const(dict, "IN_DONT_FOLLOW", IN_DONT_FOLLOW); + define_const(dict, "IN_MASK_ADD", IN_MASK_ADD); + define_const(dict, "IN_ISDIR", IN_ISDIR); + define_const(dict, "IN_ONESHOT", IN_ONESHOT); + define_const(dict, "IN_ALL_EVENTS", IN_ALL_EVENTS); +} + +struct event { + PyObject_HEAD + PyObject *wd; + PyObject *mask; + PyObject *cookie; + PyObject *name; +}; + +static PyObject *event_wd(PyObject *self, void *x) +{ + struct event *evt = (struct event *) self; + Py_INCREF(evt->wd); + return evt->wd; +} + +static PyObject *event_mask(PyObject *self, void *x) +{ + struct event *evt = (struct event *) self; + Py_INCREF(evt->mask); + return evt->mask; +} + +static PyObject *event_cookie(PyObject *self, void *x) +{ + struct event *evt = (struct event *) self; + Py_INCREF(evt->cookie); + return evt->cookie; +} + +static PyObject *event_name(PyObject *self, void *x) +{ + struct event *evt = (struct event *) self; + Py_INCREF(evt->name); + return evt->name; +} + +static struct PyGetSetDef event_getsets[] = { + {"wd", event_wd, NULL, + "watch descriptor"}, + {"mask", event_mask, NULL, + "event mask"}, + {"cookie", event_cookie, NULL, + "rename cookie, if rename-related event"}, + {"name", event_name, NULL, + "file name"}, + {NULL} +}; + +PyDoc_STRVAR( + event_doc, + "event: Structure describing an inotify event."); + +static PyObject *event_new(PyTypeObject *t, PyObject *a, PyObject *k) +{ + return (*t->tp_alloc)(t, 0); +} + +static void event_dealloc(struct event *evt) +{ + Py_XDECREF(evt->wd); + Py_XDECREF(evt->mask); + Py_XDECREF(evt->cookie); + Py_XDECREF(evt->name); + + (*evt->ob_type->tp_free)(evt); +} + +static PyObject *event_repr(struct event *evt) +{ + int wd = PyInt_AsLong(evt->wd); + int cookie = evt->cookie == Py_None ? -1 : PyInt_AsLong(evt->cookie); + PyObject *ret = NULL, *pymasks = NULL, *pymask = NULL; + PyObject *join = NULL; + char *maskstr; + + join = PyString_FromString("|"); + if (join == NULL) + goto bail; + + pymasks = decode_mask(PyInt_AsLong(evt->mask)); + if (pymasks == NULL) + goto bail; + + pymask = _PyString_Join(join, pymasks); + if (pymask == NULL) + goto bail; + + maskstr = PyString_AsString(pymask); + + if (evt->name != Py_None) { + PyObject *pyname = PyString_Repr(evt->name, 1); + char *name = pyname ? PyString_AsString(pyname) : "???"; + + if (cookie == -1) + ret = PyString_FromFormat("event(wd=%d, mask=%s, name=%s)", + wd, maskstr, name); + else + ret = PyString_FromFormat("event(wd=%d, mask=%s, " + "cookie=0x%x, name=%s)", + wd, maskstr, cookie, name); + + Py_XDECREF(pyname); + } else { + if (cookie == -1) + ret = PyString_FromFormat("event(wd=%d, mask=%s)", + wd, maskstr); + else { + ret = PyString_FromFormat("event(wd=%d, mask=%s, cookie=0x%x)", + wd, maskstr, cookie); + } + } + + goto done; +bail: + Py_CLEAR(ret); + +done: + Py_XDECREF(pymask); + Py_XDECREF(pymasks); + Py_XDECREF(join); + + return ret; +} + +static PyTypeObject event_type = { + PyObject_HEAD_INIT(NULL) + 0, /*ob_size*/ + "_inotify.event", /*tp_name*/ + sizeof(struct event), /*tp_basicsize*/ + 0, /*tp_itemsize*/ + (destructor)event_dealloc, /*tp_dealloc*/ + 0, /*tp_print*/ + 0, /*tp_getattr*/ + 0, /*tp_setattr*/ + 0, /*tp_compare*/ + (reprfunc)event_repr, /*tp_repr*/ + 0, /*tp_as_number*/ + 0, /*tp_as_sequence*/ + 0, /*tp_as_mapping*/ + 0, /*tp_hash */ + 0, /*tp_call*/ + 0, /*tp_str*/ + 0, /*tp_getattro*/ + 0, /*tp_setattro*/ + 0, /*tp_as_buffer*/ + Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /*tp_flags*/ + event_doc, /* tp_doc */ + 0, /* tp_traverse */ + 0, /* tp_clear */ + 0, /* tp_richcompare */ + 0, /* tp_weaklistoffset */ + 0, /* tp_iter */ + 0, /* tp_iternext */ + 0, /* tp_methods */ + 0, /* tp_members */ + event_getsets, /* tp_getset */ + 0, /* tp_base */ + 0, /* tp_dict */ + 0, /* tp_descr_get */ + 0, /* tp_descr_set */ + 0, /* tp_dictoffset */ + 0, /* tp_init */ + 0, /* tp_alloc */ + event_new, /* tp_new */ +}; + +PyObject *read_events(PyObject *self, PyObject *args) +{ + PyObject *ctor_args = NULL; + PyObject *pybufsize = NULL; + PyObject *ret = NULL; + int bufsize = 65536; + char *buf = NULL; + int nread, pos; + int fd; + + if (!PyArg_ParseTuple(args, "i|O:read", &fd, &pybufsize)) + goto bail; + + if (pybufsize && pybufsize != Py_None) + bufsize = PyInt_AsLong(pybufsize); + + ret = PyList_New(0); + if (ret == NULL) + goto bail; + + if (bufsize <= 0) { + int r; + + Py_BEGIN_ALLOW_THREADS + r = ioctl(fd, FIONREAD, &bufsize); + Py_END_ALLOW_THREADS + + if (r == -1) { + PyErr_SetFromErrno(PyExc_OSError); + goto bail; + } + if (bufsize == 0) + goto done; + } + else { + static long name_max; + static long name_fd = -1; + long min; + + if (name_fd != fd) { + name_fd = fd; + Py_BEGIN_ALLOW_THREADS + name_max = fpathconf(fd, _PC_NAME_MAX); + Py_END_ALLOW_THREADS + } + + min = sizeof(struct inotify_event) + name_max + 1; + + if (bufsize < min) { + PyErr_Format(PyExc_ValueError, "bufsize must be at least %d", + (int) min); + goto bail; + } + } + + buf = alloca(bufsize); + + Py_BEGIN_ALLOW_THREADS + nread = read(fd, buf, bufsize); + Py_END_ALLOW_THREADS + + if (nread == -1) { + PyErr_SetFromErrno(PyExc_OSError); + goto bail; + } + + ctor_args = PyTuple_New(0); + + if (ctor_args == NULL) + goto bail; + + pos = 0; + + while (pos < nread) { + struct inotify_event *in = (struct inotify_event *) (buf + pos); + struct event *evt; + PyObject *obj; + + obj = PyObject_CallObject((PyObject *) &event_type, ctor_args); + + if (obj == NULL) + goto bail; + + evt = (struct event *) obj; + + evt->wd = PyInt_FromLong(in->wd); + evt->mask = PyInt_FromLong(in->mask); + if (in->mask & IN_MOVE) + evt->cookie = PyInt_FromLong(in->cookie); + else { + Py_INCREF(Py_None); + evt->cookie = Py_None; + } + if (in->len) + evt->name = PyString_FromString(in->name); + else { + Py_INCREF(Py_None); + evt->name = Py_None; + } + + if (!evt->wd || !evt->mask || !evt->cookie || !evt->name) + goto mybail; + + if (PyList_Append(ret, obj) == -1) + goto mybail; + + pos += sizeof(struct inotify_event) + in->len; + continue; + + mybail: + Py_CLEAR(evt->wd); + Py_CLEAR(evt->mask); + Py_CLEAR(evt->cookie); + Py_CLEAR(evt->name); + Py_DECREF(obj); + + goto bail; + } + + goto done; + +bail: + Py_CLEAR(ret); + +done: + Py_XDECREF(ctor_args); + + return ret; +} + +PyDoc_STRVAR( + read_doc, + "read(fd, bufsize[=65536]) -> list_of_events\n" + "\n" + "\nRead inotify events from a file descriptor.\n" + "\n" + " fd: file descriptor returned by init()\n" + " bufsize: size of buffer to read into, in bytes\n" + "\n" + "Return a list of event objects.\n" + "\n" + "If bufsize is > 0, block until events are available to be read.\n" + "Otherwise, immediately return all events that can be read without\n" + "blocking."); + + +static PyMethodDef methods[] = { + {"init", init, METH_VARARGS, init_doc}, + {"add_watch", add_watch, METH_VARARGS, add_watch_doc}, + {"remove_watch", remove_watch, METH_VARARGS, remove_watch_doc}, + {"read", read_events, METH_VARARGS, read_doc}, + {"decode_mask", pydecode_mask, METH_VARARGS, decode_mask_doc}, + {NULL}, +}; + +void init_inotify(void) +{ + PyObject *mod, *dict; + + if (PyType_Ready(&event_type) == -1) + return; + + mod = Py_InitModule3("_inotify", methods, doc); + + dict = PyModule_GetDict(mod); + + if (dict) + define_consts(dict); +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/hgext/inotify/linux/watcher.py Wed Apr 09 15:28:30 2008 -0700 @@ -0,0 +1,335 @@ +# watcher.py - high-level interfaces to the Linux inotify subsystem + +# Copyright 2006 Bryan O'Sullivan <bos@serpentine.com> + +# This library is free software; you can redistribute it and/or modify +# it under the terms of version 2.1 of the GNU Lesser General Public +# License, incorporated herein by reference. + +'''High-level interfaces to the Linux inotify subsystem. + +The inotify subsystem provides an efficient mechanism for file status +monitoring and change notification. + +The Watcher class hides the low-level details of the inotify +interface, and provides a Pythonic wrapper around it. It generates +events that provide somewhat more information than raw inotify makes +available. + +The AutoWatcher class is more useful, as it automatically watches +newly-created directories on your behalf.''' + +__author__ = "Bryan O'Sullivan <bos@serpentine.com>" + +import _inotify as inotify +import array +import errno +import fcntl +import os +import termios + + +class Event(object): + '''Derived inotify event class. + + The following fields are available: + + mask: event mask, indicating what kind of event this is + + cookie: rename cookie, if a rename-related event + + path: path of the directory in which the event occurred + + name: name of the directory entry to which the event occurred + (may be None if the event happened to a watched directory) + + fullpath: complete path at which the event occurred + + wd: watch descriptor that triggered this event''' + + __slots__ = ( + 'cookie', + 'fullpath', + 'mask', + 'name', + 'path', + 'raw', + 'wd', + ) + + def __init__(self, raw, path): + self.path = path + self.raw = raw + if raw.name: + self.fullpath = path + '/' + raw.name + else: + self.fullpath = path + + self.wd = raw.wd + self.mask = raw.mask + self.cookie = raw.cookie + self.name = raw.name + + def __repr__(self): + r = repr(self.raw) + return 'Event(path=' + repr(self.path) + ', ' + r[r.find('(')+1:] + + +_event_props = { + 'access': 'File was accessed', + 'modify': 'File was modified', + 'attrib': 'Attribute of a directory entry was changed', + 'close_write': 'File was closed after being written to', + 'close_nowrite': 'File was closed without being written to', + 'open': 'File was opened', + 'moved_from': 'Directory entry was renamed from this name', + 'moved_to': 'Directory entry was renamed to this name', + 'create': 'Directory entry was created', + 'delete': 'Directory entry was deleted', + 'delete_self': 'The watched directory entry was deleted', + 'move_self': 'The watched directory entry was renamed', + 'unmount': 'Directory was unmounted, and can no longer be watched', + 'q_overflow': 'Kernel dropped events due to queue overflow', + 'ignored': 'Directory entry is no longer being watched', + 'isdir': 'Event occurred on a directory', + } + +for k, v in _event_props.iteritems(): + mask = getattr(inotify, 'IN_' + k.upper()) + def getter(self): + return self.mask & mask + getter.__name__ = k + getter.__doc__ = v + setattr(Event, k, property(getter, doc=v)) + +del _event_props + + +class Watcher(object): + '''Provide a Pythonic interface to the low-level inotify API. + + Also adds derived information to each event that is not available + through the normal inotify API, such as directory name.''' + + __slots__ = ( + 'fd', + '_paths', + '_wds', + ) + + def __init__(self): + '''Create a new inotify instance.''' + + self.fd = inotify.init() + self._paths = {} + self._wds = {} + + def fileno(self): + '''Return the file descriptor this watcher uses. + + Useful for passing to select and poll.''' + + return self.fd + + def add(self, path, mask): + '''Add or modify a watch. + + Return the watch descriptor added or modified.''' + + path = os.path.normpath(path) + wd = inotify.add_watch(self.fd, path, mask) + self._paths[path] = wd, mask + self._wds[wd] = path, mask + return wd + + def remove(self, wd): + '''Remove the given watch.''' + + inotify.remove_watch(self.fd, wd) + self._remove(wd) + + def _remove(self, wd): + path_mask = self._wds.pop(wd, None) + if path_mask is not None: + self._paths.pop(path_mask[0]) + + def path(self, path): + '''Return a (watch descriptor, event mask) pair for the given path. + + If the path is not being watched, return None.''' + + return self._paths.get(path) + + def wd(self, wd): + '''Return a (path, event mask) pair for the given watch descriptor. + + If the watch descriptor is not valid or not associated with + this watcher, return None.''' + + return self._wds.get(wd) + + def read(self, bufsize=None): + '''Read a list of queued inotify events. + + If bufsize is zero, only return those events that can be read + immediately without blocking. Otherwise, block until events are + available.''' + + events = [] + for evt in inotify.read(self.fd, bufsize): + events.append(Event(evt, self._wds[evt.wd][0])) + if evt.mask & inotify.IN_IGNORED: + self._remove(evt.wd) + elif evt.mask & inotify.IN_UNMOUNT: + self.close() + return events + + def close(self): + '''Shut down this watcher. + + All subsequent method calls are likely to raise exceptions.''' + + os.close(self.fd) + self.fd = None + self._paths = None + self._wds = None + + def __len__(self): + '''Return the number of active watches.''' + + return len(self._paths) + + def __iter__(self): + '''Yield a (path, watch descriptor, event mask) tuple for each + entry being watched.''' + + for path, (wd, mask) in self._paths.iteritems(): + yield path, wd, mask + + def __del__(self): + if self.fd is not None: + os.close(self.fd) + + ignored_errors = [errno.ENOENT, errno.EPERM, errno.ENOTDIR] + + def add_iter(self, path, mask, onerror=None): + '''Add or modify watches over path and its subdirectories. + + Yield each added or modified watch descriptor. + + To ensure that this method runs to completion, you must + iterate over all of its results, even if you do not care what + they are. For example: + + for wd in w.add_iter(path, mask): + pass + + By default, errors are ignored. If optional arg "onerror" is + specified, it should be a function; it will be called with one + argument, an OSError instance. It can report the error to + continue with the walk, or raise the exception to abort the + walk.''' + + # Add the IN_ONLYDIR flag to the event mask, to avoid a possible + # race when adding a subdirectory. In the time between the + # event being queued by the kernel and us processing it, the + # directory may have been deleted, or replaced with a different + # kind of entry with the same name. + + submask = mask | inotify.IN_ONLYDIR + + try: + yield self.add(path, mask) + except OSError, err: + if onerror and err.errno not in self.ignored_errors: + onerror(err) + for root, dirs, names in os.walk(path, topdown=False, onerror=onerror): + for d in dirs: + try: + yield self.add(root + '/' + d, submask) + except OSError, err: + if onerror and err.errno not in self.ignored_errors: + onerror(err) + + def add_all(self, path, mask, onerror=None): + '''Add or modify watches over path and its subdirectories. + + Return a list of added or modified watch descriptors. + + By default, errors are ignored. If optional arg "onerror" is + specified, it should be a function; it will be called with one + argument, an OSError instance. It can report the error to + continue with the walk, or raise the exception to abort the + walk.''' + + return [w for w in self.add_iter(path, mask, onerror)] + + +class AutoWatcher(Watcher): + '''Watcher class that automatically watches newly created directories.''' + + __slots__ = ( + 'addfilter', + ) + + def __init__(self, addfilter=None): + '''Create a new inotify instance. + + This instance will automatically watch newly created + directories. + + If the optional addfilter parameter is not None, it must be a + callable that takes one parameter. It will be called each time + a directory is about to be automatically watched. If it returns + True, the directory will be watched if it still exists, + otherwise, it will beb skipped.''' + + super(AutoWatcher, self).__init__() + self.addfilter = addfilter + + _dir_create_mask = inotify.IN_ISDIR | inotify.IN_CREATE + + def read(self, bufsize=None): + events = super(AutoWatcher, self).read(bufsize) + for evt in events: + if evt.mask & self._dir_create_mask == self._dir_create_mask: + if self.addfilter is None or self.addfilter(evt): + parentmask = self._wds[evt.wd][1] + # See note about race avoidance via IN_ONLYDIR above. + mask = parentmask | inotify.IN_ONLYDIR + try: + self.add_all(evt.fullpath, mask) + except OSError, err: + if err.errno not in self.ignored_errors: + raise + return events + + +class Threshold(object): + '''Class that indicates whether a file descriptor has reached a + threshold of readable bytes available. + + This class is not thread-safe.''' + + __slots__ = ( + 'fd', + 'threshold', + '_iocbuf', + ) + + def __init__(self, fd, threshold=1024): + self.fd = fd + self.threshold = threshold + self._iocbuf = array.array('i', [0]) + + def readable(self): + '''Return the number of bytes readable on this file descriptor.''' + + fcntl.ioctl(self.fd, termios.FIONREAD, self._iocbuf, True) + return self._iocbuf[0] + + def __call__(self): + '''Indicate whether the number of readable bytes has met or + exceeded the threshold.''' + + return self.readable() >= self.threshold
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/hgext/inotify/server.py Wed Apr 09 15:28:30 2008 -0700 @@ -0,0 +1,717 @@ +# server.py - inotify status server +# +# Copyright 2006, 2007, 2008 Bryan O'Sullivan <bos@serpentine.com> +# Copyright 2007, 2008 Brendan Cully <brendan@kublai.com> +# +# This software may be used and distributed according to the terms +# of the GNU General Public License, incorporated herein by reference. + +from mercurial.i18n import gettext as _ +from mercurial import osutil, ui, util +import common +import errno, os, select, socket, stat, struct, sys, time + +try: + import hgext.inotify.linux as inotify + from hgext.inotify.linux import watcher +except ImportError: + print >> sys.stderr, '*** native support is required for this extension' + raise + +class AlreadyStartedException(Exception): pass + +def join(a, b): + if a: + if a[-1] == '/': + return a + b + return a + '/' + b + return b + +walk_ignored_errors = (errno.ENOENT, errno.ENAMETOOLONG) + +def walkrepodirs(repo): + '''Iterate over all subdirectories of this repo. + Exclude the .hg directory, any nested repos, and ignored dirs.''' + rootslash = repo.root + os.sep + def walkit(dirname, top): + hginside = False + try: + for name, kind in osutil.listdir(rootslash + dirname): + if kind == stat.S_IFDIR: + if name == '.hg': + hginside = True + if not top: break + else: + d = join(dirname, name) + if repo.dirstate._ignore(d): + continue + for subdir, hginsub in walkit(d, False): + if not hginsub: + yield subdir, False + except OSError, err: + if err.errno not in walk_ignored_errors: + raise + yield rootslash + dirname, hginside + for dirname, hginside in walkit('', True): + yield dirname + +def walk(repo, root): + '''Like os.walk, but only yields regular files.''' + + # This function is critical to performance during startup. + + reporoot = root == '' + rootslash = repo.root + os.sep + + def walkit(root, reporoot): + files, dirs = [], [] + hginside = False + + try: + fullpath = rootslash + root + for name, kind in osutil.listdir(fullpath): + if kind == stat.S_IFDIR: + if name == '.hg': + hginside = True + if reporoot: + continue + else: + break + dirs.append(name) + elif kind in (stat.S_IFREG, stat.S_IFLNK): + path = join(root, name) + files.append((name, kind)) + + yield hginside, fullpath, dirs, files + + for subdir in dirs: + path = join(root, subdir) + if repo.dirstate._ignore(path): + continue + for result in walkit(path, False): + if not result[0]: + yield result + except OSError, err: + if err.errno not in walk_ignored_errors: + raise + for result in walkit(root, reporoot): + yield result[1:] + +def _explain_watch_limit(ui, repo, count): + path = '/proc/sys/fs/inotify/max_user_watches' + try: + limit = int(file(path).read()) + except IOError, err: + if err.errno != errno.ENOENT: + raise + raise util.Abort(_('this system does not seem to ' + 'support inotify')) + ui.warn(_('*** the current per-user limit on the number ' + 'of inotify watches is %s\n') % limit) + ui.warn(_('*** this limit is too low to watch every ' + 'directory in this repository\n')) + ui.warn(_('*** counting directories: ')) + ndirs = len(list(walkrepodirs(repo))) + ui.warn(_('found %d\n') % ndirs) + newlimit = min(limit, 1024) + while newlimit < ((limit + ndirs) * 1.1): + newlimit *= 2 + ui.warn(_('*** to raise the limit from %d to %d (run as root):\n') % + (limit, newlimit)) + ui.warn(_('*** echo %d > %s\n') % (newlimit, path)) + raise util.Abort(_('cannot watch %s until inotify watch limit is raised') + % repo.root) + +class Watcher(object): + poll_events = select.POLLIN + statuskeys = 'almr!?' + + def __init__(self, ui, repo, master): + self.ui = ui + self.repo = repo + self.wprefix = self.repo.wjoin('') + self.timeout = None + self.master = master + self.mask = ( + inotify.IN_ATTRIB | + inotify.IN_CREATE | + inotify.IN_DELETE | + inotify.IN_DELETE_SELF | + inotify.IN_MODIFY | + inotify.IN_MOVED_FROM | + inotify.IN_MOVED_TO | + inotify.IN_MOVE_SELF | + inotify.IN_ONLYDIR | + inotify.IN_UNMOUNT | + 0) + try: + self.watcher = watcher.Watcher() + except OSError, err: + raise util.Abort(_('inotify service not available: %s') % + err.strerror) + self.threshold = watcher.Threshold(self.watcher) + self.registered = True + self.fileno = self.watcher.fileno + + self.repo.dirstate.__class__.inotifyserver = True + + self.tree = {} + self.statcache = {} + self.statustrees = dict([(s, {}) for s in self.statuskeys]) + + self.watches = 0 + self.last_event = None + + self.eventq = {} + self.deferred = 0 + + self.ds_info = self.dirstate_info() + self.scan() + + def event_time(self): + last = self.last_event + now = time.time() + self.last_event = now + + if last is None: + return 'start' + delta = now - last + if delta < 5: + return '+%.3f' % delta + if delta < 50: + return '+%.2f' % delta + return '+%.1f' % delta + + def dirstate_info(self): + try: + st = os.lstat(self.repo.join('dirstate')) + return st.st_mtime, st.st_ino + except OSError, err: + if err.errno != errno.ENOENT: + raise + return 0, 0 + + def add_watch(self, path, mask): + if not path: + return + if self.watcher.path(path) is None: + if self.ui.debugflag: + self.ui.note(_('watching %r\n') % path[len(self.wprefix):]) + try: + self.watcher.add(path, mask) + self.watches += 1 + except OSError, err: + if err.errno in (errno.ENOENT, errno.ENOTDIR): + return + if err.errno != errno.ENOSPC: + raise + _explain_watch_limit(self.ui, self.repo, self.watches) + + def setup(self): + self.ui.note(_('watching directories under %r\n') % self.repo.root) + self.add_watch(self.repo.path, inotify.IN_DELETE) + self.check_dirstate() + + def wpath(self, evt): + path = evt.fullpath + if path == self.repo.root: + return '' + if path.startswith(self.wprefix): + return path[len(self.wprefix):] + raise 'wtf? ' + path + + def dir(self, tree, path): + if path: + for name in path.split('/'): + tree.setdefault(name, {}) + tree = tree[name] + return tree + + def lookup(self, path, tree): + if path: + try: + for name in path.split('/'): + tree = tree[name] + except KeyError: + return 'x' + except TypeError: + return 'd' + return tree + + def split(self, path): + c = path.rfind('/') + if c == -1: + return '', path + return path[:c], path[c+1:] + + def filestatus(self, fn, st): + try: + type_, mode, size, time = self.repo.dirstate._map[fn][:4] + except KeyError: + type_ = '?' + if type_ == 'n': + if not st: + return '!' + st_mode, st_size, st_mtime = st + if size and (size != st_size or (mode ^ st_mode) & 0100): + return 'm' + if time != int(st_mtime): + return 'l' + return 'n' + if type_ in 'ma' and not st: + return '!' + if type_ == '?' and self.repo.dirstate._ignore(fn): + return 'i' + return type_ + + def updatestatus(self, wfn, st=None, status=None, oldstatus=None): + if st: + status = self.filestatus(wfn, st) + else: + self.statcache.pop(wfn, None) + root, fn = self.split(wfn) + d = self.dir(self.tree, root) + if oldstatus is None: + oldstatus = d.get(fn) + isdir = False + if oldstatus: + try: + if not status: + if oldstatus in 'almn': + status = '!' + elif oldstatus == 'r': + status = 'r' + except TypeError: + # oldstatus may be a dict left behind by a deleted + # directory + isdir = True + else: + if oldstatus in self.statuskeys and oldstatus != status: + del self.dir(self.statustrees[oldstatus], root)[fn] + if self.ui.debugflag and oldstatus != status: + if isdir: + self.ui.note('status: %r dir(%d) -> %s\n' % + (wfn, len(oldstatus), status)) + else: + self.ui.note('status: %r %s -> %s\n' % + (wfn, oldstatus, status)) + if not isdir: + if status and status != 'i': + d[fn] = status + if status in self.statuskeys: + dd = self.dir(self.statustrees[status], root) + if oldstatus != status or fn not in dd: + dd[fn] = status + else: + d.pop(fn, None) + + def check_deleted(self, key): + # Files that had been deleted but were present in the dirstate + # may have vanished from the dirstate; we must clean them up. + nuke = [] + for wfn, ignore in self.walk(key, self.statustrees[key]): + if wfn not in self.repo.dirstate: + nuke.append(wfn) + for wfn in nuke: + root, fn = self.split(wfn) + del self.dir(self.statustrees[key], root)[fn] + del self.dir(self.tree, root)[fn] + + def scan(self, topdir=''): + self.handle_timeout() + ds = self.repo.dirstate._map.copy() + self.add_watch(join(self.repo.root, topdir), self.mask) + for root, dirs, entries in walk(self.repo, topdir): + for d in dirs: + self.add_watch(join(root, d), self.mask) + wroot = root[len(self.wprefix):] + d = self.dir(self.tree, wroot) + for fn, kind in entries: + wfn = join(wroot, fn) + self.updatestatus(wfn, self.getstat(wfn)) + ds.pop(wfn, None) + wtopdir = topdir + if wtopdir and wtopdir[-1] != '/': + wtopdir += '/' + for wfn, state in ds.iteritems(): + if not wfn.startswith(wtopdir): + continue + status = state[0] + st = self.getstat(wfn) + if status == 'r' and not st: + self.updatestatus(wfn, st, status=status) + else: + self.updatestatus(wfn, st, oldstatus=status) + self.check_deleted('!') + self.check_deleted('r') + + def check_dirstate(self): + ds_info = self.dirstate_info() + if ds_info == self.ds_info: + return + self.ds_info = ds_info + if not self.ui.debugflag: + self.last_event = None + self.ui.note(_('%s dirstate reload\n') % self.event_time()) + self.repo.dirstate.invalidate() + self.scan() + self.ui.note(_('%s end dirstate reload\n') % self.event_time()) + + def walk(self, states, tree, prefix=''): + # This is the "inner loop" when talking to the client. + + for name, val in tree.iteritems(): + path = join(prefix, name) + try: + if val in states: + yield path, val + except TypeError: + for p in self.walk(states, val, path): + yield p + + def update_hgignore(self): + # An update of the ignore file can potentially change the + # states of all unknown and ignored files. + + # XXX If the user has other ignore files outside the repo, or + # changes their list of ignore files at run time, we'll + # potentially never see changes to them. We could get the + # client to report to us what ignore data they're using. + # But it's easier to do nothing than to open that can of + # worms. + + if self.repo.dirstate.ignorefunc is not None: + self.repo.dirstate.ignorefunc = None + self.ui.note('rescanning due to .hgignore change\n') + self.scan() + + def getstat(self, wpath): + try: + return self.statcache[wpath] + except KeyError: + try: + return self.stat(wpath) + except OSError, err: + if err.errno != errno.ENOENT: + raise + + def stat(self, wpath): + try: + st = os.lstat(join(self.wprefix, wpath)) + ret = st.st_mode, st.st_size, st.st_mtime + self.statcache[wpath] = ret + return ret + except OSError, err: + self.statcache.pop(wpath, None) + raise + + def created(self, wpath): + if wpath == '.hgignore': + self.update_hgignore() + try: + st = self.stat(wpath) + if stat.S_ISREG(st[0]): + self.updatestatus(wpath, st) + except OSError, err: + pass + + def modified(self, wpath): + if wpath == '.hgignore': + self.update_hgignore() + try: + st = self.stat(wpath) + if stat.S_ISREG(st[0]): + if self.repo.dirstate[wpath] in 'lmn': + self.updatestatus(wpath, st) + except OSError: + pass + + def deleted(self, wpath): + if wpath == '.hgignore': + self.update_hgignore() + elif wpath.startswith('.hg/'): + if wpath == '.hg/wlock': + self.check_dirstate() + return + + self.updatestatus(wpath, None) + + def schedule_work(self, wpath, evt): + self.eventq.setdefault(wpath, []) + prev = self.eventq[wpath] + try: + if prev and evt == 'm' and prev[-1] in 'cm': + return + self.eventq[wpath].append(evt) + finally: + self.deferred += 1 + self.timeout = 250 + + def deferred_event(self, wpath, evt): + if evt == 'c': + self.created(wpath) + elif evt == 'm': + self.modified(wpath) + elif evt == 'd': + self.deleted(wpath) + + def process_create(self, wpath, evt): + if self.ui.debugflag: + self.ui.note(_('%s event: created %s\n') % + (self.event_time(), wpath)) + + if evt.mask & inotify.IN_ISDIR: + self.scan(wpath) + else: + self.schedule_work(wpath, 'c') + + def process_delete(self, wpath, evt): + if self.ui.debugflag: + self.ui.note(('%s event: deleted %s\n') % + (self.event_time(), wpath)) + + if evt.mask & inotify.IN_ISDIR: + self.scan(wpath) + else: + self.schedule_work(wpath, 'd') + + def process_modify(self, wpath, evt): + if self.ui.debugflag: + self.ui.note(_('%s event: modified %s\n') % + (self.event_time(), wpath)) + + if not (evt.mask & inotify.IN_ISDIR): + self.schedule_work(wpath, 'm') + + def process_unmount(self, evt): + self.ui.warn(_('filesystem containing %s was unmounted\n') % + evt.fullpath) + sys.exit(0) + + def handle_event(self, fd, event): + if self.ui.debugflag: + self.ui.note('%s readable: %d bytes\n' % + (self.event_time(), self.threshold.readable())) + if not self.threshold(): + if self.registered: + if self.ui.debugflag: + self.ui.note('%s below threshold - unhooking\n' % + (self.event_time())) + self.master.poll.unregister(fd) + self.registered = False + self.timeout = 250 + else: + self.read_events() + + def read_events(self, bufsize=None): + events = self.watcher.read(bufsize) + if self.ui.debugflag: + self.ui.note('%s reading %d events\n' % + (self.event_time(), len(events))) + for evt in events: + wpath = self.wpath(evt) + if evt.mask & inotify.IN_UNMOUNT: + self.process_unmount(wpath, evt) + elif evt.mask & (inotify.IN_MODIFY | inotify.IN_ATTRIB): + self.process_modify(wpath, evt) + elif evt.mask & (inotify.IN_DELETE | inotify.IN_DELETE_SELF | + inotify.IN_MOVED_FROM): + self.process_delete(wpath, evt) + elif evt.mask & (inotify.IN_CREATE | inotify.IN_MOVED_TO): + self.process_create(wpath, evt) + + def handle_timeout(self): + if not self.registered: + if self.ui.debugflag: + self.ui.note('%s hooking back up with %d bytes readable\n' % + (self.event_time(), self.threshold.readable())) + self.read_events(0) + self.master.poll.register(self, select.POLLIN) + self.registered = True + + if self.eventq: + if self.ui.debugflag: + self.ui.note('%s processing %d deferred events as %d\n' % + (self.event_time(), self.deferred, + len(self.eventq))) + eventq = self.eventq.items() + eventq.sort() + for wpath, evts in eventq: + for evt in evts: + self.deferred_event(wpath, evt) + self.eventq.clear() + self.deferred = 0 + self.timeout = None + + def shutdown(self): + self.watcher.close() + +class Server(object): + poll_events = select.POLLIN + + def __init__(self, ui, repo, watcher, timeout): + self.ui = ui + self.repo = repo + self.watcher = watcher + self.timeout = timeout + self.sock = socket.socket(socket.AF_UNIX) + self.sockpath = self.repo.join('inotify.sock') + try: + self.sock.bind(self.sockpath) + except socket.error, err: + if err[0] == errno.EADDRINUSE: + raise AlreadyStartedException(_('could not start server: %s') \ + % err[1]) + raise + self.sock.listen(5) + self.fileno = self.sock.fileno + + def handle_timeout(self): + pass + + def handle_event(self, fd, event): + sock, addr = self.sock.accept() + + cs = common.recvcs(sock) + version = ord(cs.read(1)) + + sock.sendall(chr(common.version)) + + if version != common.version: + self.ui.warn(_('received query from incompatible client ' + 'version %d\n') % version) + return + + names = cs.read().split('\0') + + states = names.pop() + + self.ui.note(_('answering query for %r\n') % states) + + if self.watcher.timeout: + # We got a query while a rescan is pending. Make sure we + # rescan before responding, or we could give back a wrong + # answer. + self.watcher.handle_timeout() + + if not names: + def genresult(states, tree): + for fn, state in self.watcher.walk(states, tree): + yield fn + else: + def genresult(states, tree): + for fn in names: + l = self.watcher.lookup(fn, tree) + try: + if l in states: + yield fn + except TypeError: + for f, s in self.watcher.walk(states, l, fn): + yield f + + results = ['\0'.join(r) for r in [ + genresult('l', self.watcher.statustrees['l']), + genresult('m', self.watcher.statustrees['m']), + genresult('a', self.watcher.statustrees['a']), + genresult('r', self.watcher.statustrees['r']), + genresult('!', self.watcher.statustrees['!']), + '?' in states and genresult('?', self.watcher.statustrees['?']) or [], + [], + 'c' in states and genresult('n', self.watcher.tree) or [], + ]] + + try: + try: + sock.sendall(struct.pack(common.resphdrfmt, + *map(len, results))) + sock.sendall(''.join(results)) + finally: + sock.shutdown(socket.SHUT_WR) + except socket.error, err: + if err[0] != errno.EPIPE: + raise + + def shutdown(self): + self.sock.close() + try: + os.unlink(self.sockpath) + except OSError, err: + if err.errno != errno.ENOENT: + raise + +class Master(object): + def __init__(self, ui, repo, timeout=None): + self.ui = ui + self.repo = repo + self.poll = select.poll() + self.watcher = Watcher(ui, repo, self) + self.server = Server(ui, repo, self.watcher, timeout) + self.table = {} + for obj in (self.watcher, self.server): + fd = obj.fileno() + self.table[fd] = obj + self.poll.register(fd, obj.poll_events) + + def register(self, fd, mask): + self.poll.register(fd, mask) + + def shutdown(self): + for obj in self.table.itervalues(): + obj.shutdown() + + def run(self): + self.watcher.setup() + self.ui.note(_('finished setup\n')) + if os.getenv('TIME_STARTUP'): + sys.exit(0) + while True: + timeout = None + timeobj = None + for obj in self.table.itervalues(): + if obj.timeout is not None and (timeout is None or obj.timeout < timeout): + timeout, timeobj = obj.timeout, obj + try: + if self.ui.debugflag: + if timeout is None: + self.ui.note('polling: no timeout\n') + else: + self.ui.note('polling: %sms timeout\n' % timeout) + events = self.poll.poll(timeout) + except select.error, err: + if err[0] == errno.EINTR: + continue + raise + if events: + for fd, event in events: + self.table[fd].handle_event(fd, event) + elif timeobj: + timeobj.handle_timeout() + +def start(ui, repo): + m = Master(ui, repo) + sys.stdout.flush() + sys.stderr.flush() + + pid = os.fork() + if pid: + return pid + + os.setsid() + + fd = os.open('/dev/null', os.O_RDONLY) + os.dup2(fd, 0) + if fd > 0: + os.close(fd) + + fd = os.open(ui.config('inotify', 'log', '/dev/null'), + os.O_RDWR | os.O_CREAT | os.O_TRUNC) + os.dup2(fd, 1) + os.dup2(fd, 2) + if fd > 2: + os.close(fd) + + try: + m.run() + finally: + m.shutdown() + os._exit(0)
--- a/hgext/interhg.py Wed Apr 09 15:27:57 2008 -0700 +++ b/hgext/interhg.py Wed Apr 09 15:28:30 2008 -0700 @@ -27,9 +27,9 @@ import re from mercurial.hgweb import hgweb_mod -from mercurial import templater +from mercurial import templatefilters -orig_escape = templater.common_filters["escape"] +orig_escape = templatefilters.filters["escape"] interhg_table = [] @@ -39,7 +39,7 @@ escstr = regexp.sub(format, escstr) return escstr -templater.common_filters["escape"] = interhg_escape +templatefilters.filters["escape"] = interhg_escape orig_refresh = hgweb_mod.hgweb.refresh
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/hgext/keyword.py Wed Apr 09 15:28:30 2008 -0700 @@ -0,0 +1,559 @@ +# keyword.py - $Keyword$ expansion for Mercurial +# +# Copyright 2007, 2008 Christian Ebert <blacktrash@gmx.net> +# +# This software may be used and distributed according to the terms +# of the GNU General Public License, incorporated herein by reference. +# +# $Id$ +# +# Keyword expansion hack against the grain of a DSCM +# +# There are many good reasons why this is not needed in a distributed +# SCM, still it may be useful in very small projects based on single +# files (like LaTeX packages), that are mostly addressed to an audience +# not running a version control system. +# +# For in-depth discussion refer to +# <http://www.selenic.com/mercurial/wiki/index.cgi/KeywordPlan>. +# +# Keyword expansion is based on Mercurial's changeset template mappings. +# +# Binary files are not touched. +# +# Setup in hgrc: +# +# [extensions] +# # enable extension +# hgext.keyword = +# +# Files to act upon/ignore are specified in the [keyword] section. +# Customized keyword template mappings in the [keywordmaps] section. +# +# Run "hg help keyword" and "hg kwdemo" to get info on configuration. + +'''keyword expansion in local repositories + +This extension expands RCS/CVS-like or self-customized $Keywords$ +in tracked text files selected by your configuration. + +Keywords are only expanded in local repositories and not stored in +the change history. The mechanism can be regarded as a convenience +for the current user or for archive distribution. + +Configuration is done in the [keyword] and [keywordmaps] sections +of hgrc files. + +Example: + + [keyword] + # expand keywords in every python file except those matching "x*" + **.py = + x* = ignore + +Note: the more specific you are in your filename patterns + the less you lose speed in huge repos. + +For [keywordmaps] template mapping and expansion demonstration and +control run "hg kwdemo". + +An additional date template filter {date|utcdate} is provided. + +The default template mappings (view with "hg kwdemo -d") can be replaced +with customized keywords and templates. +Again, run "hg kwdemo" to control the results of your config changes. + +Before changing/disabling active keywords, run "hg kwshrink" to avoid +the risk of inadvertedly storing expanded keywords in the change history. + +To force expansion after enabling it, or a configuration change, run +"hg kwexpand". + +Also, when committing with the record extension or using mq's qrecord, be aware +that keywords cannot be updated. Again, run "hg kwexpand" on the files in +question to update keyword expansions after all changes have been checked in. + +Expansions spanning more than one line and incremental expansions, +like CVS' $Log$, are not supported. A keyword template map +"Log = {desc}" expands to the first line of the changeset description. +''' + +from mercurial import commands, cmdutil, context, dispatch, filelog, revlog +from mercurial import patch, localrepo, templater, templatefilters, util +from mercurial.hgweb import webcommands +from mercurial.node import nullid, hex +from mercurial.i18n import _ +import re, shutil, tempfile, time + +commands.optionalrepo += ' kwdemo' + +# hg commands that do not act on keywords +nokwcommands = ('add addremove bundle copy export grep incoming init' + ' log outgoing push rename rollback tip' + ' convert email glog') + +# hg commands that trigger expansion only when writing to working dir, +# not when reading filelog, and unexpand when reading from working dir +restricted = 'record qfold qimport qnew qpush qrefresh qrecord' + +def utcdate(date): + '''Returns hgdate in cvs-like UTC format.''' + return time.strftime('%Y/%m/%d %H:%M:%S', time.gmtime(date[0])) + + +# make keyword tools accessible +kwtools = {'templater': None, 'hgcmd': None} + +# store originals of monkeypatches +_patchfile_init = patch.patchfile.__init__ +_patch_diff = patch.diff +_dispatch_parse = dispatch._parse +_webcommands_changeset = webcommands.changeset +_webcommands_filediff = webcommands.filediff + +def _kwpatchfile_init(self, ui, fname, missing=False): + '''Monkeypatch/wrap patch.patchfile.__init__ to avoid + rejects or conflicts due to expanded keywords in working dir.''' + _patchfile_init(self, ui, fname, missing=missing) + # shrink keywords read from working dir + kwt = kwtools['templater'] + self.lines = kwt.shrinklines(self.fname, self.lines) + +def _kw_diff(repo, node1=None, node2=None, files=None, match=util.always, + fp=None, changes=None, opts=None): + '''Monkeypatch patch.diff to avoid expansion except when + comparing against working dir.''' + if node2 is not None: + kwtools['templater'].matcher = util.never + elif node1 is not None and node1 != repo.changectx().node(): + kwtools['templater'].restrict = True + _patch_diff(repo, node1=node1, node2=node2, files=files, match=match, + fp=fp, changes=changes, opts=opts) + +def _kwweb_changeset(web, req, tmpl): + '''Wraps webcommands.changeset turning off keyword expansion.''' + kwtools['templater'].matcher = util.never + return _webcommands_changeset(web, req, tmpl) + +def _kwweb_filediff(web, req, tmpl): + '''Wraps webcommands.filediff turning off keyword expansion.''' + kwtools['templater'].matcher = util.never + return _webcommands_filediff(web, req, tmpl) + +def _kwdispatch_parse(ui, args): + '''Monkeypatch dispatch._parse to obtain running hg command.''' + cmd, func, args, options, cmdoptions = _dispatch_parse(ui, args) + kwtools['hgcmd'] = cmd + return cmd, func, args, options, cmdoptions + +# dispatch._parse is run before reposetup, so wrap it here +# all other actual monkey patching is done at end of reposetup +dispatch._parse = _kwdispatch_parse + + +class kwtemplater(object): + ''' + Sets up keyword templates, corresponding keyword regex, and + provides keyword substitution functions. + ''' + templates = { + 'Revision': '{node|short}', + 'Author': '{author|user}', + 'Date': '{date|utcdate}', + 'RCSFile': '{file|basename},v', + 'Source': '{root}/{file},v', + 'Id': '{file|basename},v {node|short} {date|utcdate} {author|user}', + 'Header': '{root}/{file},v {node|short} {date|utcdate} {author|user}', + } + + def __init__(self, ui, repo, inc, exc): + self.ui = ui + self.repo = repo + self.matcher = util.matcher(repo.root, inc=inc, exc=exc)[1] + self.restrict = kwtools['hgcmd'] in restricted.split() + + kwmaps = self.ui.configitems('keywordmaps') + if kwmaps: # override default templates + kwmaps = [(k, templater.parsestring(v, quoted=False)) + for (k, v) in kwmaps] + self.templates = dict(kwmaps) + escaped = map(re.escape, self.templates.keys()) + kwpat = r'\$(%s)(: [^$\n\r]*? )??\$' % '|'.join(escaped) + self.re_kw = re.compile(kwpat) + + templatefilters.filters['utcdate'] = utcdate + self.ct = cmdutil.changeset_templater(self.ui, self.repo, + False, '', False) + + def getnode(self, path, fnode): + '''Derives changenode from file path and filenode.''' + # used by kwfilelog.read and kwexpand + c = context.filectx(self.repo, path, fileid=fnode) + return c.node() + + def substitute(self, data, path, node, subfunc): + '''Replaces keywords in data with expanded template.''' + def kwsub(mobj): + kw = mobj.group(1) + self.ct.use_template(self.templates[kw]) + self.ui.pushbuffer() + self.ct.show(changenode=node, root=self.repo.root, file=path) + ekw = templatefilters.firstline(self.ui.popbuffer()) + return '$%s: %s $' % (kw, ekw) + return subfunc(kwsub, data) + + def expand(self, path, node, data): + '''Returns data with keywords expanded.''' + if not self.restrict and self.matcher(path) and not util.binary(data): + changenode = self.getnode(path, node) + return self.substitute(data, path, changenode, self.re_kw.sub) + return data + + def iskwfile(self, path, islink): + '''Returns true if path matches [keyword] pattern + and is not a symbolic link. + Caveat: localrepository._link fails on Windows.''' + return self.matcher(path) and not islink(path) + + def overwrite(self, node=None, expand=True, files=None): + '''Overwrites selected files expanding/shrinking keywords.''' + ctx = self.repo.changectx(node) + mf = ctx.manifest() + if node is not None: # commit + files = [f for f in ctx.files() if f in mf] + notify = self.ui.debug + else: # kwexpand/kwshrink + notify = self.ui.note + candidates = [f for f in files if self.iskwfile(f, mf.linkf)] + if candidates: + self.restrict = True # do not expand when reading + candidates.sort() + action = expand and 'expanding' or 'shrinking' + for f in candidates: + fp = self.repo.file(f) + data = fp.read(mf[f]) + if util.binary(data): + continue + if expand: + changenode = node or self.getnode(f, mf[f]) + data, found = self.substitute(data, f, changenode, + self.re_kw.subn) + else: + found = self.re_kw.search(data) + if found: + notify(_('overwriting %s %s keywords\n') % (f, action)) + self.repo.wwrite(f, data, mf.flags(f)) + self.repo.dirstate.normal(f) + self.restrict = False + + def shrinktext(self, text): + '''Unconditionally removes all keyword substitutions from text.''' + return self.re_kw.sub(r'$\1$', text) + + def shrink(self, fname, text): + '''Returns text with all keyword substitutions removed.''' + if self.matcher(fname) and not util.binary(text): + return self.shrinktext(text) + return text + + def shrinklines(self, fname, lines): + '''Returns lines with keyword substitutions removed.''' + if self.matcher(fname): + text = ''.join(lines) + if not util.binary(text): + return self.shrinktext(text).splitlines(True) + return lines + + def wread(self, fname, data): + '''If in restricted mode returns data read from wdir with + keyword substitutions removed.''' + return self.restrict and self.shrink(fname, data) or data + +class kwfilelog(filelog.filelog): + ''' + Subclass of filelog to hook into its read, add, cmp methods. + Keywords are "stored" unexpanded, and processed on reading. + ''' + def __init__(self, opener, path): + super(kwfilelog, self).__init__(opener, path) + self.kwt = kwtools['templater'] + self.path = path + + def read(self, node): + '''Expands keywords when reading filelog.''' + data = super(kwfilelog, self).read(node) + return self.kwt.expand(self.path, node, data) + + def add(self, text, meta, tr, link, p1=None, p2=None): + '''Removes keyword substitutions when adding to filelog.''' + text = self.kwt.shrink(self.path, text) + return super(kwfilelog, self).add(text, meta, tr, link, p1=p1, p2=p2) + + def cmp(self, node, text): + '''Removes keyword substitutions for comparison.''' + text = self.kwt.shrink(self.path, text) + if self.renamed(node): + t2 = super(kwfilelog, self).read(node) + return t2 != text + return revlog.revlog.cmp(self, node, text) + +def _status(ui, repo, kwt, *pats, **opts): + '''Bails out if [keyword] configuration is not active. + Returns status of working directory.''' + if kwt: + files, match, anypats = cmdutil.matchpats(repo, pats, opts) + return repo.status(files=files, match=match, list_clean=True) + if ui.configitems('keyword'): + raise util.Abort(_('[keyword] patterns cannot match')) + raise util.Abort(_('no [keyword] patterns configured')) + +def _kwfwrite(ui, repo, expand, *pats, **opts): + '''Selects files and passes them to kwtemplater.overwrite.''' + kwt = kwtools['templater'] + status = _status(ui, repo, kwt, *pats, **opts) + modified, added, removed, deleted, unknown, ignored, clean = status + if modified or added or removed or deleted: + raise util.Abort(_('outstanding uncommitted changes in given files')) + wlock = lock = None + try: + wlock = repo.wlock() + lock = repo.lock() + kwt.overwrite(expand=expand, files=clean) + finally: + del wlock, lock + + +def demo(ui, repo, *args, **opts): + '''print [keywordmaps] configuration and an expansion example + + Show current, custom, or default keyword template maps + and their expansion. + + Extend current configuration by specifying maps as arguments + and optionally by reading from an additional hgrc file. + + Override current keyword template maps with "default" option. + ''' + def demostatus(stat): + ui.status(_('\n\t%s\n') % stat) + + def demoitems(section, items): + ui.write('[%s]\n' % section) + for k, v in items: + ui.write('%s = %s\n' % (k, v)) + + msg = 'hg keyword config and expansion example' + kwstatus = 'current' + fn = 'demo.txt' + branchname = 'demobranch' + tmpdir = tempfile.mkdtemp('', 'kwdemo.') + ui.note(_('creating temporary repo at %s\n') % tmpdir) + repo = localrepo.localrepository(ui, path=tmpdir, create=True) + ui.setconfig('keyword', fn, '') + if args or opts.get('rcfile'): + kwstatus = 'custom' + if opts.get('rcfile'): + ui.readconfig(opts.get('rcfile')) + if opts.get('default'): + kwstatus = 'default' + kwmaps = kwtemplater.templates + if ui.configitems('keywordmaps'): + # override maps from optional rcfile + for k, v in kwmaps.iteritems(): + ui.setconfig('keywordmaps', k, v) + elif args: + # simulate hgrc parsing + rcmaps = ['[keywordmaps]\n'] + [a + '\n' for a in args] + fp = repo.opener('hgrc', 'w') + fp.writelines(rcmaps) + fp.close() + ui.readconfig(repo.join('hgrc')) + if not opts.get('default'): + kwmaps = dict(ui.configitems('keywordmaps')) or kwtemplater.templates + reposetup(ui, repo) + for k, v in ui.configitems('extensions'): + if k.endswith('keyword'): + extension = '%s = %s' % (k, v) + break + demostatus('config using %s keyword template maps' % kwstatus) + ui.write('[extensions]\n%s\n' % extension) + demoitems('keyword', ui.configitems('keyword')) + demoitems('keywordmaps', kwmaps.iteritems()) + keywords = '$' + '$\n$'.join(kwmaps.keys()) + '$\n' + repo.wopener(fn, 'w').write(keywords) + repo.add([fn]) + path = repo.wjoin(fn) + ui.note(_('\n%s keywords written to %s:\n') % (kwstatus, path)) + ui.note(keywords) + ui.note('\nhg -R "%s" branch "%s"\n' % (tmpdir, branchname)) + # silence branch command if not verbose + quiet = ui.quiet + ui.quiet = not ui.verbose + commands.branch(ui, repo, branchname) + ui.quiet = quiet + for name, cmd in ui.configitems('hooks'): + if name.split('.', 1)[0].find('commit') > -1: + repo.ui.setconfig('hooks', name, '') + ui.note(_('unhooked all commit hooks\n')) + ui.note('hg -R "%s" ci -m "%s"\n' % (tmpdir, msg)) + repo.commit(text=msg) + format = ui.verbose and ' in %s' % path or '' + demostatus('%s keywords expanded%s' % (kwstatus, format)) + ui.write(repo.wread(fn)) + ui.debug(_('\nremoving temporary repo %s\n') % tmpdir) + shutil.rmtree(tmpdir, ignore_errors=True) + +def expand(ui, repo, *pats, **opts): + '''expand keywords in working directory + + Run after (re)enabling keyword expansion. + + kwexpand refuses to run if given files contain local changes. + ''' + # 3rd argument sets expansion to True + _kwfwrite(ui, repo, True, *pats, **opts) + +def files(ui, repo, *pats, **opts): + '''print files currently configured for keyword expansion + + Crosscheck which files in working directory are potential targets for + keyword expansion. + That is, files matched by [keyword] config patterns but not symlinks. + ''' + kwt = kwtools['templater'] + status = _status(ui, repo, kwt, *pats, **opts) + modified, added, removed, deleted, unknown, ignored, clean = status + files = modified + added + clean + if opts.get('untracked'): + files += unknown + files.sort() + wctx = repo.workingctx() + islink = lambda p: 'l' in wctx.fileflags(p) + kwfiles = [f for f in files if kwt.iskwfile(f, islink)] + cwd = pats and repo.getcwd() or '' + kwfstats = not opts.get('ignore') and (('K', kwfiles),) or () + if opts.get('all') or opts.get('ignore'): + kwfstats += (('I', [f for f in files if f not in kwfiles]),) + for char, filenames in kwfstats: + format = (opts.get('all') or ui.verbose) and '%s %%s\n' % char or '%s\n' + for f in filenames: + ui.write(format % repo.pathto(f, cwd)) + +def shrink(ui, repo, *pats, **opts): + '''revert expanded keywords in working directory + + Run before changing/disabling active keywords + or if you experience problems with "hg import" or "hg merge". + + kwshrink refuses to run if given files contain local changes. + ''' + # 3rd argument sets expansion to False + _kwfwrite(ui, repo, False, *pats, **opts) + + +def reposetup(ui, repo): + '''Sets up repo as kwrepo for keyword substitution. + Overrides file method to return kwfilelog instead of filelog + if file matches user configuration. + Wraps commit to overwrite configured files with updated + keyword substitutions. + This is done for local repos only, and only if there are + files configured at all for keyword substitution.''' + + try: + if (not repo.local() or kwtools['hgcmd'] in nokwcommands.split() + or '.hg' in util.splitpath(repo.root) + or repo._url.startswith('bundle:')): + return + except AttributeError: + pass + + inc, exc = [], ['.hg*'] + for pat, opt in ui.configitems('keyword'): + if opt != 'ignore': + inc.append(pat) + else: + exc.append(pat) + if not inc: + return + + kwtools['templater'] = kwt = kwtemplater(ui, repo, inc, exc) + + class kwrepo(repo.__class__): + def file(self, f): + if f[0] == '/': + f = f[1:] + return kwfilelog(self.sopener, f) + + def wread(self, filename): + data = super(kwrepo, self).wread(filename) + return kwt.wread(filename, data) + + def commit(self, files=None, text='', user=None, date=None, + match=util.always, force=False, force_editor=False, + p1=None, p2=None, extra={}, empty_ok=False): + wlock = lock = None + _p1 = _p2 = None + try: + wlock = self.wlock() + lock = self.lock() + # store and postpone commit hooks + commithooks = {} + for name, cmd in ui.configitems('hooks'): + if name.split('.', 1)[0] == 'commit': + commithooks[name] = cmd + ui.setconfig('hooks', name, None) + if commithooks: + # store parents for commit hook environment + if p1 is None: + _p1, _p2 = repo.dirstate.parents() + else: + _p1, _p2 = p1, p2 or nullid + _p1 = hex(_p1) + if _p2 == nullid: + _p2 = '' + else: + _p2 = hex(_p2) + + node = super(kwrepo, + self).commit(files=files, text=text, user=user, + date=date, match=match, force=force, + force_editor=force_editor, + p1=p1, p2=p2, extra=extra, + empty_ok=empty_ok) + + # restore commit hooks + for name, cmd in commithooks.iteritems(): + ui.setconfig('hooks', name, cmd) + if node is not None: + kwt.overwrite(node=node) + repo.hook('commit', node=node, parent1=_p1, parent2=_p2) + return node + finally: + del wlock, lock + + repo.__class__ = kwrepo + patch.patchfile.__init__ = _kwpatchfile_init + patch.diff = _kw_diff + webcommands.changeset = webcommands.rev = _kwweb_changeset + webcommands.filediff = webcommands.diff = _kwweb_filediff + + +cmdtable = { + 'kwdemo': + (demo, + [('d', 'default', None, _('show default keyword template maps')), + ('f', 'rcfile', [], _('read maps from rcfile'))], + _('hg kwdemo [-d] [-f RCFILE] [TEMPLATEMAP]...')), + 'kwexpand': (expand, commands.walkopts, + _('hg kwexpand [OPTION]... [FILE]...')), + 'kwfiles': + (files, + [('a', 'all', None, _('show keyword status flags of all files')), + ('i', 'ignore', None, _('show files excluded from expansion')), + ('u', 'untracked', None, _('additionally show untracked files')), + ] + commands.walkopts, + _('hg kwfiles [OPTION]... [FILE]...')), + 'kwshrink': (shrink, commands.walkopts, + _('hg kwshrink [OPTION]... [FILE]...')), +}
--- a/hgext/mq.py Wed Apr 09 15:27:57 2008 -0700 +++ b/hgext/mq.py Wed Apr 09 15:28:30 2008 -0700 @@ -1,4 +1,4 @@ -# queue.py - patch queues for mercurial +# mq.py - patch queues for mercurial # # Copyright 2005, 2006 Chris Mason <mason@suse.com> # @@ -30,6 +30,8 @@ ''' from mercurial.i18n import _ +from mercurial.node import bin, hex, short +from mercurial.repo import RepoError from mercurial import commands, cmdutil, hg, patch, revlog, util from mercurial import repair import os, sys, re, errno @@ -224,7 +226,7 @@ def write_list(items, path): fp = self.opener(path, 'w') for i in items: - print >> fp, i + fp.write("%s\n" % i) fp.close() if self.applied_dirty: write_list(map(str, self.applied), self.status_path) if self.series_dirty: write_list(self.full_series, self.series_path) @@ -600,9 +602,21 @@ raise util.Abort(_("local changes found")) return m, a, r, d + _reserved = ('series', 'status', 'guards') + def check_reserved_name(self, name): + if (name in self._reserved or name.startswith('.hg') + or name.startswith('.mq')): + raise util.Abort(_('"%s" cannot be used as the name of a patch') + % name) + def new(self, repo, patch, *pats, **opts): msg = opts.get('msg') force = opts.get('force') + user = opts.get('user') + date = opts.get('date') + if date: + date = util.parsedate(date) + self.check_reserved_name(patch) if os.path.exists(self.join(patch)): raise util.Abort(_('patch "%s" already exists') % patch) if opts.get('include') or opts.get('exclude') or pats: @@ -617,7 +631,7 @@ try: insert = self.full_series_end() commitmsg = msg and msg or ("[mq]: %s" % patch) - n = repo.commit(commitfiles, commitmsg, match=match, force=True) + n = repo.commit(commitfiles, commitmsg, user, date, match=match, force=True) if n == None: raise util.Abort(_("repo commit failed")) self.full_series[insert:insert] = [patch] @@ -626,6 +640,15 @@ self.series_dirty = 1 self.applied_dirty = 1 p = self.opener(patch, "w") + if date: + p.write("# HG changeset patch\n") + if user: + p.write("# User " + user + "\n") + p.write("# Date %d %d\n" % date) + p.write("\n") + elif user: + p.write("From: " + user + "\n") + p.write("\n") if msg: msg = msg + "\n" p.write(msg) @@ -639,14 +662,14 @@ finally: del wlock - def strip(self, repo, rev, update=True, backup="all"): + def strip(self, repo, rev, update=True, backup="all", force=None): wlock = lock = None try: wlock = repo.wlock() lock = repo.lock() if update: - self.check_localchanges(repo, refresh=False) + self.check_localchanges(repo, force=force, refresh=False) urev = self.qparents(repo, rev) hg.clean(repo, urev) repo.dirstate.write() @@ -743,6 +766,9 @@ def push(self, repo, patch=None, force=False, list=False, mergeq=None): wlock = repo.wlock() + if repo.dirstate.parents()[0] != repo.changelog.tip(): + self.ui.status(_("(working directory not at tip)\n")) + try: patch = self.lookup(patch) # Suppose our series file is: A B C and the current 'top' @@ -861,10 +887,16 @@ start = info[0] rev = revlog.bin(info[1]) + if update: + top = self.check_toppatch(repo) + + if repo.changelog.heads(rev) != [revlog.bin(self.applied[-1].rev)]: + raise util.Abort("popping would remove a revision not " + "managed by this patch queue") + # we know there are no local changes, so we can make a simplified # form of hg.update. if update: - top = self.check_toppatch(repo) qp = self.qparents(repo, rev) changes = repo.changelog.read(qp) mmap = repo.manifest.read(changes[0]) @@ -887,8 +919,8 @@ except: pass repo.dirstate.forget(f) repo.dirstate.setparents(qp, revlog.nullid) + del self.applied[start:end] self.strip(repo, rev, update=False, backup='strip') - del self.applied[start:end] if len(self.applied): self.ui.write("Now at: %s\n" % self.applied[-1].name) else: @@ -910,11 +942,16 @@ if len(self.applied) == 0: self.ui.write("No patches applied\n") return 1 + newdate = opts.get('date') + if newdate: + newdate = '%d %d' % util.parsedate(newdate) wlock = repo.wlock() try: self.check_toppatch(repo) (top, patchfn) = (self.applied[-1].rev, self.applied[-1].name) top = revlog.bin(top) + if repo.changelog.heads(top) != [top]: + raise util.Abort("cannot refresh a revision with children") cparents = repo.changelog.parents(top) patchparent = self.qparents(repo, top) message, comments, user, date, patchfound = self.readheaders(patchfn) @@ -945,6 +982,33 @@ while message[mi] != comments[ci]: ci += 1 del comments[ci] + + def setheaderfield(comments, prefixes, new): + # Update all references to a field in the patch header. + # If none found, add it email style. + res = False + for prefix in prefixes: + for i in xrange(len(comments)): + if comments[i].startswith(prefix): + comments[i] = prefix + new + res = True + break + return res + + newuser = opts.get('user') + if newuser: + if not setheaderfield(comments, ['From: ', '# User '], newuser): + try: + patchheaderat = comments.index('# HG changeset patch') + comments.insert(patchheaderat + 1,'# User ' + newuser) + except ValueError: + comments = ['From: ' + newuser, ''] + comments + user = newuser + + if newdate: + if setheaderfield(comments, ['# Date '], newdate): + date = newdate + if msg: comments.append(msg) @@ -1070,12 +1134,16 @@ else: message = msg + if not user: + user = changes[1] + + self.applied.pop() + self.applied_dirty = 1 self.strip(repo, top, update=False, backup='strip') - n = repo.commit(filelist, message, changes[1], match=matchfn, + n = repo.commit(filelist, message, user, date, match=matchfn, force=1) - self.applied[-1] = statusentry(revlog.hex(n), patchfn) - self.applied_dirty = 1 + self.applied.append(statusentry(revlog.hex(n), patchfn)) self.removeundo(repo) else: self.printdiff(repo, patchparent, fp=patchf) @@ -1191,7 +1259,7 @@ elif lines[i].startswith('Dirstate:'): l = lines[i].rstrip() l = l[10:].split(' ') - qpp = [ hg.bin(x) for x in l ] + qpp = [ bin(x) for x in l ] elif datastart != None: l = lines[i].rstrip() se = statusentry(l) @@ -1214,7 +1282,7 @@ if rev not in heads: self.ui.warn("save entry has children, leaving it alone\n") else: - self.ui.warn("removing save entry %s\n" % hg.short(rev)) + self.ui.warn("removing save entry %s\n" % short(rev)) pp = repo.dirstate.parents() if rev in pp: update = True @@ -1223,9 +1291,9 @@ self.strip(repo, rev, update=update, backup='strip') if qpp: self.ui.warn("saved queue repository parents: %s %s\n" % - (hg.short(qpp[0]), hg.short(qpp[1]))) + (short(qpp[0]), short(qpp[1]))) if qupdate: - print "queue directory updating" + self.ui.status(_("queue directory updating\n")) r = self.qrepo() if not r: self.ui.warn("Unable to load queue repository\n") @@ -1248,7 +1316,7 @@ r = self.qrepo() if r: pp = r.dirstate.parents() - msg += "\nDirstate: %s %s" % (hg.hex(pp[0]), hg.hex(pp[1])) + msg += "\nDirstate: %s %s" % (hex(pp[0]), hex(pp[1])) msg += "\n\nPatch Data:\n" text = msg + "\n".join([str(x) for x in self.applied]) + '\n' + (ar and "\n".join(ar) + '\n' or "") @@ -1364,6 +1432,7 @@ if not patchname: patchname = normname('%d.diff' % r) + self.check_reserved_name(patchname) checkseries(patchname) checkfile(patchname) self.full_series.insert(0, patchname) @@ -1386,6 +1455,7 @@ raise util.Abort(_('-e is incompatible with import from -')) if not patchname: patchname = normname(filename) + self.check_reserved_name(patchname) if not os.path.isfile(self.join(patchname)): raise util.Abort(_("patch %s does not exist") % patchname) else: @@ -1395,11 +1465,12 @@ raise util.Abort(_('need --name to import a patch from -')) text = sys.stdin.read() else: - text = file(filename).read() + text = file(filename, 'rb').read() except IOError: raise util.Abort(_("unable to read %s") % patchname) if not patchname: patchname = normname(os.path.basename(filename)) + self.check_reserved_name(patchname) checkfile(patchname) patchf = self.opener(patchname, "w") patchf.write(text) @@ -1492,6 +1563,8 @@ if r: if not os.path.exists(r.wjoin('.hgignore')): fp = r.wopener('.hgignore', 'w') + fp.write('^\\.hg\n') + fp.write('^\\.mq\n') fp.write('syntax: glob\n') fp.write('status\n') fp.write('guards\n') @@ -1529,7 +1602,7 @@ patchespath = opts['patches'] or patchdir(sr) try: pr = hg.repository(ui, patchespath) - except hg.RepoError: + except RepoError: raise util.Abort(_('versioned patch repository not found' ' (see qinit -c)')) qbase, destrev = None, None @@ -1542,6 +1615,11 @@ del heads[h] destrev = heads.keys() destrev.append(sr.changelog.parents(qbase)[0]) + elif sr.capable('lookup'): + try: + qbase = sr.lookup('qbase') + except RepoError: + pass ui.note(_('cloning main repo\n')) sr, dr = hg.clone(ui, sr.url(), dest, pull=opts['pull'], @@ -1605,6 +1683,13 @@ return q.qseries(repo, start=l-2, length=1, status='A', summary=opts.get('summary')) +def setupheaderopts(ui, opts): + def do(opt,val): + if not opts[opt] and opts['current' + opt]: + opts[opt] = val + do('user', ui.username()) + do('date', "%d %d" % util.makedate()) + def new(ui, repo, patch, *args, **opts): """create a new patch @@ -1623,6 +1708,7 @@ if opts['edit']: message = ui.edit(message, ui.username()) opts['msg'] = message + setupheaderopts(ui, opts) q.new(repo, patch, *args, **opts) q.save_dirty() return 0 @@ -1648,6 +1734,7 @@ patch = q.applied[-1].name (message, comment, user, date, hasdiff) = q.readheaders(patch) message = ui.edit('\n'.join(message), user or ui.username()) + setupheaderopts(ui, opts) ret = q.refresh(repo, pats, msg=message, **opts) q.save_dirty() return ret @@ -1956,7 +2043,7 @@ elif opts['nobackup']: backup = 'none' update = repo.dirstate.parents()[0] != revlog.nullid - repo.mq.strip(repo, rev, backup=backup, update=update) + repo.mq.strip(repo, rev, backup=backup, update=update, force=opts['force']) return 0 def select(ui, repo, *args, **opts): @@ -2096,6 +2183,12 @@ return tagscache mqtags = [(revlog.bin(patch.rev), patch.name) for patch in q.applied] + + if mqtags[-1][0] not in self.changelog.nodemap: + self.ui.warn('mq status file refers to unknown node %s\n' + % revlog.short(mqtags[-1][0])) + return tagscache + mqtags.append((mqtags[-1][0], 'qtip')) mqtags.append((mqtags[0][0], 'qbase')) mqtags.append((self.changelog.parents(mqtags[0][0])[0], 'qparent')) @@ -2107,16 +2200,19 @@ return tagscache - def _branchtags(self): + def _branchtags(self, partial, lrev): q = self.mq if not q.applied: - return super(mqrepo, self)._branchtags() + return super(mqrepo, self)._branchtags(partial, lrev) - self.branchcache = {} # avoid recursion in changectx cl = self.changelog - partial, last, lrev = self._readbranchcache() + qbasenode = revlog.bin(q.applied[0].rev) + if qbasenode not in cl.nodemap: + self.ui.warn('mq status file refers to unknown node %s\n' + % revlog.short(qbasenode)) + return super(mqrepo, self)._branchtags(partial, lrev) - qbase = cl.rev(revlog.bin(q.applied[0].rev)) + qbase = cl.rev(qbasenode) start = lrev + 1 if start < qbase: # update the cache (excluding the patches) and save it @@ -2138,6 +2234,12 @@ seriesopts = [('s', 'summary', None, _('print first line of patch header'))] +headeropts = [ + ('U', 'currentuser', None, _('add "From: <current user>" to patch')), + ('u', 'user', '', _('add "From: <given user>" to patch')), + ('D', 'currentdate', None, _('add "Date: <current date>" to patch')), + ('d', 'date', '', _('add "Date: <given date>" to patch'))] + cmdtable = { "qapplied": (applied, [] + seriesopts, _('hg qapplied [-s] [PATCH]')), "qclone": @@ -2156,8 +2258,9 @@ "^qdiff": (diff, [('g', 'git', None, _('use git extended diff format')), + ('U', 'unified', 3, _('number of lines of context to show')), ] + commands.walkopts, - _('hg qdiff [-I] [-X] [-g] [FILE]...')), + _('hg qdiff [-I] [-X] [-U NUM] [-g] [FILE]...')), "qdelete|qremove|qrm": (delete, [('k', 'keep', None, _('keep patch file')), @@ -2196,7 +2299,7 @@ [('e', 'edit', None, _('edit commit message')), ('f', 'force', None, _('import uncommitted changes into patch')), ('g', 'git', None, _('use git extended diff format')), - ] + commands.walkopts + commands.commitopts, + ] + commands.walkopts + commands.commitopts + headeropts, _('hg qnew [-e] [-m TEXT] [-l FILE] [-f] PATCH [FILE]...')), "qnext": (next, [] + seriesopts, _('hg qnext [-s]')), "qprev": (prev, [] + seriesopts, _('hg qprev [-s]')), @@ -2219,7 +2322,7 @@ [('e', 'edit', None, _('edit commit message')), ('g', 'git', None, _('use git extended diff format')), ('s', 'short', None, _('refresh only files already in the patch')), - ] + commands.walkopts + commands.commitopts, + ] + commands.walkopts + commands.commitopts + headeropts, _('hg qrefresh [-I] [-X] [-e] [-m TEXT] [-l FILE] [-s] [FILE]...')), 'qrename|qmv': (rename, [], _('hg qrename PATCH1 [PATCH2]')), @@ -2249,7 +2352,7 @@ _('hg qseries [-ms]')), "^strip": (strip, - [('f', 'force', None, _('force multi-head removal')), + [('f', 'force', None, _('force removal with local changes')), ('b', 'backup', None, _('bundle unrelated changesets')), ('n', 'nobackup', None, _('no backups'))], _('hg strip [-f] [-b] [-n] REV')),
--- a/hgext/notify.py Wed Apr 09 15:27:57 2008 -0700 +++ b/hgext/notify.py Wed Apr 09 15:28:30 2008 -0700 @@ -66,7 +66,7 @@ # push changes to, they can manage their own subscriptions. from mercurial.i18n import _ -from mercurial.node import * +from mercurial.node import bin, short from mercurial import patch, cmdutil, templater, util, mail import email.Parser, fnmatch, socket, time @@ -135,7 +135,7 @@ def fixmail(self, addr): '''try to clean up email addresses.''' - addr = templater.email(addr.strip()) + addr = util.email(addr.strip()) if self.domain: a = addr.find('@localhost') if a != -1: @@ -210,9 +210,7 @@ del msg['From'] msg['From'] = sender - msg['Date'] = util.datestr(date=util.makedate(), - format="%a, %d %b %Y %H:%M:%S", - timezone=True) + msg['Date'] = util.datestr(format="%a, %d %b %Y %H:%M:%S %1%2") fix_subject() fix_sender() @@ -231,13 +229,11 @@ else: self.ui.status(_('notify: sending %d subscribers %d changes\n') % (len(self.subs), count)) - mail.sendmail(self.ui, templater.email(msg['From']), + mail.sendmail(self.ui, util.email(msg['From']), self.subs, msgtext) def diff(self, node, ref): maxdiff = int(self.ui.config('notify', 'maxdiff', 300)) - if maxdiff == 0: - return prev = self.repo.changelog.parents(node)[0] self.ui.pushbuffer() patch.diff(self.repo, prev, ref) @@ -247,6 +243,8 @@ # s may be nil, don't include the header if it is if s: self.ui.write('\ndiffstat:\n\n%s' % s) + if maxdiff == 0: + return if maxdiff > 0 and len(difflines) > maxdiff: self.ui.write(_('\ndiffs (truncated from %d to %d lines):\n\n') % (len(difflines), maxdiff))
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/hgext/pager.py Wed Apr 09 15:28:30 2008 -0700 @@ -0,0 +1,65 @@ +# pager.py - display output using a pager +# +# Copyright 2008 David Soria Parra <dsp@php.net> +# +# This software may be used and distributed according to the terms +# of the GNU General Public License, incorporated herein by reference. +# +# To load the extension, add it to your .hgrc file: +# +# [extension] +# hgext.pager = +# +# Run "hg help pager" to get info on configuration. + +'''browse command output with external pager + +To set the pager that should be used, set the application variable: + + [pager] + pager = LESS='FSRX' less + +If no pager is set, the pager extensions uses the environment +variable $PAGER. If neither pager.pager, nor $PAGER is set, no pager +is used. + +If you notice "BROKEN PIPE" error messages, you can disable them +by setting: + + [pager] + quiet = True + +You can disable the pager for certain commands by adding them to the +pager.ignore list: + + [pager] + ignore = version, help, update + +You can also enable the pager only for certain commands using pager.attend: + + [pager] + attend = log + +If pager.attend is present, pager.ignore will be ignored. + +To ignore global commands like "hg version" or "hg help", you have to specify +them in the global .hgrc +''' + +import sys, os, signal +from mercurial import dispatch + +def uisetup(ui): + def pagecmd(ui, options, cmd, cmdfunc): + p = ui.config("pager", "pager", os.environ.get("PAGER")) + if p and sys.stdout.isatty() and '--debugger' not in sys.argv: + attend = ui.configlist('pager', 'attend') + if (cmd in attend or + (cmd not in ui.configlist('pager', 'ignore') and not attend)): + sys.stderr = sys.stdout = os.popen(p, "wb") + if ui.configbool('pager', 'quiet'): + signal.signal(signal.SIGPIPE, signal.SIG_DFL) + return oldrun(ui, options, cmd, cmdfunc) + + oldrun = dispatch._runcommand + dispatch._runcommand = pagecmd
--- a/hgext/patchbomb.py Wed Apr 09 15:27:57 2008 -0700 +++ b/hgext/patchbomb.py Wed Apr 09 15:28:30 2008 -0700 @@ -64,12 +64,12 @@ # # That should be all. Now your patchbomb is on its way out. -import os, errno, socket, tempfile +import os, errno, socket, tempfile, cStringIO import email.MIMEMultipart, email.MIMEText, email.MIMEBase -import email.Utils, email.Encoders -from mercurial import cmdutil, commands, hg, mail, ui, patch, util +import email.Utils, email.Encoders, email.Generator +from mercurial import cmdutil, commands, hg, mail, patch, util from mercurial.i18n import _ -from mercurial.node import * +from mercurial.node import bin def patchbomb(ui, repo, *revs, **opts): '''send changesets by email @@ -117,13 +117,17 @@ def prompt(prompt, default = None, rest = ': ', empty_ok = False): if not ui.interactive: return default - if default: prompt += ' [%s]' % default + if default: + prompt += ' [%s]' % default prompt += rest while True: r = ui.prompt(prompt, default=default) - if r: return r - if default is not None: return default - if empty_ok: return r + if r: + return r + if default is not None: + return default + if empty_ok: + return r ui.warn(_('Please enter a valid value.\n')) def confirm(s, denial): @@ -149,27 +153,33 @@ body = '' for line in patch: if line.startswith('#'): - if line.startswith('# Node ID'): node = line.split()[-1] + if line.startswith('# Node ID'): + node = line.split()[-1] continue - if (line.startswith('diff -r') - or line.startswith('diff --git')): + if line.startswith('diff -r') or line.startswith('diff --git'): break desc.append(line) - if not node: raise ValueError + if not node: + raise ValueError - #body = ('\n'.join(desc[1:]).strip() or - # 'Patch subject is complete summary.') - #body += '\n\n\n' + if opts['attach']: + body = ('\n'.join(desc[1:]).strip() or + 'Patch subject is complete summary.') + body += '\n\n\n' - if opts['plain']: - while patch and patch[0].startswith('# '): patch.pop(0) - if patch: patch.pop(0) - while patch and not patch[0].strip(): patch.pop(0) - if opts['diffstat']: + if opts.get('plain'): + while patch and patch[0].startswith('# '): + patch.pop(0) + if patch: + patch.pop(0) + while patch and not patch[0].strip(): + patch.pop(0) + if opts.get('diffstat'): body += cdiffstat('\n'.join(desc), patch) + '\n\n' - if opts['attach']: + if opts.get('attach') or opts.get('inline'): msg = email.MIMEMultipart.MIMEMultipart() - if body: msg.attach(email.MIMEText.MIMEText(body, 'plain')) + if body: + msg.attach(email.MIMEText.MIMEText(body, 'plain')) p = email.MIMEText.MIMEText('\n'.join(patch), 'x-patch') binnode = bin(node) # if node is mq patch, it will have patch file name as tag @@ -179,10 +189,13 @@ patchname = patchname[0] elif total > 1: patchname = cmdutil.make_filename(repo, '%b-%n.patch', - binnode, idx, total) + binnode, idx, total) else: patchname = cmdutil.make_filename(repo, '%b.patch', binnode) - p['Content-Disposition'] = 'inline; filename=' + patchname + disposition = 'inline' + if opts['attach']: + disposition = 'attachment' + p['Content-Disposition'] = disposition + '; filename=' + patchname msg.attach(p) else: body += '\n'.join(patch) @@ -190,7 +203,7 @@ subj = desc[0].strip().rstrip('. ') if total == 1: - subj = '[PATCH] ' + (opts['subject'] or subj) + subj = '[PATCH] ' + (opts.get('subject') or subj) else: tlen = len(str(total)) subj = '[PATCH %0*d of %d] %s' % (tlen, idx, total, subj) @@ -216,7 +229,7 @@ tmpfn = os.path.join(tmpdir, 'bundle') try: commands.bundle(ui, repo, tmpfn, dest, **opts) - return open(tmpfn).read() + return open(tmpfn, 'rb').read() finally: try: os.unlink(tmpfn) @@ -224,7 +237,7 @@ pass os.rmdir(tmpdir) - if not (opts['test'] or opts['mbox']): + if not (opts.get('test') or opts.get('mbox')): # really sending mail.validateconfig(ui) @@ -234,7 +247,8 @@ cmdutil.setremoteconfig(ui, opts) if opts.get('outgoing') and opts.get('bundle'): - raise util.Abort(_("--outgoing mode always on with --bundle; do not re-specify --outgoing")) + raise util.Abort(_("--outgoing mode always on with --bundle;" + " do not re-specify --outgoing")) if opts.get('outgoing') or opts.get('bundle'): if len(revs) > 1: @@ -254,13 +268,22 @@ # start if opts.get('date'): - start_time = util.parsedate(opts['date']) + start_time = util.parsedate(opts.get('date')) else: start_time = util.makedate() def genmsgid(id): return '<%s.%s@%s>' % (id[:20], int(start_time[0]), socket.getfqdn()) + def getdescription(body, sender): + if opts.get('desc'): + body = open(opts.get('desc')).read() + else: + ui.write(_('\nWrite the introductory message for the ' + 'patch series.\n\n')) + body = ui.edit(body, sender) + return body + def getexportmsgs(): patches = [] @@ -285,7 +308,8 @@ jumbo = [] msgs = [] - ui.write(_('This patch series consists of %d patches.\n\n') % len(patches)) + ui.write(_('This patch series consists of %d patches.\n\n') + % len(patches)) for p, i in zip(patches, xrange(len(patches))): jumbo.extend(p) @@ -295,24 +319,18 @@ tlen = len(str(len(patches))) subj = '[PATCH %0*d of %d] %s' % ( - tlen, 0, - len(patches), - opts['subject'] or - prompt('Subject:', rest = ' [PATCH %0*d of %d] ' % (tlen, 0, - len(patches)))) + tlen, 0, len(patches), + opts.get('subject') or + prompt('Subject:', + rest=' [PATCH %0*d of %d] ' % (tlen, 0, len(patches)))) body = '' - if opts['diffstat']: + if opts.get('diffstat'): d = cdiffstat(_('Final summary:\n'), jumbo) - if d: body = '\n' + d + if d: + body = '\n' + d - if opts['desc']: - body = open(opts['desc']).read() - else: - ui.write(_('\nWrite the introductory message for the ' - 'patch series.\n\n')) - body = ui.edit(body, sender) - + body = getdescription(body, sender) msg = email.MIMEText.MIMEText(body) msg['Subject'] = subj @@ -320,11 +338,10 @@ return msgs def getbundlemsgs(bundle): - subj = (opts['subject'] + subj = (opts.get('subject') or prompt('Subject:', default='A bundle for your repository')) - ui.write(_('\nWrite the introductory message for the bundle.\n\n')) - body = ui.edit('', sender) + body = getdescription('', sender) msg = email.MIMEMultipart.MIMEMultipart() if body: msg.attach(email.MIMEText.MIMEText(body, 'plain')) @@ -337,7 +354,7 @@ msg['Subject'] = subj return [msg] - sender = (opts['from'] or ui.config('email', 'from') or + sender = (opts.get('from') or ui.config('email', 'from') or ui.config('patchbomb', 'from') or prompt('From', ui.username())) @@ -347,15 +364,15 @@ msgs = getexportmsgs() def getaddrs(opt, prpt, default = None): - addrs = opts[opt] or (ui.config('email', opt) or - ui.config('patchbomb', opt) or - prompt(prpt, default = default)).split(',') + addrs = opts.get(opt) or (ui.config('email', opt) or + ui.config('patchbomb', opt) or + prompt(prpt, default = default)).split(',') return [a.strip() for a in addrs if a.strip()] to = getaddrs('to', 'To') cc = getaddrs('cc', 'Cc', '') - bcc = opts['bcc'] or (ui.config('email', 'bcc') or + bcc = opts.get('bcc') or (ui.config('email', 'bcc') or ui.config('patchbomb', 'bcc') or '').split(',') bcc = [a.strip() for a in bcc if a.strip()] @@ -364,6 +381,7 @@ parent = None sender_addr = email.Utils.parseaddr(sender)[1] + sendmail = None for m in msgs: try: m['Message-Id'] = genmsgid(m['X-Mercurial-Node']) @@ -373,48 +391,56 @@ m['In-Reply-To'] = parent else: parent = m['Message-Id'] - m['Date'] = util.datestr(date=start_time, - format="%a, %d %b %Y %H:%M:%S", timezone=True) + m['Date'] = util.datestr(start_time, "%a, %d %b %Y %H:%M:%S %1%2") start_time = (start_time[0] + 1, start_time[1]) m['From'] = sender m['To'] = ', '.join(to) - if cc: m['Cc'] = ', '.join(cc) - if bcc: m['Bcc'] = ', '.join(bcc) - if opts['test']: + if cc: + m['Cc'] = ', '.join(cc) + if bcc: + m['Bcc'] = ', '.join(bcc) + if opts.get('test'): ui.status('Displaying ', m['Subject'], ' ...\n') ui.flush() if 'PAGER' in os.environ: fp = os.popen(os.environ['PAGER'], 'w') else: fp = ui + generator = email.Generator.Generator(fp, mangle_from_=False) try: - fp.write(m.as_string(0)) + generator.flatten(m, 0) fp.write('\n') except IOError, inst: if inst.errno != errno.EPIPE: raise if fp is not ui: fp.close() - elif opts['mbox']: + elif opts.get('mbox'): ui.status('Writing ', m['Subject'], ' ...\n') - fp = open(opts['mbox'], m.has_key('In-Reply-To') and 'ab+' or 'wb+') - date = util.datestr(date=start_time, - format='%a %b %d %H:%M:%S %Y', timezone=False) + fp = open(opts.get('mbox'), 'In-Reply-To' in m and 'ab+' or 'wb+') + generator = email.Generator.Generator(fp, mangle_from_=True) + date = util.datestr(start_time, '%a %b %d %H:%M:%S %Y') fp.write('From %s %s\n' % (sender_addr, date)) - fp.write(m.as_string(0)) + generator.flatten(m, 0) fp.write('\n\n') fp.close() else: + if not sendmail: + sendmail = mail.connect(ui) ui.status('Sending ', m['Subject'], ' ...\n') # Exim does not remove the Bcc field del m['Bcc'] - mail.sendmail(ui, sender, to + bcc + cc, m.as_string(0)) + fp = cStringIO.StringIO() + generator = email.Generator.Generator(fp, mangle_from_=False) + generator.flatten(m, 0) + sendmail(sender, to + bcc + cc, fp.getvalue()) cmdtable = { "email": (patchbomb, - [('a', 'attach', None, _('send patches as inline attachments')), + [('a', 'attach', None, _('send patches as attachments')), + ('i', 'inline', None, _('send patches as inline attachments')), ('', 'bcc', [], _('email addresses of blind copy recipients')), ('c', 'cc', [], _('email addresses of copy recipients')), ('d', 'diffstat', None, _('add diffstat output to messages')),
--- a/hgext/purge.py Wed Apr 09 15:27:57 2008 -0700 +++ b/hgext/purge.py Wed Apr 09 15:28:30 2008 -0700 @@ -27,7 +27,7 @@ # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. -from mercurial import hg, util, commands +from mercurial import util, commands from mercurial.i18n import _ import os
--- a/hgext/record.py Wed Apr 09 15:27:57 2008 -0700 +++ b/hgext/record.py Wed Apr 09 15:28:30 2008 -0700 @@ -5,19 +5,27 @@ # This software may be used and distributed according to the terms of # the GNU General Public License, incorporated herein by reference. -'''interactive change selection during commit''' +'''interactive change selection during commit or qrefresh''' from mercurial.i18n import _ -from mercurial import cmdutil, commands, cmdutil, hg, mdiff, patch, revlog +from mercurial import cmdutil, commands, extensions, hg, mdiff, patch from mercurial import util -import copy, cStringIO, errno, operator, os, re, shutil, tempfile +import copy, cStringIO, errno, operator, os, re, tempfile lines_re = re.compile(r'@@ -(\d+),(\d+) \+(\d+),(\d+) @@\s*(.*)') def scanpatch(fp): + """like patch.iterhunks, but yield different events + + - ('file', [header_lines + fromfile + tofile]) + - ('context', [context_lines]) + - ('hunk', [hunk_lines]) + - ('range', (-start,len, +start,len, diffp)) + """ lr = patch.linereader(fp) def scanwhile(first, p): + """scan lr while predicate holds""" lines = [first] while True: line = lr.readline() @@ -58,6 +66,10 @@ raise patch.PatchError('unknown patch content: %r' % line) class header(object): + """patch header + + XXX shoudn't we move this to mercurial/patch.py ? + """ diff_re = re.compile('diff --git a/(.*) b/(.*)$') allhunks_re = re.compile('(?:index|new file|deleted file) ') pretty_re = re.compile('(?:new file|deleted file) ') @@ -115,11 +127,16 @@ return True def countchanges(hunk): + """hunk -> (n+,n-)""" add = len([h for h in hunk if h[0] == '+']) rem = len([h for h in hunk if h[0] == '-']) return add, rem class hunk(object): + """patch hunk + + XXX shouldn't we merge this with patch.hunk ? + """ maxcontext = 3 def __init__(self, header, fromline, toline, proc, before, hunk, after): @@ -154,7 +171,9 @@ return '<hunk %r@%d>' % (self.filename(), self.fromline) def parsepatch(fp): + """patch -> [] of hunks """ class parser(object): + """patch parsing state machine""" def __init__(self): self.fromline = 0 self.toline = 0 @@ -227,10 +246,14 @@ return p.finished() def filterpatch(ui, chunks): + """Interactively filter patch chunks into applied-only chunks""" chunks = list(chunks) chunks.reverse() seen = {} def consumefile(): + """fetch next portion from chunks until a 'header' is seen + NB: header == new-file mark + """ consumed = [] while chunks: if isinstance(chunks[-1], header): @@ -238,17 +261,27 @@ else: consumed.append(chunks.pop()) return consumed - resp_all = [None] - resp_file = [None] - applied = {} + + resp_all = [None] # this two are changed from inside prompt, + resp_file = [None] # so can't be usual variables + applied = {} # 'filename' -> [] of chunks def prompt(query): + """prompt query, and process base inputs + + - y/n for the rest of file + - y/n for the rest + - ? (help) + - q (quit) + + else, input is returned to the caller. + """ if resp_all[0] is not None: return resp_all[0] if resp_file[0] is not None: return resp_file[0] while True: - r = (ui.prompt(query + _(' [Ynsfdaq?] '), '[Ynsfdaq?]?$', - matchflags=re.I) or 'y').lower() + r = (ui.prompt(query + _(' [Ynsfdaq?] '), '(?i)[Ynsfdaq?]?$') + or 'y').lower() if r == '?': c = record.__doc__.find('y - record this change') for l in record.__doc__[c:].splitlines(): @@ -268,6 +301,7 @@ while chunks: chunk = chunks.pop() if isinstance(chunk, header): + # new-file mark resp_file = [None] fixoffset = 0 hdr = ''.join(chunk.header) @@ -286,6 +320,7 @@ else: consumefile() else: + # new hunk if resp_file[0] is None and resp_all[0] is None: chunk.pretty(ui) r = prompt(_('record this change to %r?') % @@ -306,6 +341,8 @@ If a list of files is omitted, all changes reported by "hg status" will be candidates for recording. + See 'hg help dates' for a list of formats valid for -d/--date. + You will be prompted for whether to record changes to each modified file, and for files with multiple changes, for each change to use. For each query, the following responses are @@ -323,10 +360,48 @@ ? - display help''' + def record_committer(ui, repo, pats, opts): + commands.commit(ui, repo, *pats, **opts) + + dorecord(ui, repo, record_committer, *pats, **opts) + + +def qrecord(ui, repo, patch, *pats, **opts): + '''interactively record a new patch + + see 'hg help qnew' & 'hg help record' for more information and usage + ''' + + try: + mq = extensions.find('mq') + except KeyError: + raise util.Abort(_("'mq' extension not loaded")) + + def qrecord_committer(ui, repo, pats, opts): + mq.new(ui, repo, patch, *pats, **opts) + + opts = opts.copy() + opts['force'] = True # always 'qnew -f' + dorecord(ui, repo, qrecord_committer, *pats, **opts) + + +def dorecord(ui, repo, committer, *pats, **opts): if not ui.interactive: raise util.Abort(_('running non-interactively, use commit instead')) def recordfunc(ui, repo, files, message, match, opts): + """This is generic record driver. + + It's job is to interactively filter local changes, and accordingly + prepare working dir into a state, where the job can be delegated to + non-interactive commit command such as 'commit' or 'qrefresh'. + + After the actual job is done by non-interactive command, working dir + state is restored to original. + + In the end we'll record intresting changes, and everything else will be + left in place, so the user can continue his work. + """ if files: changes = None else: @@ -339,6 +414,7 @@ match=match, changes=changes, opts=diffopts, fp=fp) fp.seek(0) + # 1. filter patch, so we have intending-to apply subset of it chunks = filterpatch(ui, parsepatch(fp)) del fp @@ -357,6 +433,7 @@ changes = repo.status(files=newfiles, match=match)[:5] modified = dict.fromkeys(changes[0]) + # 2. backup changed files, so we can restore them in the end backups = {} backupdir = repo.join('record-backups') try: @@ -365,6 +442,7 @@ if err.errno != errno.EEXIST: raise try: + # backup continues for f in newfiles: if f not in modified: continue @@ -382,19 +460,32 @@ dopatch = fp.tell() fp.seek(0) + # 3a. apply filtered patch to clean repo (clean) if backups: hg.revert(repo, repo.dirstate.parents()[0], backups.has_key) + # 3b. (apply) if dopatch: ui.debug('applying patch\n') ui.debug(fp.getvalue()) patch.internalpatch(fp, ui, 1, repo.root) del fp - repo.commit(newfiles, message, opts['user'], opts['date'], match, - force_editor=opts.get('force_editor')) + # 4. We prepared working directory according to filtered patch. + # Now is the time to delegate the job to commit/qrefresh or the like! + + # it is important to first chdir to repo root -- we'll call a + # highlevel command with list of pathnames relative to repo root + cwd = os.getcwd() + os.chdir(repo.root) + try: + committer(ui, repo, newfiles, opts) + finally: + os.chdir(cwd) + return 0 finally: + # 5. finally restore backed-up files try: for realname, tmpname in backups.iteritems(): ui.debug('restoring %r to %r\n' % (tmpname, realname)) @@ -408,8 +499,29 @@ cmdtable = { "record": (record, - [('A', 'addremove', None, - _('mark new/missing files as added/removed before committing')), - ] + commands.walkopts + commands.commitopts + commands.commitopts2, + + # add commit options + commands.table['^commit|ci'][1], + _('hg record [OPTION]... [FILE]...')), } + + +def extsetup(): + try: + mq = extensions.find('mq') + except KeyError: + return + + qcmdtable = { + "qrecord": + (qrecord, + + # add qnew options, except '--force' + [opt for opt in mq.cmdtable['qnew'][1] if opt[1] != 'force'], + + _('hg qrecord [OPTION]... PATCH [FILE]...')), + } + + cmdtable.update(qcmdtable) +
--- a/hgext/transplant.py Wed Apr 09 15:27:57 2008 -0700 +++ b/hgext/transplant.py Wed Apr 09 15:28:30 2008 -0700 @@ -7,7 +7,7 @@ from mercurial.i18n import _ import os, tempfile -from mercurial import bundlerepo, changegroup, cmdutil, commands, hg, merge +from mercurial import bundlerepo, changegroup, cmdutil, hg, merge from mercurial import patch, revlog, util '''patch transplanting tool
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/hgext/win32mbcs.py Wed Apr 09 15:28:30 2008 -0700 @@ -0,0 +1,158 @@ +# win32mbcs.py -- MBCS filename support for Mercurial on Windows +# +# Copyright (c) 2008 Shun-ichi Goto <shunichi.goto@gmail.com> +# +# Version: 0.1 +# Author: Shun-ichi Goto <shunichi.goto@gmail.com> +# +# This software may be used and distributed according to the terms +# of the GNU General Public License, incorporated herein by reference. +# +"""Allow to use shift_jis/big5 filenames on Windows. + +There is a well known issue "0x5c problem" on Windows. It is a +trouble on handling path name as raw encoded byte sequence of +problematic encodings like shift_jis or big5. The primary intent +of this extension is to allow using such a encoding on Mercurial +without strange file operation error. + +By enabling this extension, hook mechanism is activated and some +functions are altered. Usually, this encoding is your local encoding +on your system by default. So you can get benefit simply by enabling +this extension. + +The encoding for filename is same one for terminal by default. You +can change the encoding by setting HGENCODING environment variable. + +This extension is usefull for: + * Japanese Windows user using shift_jis encoding. + * Chinese Windows user using big5 encoding. + * Users who want to use a repository created with such a encoding. + +Note: Unix people does not need to use this extension. + +""" + +import os +from mercurial.i18n import _ +from mercurial import util + +__all__ = ['install', 'uninstall', 'reposetup'] + + +# codec and alias names of sjis and big5 to be faked. +_problematic_encodings = util.frozenset([ + 'big5', 'big5-tw', 'csbig5', + 'big5hkscs', 'big5-hkscs', 'hkscs', + 'cp932', '932', 'ms932', 'mskanji', 'ms-kanji', + 'shift_jis', 'csshiftjis', 'shiftjis', 'sjis', 's_jis', + 'shift_jis_2004', 'shiftjis2004', 'sjis_2004', 'sjis2004', + 'shift_jisx0213', 'shiftjisx0213', 'sjisx0213', 's_jisx0213', + ]) + +# attribute name to store original function +_ORIGINAL = '_original' + +_ui = None + +def decode_with_check(arg): + if isinstance(arg, tuple): + return tuple(map(decode_with_check, arg)) + elif isinstance(arg, list): + return map(decode_with_check, arg) + elif isinstance(arg, str): + uarg = arg.decode(util._encoding) + if arg == uarg.encode(util._encoding): + return uarg + else: + raise UnicodeError("Not local encoding") + else: + return arg + +def encode_with_check(arg): + if isinstance(arg, tuple): + return tuple(map(encode_with_check, arg)) + elif isinstance(arg, list): + return map(encode_with_check, arg) + elif isinstance(arg, unicode): + ret = arg.encode(util._encoding) + return ret + else: + return arg + +def wrap(func): + + def wrapped(*args): + # check argument is unicode, then call original + for arg in args: + if isinstance(arg, unicode): + return func(*args) + # make decoded argument list into uargs + try: + args = decode_with_check(args) + except UnicodeError, exc: + # If not encoded with _local_fs_encoding, report it then + # continue with calling original function. + _ui.warn(_("WARNING: [win32mbcs] filename conversion fail for" + + " %s: '%s'\n") % (util._encoding, args)) + return func(*args) + # call as unicode operation, then return with encoding + return encode_with_check(func(*args)) + + # fake is only for relevant environment. + if hasattr(func, _ORIGINAL) or \ + util._encoding.lower() not in _problematic_encodings: + return func + else: + f = wrapped + f.__name__ = func.__name__ + setattr(f, _ORIGINAL, func) # hold original to restore + return f + +def unwrap(func): + return getattr(func, _ORIGINAL, func) + +def install(): + # wrap some python functions and mercurial functions + # to handle raw bytes on Windows. + # NOTE: dirname and basename is safe because they use result + # of os.path.split() + global _ui + if not _ui: + from mercurial import ui + _ui = ui.ui() + os.path.join = wrap(os.path.join) + os.path.split = wrap(os.path.split) + os.path.splitext = wrap(os.path.splitext) + os.path.splitunc = wrap(os.path.splitunc) + os.path.normpath = wrap(os.path.normpath) + os.path.normcase = wrap(os.path.normcase) + os.makedirs = wrap(os.makedirs) + util.endswithsep = wrap(util.endswithsep) + util.splitpath = wrap(util.splitpath) + +def uninstall(): + # restore original functions. + os.path.join = unwrap(os.path.join) + os.path.split = unwrap(os.path.split) + os.path.splitext = unwrap(os.path.splitext) + os.path.splitunc = unwrap(os.path.splitunc) + os.path.normpath = unwrap(os.path.normpath) + os.path.normcase = unwrap(os.path.normcase) + os.makedirs = unwrap(os.makedirs) + util.endswithsep = unwrap(util.endswithsep) + util.splitpath = unwrap(util.splitpath) + + +def reposetup(ui, repo): + # TODO: decide use of config section for this extension + global _ui + _ui = ui + if not os.path.supports_unicode_filenames: + ui.warn(_("[win32mbcs] cannot activate on this platform.\n")) + return + # install features of this extension + install() + ui.debug(_("[win32mbcs] activeted with encoding: %s\n") % util._encoding) + +# win32mbcs.py ends here
--- a/hgext/win32text.py Wed Apr 09 15:27:57 2008 -0700 +++ b/hgext/win32text.py Wed Apr 09 15:28:30 2008 -0700 @@ -1,35 +1,76 @@ -from mercurial import util, ui +# win32text.py - LF <-> CRLF/CR translation utilities for Windows/Mac users +# +# This software may be used and distributed according to the terms +# of the GNU General Public License, incorporated herein by reference. +# +# To perform automatic newline conversion, use: +# +# [extensions] +# hgext.win32text = +# [encode] +# ** = cleverencode: +# # or ** = macencode: +# [decode] +# ** = cleverdecode: +# # or ** = macdecode: +# +# If not doing conversion, to make sure you do not commit CRLF/CR by accident: +# +# [hooks] +# pretxncommit.crlf = python:hgext.win32text.forbidcrlf +# # or pretxncommit.cr = python:hgext.win32text.forbidcr +# +# To do the same check on a server to prevent CRLF/CR from being pushed or +# pulled: +# +# [hooks] +# pretxnchangegroup.crlf = python:hgext.win32text.forbidcrlf +# # or pretxnchangegroup.cr = python:hgext.win32text.forbidcr + from mercurial.i18n import gettext as _ +from mercurial.node import bin, short import re # regexp for single LF without CR preceding. re_single_lf = re.compile('(^|[^\r])\n', re.MULTILINE) -def dumbdecode(s, cmd): - # warn if already has CRLF in repository. +newlinestr = {'\r\n': 'CRLF', '\r': 'CR'} +filterstr = {'\r\n': 'clever', '\r': 'mac'} + +def checknewline(s, newline, ui=None, repo=None, filename=None): + # warn if already has 'newline' in repository. # it might cause unexpected eol conversion. # see issue 302: # http://www.selenic.com/mercurial/bts/issue302 - if '\r\n' in s: - u = ui.ui() - u.warn(_('WARNING: file in repository already has CRLF line ending \n' - ' which does not need eol conversion by win32text plugin.\n' - ' Please reconsider encode/decode setting in' - ' mercurial.ini or .hg/hgrc\n' - ' before next commit.\n')) + if newline in s and ui and filename and repo: + ui.warn(_('WARNING: %s already has %s line endings\n' + 'and does not need EOL conversion by the win32text plugin.\n' + 'Before your next commit, please reconsider your ' + 'encode/decode settings in \nMercurial.ini or %s.\n') % + (filename, newlinestr[newline], repo.join('hgrc'))) + +def dumbdecode(s, cmd, **kwargs): + checknewline(s, '\r\n', **kwargs) # replace single LF to CRLF return re_single_lf.sub('\\1\r\n', s) def dumbencode(s, cmd): return s.replace('\r\n', '\n') +def macdumbdecode(s, cmd, **kwargs): + checknewline(s, '\r', **kwargs) + return s.replace('\n', '\r') + +def macdumbencode(s, cmd): + return s.replace('\r', '\n') + def clevertest(s, cmd): if '\0' in s: return False return True -def cleverdecode(s, cmd): +def cleverdecode(s, cmd, **kwargs): if clevertest(s, cmd): - return dumbdecode(s, cmd) + return dumbdecode(s, cmd, **kwargs) return s def cleverencode(s, cmd): @@ -37,9 +78,70 @@ return dumbencode(s, cmd) return s -util.filtertable.update({ +def macdecode(s, cmd, **kwargs): + if clevertest(s, cmd): + return macdumbdecode(s, cmd, **kwargs) + return s + +def macencode(s, cmd): + if clevertest(s, cmd): + return macdumbencode(s, cmd) + return s + +_filters = { 'dumbdecode:': dumbdecode, 'dumbencode:': dumbencode, 'cleverdecode:': cleverdecode, 'cleverencode:': cleverencode, - }) + 'macdumbdecode:': macdumbdecode, + 'macdumbencode:': macdumbencode, + 'macdecode:': macdecode, + 'macencode:': macencode, + } + +def forbidnewline(ui, repo, hooktype, node, newline, **kwargs): + halt = False + for rev in xrange(repo.changelog.rev(bin(node)), repo.changelog.count()): + c = repo.changectx(rev) + for f in c.files(): + if f not in c: + continue + data = c[f].data() + if '\0' not in data and newline in data: + if not halt: + ui.warn(_('Attempt to commit or push text file(s) ' + 'using %s line endings\n') % + newlinestr[newline]) + ui.warn(_('in %s: %s\n') % (short(c.node()), f)) + halt = True + if halt and hooktype == 'pretxnchangegroup': + crlf = newlinestr[newline].lower() + filter = filterstr[newline] + ui.warn(_('\nTo prevent this mistake in your local repository,\n' + 'add to Mercurial.ini or .hg/hgrc:\n' + '\n' + '[hooks]\n' + 'pretxncommit.%s = python:hgext.win32text.forbid%s\n' + '\n' + 'and also consider adding:\n' + '\n' + '[extensions]\n' + 'hgext.win32text =\n' + '[encode]\n' + '** = %sencode:\n' + '[decode]\n' + '** = %sdecode:\n') % (crlf, crlf, filter, filter)) + return halt + +def forbidcrlf(ui, repo, hooktype, node, **kwargs): + return forbidnewline(ui, repo, hooktype, node, '\r\n', **kwargs) + +def forbidcr(ui, repo, hooktype, node, **kwargs): + return forbidnewline(ui, repo, hooktype, node, '\r', **kwargs) + +def reposetup(ui, repo): + if not repo.local(): + return + for name, fn in _filters.iteritems(): + repo.adddatafilter(name, fn) +
--- a/hgmerge Wed Apr 09 15:27:57 2008 -0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,213 +0,0 @@ -#!/bin/sh -# -# hgmerge - default merge helper for Mercurial -# -# This tries to find a way to do three-way merge on the current system. -# The result ought to end up in $1. Script is run in root directory of -# repository. -# -# Environment variables set by Mercurial: -# HG_FILE name of file within repo -# HG_MY_NODE revision being merged -# HG_OTHER_NODE revision being merged - -set -e # bail out quickly on failure - -LOCAL="$1" -BASE="$2" -OTHER="$3" - -if [ -n "$VISUAL" ]; then - EDIT_PROG="$VISUAL" -elif [ -n "$EDITOR" ]; then - EDIT_PROG="$EDITOR" -else - EDIT_PROG="vi" -fi - -# find decent versions of our utilities, insisting on the GNU versions where we -# need to -MERGE="merge" -DIFF3="gdiff3" -DIFF="gdiff" -PATCH="gpatch" - -type "$MERGE" >/dev/null 2>&1 || MERGE= -type "$DIFF3" >/dev/null 2>&1 || DIFF3="diff3" -$DIFF3 --version >/dev/null 2>&1 || DIFF3= -type "$DIFF" >/dev/null 2>&1 || DIFF="diff" -type "$DIFF" >/dev/null 2>&1 || DIFF= -type "$PATCH" >/dev/null 2>&1 || PATCH="patch" -type "$PATCH" >/dev/null 2>&1 || PATCH= - -# find optional visual utilities -FILEMERGE="/Developer/Applications/Utilities/FileMerge.app/Contents/MacOS/FileMerge" -KDIFF3="kdiff3" -TKDIFF="tkdiff" -MELD="meld" - -type "$FILEMERGE" >/dev/null 2>&1 || FILEMERGE= -type "$KDIFF3" >/dev/null 2>&1 || KDIFF3= -type "$TKDIFF" >/dev/null 2>&1 || TKDIFF= -type "$MELD" >/dev/null 2>&1 || MELD= - -# Hack for Solaris -TEST="/usr/bin/test" -type "$TEST" >/dev/null 2>&1 || TEST="/bin/test" -type "$TEST" >/dev/null 2>&1 || TEST="test" - -# random part of names -RAND="$RANDOM$RANDOM" - -# temporary directory for diff+patch merge -HGTMP="${TMPDIR-/tmp}/hgmerge.$RAND" - -# backup file -BACKUP="$LOCAL.orig.$RAND" - -# file used to test for file change -CHGTEST="$LOCAL.chg.$RAND" - -# put all your required cleanup here -cleanup() { - rm -f "$BACKUP" "$CHGTEST" - rm -rf "$HGTMP" -} - -# functions concerning program exit -success() { - cleanup - exit 0 -} - -failure() { - echo "merge failed" 1>&2 - mv "$BACKUP" "$LOCAL" - cleanup - exit 1 -} - -# Ask if the merge was successful -ask_if_merged() { - while true; do - echo "$LOCAL seems unchanged." - echo "Was the merge successful? [y/n]" - read answer - case "$answer" in - y*|Y*) success;; - n*|N*) failure;; - esac - done -} - -# Check if conflict markers are present and ask if the merge was successful -conflicts_or_success() { - while egrep '^(<<<<<<< .*|=======|>>>>>>> .*)$' "$LOCAL" >/dev/null; do - echo "$LOCAL contains conflict markers." - echo "Keep this version? [y/n]" - read answer - case "$answer" in - y*|Y*) success;; - n*|N*) failure;; - esac - done - success -} - -# Clean up when interrupted -trap "failure" 1 2 3 6 15 # HUP INT QUIT ABRT TERM - -# Back up our file (and try hard to keep the mtime unchanged) -mv "$LOCAL" "$BACKUP" -cp "$BACKUP" "$LOCAL" - -# Attempt to do a non-interactive merge -if [ -n "$MERGE" -o -n "$DIFF3" ]; then - if [ -n "$MERGE" ]; then - $MERGE "$LOCAL" "$BASE" "$OTHER" 2> /dev/null && success - elif [ -n "$DIFF3" ]; then - $DIFF3 -m "$BACKUP" "$BASE" "$OTHER" > "$LOCAL" && success - fi - if [ $? -gt 1 ]; then - echo "automatic merge failed! Exiting." 1>&2 - failure - fi -fi - -# on MacOS X try FileMerge.app, shipped with Apple's developer tools -if [ -n "$FILEMERGE" ]; then - cp "$BACKUP" "$LOCAL" - cp "$BACKUP" "$CHGTEST" - # filemerge prefers the right by default - $FILEMERGE -left "$OTHER" -right "$LOCAL" -ancestor "$BASE" -merge "$LOCAL" - [ $? -ne 0 ] && echo "FileMerge failed to launch" && failure - $TEST "$LOCAL" -nt "$CHGTEST" && conflicts_or_success || ask_if_merged -fi - -if [ -n "$DISPLAY" ]; then - # try using kdiff3, which is fairly nice - if [ -n "$KDIFF3" ]; then - $KDIFF3 --auto "$BASE" "$BACKUP" "$OTHER" -o "$LOCAL" || failure - conflicts_or_success - fi - - # try using tkdiff, which is a bit less sophisticated - if [ -n "$TKDIFF" ]; then - $TKDIFF "$BACKUP" "$OTHER" -a "$BASE" -o "$LOCAL" || failure - conflicts_or_success - fi - - if [ -n "$MELD" ]; then - cp "$BACKUP" "$CHGTEST" - # protect our feet - meld allows us to save to the left file - cp "$BACKUP" "$LOCAL.tmp.$RAND" - # Meld doesn't have automatic merging, so to reduce intervention - # use the file with conflicts - $MELD "$LOCAL.tmp.$RAND" "$LOCAL" "$OTHER" || failure - # Also it doesn't return good error code - $TEST "$LOCAL" -nt "$CHGTEST" && conflicts_or_success || ask_if_merged - fi -fi - -# Attempt to do a merge with $EDIT_PROG -if [ -n "$MERGE" -o -n "$DIFF3" ]; then - echo "conflicts detected in $LOCAL" - cp "$BACKUP" "$CHGTEST" - case "$EDIT_PROG" in - "emacs") - $EDIT_PROG "$LOCAL" --eval '(condition-case nil (smerge-mode 1) (error nil))' || failure - ;; - *) - $EDIT_PROG "$LOCAL" || failure - ;; - esac - # Some editors do not return meaningful error codes - # Do not take any chances - $TEST "$LOCAL" -nt "$CHGTEST" && conflicts_or_success || ask_if_merged -fi - -# attempt to manually merge with diff and patch -if [ -n "$DIFF" -a -n "$PATCH" ]; then - - (umask 077 && mkdir "$HGTMP") || { - echo "Could not create temporary directory $HGTMP" 1>&2 - failure - } - - $DIFF -u "$BASE" "$OTHER" > "$HGTMP/diff" || : - if $PATCH "$LOCAL" < "$HGTMP/diff"; then - success - else - # If rejects are empty after using the editor, merge was ok - $EDIT_PROG "$LOCAL" "$LOCAL.rej" || failure - $TEST -s "$LOCAL.rej" || success - fi - failure -fi - -echo -echo "hgmerge: unable to find any merge utility!" -echo "supported programs:" -echo "merge, FileMerge, tkdiff, kdiff3, meld, diff+patch" -echo -failure
--- a/hgweb.cgi Wed Apr 09 15:27:57 2008 -0700 +++ b/hgweb.cgi Wed Apr 09 15:28:30 2008 -0700 @@ -9,9 +9,9 @@ # enable importing on demand to reduce startup time from mercurial import demandimport; demandimport.enable() -# send python tracebacks to the browser if an error occurs: -import cgitb -cgitb.enable() +# Uncomment to send python tracebacks to the browser if an error occurs: +#import cgitb +#cgitb.enable() # If you'd like to serve pages with UTF-8 instead of your default # locale charset, you can do so by uncommenting the following lines.
--- a/hgwebdir.cgi Wed Apr 09 15:27:57 2008 -0700 +++ b/hgwebdir.cgi Wed Apr 09 15:28:30 2008 -0700 @@ -9,9 +9,9 @@ # enable importing on demand to reduce startup time from mercurial import demandimport; demandimport.enable() -# send python tracebacks to the browser if an error occurs: -import cgitb -cgitb.enable() +# Uncomment to send python tracebacks to the browser if an error occurs: +#import cgitb +#cgitb.enable() # If you'd like to serve pages with UTF-8 instead of your default # locale charset, you can do so by uncommenting the following lines.
--- a/mercurial/archival.py Wed Apr 09 15:27:57 2008 -0700 +++ b/mercurial/archival.py Wed Apr 09 15:28:30 2008 -0700 @@ -6,7 +6,7 @@ # the GNU General Public License, incorporated herein by reference. from i18n import _ -from node import * +from node import hex import cStringIO, os, stat, tarfile, time, util, zipfile import zlib, gzip @@ -15,7 +15,7 @@ safe for consumers.''' if prefix: - prefix = prefix.replace('\\', '/') + prefix = util.normpath(prefix) else: if not isinstance(dest, str): raise ValueError('dest must be string if no prefix') @@ -52,7 +52,8 @@ def _write_gzip_header(self): self.fileobj.write('\037\213') # magic header self.fileobj.write('\010') # compression method - fname = self.filename[:-3] + # Python 2.6 deprecates self.filename + fname = getattr(self, 'name', None) or self.filename flags = 0 if fname: flags = gzip.FNAME @@ -208,12 +209,16 @@ archiver.addfile(name, mode, islink, data) ctx = repo.changectx(node) + if kind not in archivers: + raise util.Abort(_("unknown archive type '%s'" % kind)) archiver = archivers[kind](dest, prefix, mtime or ctx.date()[0]) m = ctx.manifest() items = m.items() items.sort() - write('.hg_archival.txt', 0644, False, - lambda: 'repo: %s\nnode: %s\n' % (hex(repo.changelog.node(0)), hex(node))) + if repo.ui.configbool("ui", "archivemeta", True): + write('.hg_archival.txt', 0644, False, + lambda: 'repo: %s\nnode: %s\n' % ( + hex(repo.changelog.node(0)), hex(node))) for filename, filenode in items: write(filename, m.execf(filename) and 0755 or 0644, m.linkf(filename), lambda: repo.file(filename).read(filenode))
--- a/mercurial/bundlerepo.py Wed Apr 09 15:27:57 2008 -0700 +++ b/mercurial/bundlerepo.py Wed Apr 09 15:28:30 2008 -0700 @@ -10,10 +10,10 @@ of the GNU General Public License, incorporated herein by reference. """ -from node import * +from node import hex, nullid, short from i18n import _ -import changegroup, util, os, struct, bz2, tempfile, mdiff -import localrepo, changelog, manifest, filelog, revlog +import changegroup, util, os, struct, bz2, tempfile, shutil, mdiff +import repo, localrepo, changelog, manifest, filelog, revlog class bundlerevlog(revlog.revlog): def __init__(self, opener, indexfile, bundlefile, @@ -48,7 +48,8 @@ continue for p in (p1, p2): if not p in self.nodemap: - raise revlog.LookupError(hex(p1), _("unknown parent %s") % short(p1)) + raise revlog.LookupError(p1, self.indexfile, + _("unknown parent")) if linkmapper is None: link = n else: @@ -152,10 +153,18 @@ class bundlerepository(localrepo.localrepository): def __init__(self, ui, path, bundlename): - localrepo.localrepository.__init__(self, ui, path) + self._tempparent = None + try: + localrepo.localrepository.__init__(self, ui, path) + except repo.RepoError: + self._tempparent = tempfile.mkdtemp() + tmprepo = localrepo.instance(ui,self._tempparent,1) + localrepo.localrepository.__init__(self, ui, self._tempparent) - self._url = 'bundle:' + bundlename - if path: self._url += '+' + path + if path: + self._url = 'bundle:' + path + '+' + bundlename + else: + self._url = 'bundle:' + bundlename self.tempfile = None self.bundlefile = open(bundlename, "rb") @@ -218,9 +227,6 @@ def url(self): return self._url - def dev(self): - return -1 - def file(self, f): if not self.bundlefilespos: self.bundlefile.seek(self.filestart) @@ -252,6 +258,11 @@ tempfile = getattr(self, 'tempfile', None) if tempfile is not None: os.unlink(tempfile) + if self._tempparent: + shutil.rmtree(self._tempparent, True) + + def cancopy(self): + return False def instance(ui, path, create): if create:
--- a/mercurial/byterange.py Wed Apr 09 15:27:57 2008 -0700 +++ b/mercurial/byterange.py Wed Apr 09 15:28:30 2008 -0700 @@ -233,7 +233,7 @@ size = (lb - fb) fo = RangeableFileObject(fo, (fb, lb)) headers = mimetools.Message(StringIO( - 'Content-Type: %s\nContent-Length: %d\nLast-modified: %s\n' % + 'Content-Type: %s\nContent-Length: %d\nLast-Modified: %s\n' % (mtype or 'text/plain', size, modified))) return urllib.addinfourl(fo, headers, 'file:'+file)
--- a/mercurial/changegroup.py Wed Apr 09 15:27:57 2008 -0700 +++ b/mercurial/changegroup.py Wed Apr 09 15:28:30 2008 -0700 @@ -53,6 +53,9 @@ "HG10GZ": ("HG10GZ", lambda: zlib.compressobj()), } +# hgweb uses this list to communicate it's preferred type +bundlepriority = ['HG10GZ', 'HG10BZ', 'HG10UN'] + def writebundle(cg, filename, bundletype): """Write a bundle file and return its filename. @@ -80,9 +83,13 @@ # in case of sshrepo because we don't know the end of the stream # an empty chunkiter is the end of the changegroup + # a changegroup has at least 2 chunkiters (changelog and manifest). + # after that, an empty chunkiter is the end of the changegroup empty = False - while not empty: + count = 0 + while not empty or count <= 2: empty = True + count += 1 for chunk in chunkiter(cg): empty = False fh.write(z.compress(chunkheader(len(chunk)))) @@ -101,22 +108,34 @@ if cleanup is not None: os.unlink(cleanup) -def readbundle(fh, fname): - header = fh.read(6) - if not header.startswith("HG"): - raise util.Abort(_("%s: not a Mercurial bundle file") % fname) - elif not header.startswith("HG10"): - raise util.Abort(_("%s: unknown bundle version") % fname) - - if header == "HG10BZ": +def unbundle(header, fh): + if header == 'HG10UN': + return fh + elif not header.startswith('HG'): + # old client with uncompressed bundle + def generator(f): + yield header + for chunk in f: + yield chunk + elif header == 'HG10GZ': + def generator(f): + zd = zlib.decompressobj() + for chunk in f: + yield zd.decompress(chunk) + elif header == 'HG10BZ': def generator(f): zd = bz2.BZ2Decompressor() zd.decompress("BZ") for chunk in util.filechunkiter(f, 4096): yield zd.decompress(chunk) - return util.chunkbuffer(generator(fh)) - elif header == "HG10UN": - return fh + return util.chunkbuffer(generator(fh)) - raise util.Abort(_("%s: unknown bundle compression type") - % fname) +def readbundle(fh, fname): + header = fh.read(6) + if not header.startswith('HG'): + raise util.Abort(_('%s: not a Mercurial bundle file') % fname) + if not header.startswith('HG10'): + raise util.Abort(_('%s: unknown bundle version') % fname) + elif header not in bundletypes: + raise util.Abort(_('%s: unknown bundle compression type') % fname) + return unbundle(header, fh)
--- a/mercurial/changelog.py Wed Apr 09 15:27:57 2008 -0700 +++ b/mercurial/changelog.py Wed Apr 09 15:28:30 2008 -0700 @@ -5,9 +5,9 @@ # This software may be used and distributed according to the terms # of the GNU General Public License, incorporated herein by reference. -from revlog import * -from i18n import _ -import os, time, util +from node import bin, hex, nullid +from revlog import revlog +import util def _string_escape(text): """ @@ -16,16 +16,13 @@ >>> s 'ab\\ncd\\\\\\\\n\\x00ab\\rcd\\\\\\n' >>> res = _string_escape(s) - >>> s == _string_unescape(res) + >>> s == res.decode('string_escape') True """ # subset of the string_escape codec text = text.replace('\\', '\\\\').replace('\n', '\\n').replace('\r', '\\r') return text.replace('\0', '\\0') -def _string_unescape(text): - return text.decode('string_escape') - class appender: '''the changelog index must be update last on disk, so we use this class to delay writes to it''' @@ -111,6 +108,9 @@ # if we're doing an initial clone, divert to another file if self._delaycount == 0: self._delayname = fp.name + if not self.count(): + # make sure to truncate the file + mode = mode.replace('a', 'w') return self._realopener(name + ".a", mode) # otherwise, divert to memory return appender(fp, self._delaybuf) @@ -123,10 +123,9 @@ def decode_extra(self, text): extra = {} for l in text.split('\0'): - if not l: - continue - k, v = _string_unescape(l).split(':', 1) - extra[k] = v + if l: + k, v = l.decode('string_escape').split(':', 1) + extra[k] = v return extra def encode_extra(self, d): @@ -136,7 +135,7 @@ items = [_string_escape('%s:%s' % (k, d[k])) for k in keys] return "\0".join(items) - def extract(self, text): + def read(self, node): """ format used: nodeid\n : manifest node in ascii @@ -149,6 +148,7 @@ changelog v0 doesn't use extra """ + text = self.revision(node) if not text: return (nullid, "", (0, 0), [], "", {'branch': 'default'}) last = text.index("\n\n") @@ -175,9 +175,6 @@ files = l[3:] return (manifest, user, (time, timezone), files, desc, extra) - def read(self, node): - return self.extract(self.revision(node)) - def add(self, manifest, list, desc, transaction, p1=None, p2=None, user=None, date=None, extra={}):
--- a/mercurial/cmdutil.py Wed Apr 09 15:27:57 2008 -0700 +++ b/mercurial/cmdutil.py Wed Apr 09 15:28:30 2008 -0700 @@ -5,10 +5,10 @@ # This software may be used and distributed according to the terms # of the GNU General Public License, incorporated herein by reference. -from node import * +from node import hex, nullid, nullrev, short from i18n import _ import os, sys, bisect, stat -import mdiff, bdiff, util, templater, patch, errno +import mdiff, bdiff, util, templater, templatefilters, patch, errno revrangesep = ':' @@ -50,7 +50,7 @@ """Return (aliases, command table entry) for command string.""" choice = findpossible(ui, cmd, table) - if choice.has_key(cmd): + if cmd in choice: return choice[cmd] if len(choice) > 1: @@ -64,6 +64,8 @@ raise UnknownCommand(cmd) def bail_if_changed(repo): + if repo.dirstate.parents()[1] != nullid: + raise util.Abort(_('outstanding uncommitted merge')) modified, added, removed, deleted = repo.status()[:4] if modified or added or removed or deleted: raise util.Abort(_("outstanding uncommitted changes")) @@ -87,6 +89,19 @@ (logfile, inst.strerror)) return message +def loglimit(opts): + """get the log limit according to option -l/--limit""" + limit = opts.get('limit') + if limit: + try: + limit = int(limit) + except ValueError: + raise util.Abort(_('limit must be a positive integer')) + if limit <= 0: raise util.Abort(_('limit must be positive')) + else: + limit = sys.maxint + return limit + def setremoteconfig(ui, opts): "copy remote options to ui tree" if opts.get('ssh'): @@ -455,12 +470,12 @@ if len(pats) == 1: raise util.Abort(_('no destination specified')) dest = pats.pop() - destdirexists = os.path.isdir(dest) + destdirexists = os.path.isdir(dest) and not os.path.islink(dest) if not destdirexists: if len(pats) > 1 or util.patkind(pats[0], None)[0]: raise util.Abort(_('with multiple sources, destination must be an ' 'existing directory')) - if dest.endswith(os.sep) or os.altsep and dest.endswith(os.altsep): + if util.endswithsep(dest): raise util.Abort(_('destination %s is not a directory') % dest) tfn = targetpathfn @@ -493,6 +508,15 @@ rfd, wfd = os.pipe() args = sys.argv[:] args.append('--daemon-pipefds=%d,%d' % (rfd, wfd)) + # Don't pass --cwd to the child process, because we've already + # changed directory. + for i in xrange(1,len(args)): + if args[i].startswith('--cwd='): + del args[i] + break + elif args[i].startswith('--cwd'): + del args[i:i+2] + break pid = os.spawnvp(os.P_NOWAIT | getattr(os, 'P_DETACH', 0), args[0], args) os.close(wfd) @@ -662,7 +686,7 @@ def __init__(self, ui, repo, patch, mapfile, buffered): changeset_printer.__init__(self, ui, repo, patch, buffered) - filters = templater.common_filters.copy() + filters = templatefilters.filters.copy() filters['formatnode'] = (ui.debugflag and (lambda x: x) or (lambda x: x[:12])) self.t = templater.templater(mapfile, filters, @@ -771,10 +795,10 @@ def showcopies(**args): c = [{'name': x[0], 'source': x[1]} for x in copies] return showlist('file_copy', c, plural='file_copies', **args) - + files = [] def getfiles(): - if not files: + if not files: files[:] = self.repo.status( log.parents(changenode)[0], changenode)[:3] return files @@ -891,7 +915,7 @@ def finddate(ui, repo, date): """Find the tipmost changeset that matches the given date spec""" - df = util.matchdate(date + " to " + date) + df = util.matchdate(date) get = util.cachefunc(lambda r: repo.changectx(r).changeset()) changeiter, matchfn = walkchangerevs(ui, repo, [], get, {'rev':None}) results = {} @@ -956,7 +980,7 @@ if follow: defrange = '%s:0' % repo.changectx().rev() else: - defrange = 'tip:0' + defrange = '-1:0' revs = revrange(repo, opts['rev'] or [defrange]) wanted = {} slowpath = anypats or opts.get('removed') @@ -1104,10 +1128,16 @@ def commit(ui, repo, commitfunc, pats, opts): '''commit the specified files or all outstanding changes''' + date = opts.get('date') + if date: + opts['date'] = util.parsedate(date) message = logmessage(opts) - if opts['addremove']: + # extract addremove carefully -- this function can be called from a command + # that doesn't support addremove + if opts.get('addremove'): addremove(repo, pats, opts) + fns, match, anypats = matchpats(repo, pats, opts) if pats: status = repo.status(files=fns, match=match) @@ -1119,10 +1149,11 @@ continue if f not in files: rf = repo.wjoin(f) + rel = repo.pathto(f) try: mode = os.lstat(rf)[stat.ST_MODE] except OSError: - raise util.Abort(_("file %s not found!") % rf) + raise util.Abort(_("file %s not found!") % rel) if stat.S_ISDIR(mode): name = f + '/' if slist is None: @@ -1131,12 +1162,12 @@ i = bisect.bisect(slist, name) if i >= len(slist) or not slist[i].startswith(name): raise util.Abort(_("no match under directory %s!") - % rf) + % rel) elif not (stat.S_ISREG(mode) or stat.S_ISLNK(mode)): raise util.Abort(_("can't commit %s: " - "unsupported file type!") % rf) + "unsupported file type!") % rel) elif f not in repo.dirstate: - raise util.Abort(_("file %s not tracked!") % rf) + raise util.Abort(_("file %s not tracked!") % rel) else: files = [] try:
--- a/mercurial/commands.py Wed Apr 09 15:27:57 2008 -0700 +++ b/mercurial/commands.py Wed Apr 09 15:28:30 2008 -0700 @@ -5,13 +5,14 @@ # This software may be used and distributed according to the terms # of the GNU General Public License, incorporated herein by reference. -from node import * +from node import hex, nullid, nullrev, short +from repo import RepoError, NoCapability from i18n import _ import os, re, sys, urllib -import hg, util, revlog, bundlerepo, extensions +import hg, util, revlog, bundlerepo, extensions, copies import difflib, patch, time, help, mdiff, tempfile -import errno, version, socket -import archival, changegroup, cmdutil, hgweb.server, sshserver +import version, socket +import archival, changegroup, cmdutil, hgweb.server, sshserver, hbisect # Commands start here, listed alphabetically @@ -26,17 +27,23 @@ If no names are given, add all files in the repository. """ + rejected = None + exacts = {} names = [] - for src, abs, rel, exact in cmdutil.walk(repo, pats, opts): + for src, abs, rel, exact in cmdutil.walk(repo, pats, opts, + badmatch=util.always): if exact: if ui.verbose: ui.status(_('adding %s\n') % rel) names.append(abs) + exacts[abs] = 1 elif abs not in repo.dirstate: ui.status(_('adding %s\n') % rel) names.append(abs) if not opts.get('dry_run'): - repo.add(names) + rejected = repo.add(names) + rejected = [p for p in rejected if p in exacts] + return rejected and 1 or 0 def addremove(ui, repo, *pats, **opts): """add all new files, delete all missing files @@ -46,11 +53,11 @@ New files are ignored if they match any of the patterns in .hgignore. As with add, these changes take effect at the next commit. - Use the -s option to detect renamed files. With a parameter > 0, + Use the -s option to detect renamed files. With a parameter > 0, this compares every removed file with every added file and records - those similar enough as renames. This option takes a percentage + those similar enough as renames. This option takes a percentage between 0 (disabled) and 100 (files must be identical) as its - parameter. Detecting renamed files this way can be expensive. + parameter. Detecting renamed files this way can be expensive. """ try: sim = float(opts.get('similarity') or 0) @@ -72,7 +79,8 @@ detects as binary. With -a, annotate will generate an annotation anyway, probably with undesirable results. """ - getdate = util.cachefunc(lambda x: util.datestr(x[0].date())) + datefunc = ui.quiet and util.shortdate or util.datestr + getdate = util.cachefunc(lambda x: datefunc(x[0].date())) if not pats: raise util.Abort(_('at least one file name or pattern required')) @@ -126,7 +134,7 @@ By default, the revision used is the parent of the working directory; use "-r" to specify a different revision. - To specify the type of archive to create, use "-t". Valid + To specify the type of archive to create, use "-t". Valid types are: "files" (default): a directory full of files @@ -140,7 +148,7 @@ using a format string; see "hg help export" for details. Each member added to an archive file has a directory prefix - prepended. Use "-p" to specify a format string for the prefix. + prepended. Use "-p" to specify a format string for the prefix. The default is the basename of the archive, with suffixes removed. ''' @@ -166,20 +174,21 @@ def backout(ui, repo, node=None, rev=None, **opts): '''reverse effect of earlier changeset - Commit the backed out changes as a new changeset. The new + Commit the backed out changes as a new changeset. The new changeset is a child of the backed out changeset. If you back out a changeset other than the tip, a new head is - created. This head is the parent of the working directory. If - you back out an old changeset, your working directory will appear - old after the backout. You should merge the backout changeset - with another head. + created. This head will be the new tip and you should merge this + backout changeset with another head (current one by default). The --merge option remembers the parent of the working directory before starting the backout, then merges the new head with that - changeset afterwards. This saves you from doing the merge by - hand. The result of this merge is not committed, as for a normal - merge.''' + changeset afterwards. This saves you from doing the merge by + hand. The result of this merge is not committed, as for a normal + merge. + + See 'hg help dates' for a list of formats valid for -d/--date. + ''' if rev and node: raise util.Abort(_("please specify just one revision")) @@ -189,12 +198,14 @@ if not rev: raise util.Abort(_("please specify a revision to backout")) + date = opts.get('date') + if date: + opts['date'] = util.parsedate(date) + cmdutil.bail_if_changed(repo) + node = repo.lookup(rev) + op1, op2 = repo.dirstate.parents() - if op2 != nullid: - raise util.Abort(_('outstanding uncommitted merge')) - node = repo.lookup(rev) - a = repo.changelog.ancestor(op1, node) if a != node: raise util.Abort(_('cannot back out change on a different branch')) @@ -216,11 +227,15 @@ raise util.Abort(_('cannot use --parent on non-merge changeset')) parent = p1 + # the backout should appear on the same branch + branch = repo.dirstate.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) commit_opts = opts.copy() commit_opts['addremove'] = False @@ -233,15 +248,100 @@ ui.status(_('changeset %s backs out changeset %s\n') % (nice(repo.changelog.tip()), nice(node))) if op1 != node: + hg.clean(repo, op1, show_stats=False) if opts['merge']: - ui.status(_('merging with changeset %s\n') % nice(op1)) - hg.merge(repo, hex(op1)) + ui.status(_('merging with changeset %s\n') % nice(repo.changelog.tip())) + hg.merge(repo, hex(repo.changelog.tip())) else: ui.status(_('the backout changeset is a new head - ' 'do not forget to merge\n')) ui.status(_('(use "backout --merge" ' 'if you want to auto-merge)\n')) +def bisect(ui, repo, rev=None, extra=None, + reset=None, good=None, bad=None, skip=None, noupdate=None): + """subdivision search of changesets + + This command helps to find changesets which introduce problems. + To use, mark the earliest changeset you know exhibits the problem + as bad, then mark the latest changeset which is free from the + problem as good. Bisect will update your working directory to a + revision for testing. Once you have performed tests, mark the + working directory as bad or good and bisect will either update to + another candidate changeset or announce that it has found the bad + revision. + """ + # backward compatibility + if rev in "good bad reset init".split(): + ui.warn(_("(use of 'hg bisect <cmd>' is deprecated)\n")) + cmd, rev, extra = rev, extra, None + if cmd == "good": + good = True + elif cmd == "bad": + bad = True + else: + reset = True + elif extra or good + bad + skip + reset > 1: + raise util.Abort("Incompatible arguments") + + if reset: + p = repo.join("bisect.state") + if os.path.exists(p): + os.unlink(p) + return + + # load state + state = {'good': [], 'bad': [], 'skip': []} + if os.path.exists(repo.join("bisect.state")): + for l in repo.opener("bisect.state"): + kind, node = l[:-1].split() + node = repo.lookup(node) + if kind not in state: + raise util.Abort(_("unknown bisect kind %s") % kind) + state[kind].append(node) + + # update state + node = repo.lookup(rev or '.') + if good: + state['good'].append(node) + elif bad: + state['bad'].append(node) + elif skip: + state['skip'].append(node) + + # save state + f = repo.opener("bisect.state", "w", atomictemp=True) + wlock = repo.wlock() + try: + for kind in state: + for node in state[kind]: + f.write("%s %s\n" % (kind, hex(node))) + f.rename() + finally: + del wlock + + if not state['good'] or not state['bad']: + return + + # actually bisect + node, changesets, good = hbisect.bisect(repo.changelog, state) + if changesets == 0: + ui.write(_("The first %s revision is:\n") % (good and "good" or "bad")) + displayer = cmdutil.show_changeset(ui, repo, {}) + displayer.show(changenode=node) + elif node is not None: + # compute the approximate number of remaining tests + tests, size = 0, 2 + while size <= changesets: + tests, size = tests + 1, size * 2 + rev = repo.changelog.rev(node) + ui.write(_("Testing changeset %s:%s " + "(%s changesets remaining, ~%s tests)\n") + % (rev, short(node), changesets, tests)) + if not noupdate: + cmdutil.bail_if_changed(repo) + return hg.clean(repo, node) + def branch(ui, repo, label=None, **opts): """set or show the current branch name @@ -251,6 +351,8 @@ Unless --force is specified, branch will not let you set a branch name that shadows an existing branch. + + Use the command 'hg update' to switch to an existing branch. """ if label: @@ -267,9 +369,11 @@ """list repository named branches List the repository's named branches, indicating which ones are - inactive. If active is specified, only show active branches. + inactive. If active is specified, only show active branches. A branch is considered active if it contains unmerged heads. + + Use the command 'hg update' to switch to an existing branch. """ b = repo.branchtags() heads = dict.fromkeys(repo.heads(), 1) @@ -298,8 +402,10 @@ Generate a compressed changegroup file collecting changesets not found in the other repository. - If no destination repository is specified the destination is assumed - to have all the nodes specified by one or more --base parameters. + If no destination repository is specified the destination is + assumed to have all the nodes specified by one or more --base + parameters. To create a bundle containing all changesets, use + --all (or --base null). The bundle file can then be transferred using conventional means and applied to another repository with the unbundle or pull command. @@ -312,7 +418,10 @@ revs = opts.get('rev') or None if revs: revs = [repo.lookup(rev) for rev in revs] - base = opts.get('base') + if opts.get('all'): + base = ['null'] + else: + base = opts.get('base') if base: if dest: raise util.Abort(_("--base is incompatible with specifiying " @@ -360,7 +469,7 @@ or tip if no revision is checked out. Output may be to a file, in which case the name of the file is - given using a format string. The formatting rules are the same as + given using a format string. The formatting rules are the same as for the export command, with the following additions: %s basename of file being printed @@ -372,7 +481,10 @@ for src, abs, rel, exact in cmdutil.walk(repo, (file1,) + pats, opts, ctx.node()): fp = cmdutil.make_file(repo, opts['output'], ctx.node(), pathname=abs) - fp.write(ctx.filectx(abs).data()) + data = ctx.filectx(abs).data() + if opts.get('decode'): + data = repo.wwritedata(abs, data) + fp.write(data) err = 0 return err @@ -389,9 +501,9 @@ For efficiency, hardlinks are used for cloning whenever the source and destination are on the same filesystem (note this applies only - to the repository data, not to the checked out files). Some + to the repository data, not to the checked out files). Some filesystems, such as AFS, implement hardlinking incorrectly, but - do not report errors. In these cases, use the --pull option to + do not report errors. In these cases, use the --pull option to avoid hardlinking. You can safely clone repositories and checked out files using full @@ -430,23 +542,41 @@ If a list of files is omitted, all changes reported by "hg status" will be committed. + If you are committing the result of a merge, do not provide any + file names or -I/-X filters. + If no commit message is specified, the configured editor is started to enter a message. + + See 'hg help dates' for a list of formats valid for -d/--date. """ def commitfunc(ui, repo, files, message, match, opts): return repo.commit(files, message, opts['user'], opts['date'], match, force_editor=opts.get('force_editor')) - cmdutil.commit(ui, repo, commitfunc, pats, opts) + + node = cmdutil.commit(ui, repo, commitfunc, pats, opts) + if not node: + return + cl = repo.changelog + rev = cl.rev(node) + parents = cl.parentrevs(rev) + if rev - 1 in parents: + # one of the parents was the old tip + return + if (parents == (nullrev, nullrev) or + len(cl.heads(cl.node(parents[0]))) > 1 and + (parents[1] == nullrev or len(cl.heads(cl.node(parents[1]))) > 1)): + ui.status(_('created new head\n')) def copy(ui, repo, *pats, **opts): """mark files as copied for the next commit - Mark dest as having copies of source files. If dest is a - directory, copies are put in that directory. If dest is a file, + Mark dest as having copies of source files. If dest is a + directory, copies are put in that directory. If dest is a file, there can only be one source. By default, this command copies the contents of files as they - stand in the working directory. If invoked with --after, the + stand in the working directory. If invoked with --after, the operation is recorded, but no copying is performed. This command takes effect in the next commit. To undo a copy @@ -458,10 +588,22 @@ finally: del wlock -def debugancestor(ui, index, rev1, rev2): +def debugancestor(ui, repo, *args): """find the ancestor revision of two revisions in a given index""" - r = revlog.revlog(util.opener(os.getcwd(), audit=False), index) - a = r.ancestor(r.lookup(rev1), r.lookup(rev2)) + if len(args) == 3: + index, rev1, rev2 = args + r = revlog.revlog(util.opener(os.getcwd(), audit=False), index) + lookup = r.lookup + elif len(args) == 2: + if not repo: + raise util.Abort(_("There is no Mercurial repository here " + "(.hg not found)")) + rev1, rev2 = args + r = repo.changelog + lookup = repo.lookup + else: + raise util.Abort(_('either two or three arguments required')) + a = r.ancestor(lookup(rev1), lookup(rev2)) ui.write("%d:%s\n" % (r.rev(a), hex(a))) def debugcomplete(ui, cmd='', **opts): @@ -485,6 +627,14 @@ clist.sort() ui.write("%s\n" % "\n".join(clist)) +def debugfsinfo(ui, path = "."): + file('.debugfsinfo', 'w').write('') + ui.write('exec: %s\n' % (util.checkexec(path) and 'yes' or 'no')) + ui.write('symlink: %s\n' % (util.checklink(path) and 'yes' or 'no')) + ui.write('case-sensitive: %s\n' % (util.checkfolding('.debugfsinfo') + and 'yes' or 'no')) + os.unlink('.debugfsinfo') + def debugrebuildstate(ui, repo, rev=""): """rebuild the dirstate as it would look like for the given revision""" if rev == "": @@ -566,23 +716,26 @@ finally: del wlock -def debugstate(ui, repo): +def debugstate(ui, repo, nodates=None): """show the contents of the current dirstate""" k = repo.dirstate._map.items() k.sort() + timestr = "" + showdate = not nodates for file_, ent in k: - if ent[3] == -1: - # Pad or slice to locale representation - locale_len = len(time.strftime("%x %X", time.localtime(0))) - timestr = 'unset' - timestr = timestr[:locale_len] + ' '*(locale_len - len(timestr)) - else: - timestr = time.strftime("%x %X", time.localtime(ent[3])) + if showdate: + if ent[3] == -1: + # Pad or slice to locale representation + locale_len = len(time.strftime("%Y-%m-%d %H:%M:%S ", time.localtime(0))) + timestr = 'unset' + timestr = timestr[:locale_len] + ' '*(locale_len - len(timestr)) + else: + timestr = time.strftime("%Y-%m-%d %H:%M:%S ", time.localtime(ent[3])) if ent[1] & 020000: mode = 'lnk' else: mode = '%3o' % (ent[1] & 0777) - ui.write("%c %s %10d %s %s\n" % (ent[0], mode, ent[2], timestr, file_)) + ui.write("%c %s %10d %s%s\n" % (ent[0], mode, ent[2], timestr, file_)) for f in repo.dirstate.copies(): ui.write(_("copy: %s -> %s\n") % (repo.dirstate.copied(f), f)) @@ -705,47 +858,13 @@ ui.write(_(" (Current patch tool may be incompatible with patch," " or misconfigured. Please check your .hgrc file)\n")) else: - ui.write(_(" Internal patcher failure, please report this error" + ui.write(_(" Internal patcher failure, please report this error" " to http://www.selenic.com/mercurial/bts\n")) problems += patchproblems os.unlink(fa) os.unlink(fd) - # merge helper - ui.status(_("Checking merge helper...\n")) - cmd = (os.environ.get("HGMERGE") or ui.config("ui", "merge") - or "hgmerge") - cmdpath = util.find_exe(cmd) or util.find_exe(cmd.split()[0]) - if not cmdpath: - if cmd == 'hgmerge': - ui.write(_(" No merge helper set and can't find default" - " hgmerge script in PATH\n")) - ui.write(_(" (specify a merge helper in your .hgrc file)\n")) - else: - ui.write(_(" Can't find merge helper '%s' in PATH\n") % cmd) - ui.write(_(" (specify a merge helper in your .hgrc file)\n")) - problems += 1 - else: - # actually attempt a patch here - fa = writetemp("1\n2\n3\n4\n") - fl = writetemp("1\n2\n3\ninsert\n4\n") - fr = writetemp("begin\n1\n2\n3\n4\n") - r = util.system('%s "%s" "%s" "%s"' % (cmd, fl, fa, fr)) - if r: - ui.write(_(" Got unexpected merge error %d!\n") % r) - problems += 1 - m = file(fl).read() - if m != "begin\n1\n2\n3\ninsert\n4\n": - ui.write(_(" Got unexpected merge results!\n")) - ui.write(_(" (your merge helper may have the" - " wrong argument order)\n")) - ui.write(_(" Result: %r\n") % m) - problems += 1 - os.unlink(fa) - os.unlink(fl) - os.unlink(fr) - # editor ui.status(_("Checking commit editor...\n")) editor = ui.geteditor() @@ -785,7 +904,8 @@ ctx = repo.changectx(opts.get('rev', 'tip')) for src, abs, rel, exact in cmdutil.walk(repo, (file1,) + pats, opts, ctx.node()): - m = ctx.filectx(abs).renamed() + fctx = ctx.filectx(abs) + m = fctx.filelog().renamed(fctx.filenode()) if m: ui.write(_("%s renamed from %s:%s\n") % (rel, m[0], hex(m[1]))) else: @@ -843,7 +963,7 @@ as it will compare the merge changeset against its first parent only. Output may be to a file, in which case the name of the file is - given using a format string. The formatting rules are as follows: + given using a format string. The formatting rules are as follows: %% literal "%" character %H changeset hash (40 bytes of hexadecimal) @@ -877,13 +997,13 @@ Search revisions of files for a regular expression. - This command behaves differently than Unix grep. It only accepts - Python/Perl regexps. It searches repository history, not the - working directory. It always prints the revision number in which + This command behaves differently than Unix grep. It only accepts + Python/Perl regexps. It searches repository history, not the + working directory. It always prints the revision number in which a match appears. By default, grep only prints output for the first revision of a - file in which it finds a match. To get it to print every revision + file in which it finds a match. To get it to print every revision that contains a change in match status ("-" for a match that becomes a non-match, or "+" for a non-match that becomes a match), use the --all flag. @@ -894,7 +1014,7 @@ try: regexp = re.compile(pattern, reflags) except Exception, inst: - ui.warn(_("grep: invalid match pattern: %s!\n") % inst) + ui.warn(_("grep: invalid match pattern: %s\n") % inst) return None sep, eol = ':', '\n' if opts['print0']: @@ -927,6 +1047,9 @@ self.colstart = colstart self.colend = colend + def __hash__(self): + return hash((self.linenum, self.line)) + def __eq__(self, other): return self.line == other.line @@ -956,6 +1079,7 @@ prev = {} def display(fn, rev, states, prevstates): + datefunc = ui.quiet and util.shortdate or util.datestr found = False filerevmatches = {} r = prev.get(fn, -1) @@ -971,6 +1095,8 @@ cols.append(change) if opts['user']: cols.append(ui.shortuser(get(r)[1])) + if opts.get('date'): + cols.append(datefunc(get(r)[2])) if opts['files_with_matches']: c = (fn, r) if c in filerevmatches: @@ -992,19 +1118,19 @@ if st == 'window': matches.clear() elif st == 'add': - mf = repo.changectx(rev).manifest() + ctx = repo.changectx(rev) matches[rev] = {} for fn in fns: if fn in skip: continue try: - grepbody(fn, rev, getfile(fn).read(mf[fn])) + grepbody(fn, rev, getfile(fn).read(ctx.filenode(fn))) fstate.setdefault(fn, []) if follow: - copied = getfile(fn).renamed(mf[fn]) + copied = getfile(fn).renamed(ctx.filenode(fn)) if copied: copies.setdefault(rev, {})[fn] = copied[0] - except KeyError: + except revlog.LookupError: pass elif st == 'iter': states = matches[rev].items() @@ -1050,7 +1176,7 @@ are the usual targets for update and merge operations. Branch heads are changesets that have a given branch tag, but have - no child changesets with that tag. They are usually where + no child changesets with that tag. They are usually where development on the given branch takes place. """ if opts['rev']: @@ -1118,7 +1244,11 @@ ui.write('\n') aliases, i = cmdutil.findcmd(ui, name, table) # synopsis - ui.write("%s\n\n" % i[2]) + ui.write("%s\n" % i[2]) + + # aliases + if not ui.quiet and len(aliases) > 1: + ui.write(_("\naliases: %s\n") % ', '.join(aliases[1:])) # description doc = i[0].__doc__ @@ -1126,13 +1256,9 @@ doc = _("(No help text available)") if ui.quiet: doc = doc.splitlines(0)[0] - ui.write("%s\n" % doc.rstrip()) + ui.write("\n%s\n" % doc.rstrip()) if not ui.quiet: - # aliases - if len(aliases) > 1: - ui.write(_("\naliases: %s\n") % ', '.join(aliases[1:])) - # options if i[1]: option_lists.append((_("options:\n"), i[1])) @@ -1343,15 +1469,15 @@ If there are outstanding changes in the working directory, import will abort unless given the -f flag. - You can import a patch straight from a mail message. Even patches + You can import a patch straight from a mail message. Even patches as attachments work (body part must be type text/plain or - text/x-patch to be used). From and Subject headers of email - message are used as default committer and commit message. All + text/x-patch to be used). From and Subject headers of email + message are used as default committer and commit message. All text/plain body parts before first diff are added to commit message. If the imported patch was generated by hg export, user and description - from patch override values from message headers and body. Values + from patch override values from message headers and body. Values given on command line with -m and -u override these. If --exact is specified, import will set the working directory @@ -1361,9 +1487,14 @@ problems or other deficiencies in the text patch format. To read a patch from standard input, use patch name "-". + See 'hg help dates' for a list of formats valid for -d/--date. """ patches = (patch1,) + patches + date = opts.get('date') + if date: + opts['date'] = util.parsedate(date) + if opts.get('exact') or not opts['force']: cmdutil.bail_if_changed(repo) @@ -1419,7 +1550,7 @@ p2 = repo.lookup(p2) if p1 == wp[0].node(): repo.dirstate.setparents(p1, p2) - except hg.RepoError: + except RepoError: pass if opts.get('exact') or opts.get('import_branch'): repo.dirstate.setbranch(branch or 'default') @@ -1430,12 +1561,17 @@ files=files) finally: files = patch.updatedir(ui, repo, files) - n = repo.commit(files, message, user, date) - if opts.get('exact'): - if hex(n) != nodeid: - repo.rollback() - raise util.Abort(_('patch is damaged' - ' or loses information')) + if not opts.get('no_commit'): + n = repo.commit(files, message, opts.get('user') or user, + opts.get('date') or date) + if opts.get('exact'): + if hex(n) != nodeid: + repo.rollback() + raise util.Abort(_('patch is damaged' + ' or loses information')) + # Force a dirstate write so that the next transaction + # backups an up-do-date file. + repo.dirstate.write() finally: os.unlink(tmpname) finally: @@ -1453,6 +1589,7 @@ See pull for valid source format details. """ + limit = cmdutil.loglimit(opts) source, revs, checkout = hg.parseurl(ui.expandpath(source), opts['rev']) cmdutil.setremoteconfig(ui, opts) @@ -1491,10 +1628,14 @@ if opts['newest_first']: o.reverse() displayer = cmdutil.show_changeset(ui, other, opts) + count = 0 for n in o: + if count >= limit: + break parents = [p for p in other.changelog.parents(n) if p != nullid] if opts['no_merges'] and len(parents) == 2: continue + count += 1 displayer.show(changenode=n) finally: if hasattr(other, 'close'): @@ -1505,7 +1646,7 @@ def init(ui, dest=".", **opts): """create a new repository in the given directory - Initialize a new repository in the given directory. If the given + Initialize a new repository in the given directory. If the given directory does not exist, it is created. If no directory is given, the current directory is used. @@ -1523,7 +1664,7 @@ Print all files under Mercurial control whose names match the given patterns. - This command searches the entire repository by default. To search + This command searches the entire repository by default. To search just the current directory and its subdirectories, use "--include .". @@ -1565,7 +1706,7 @@ project. File history is shown without following rename or copy history of - files. Use -f/--follow with a file name to follow history across + files. Use -f/--follow with a file name to follow history across renames and copies. --follow without a file name will only show ancestors or descendants of the starting revision. --follow-first only follows the first parent of merge revisions. @@ -1574,6 +1715,8 @@ --follow is set, in which case the working directory parent is used as the starting revision. + See 'hg help dates' for a list of formats valid for -d/--date. + By default this command outputs: changeset id and hash, tags, non-trivial parents, user, date and time, and a summary for each commit. When the -v/--verbose switch is used, the list of changed @@ -1589,14 +1732,7 @@ get = util.cachefunc(lambda r: repo.changectx(r).changeset()) changeiter, matchfn = cmdutil.walkchangerevs(ui, repo, pats, get, opts) - if opts['limit']: - try: - limit = int(opts['limit']) - except ValueError: - raise util.Abort(_('limit must be a positive integer')) - if limit <= 0: raise util.Abort(_('limit must be positive')) - else: - limit = sys.maxint + limit = cmdutil.loglimit(opts) count = 0 if opts['copies'] and opts['rev']: @@ -1605,8 +1741,7 @@ endrev = repo.changelog.count() rcache = {} ncache = {} - dcache = [] - def getrenamed(fn, rev, man): + def getrenamed(fn, rev): '''looks up all renames for a file (up to endrev) the first time the file is given. It indexes on the changerev and only parses the manifest if linkrev != changerev. @@ -1626,19 +1761,22 @@ break if rev in rcache[fn]: return rcache[fn][rev] - mr = repo.manifest.rev(man) - if repo.manifest.parentrevs(mr) != (mr - 1, nullrev): - return ncache[fn].get(repo.manifest.find(man, fn)[0]) - if not dcache or dcache[0] != man: - dcache[:] = [man, repo.manifest.readdelta(man)] - if fn in dcache[1]: - return ncache[fn].get(dcache[1][fn]) + + # If linkrev != rev (i.e. rev not found in rcache) fallback to + # filectx logic. + + try: + return repo.changectx(rev).filectx(fn).renamed() + except revlog.LookupError: + pass return None df = False if opts["date"]: df = util.matchdate(opts["date"]) + only_branches = opts['only_branch'] + displayer = cmdutil.show_changeset(ui, repo, opts, True, matchfn) for st, rev, fns in changeiter: if st == 'add': @@ -1650,6 +1788,11 @@ if opts['only_merges'] and len(parents) != 2: continue + if only_branches: + revbranch = get(rev)[5]['branch'] + if revbranch not in only_branches: + continue + if df: changes = get(rev) if not df(changes[2][0]): @@ -1669,9 +1812,8 @@ copies = [] if opts.get('copies') and rev: - mf = get(rev)[0] for fn in get(rev)[3]: - rename = getrenamed(fn, rev, mf) + rename = getrenamed(fn, rev) if rename: copies.append((fn, rename[0])) displayer.show(rev, changenode, copies=copies) @@ -1690,7 +1832,7 @@ The manifest is the list of files being version controlled. If no revision is given then the first parent of the working directory is used. - With -v flag, print file permissions, symlink and executable bits. With + With -v flag, print file permissions, symlink and executable bits. With --debug flag, print file revision hashes. """ @@ -1723,7 +1865,7 @@ If no revision is specified, the working directory's parent is a head revision, and the repository contains exactly one other head, - the other head is merged with by default. Otherwise, an explicit + the other head is merged with by default. Otherwise, an explicit revision to merge with must be provided. """ @@ -1760,6 +1902,7 @@ See pull for valid destination format details. """ + limit = cmdutil.loglimit(opts) dest, revs, checkout = hg.parseurl( ui.expandpath(dest or 'default-push', dest or 'default'), opts['rev']) cmdutil.setremoteconfig(ui, opts) @@ -1776,10 +1919,14 @@ if opts['newest_first']: o.reverse() displayer = cmdutil.show_changeset(ui, repo, opts) + count = 0 for n in o: + if count >= limit: + break parents = [p for p in repo.changelog.parents(n) if p != nullid] if opts['no_merges'] and len(parents) == 2: continue + count += 1 displayer.show(changenode=n) def parents(ui, repo, file_=None, **opts): @@ -1829,18 +1976,18 @@ definition of available names. Path names are defined in the [paths] section of /etc/mercurial/hgrc - and $HOME/.hgrc. If run inside a repository, .hg/hgrc is used, too. + and $HOME/.hgrc. If run inside a repository, .hg/hgrc is used, too. """ if search: for name, path in ui.configitems("paths"): if name == search: - ui.write("%s\n" % path) + ui.write("%s\n" % util.hidepassword(path)) return ui.warn(_("not found!\n")) return 1 else: for name, path in ui.configitems("paths"): - ui.write("%s = %s\n" % (name, path)) + ui.write("%s = %s\n" % (name, util.hidepassword(path))) def postincoming(ui, repo, modheads, optupdate, checkout): if modheads == 0: @@ -1904,7 +2051,7 @@ if revs: try: revs = [other.lookup(rev) for rev in revs] - except repo.NoCapability: + except NoCapability: error = _("Other repository doesn't support revision lookup, " "so a rev cannot be specified.") raise util.Abort(error) @@ -1998,61 +2145,84 @@ Schedule the indicated files for removal from the repository. - This only removes files from the current branch, not from the - entire project history. If the files still exist in the working - directory, they will be deleted from it. If invoked with --after, - files are marked as removed, but not actually unlinked unless --force - is also given. Without exact file names, --after will only mark - files as removed if they are no longer in the working directory. + This only removes files from the current branch, not from the entire + project history. -A can be used to remove only files that have already + been deleted, -f can be used to force deletion, and -Af can be used + to remove files from the next revision without deleting them. + + The following table details the behavior of remove for different file + states (columns) and option combinations (rows). The file states are + Added, Clean, Modified and Missing (as reported by hg status). The + actions are Warn, Remove (from branch) and Delete (from disk). + + A C M ! + none W RD W R + -f R RD RD R + -A W W W R + -Af R R R R This command schedules the files to be removed at the next commit. To undo a remove before that, see hg revert. - - Modified files and added files are not removed by default. To - remove them, use the -f/--force option. """ - if not opts['after'] and not pats: + + after, force = opts.get('after'), opts.get('force') + if not pats and not after: raise util.Abort(_('no files specified')) + files, matchfn, anypats = cmdutil.matchpats(repo, pats, opts) - exact = dict.fromkeys(files) mardu = map(dict.fromkeys, repo.status(files=files, match=matchfn))[:5] modified, added, removed, deleted, unknown = mardu + remove, forget = [], [] for src, abs, rel, exact in cmdutil.walk(repo, pats, opts): + reason = None - if abs in modified and not opts['force']: - reason = _('is modified (use -f to force removal)') + if abs in removed or abs in unknown: + continue + + # last column + elif abs in deleted: + remove.append(abs) + + # rest of the third row + elif after and not force: + reason = _('still exists (use -f to force removal)') + + # rest of the first column elif abs in added: - if opts['force']: + if not force: + reason = _('has been marked for add (use -f to force removal)') + else: forget.append(abs) - continue - reason = _('has been marked for add (use -f to force removal)') - exact = 1 # force the message - elif abs not in repo.dirstate: - reason = _('is not managed') - elif opts['after'] and not exact and abs not in deleted: - continue - elif abs in removed: - continue + + # rest of the third column + elif abs in modified: + if not force: + reason = _('is modified (use -f to force removal)') + else: + remove.append(abs) + + # rest of the second column + elif not reason: + remove.append(abs) + if reason: - if exact: - ui.warn(_('not removing %s: file %s\n') % (rel, reason)) - else: - if ui.verbose or not exact: - ui.status(_('removing %s\n') % rel) - remove.append(abs) + ui.warn(_('not removing %s: file %s\n') % (rel, reason)) + elif ui.verbose or not exact: + ui.status(_('removing %s\n') % rel) + repo.forget(forget) - repo.remove(remove, unlink=opts['force'] or not opts['after']) + repo.remove(remove, unlink=not after) def rename(ui, repo, *pats, **opts): """rename files; equivalent of copy + remove - Mark dest as copies of sources; mark sources for deletion. If - dest is a directory, copies are put in that directory. If dest is + Mark dest as copies of sources; mark sources for deletion. If + dest is a directory, copies are put in that directory. If dest is a file, there can only be one source. By default, this command copies the contents of files as they - stand in the working directory. If invoked with --after, the + stand in the working directory. If invoked with --after, the operation is recorded, but no copying is performed. This command takes effect in the next commit. To undo a rename @@ -2079,19 +2249,19 @@ Using the -r option, revert the given files or directories to their contents as of a specific revision. This can be helpful to "roll - back" some or all of an earlier change. - - Revert modifies the working directory. It does not commit any - changes, or change the parent of the working directory. If you + back" some or all of an earlier change. + See 'hg help dates' for a list of formats valid for -d/--date. + + Revert modifies the working directory. It does not commit any + changes, or change the parent of the working directory. If you revert to a revision other than the parent of the working directory, the reverted files will thus appear modified afterwards. - If a file has been deleted, it is restored. If the executable + If a file has been deleted, it is restored. If the executable mode of a file was changed, it is reset. If names are given, all files matching the names are reverted. - If no arguments are given, no files are reverted. Modified files are saved with a .orig suffix before reverting. @@ -2124,16 +2294,16 @@ # but not other. names = {} - target_only = {} wlock = repo.wlock() try: # walk dirstate. + files = [] for src, abs, rel, exact in cmdutil.walk(repo, pats, opts, badmatch=mf.has_key): names[abs] = (rel, exact) - if src == 'b': - target_only[abs] = True + if src != 'b': + files.append(abs) # walk target manifest. @@ -2151,10 +2321,9 @@ if abs in names or src == 'b': continue names[abs] = (rel, exact) - target_only[abs] = True - - changes = repo.status(match=names.has_key)[:5] - modified, added, removed, deleted, unknown = map(dict.fromkeys, changes) + + changes = repo.status(files=files, match=names.has_key)[:4] + modified, added, removed, deleted = map(dict.fromkeys, changes) # if f is a rename, also revert the source cwd = repo.getcwd() @@ -2164,12 +2333,15 @@ removed[src] = None names[src] = (repo.pathto(src, cwd), True) + def removeforget(abs): + if repo.dirstate[abs] == 'a': + return _('forgetting %s\n') + return _('removing %s\n') + revert = ([], _('reverting %s\n')) add = ([], _('adding %s\n')) - remove = ([], _('removing %s\n')) - forget = ([], _('forgetting %s\n')) + remove = ([], removeforget) undelete = ([], _('undeleting %s\n')) - update = {} disptable = ( # dispatch table: @@ -2179,11 +2351,9 @@ # make backup if in target manifest # make backup if not in target manifest (modified, revert, remove, True, True), - (added, revert, forget, True, False), + (added, revert, remove, True, False), (removed, undelete, None, False, False), (deleted, revert, remove, False, False), - (unknown, add, None, True, False), - (target_only, add, None, False, False), ) entries = names.items() @@ -2194,7 +2364,6 @@ target = repo.wjoin(abs) def handle(xlist, dobackup): xlist[0].append(abs) - update[abs] = 1 if dobackup and not opts['no_backup'] and util.lexists(target): bakname = "%s.orig" % rel ui.note(_('saving current version of %s as %s\n') % @@ -2202,7 +2371,10 @@ if not opts.get('dry_run'): util.copyfile(target, bakname) if ui.verbose or not exact: - ui.status(xlist[1] % rel) + msg = xlist[1] + if not isinstance(msg, basestring): + msg = msg(abs) + ui.status(msg % rel) for table, hitlist, misslist, backuphit, backupmiss in disptable: if abs not in table: continue # file has changed in dirstate @@ -2210,10 +2382,14 @@ handle(hitlist, backuphit) elif misslist is not None: handle(misslist, backupmiss) - else: - if exact: ui.warn(_('file not managed: %s\n') % rel) break else: + if abs not in repo.dirstate: + if mfentry: + handle(add, True) + elif exact: + ui.warn(_('file not managed: %s\n') % rel) + continue # file has not changed in dirstate if node == parent: if exact: ui.warn(_('no changes needed to %s\n') % rel) @@ -2226,22 +2402,54 @@ if mfentry: # if version of file is same in parent and target # manifests, do nothing - if pmf[abs] != mfentry: + if (pmf[abs] != mfentry or + pmf.flags(abs) != mf.flags(abs)): handle(revert, False) else: handle(remove, False) if not opts.get('dry_run'): - for f in forget[0]: - repo.dirstate.forget(f) - r = hg.revert(repo, node, update.has_key) + def checkout(f): + fc = ctx[f] + repo.wwrite(f, fc.data(), fc.fileflags()) + + audit_path = util.path_auditor(repo.root) + for f in remove[0]: + if repo.dirstate[f] == 'a': + repo.dirstate.forget(f) + continue + audit_path(f) + try: + util.unlink(repo.wjoin(f)) + except OSError: + pass + repo.dirstate.remove(f) + + normal = None + if node == parent: + # We're reverting to our parent. If possible, we'd like status + # to report the file as clean. We have to use normallookup for + # merges to avoid losing information about merged/dirty files. + if p2 != nullid: + normal = repo.dirstate.normallookup + else: + normal = repo.dirstate.normal + for f in revert[0]: + checkout(f) + if normal: + normal(f) + for f in add[0]: + checkout(f) repo.dirstate.add(f) + + normal = repo.dirstate.normallookup + if node == parent and p2 == nullid: + normal = repo.dirstate.normal for f in undelete[0]: - repo.dirstate.normal(f) - for f in remove[0]: - repo.dirstate.remove(f) - return r + checkout(f) + normal(f) + finally: del wlock @@ -2286,18 +2494,18 @@ Start a local HTTP repository browser and pull server. By default, the server logs accesses to stdout and errors to - stderr. Use the "-A" and "-E" options to log to files. + stderr. Use the "-A" and "-E" options to log to files. """ if opts["stdio"]: if repo is None: - raise hg.RepoError(_("There is no Mercurial repository here" - " (.hg not found)")) + raise RepoError(_("There is no Mercurial repository here" + " (.hg not found)")) s = sshserver.sshserver(ui, repo) s.serve_forever() parentui = ui.parentui or ui - optlist = ("name templates style address port ipv6" + optlist = ("name templates style address port prefix ipv6" " accesslog errorlog webdir_conf certificate") for o in optlist.split(): if opts[o]: @@ -2306,24 +2514,36 @@ repo.ui.setconfig("web", o, str(opts[o])) if repo is None and not ui.config("web", "webdir_conf"): - raise hg.RepoError(_("There is no Mercurial repository here" - " (.hg not found)")) + raise RepoError(_("There is no Mercurial repository here" + " (.hg not found)")) class service: def init(self): util.set_signal_handler() - try: - self.httpd = hgweb.server.create_server(parentui, repo) - except socket.error, inst: - raise util.Abort(_('cannot start server: ') + inst.args[1]) + self.httpd = hgweb.server.create_server(parentui, repo) if not ui.verbose: return - if self.httpd.port != 80: - ui.status(_('listening at http://%s:%d/\n') % - (self.httpd.addr, self.httpd.port)) + if self.httpd.prefix: + prefix = self.httpd.prefix.strip('/') + '/' else: - ui.status(_('listening at http://%s/\n') % self.httpd.addr) + prefix = '' + + port = ':%d' % self.httpd.port + if port == ':80': + port = '' + + bindaddr = self.httpd.addr + if bindaddr == '0.0.0.0': + bindaddr = '*' + elif ':' in bindaddr: # IPv6 + bindaddr = '[%s]' % bindaddr + + fqaddr = self.httpd.fqaddr + if ':' in fqaddr: + fqaddr = '[%s]' % fqaddr + ui.status(_('listening at http://%s%s/%s (bound to %s:%d)\n') % + (fqaddr, port, prefix, bindaddr, self.httpd.port)) def run(self): self.httpd.serve_forever() @@ -2335,9 +2555,14 @@ def status(ui, repo, *pats, **opts): """show changed files in the working directory - Show status of files in the repository. If names are given, only - files that match are shown. Files that are clean or ignored, are - not listed unless -c (clean), -i (ignored) or -A is given. + Show status of files in the repository. If names are given, only + files that match are shown. Files that are clean or ignored or + source of a copy/move operation, are not listed unless -c (clean), + -i (ignored), -C (copies) or -A is given. Unless options described + with "show only ..." are given, the options -mardu are used. + + Option -q/--quiet hides untracked (unknown and ignored) files + unless explicitly requested with -u/--unknown or -i/-ignored. NOTE: status may appear to disagree with diff if permissions have changed or a merge has occurred. The standard diff format does not @@ -2354,7 +2579,7 @@ C = clean ! = deleted, but still tracked ? = not tracked - I = ignored (not shown by default) + I = ignored = the previous added file was copied from here """ @@ -2365,9 +2590,17 @@ cwd = (pats and repo.getcwd()) or '' modified, added, removed, deleted, unknown, ignored, clean = [ n for n in repo.status(node1=node1, node2=node2, files=files, - match=matchfn, - list_ignored=all or opts['ignored'], - list_clean=all or opts['clean'])] + match=matchfn, + list_ignored=opts['ignored'] + or all and not ui.quiet, + list_clean=opts['clean'] or all, + list_unknown=opts['unknown'] + or not (ui.quiet or + opts['modified'] or + opts['added'] or + opts['removed'] or + opts['deleted'] or + opts['ignored']))] changetypes = (('modified', 'M', modified), ('added', 'A', added), @@ -2378,11 +2611,28 @@ explicit_changetypes = changetypes + (('clean', 'C', clean),) + copy = {} + showcopy = {} + if ((all or opts.get('copies')) and not opts.get('no_status')): + if opts.get('rev') == []: + # fast path, more correct with merge parents + showcopy = copy = repo.dirstate.copies().copy() + else: + ctxn = repo.changectx(nullid) + ctx1 = repo.changectx(node1) + ctx2 = repo.changectx(node2) + if node2 is None: + ctx2 = repo.workingctx() + copy, diverge = copies.copies(repo, ctx1, ctx2, ctxn) + for k, v in copy.items(): + copy[v] = k + end = opts['print0'] and '\0' or '\n' for opt, char, changes in ([ct for ct in explicit_changetypes if all or opts[ct[0]]] or changetypes): + if opts['no_status']: format = "%%s%s" % end else: @@ -2390,18 +2640,16 @@ for f in changes: ui.write(format % repo.pathto(f, cwd)) - if ((all or opts.get('copies')) and not opts.get('no_status')): - copied = repo.dirstate.copied(f) - if copied: - ui.write(' %s%s' % (repo.pathto(copied, cwd), end)) - -def tag(ui, repo, name, rev_=None, **opts): - """add a tag for the current or given revision + if f in copy and (f in added or f in showcopy): + ui.write(' %s%s' % (repo.pathto(copy[f], cwd), end)) + +def tag(ui, repo, name1, *names, **opts): + """add one or more tags for the current or given revision Name a particular revision using <name>. Tags are used to name particular revisions of the repository and are - very useful to compare different revision, to go back to significant + very useful to compare different revisions, to go back to significant earlier versions or to mark branch points as releases, etc. If no revision is given, the parent of the working directory is used, @@ -2410,46 +2658,54 @@ To facilitate version control, distribution, and merging of tags, they are stored as a file named ".hgtags" which is managed similarly to other project files and can be hand-edited if - necessary. The file '.hg/localtags' is used for local tags (not + necessary. The file '.hg/localtags' is used for local tags (not shared among repositories). + + See 'hg help dates' for a list of formats valid for -d/--date. """ - if name in ['tip', '.', 'null']: - raise util.Abort(_("the name '%s' is reserved") % name) - if rev_ is not None: - ui.warn(_("use of 'hg tag NAME [REV]' is deprecated, " - "please use 'hg tag [-r REV] NAME' instead\n")) - if opts['rev']: - raise util.Abort(_("use only one form to specify the revision")) + + rev_ = None + names = (name1,) + names + if len(names) != len(dict.fromkeys(names)): + raise util.Abort(_('tag names must be unique')) + for n in names: + if n in ['tip', '.', 'null']: + raise util.Abort(_('the name \'%s\' is reserved') % n) if opts['rev'] and opts['remove']: raise util.Abort(_("--rev and --remove are incompatible")) if opts['rev']: rev_ = opts['rev'] message = opts['message'] if opts['remove']: - tagtype = repo.tagtype(name) - - if not tagtype: - raise util.Abort(_('tag %s does not exist') % name) - if opts['local'] and tagtype == 'global': - raise util.Abort(_('%s tag is global') % name) - if not opts['local'] and tagtype == 'local': - raise util.Abort(_('%s tag is local') % name) - + expectedtype = opts['local'] and 'local' or 'global' + for n in names: + if not repo.tagtype(n): + raise util.Abort(_('tag \'%s\' does not exist') % n) + if repo.tagtype(n) != expectedtype: + raise util.Abort(_('tag \'%s\' is not a %s tag') % + (n, expectedtype)) rev_ = nullid if not message: - message = _('Removed tag %s') % name - elif name in repo.tags() and not opts['force']: - raise util.Abort(_('a tag named %s already exists (use -f to force)') - % name) + message = _('Removed tag %s') % ', '.join(names) + elif not opts['force']: + for n in names: + if n in repo.tags(): + raise util.Abort(_('tag \'%s\' already exists ' + '(use -f to force)') % n) if not rev_ and repo.dirstate.parents()[1] != nullid: raise util.Abort(_('uncommitted merge - please provide a ' 'specific revision')) r = repo.changectx(rev_).node() if not message: - message = _('Added tag %s for changeset %s') % (name, short(r)) - - repo.tag(name, r, message, opts['local'], opts['user'], opts['date']) + message = (_('Added tag %s for changeset %s') % + (', '.join(names), short(r))) + + date = opts.get('date') + if date: + date = util.parsedate(date) + + repo.tag(names, r, message, opts['local'], opts['user'], date) def tags(ui, repo): """list repository tags @@ -2487,7 +2743,14 @@ def tip(ui, repo, **opts): """show the tip revision - Show the tip revision. + The tip revision (usually just called the tip) is the most + recently added changeset in the repository, the most recently + changed head. + + If you have just made a commit, that commit will be the tip. If + you have just pulled changes from another repository, the tip of + that repository becomes the current tip. The "tip" tag is special + and cannot be renamed or assigned to a different changeset. """ cmdutil.show_changeset(ui, repo, opts).show(nullrev+repo.changelog.count()) @@ -2498,13 +2761,19 @@ bundle command. """ fnames = (fname1,) + fnames - for fname in fnames: - if os.path.exists(fname): - f = open(fname, "rb") - else: - f = urllib.urlopen(fname) - gen = changegroup.readbundle(f, fname) - modheads = repo.addchangegroup(gen, 'unbundle', 'bundle:' + fname) + + lock = None + try: + lock = repo.lock() + for fname in fnames: + if os.path.exists(fname): + f = open(fname, "rb") + else: + f = urllib.urlopen(fname) + gen = changegroup.readbundle(f, fname) + modheads = repo.addchangegroup(gen, 'unbundle', 'bundle:' + fname) + finally: + del lock return postincoming(ui, repo, modheads, opts['update'], None) @@ -2514,15 +2783,16 @@ Update the working directory to the specified revision, or the tip of the current branch if none is specified. - If there are no outstanding changes in the working directory and - there is a linear relationship between the current version and the - requested version, the result is the requested version. - - To merge the working directory with another revision, use the - merge command. - - By default, update will refuse to run if doing so would require - discarding local changes. + If the requested revision is a descendant of the working + directory, any outstanding changes in the working directory will + be merged into the result. If it is not directly descended but is + on the same named branch, update aborts with a suggestion to use + merge or update -C instead. + + If the requested revision is on a different named branch and the + working directory is clean, update quietly switches branches. + + See 'hg help dates' for a list of formats valid for --date. """ if rev and node: raise util.Abort(_("please specify just one revision")) @@ -2557,7 +2827,7 @@ ui.write(_("Mercurial Distributed SCM (version %s)\n") % version.get_version()) ui.status(_( - "\nCopyright (C) 2005-2007 Matt Mackall <mpm@selenic.com> and others\n" + "\nCopyright (C) 2005-2008 Matt Mackall <mpm@selenic.com> and others\n" "This is free software; see the source for copying conditions. " "There is NO\nwarranty; " "not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n" @@ -2609,6 +2879,17 @@ ('u', 'user', '', _('record user as committer')), ] +templateopts = [ + ('', 'style', '', _('display using template map file')), + ('', 'template', '', _('display with template')), +] + +logopts = [ + ('p', 'patch', None, _('show patch')), + ('l', 'limit', '', _('limit number of changes displayed')), + ('M', 'no-merges', None, _('do not show merges')), +] + templateopts + table = { "^add": (add, walkopts + dryrunopts, _('hg add [OPTION]... [FILE]...')), "addremove": @@ -2617,13 +2898,13 @@ _('guess renamed files by similarity (0<=s<=100)')), ] + walkopts + dryrunopts, _('hg addremove [OPTION]... [FILE]...')), - "^annotate": + "^annotate|blame": (annotate, [('r', 'rev', '', _('annotate the specified revision')), ('f', 'follow', None, _('follow file copies and renames')), ('a', 'text', None, _('treat all files as text')), - ('u', 'user', None, _('list the author')), - ('d', 'date', None, _('list the date')), + ('u', 'user', None, _('list the author (long with -v)')), + ('d', 'date', None, _('list the date (short with -q)')), ('n', 'number', None, _('list the revision number (default)')), ('c', 'changeset', None, _('list the changeset')), ('l', 'line-number', None, @@ -2646,11 +2927,19 @@ ('r', 'rev', '', _('revision to backout')), ] + walkopts + commitopts + commitopts2, _('hg backout [OPTION]... [-r] REV')), + "bisect": + (bisect, + [('r', 'reset', False, _('reset bisect state')), + ('g', 'good', False, _('mark changeset good')), + ('b', 'bad', False, _('mark changeset bad')), + ('s', 'skip', False, _('skip testing changeset')), + ('U', 'noupdate', False, _('do not update to target'))], + _("hg bisect [-gbsr] [REV]")), "branch": (branch, [('f', 'force', None, _('set branch name even if it shadows an existing branch'))], - _('hg branch [NAME]')), + _('hg branch [-f] [NAME]')), "branches": (branches, [('a', 'active', False, @@ -2661,15 +2950,18 @@ [('f', 'force', None, _('run even when remote repository is unrelated')), ('r', 'rev', [], - _('a changeset you would like to bundle')), + _('a changeset up to which you would like to bundle')), ('', 'base', [], _('a base changeset to specify instead of a destination')), + ('a', 'all', None, + _('bundle all changesets in the repository')), ] + remoteopts, - _('hg bundle [-f] [-r REV]... [--base REV]... FILE [DEST]')), + _('hg bundle [-f] [-a] [-r REV]... [--base REV]... FILE [DEST]')), "cat": (cat, [('o', 'output', '', _('print output to file with formatted name')), ('r', 'rev', '', _('print the given revision')), + ('', 'decode', None, _('apply any matching decode filter')), ] + walkopts, _('hg cat [OPTION]... FILE...')), "^clone": @@ -2695,31 +2987,45 @@ _('forcibly copy over an existing managed file')), ] + walkopts + dryrunopts, _('hg copy [OPTION]... [SOURCE]... DEST')), - "debugancestor": (debugancestor, [], _('debugancestor INDEX REV1 REV2')), + "debugancestor": (debugancestor, [], + _('hg debugancestor [INDEX] REV1 REV2')), + "debugcheckstate": (debugcheckstate, [], _('hg debugcheckstate')), "debugcomplete": (debugcomplete, [('o', 'options', None, _('show the command options'))], - _('debugcomplete [-o] CMD')), - "debuginstall": (debuginstall, [], _('debuginstall')), + _('hg debugcomplete [-o] CMD')), + "debugdate": + (debugdate, + [('e', 'extended', None, _('try extended date formats'))], + _('hg debugdate [-e] DATE [RANGE]')), + "debugdata": (debugdata, [], _('hg debugdata FILE REV')), + "debugfsinfo": (debugfsinfo, [], _('hg debugfsinfo [PATH]')), + "debugindex": (debugindex, [], _('hg debugindex FILE')), + "debugindexdot": (debugindexdot, [], _('hg debugindexdot FILE')), + "debuginstall": (debuginstall, [], _('hg debuginstall')), + "debugrawcommit|rawcommit": + (rawcommit, + [('p', 'parent', [], _('parent')), + ('F', 'files', '', _('file list')) + ] + commitopts + commitopts2, + _('hg debugrawcommit [OPTION]... [FILE]...')), "debugrebuildstate": (debugrebuildstate, [('r', 'rev', '', _('revision to rebuild to'))], - _('debugrebuildstate [-r REV] [REV]')), - "debugcheckstate": (debugcheckstate, [], _('debugcheckstate')), - "debugsetparents": (debugsetparents, [], _('debugsetparents REV1 [REV2]')), - "debugstate": (debugstate, [], _('debugstate')), - "debugdate": - (debugdate, - [('e', 'extended', None, _('try extended date formats'))], - _('debugdate [-e] DATE [RANGE]')), - "debugdata": (debugdata, [], _('debugdata FILE REV')), - "debugindex": (debugindex, [], _('debugindex FILE')), - "debugindexdot": (debugindexdot, [], _('debugindexdot FILE')), + _('hg debugrebuildstate [-r REV] [REV]')), "debugrename": (debugrename, [('r', 'rev', '', _('revision to debug'))], - _('debugrename [-r REV] FILE')), - "debugwalk": (debugwalk, walkopts, _('debugwalk [OPTION]... [FILE]...')), + _('hg debugrename [-r REV] FILE')), + "debugsetparents": + (debugsetparents, + [], + _('hg debugsetparents REV1 [REV2]')), + "debugstate": + (debugstate, + [('', 'nodates', None, _('do not display the saved mtime'))], + _('hg debugstate [OPTS]')), + "debugwalk": (debugwalk, walkopts, _('hg debugwalk [OPTION]... [FILE]...')), "^diff": (diff, [('r', 'rev', [], _('revision')), @@ -2734,6 +3040,8 @@ _('ignore changes in the amount of white space')), ('B', 'ignore-blank-lines', None, _('ignore changes whose lines are all blank')), + ('U', 'unified', '', + _('number of lines of context to show')) ] + walkopts, _('hg diff [OPTION]... [-r REV1 [-r REV2]] [FILE]...')), "^export": @@ -2755,14 +3063,14 @@ _('print only filenames and revs that match')), ('n', 'line-number', None, _('print matching line numbers')), ('r', 'rev', [], _('search in given revision range')), - ('u', 'user', None, _('print user who committed change')), + ('u', 'user', None, _('list the author (long with -v)')), + ('d', 'date', None, _('list the date (short with -q)')), ] + walkopts, _('hg grep [OPTION]... PATTERN [FILE]...')), "heads": (heads, - [('', 'style', '', _('display using template map file')), - ('r', 'rev', '', _('show only heads which are descendants of rev')), - ('', 'template', '', _('display with template'))], + [('r', 'rev', '', _('show only heads which are descendants of rev')), + ] + templateopts, _('hg heads [-r REV] [REV]...')), "help": (help_, [], _('hg help [COMMAND]')), "identify|id": @@ -2781,22 +3089,22 @@ ('b', 'base', '', _('base path')), ('f', 'force', None, _('skip check for outstanding uncommitted changes')), + ('', 'no-commit', None, _("don't commit, just update the working directory")), ('', 'exact', None, _('apply patch to the nodes from which it was generated')), ('', 'import-branch', None, - _('Use any branch information in patch (implied by --exact)'))] + commitopts, - _('hg import [-p NUM] [-m MESSAGE] [-f] PATCH...')), - "incoming|in": (incoming, - [('M', 'no-merges', None, _('do not show merges')), - ('f', 'force', None, + _('Use any branch information in patch (implied by --exact)'))] + + commitopts + commitopts2, + _('hg import [OPTION]... PATCH...')), + "incoming|in": + (incoming, + [('f', 'force', None, _('run even when remote repository is unrelated')), - ('', 'style', '', _('display using template map file')), ('n', 'newest-first', None, _('show newest record first')), ('', 'bundle', '', _('file to store the bundles into')), - ('p', 'patch', None, _('show patch')), - ('r', 'rev', [], _('a specific revision up to which you would like to pull')), - ('', 'template', '', _('display with template')), - ] + remoteopts, + ('r', 'rev', [], + _('a specific revision up to which you would like to pull')), + ] + logopts + remoteopts, _('hg incoming [-p] [-n] [-M] [-f] [-r REV]...' ' [--bundle FILENAME] [SOURCE]')), "^init": @@ -2821,41 +3129,37 @@ ('d', 'date', '', _('show revs matching date spec')), ('C', 'copies', None, _('show copied files')), ('k', 'keyword', [], _('do case-insensitive search for a keyword')), - ('l', 'limit', '', _('limit number of changes displayed')), ('r', 'rev', [], _('show the specified revision or range')), ('', 'removed', None, _('include revs where files were removed')), - ('M', 'no-merges', None, _('do not show merges')), - ('', 'style', '', _('display using template map file')), ('m', 'only-merges', None, _('show only merges')), - ('p', 'patch', None, _('show patch')), + ('b', 'only-branch', [], + _('show only changesets within the given named branch')), ('P', 'prune', [], _('do not display revision or any of its ancestors')), - ('', 'template', '', _('display with template')), - ] + walkopts, + ] + logopts + walkopts, _('hg log [OPTION]... [FILE]')), - "manifest": (manifest, [('r', 'rev', '', _('revision to display'))], - _('hg manifest [-r REV]')), + "manifest": + (manifest, + [('r', 'rev', '', _('revision to display'))], + _('hg manifest [-r REV]')), "^merge": (merge, [('f', 'force', None, _('force a merge with outstanding changes')), ('r', 'rev', '', _('revision to merge')), ], _('hg merge [-f] [[-r] REV]')), - "outgoing|out": (outgoing, - [('M', 'no-merges', None, _('do not show merges')), - ('f', 'force', None, + "outgoing|out": + (outgoing, + [('f', 'force', None, _('run even when remote repository is unrelated')), - ('p', 'patch', None, _('show patch')), - ('', 'style', '', _('display using template map file')), - ('r', 'rev', [], _('a specific revision you would like to push')), + ('r', 'rev', [], + _('a specific revision up to which you would like to push')), ('n', 'newest-first', None, _('show newest record first')), - ('', 'template', '', _('display with template')), - ] + remoteopts, + ] + logopts + remoteopts, _('hg outgoing [-M] [-p] [-n] [-f] [-r REV]... [DEST]')), "^parents": (parents, [('r', 'rev', '', _('show parents from the specified rev')), - ('', 'style', '', _('display using template map file')), - ('', 'template', '', _('display with template'))], + ] + templateopts, _('hg parents [-r REV] [FILE]')), "paths": (paths, [], _('hg paths [NAME]')), "^pull": @@ -2871,20 +3175,16 @@ "^push": (push, [('f', 'force', None, _('force push')), - ('r', 'rev', [], _('a specific revision you would like to push')), + ('r', 'rev', [], + _('a specific revision up to which you would like to push')), ] + remoteopts, _('hg push [-f] [-r REV]... [-e CMD] [--remotecmd CMD] [DEST]')), - "debugrawcommit|rawcommit": - (rawcommit, - [('p', 'parent', [], _('parent')), - ('F', 'files', '', _('file list')) - ] + commitopts + commitopts2, - _('hg debugrawcommit [OPTION]... [FILE]...')), "recover": (recover, [], _('hg recover')), "^remove|rm": (remove, - [('A', 'after', None, _('record remove without deleting')), - ('f', 'force', None, _('remove file even if modified')), + [('A', 'after', None, _('record delete for missing files')), + ('f', 'force', None, + _('remove (and delete) file even if added or modified')), ] + walkopts, _('hg remove [OPTION]... FILE...')), "rename|mv": @@ -2904,18 +3204,15 @@ _('hg revert [OPTION]... [-r REV] [NAME]...')), "rollback": (rollback, [], _('hg rollback')), "root": (root, [], _('hg root')), - "showconfig|debugconfig": - (showconfig, - [('u', 'untrusted', None, _('show untrusted configuration options'))], - _('showconfig [-u] [NAME]...')), "^serve": (serve, [('A', 'accesslog', '', _('name of access log file to write to')), ('d', 'daemon', None, _('run server in background')), ('', 'daemon-pipefds', '', _('used internally by daemon mode')), ('E', 'errorlog', '', _('name of error log file to write to')), - ('p', 'port', 0, _('port to use (default: 8000)')), - ('a', 'address', '', _('address to use')), + ('p', 'port', 0, _('port to listen on (default: 8000)')), + ('a', 'address', '', _('address to listen on (default: all interfaces)')), + ('', 'prefix', '', _('prefix path to serve from (default: server root)')), ('n', 'name', '', _('name to show in web pages (default: working dir)')), ('', 'webdir-conf', '', _('name of the webdir config file' @@ -2927,6 +3224,10 @@ ('6', 'ipv6', None, _('use IPv6 in addition to IPv4')), ('', 'certificate', '', _('SSL certificate file'))], _('hg serve [OPTION]...')), + "showconfig|debugconfig": + (showconfig, + [('u', 'untrusted', None, _('show untrusted configuration options'))], + _('hg showconfig [-u] [NAME]...')), "^status|st": (status, [('A', 'all', None, _('show status of all files')), @@ -2953,13 +3254,12 @@ # -l/--local is already there, commitopts cannot be used ('m', 'message', '', _('use <text> as commit message')), ] + commitopts2, - _('hg tag [-l] [-m TEXT] [-d DATE] [-u USER] [-r REV] NAME')), + _('hg tag [-l] [-m TEXT] [-d DATE] [-u USER] [-r REV] NAME...')), "tags": (tags, [], _('hg tags')), "tip": (tip, - [('', 'style', '', _('display using template map file')), - ('p', 'patch', None, _('show patch')), - ('', 'template', '', _('display with template'))], + [('p', 'patch', None, _('show patch')), + ] + templateopts, _('hg tip [-p]')), "unbundle": (unbundle, @@ -2976,6 +3276,6 @@ "version": (version_, [], _('hg version')), } -norepo = ("clone init version help debugancestor debugcomplete debugdata" - " debugindex debugindexdot debugdate debuginstall") -optionalrepo = ("identify paths serve showconfig") +norepo = ("clone init version help debugcomplete debugdata" + " debugindex debugindexdot debugdate debuginstall debugfsinfo") +optionalrepo = ("identify paths serve showconfig debugancestor")
--- a/mercurial/context.py Wed Apr 09 15:27:57 2008 -0700 +++ b/mercurial/context.py Wed Apr 09 15:28:30 2008 -0700 @@ -5,9 +5,9 @@ # This software may be used and distributed according to the terms # of the GNU General Public License, incorporated herein by reference. -from node import * +from node import nullid, nullrev, short from i18n import _ -import ancestor, bdiff, repo, revlog, util, os, errno +import ancestor, bdiff, revlog, util, os, errno class changectx(object): """A changecontext object makes access to data related to a particular @@ -34,6 +34,12 @@ def __repr__(self): return "<changectx %s>" % str(self) + def __hash__(self): + try: + return hash(self._rev) + except AttributeError: + return id(self) + def __eq__(self, other): try: return self._rev == other._rev @@ -100,13 +106,15 @@ try: return self._manifest[path], self._manifest.flags(path) except KeyError: - raise revlog.LookupError(path, _("'%s' not found in manifest") % path) + raise revlog.LookupError(self._node, path, + _('not found in manifest')) if '_manifestdelta' in self.__dict__ or path in self.files(): if path in self._manifestdelta: return self._manifestdelta[path], self._manifestdelta.flags(path) node, flag = self._repo.manifest.find(self._changeset[0], path) if not node: - raise revlog.LookupError(path, _("'%s' not found in manifest") % path) + raise revlog.LookupError(self._node, path, + _('not found in manifest')) return node, flag @@ -159,12 +167,11 @@ if filelog: self._filelog = filelog - if fileid is None: - if changectx is None: - self._changeid = changeid - else: - self._changectx = changectx - else: + if changeid is not None: + self._changeid = changeid + if changectx is not None: + self._changectx = changectx + if fileid is not None: self._fileid = fileid def __getattr__(self, name): @@ -175,7 +182,10 @@ self._filelog = self._repo.file(self._path) return self._filelog elif name == '_changeid': - self._changeid = self._filelog.linkrev(self._filenode) + if '_changectx' in self.__dict__: + self._changeid = self._changectx.rev() + else: + self._changeid = self._filelog.linkrev(self._filenode) return self._changeid elif name == '_filenode': if '_fileid' in self.__dict__: @@ -186,6 +196,9 @@ elif name == '_filerev': self._filerev = self._filelog.rev(self._filenode) return self._filerev + elif name == '_repopath': + self._repopath = self._path + return self._repopath else: raise AttributeError, name @@ -203,6 +216,12 @@ def __repr__(self): return "<filectx %s>" % str(self) + def __hash__(self): + try: + return hash((self._path, self._fileid)) + except AttributeError: + return id(self) + def __eq__(self, other): try: return (self._path == other._path @@ -229,8 +248,11 @@ def rev(self): if '_changectx' in self.__dict__: return self._changectx.rev() + if '_changeid' in self.__dict__: + return self._changectx.rev() return self._filelog.linkrev(self._filenode) + def linkrev(self): return self._filelog.linkrev(self._filenode) def node(self): return self._changectx.node() def user(self): return self._changectx.user() def date(self): return self._changectx.date() @@ -241,18 +263,42 @@ def changectx(self): return self._changectx def data(self): return self._filelog.read(self._filenode) - def renamed(self): return self._filelog.renamed(self._filenode) def path(self): return self._path def size(self): return self._filelog.size(self._filerev) def cmp(self, text): return self._filelog.cmp(self._filenode, text) + def renamed(self): + """check if file was actually renamed in this changeset revision + + If rename logged in file revision, we report copy for changeset only + if file revisions linkrev points back to the changeset in question + or both changeset parents contain different file revisions. + """ + + renamed = self._filelog.renamed(self._filenode) + if not renamed: + return renamed + + if self.rev() == self.linkrev(): + return renamed + + name = self.path() + fnode = self._filenode + for p in self._changectx.parents(): + try: + if fnode == p.filenode(name): + return None + except revlog.LookupError: + pass + return renamed + def parents(self): p = self._path fl = self._filelog pl = [(p, n, fl) for n in self._filelog.parents(self._filenode)] - r = self.renamed() + r = self._filelog.renamed(self._filenode) if r: pl[0] = (r[0], r[1], None) @@ -318,7 +364,7 @@ return [getctx(p, n) for p, n in pl if n != nullrev] # use linkrev to find the first changeset where self appeared - if self.rev() != self._filelog.linkrev(self._filenode): + if self.rev() != self.linkrev(): base = self.filectx(self.filerev()) else: base = self @@ -373,7 +419,7 @@ pl = [(n.path(), n.filenode()) for n in c.parents()] acache[(c._path, None)] = pl - flcache = {self._path:self._filelog, fc2._path:fc2._filelog} + flcache = {self._repopath:self._filelog, fc2._repopath:fc2._filelog} def parents(vertex): if vertex in acache: return acache[vertex] @@ -483,7 +529,7 @@ return self._manifest.flags(path) except KeyError: return '' - + pnode = self._parents[0].changeset()[0] orig = self._repo.dirstate.copies().get(path, path) node, flag = self._repo.manifest.find(pnode, orig)
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/mercurial/copies.py Wed Apr 09 15:28:30 2008 -0700 @@ -0,0 +1,233 @@ +# copies.py - copy detection for Mercurial +# +# Copyright 2008 Matt Mackall <mpm@selenic.com> +# +# This software may be used and distributed according to the terms +# of the GNU General Public License, incorporated herein by reference. + +from node import nullid, nullrev +from i18n import _ +import util, heapq + +def _nonoverlap(d1, d2, d3): + "Return list of elements in d1 not in d2 or d3" + l = [d for d in d1 if d not in d3 and d not in d2] + l.sort() + return l + +def _dirname(f): + s = f.rfind("/") + if s == -1: + return "" + return f[:s] + +def _dirs(files): + d = {} + for f in files: + f = _dirname(f) + while f not in d: + d[f] = True + f = _dirname(f) + return d + +def _findoldnames(fctx, limit): + "find files that path was copied from, back to linkrev limit" + old = {} + seen = {} + orig = fctx.path() + visit = [(fctx, 0)] + while visit: + fc, depth = visit.pop() + s = str(fc) + if s in seen: + continue + seen[s] = 1 + if fc.path() != orig and fc.path() not in old: + old[fc.path()] = (depth, fc.path()) # remember depth + if fc.rev() < limit and fc.rev() is not None: + continue + visit += [(p, depth - 1) for p in fc.parents()] + + # return old names sorted by depth + old = old.values() + old.sort() + return [o[1] for o in old] + +def _findlimit(repo, a, b): + "find the earliest revision that's an ancestor of a or b but not both" + # basic idea: + # - mark a and b with different sides + # - if a parent's children are all on the same side, the parent is + # on that side, otherwise it is on no side + # - walk the graph in topological order with the help of a heap; + # - add unseen parents to side map + # - clear side of any parent that has children on different sides + # - track number of interesting revs that might still be on a side + # - track the lowest interesting rev seen + # - quit when interesting revs is zero + + cl = repo.changelog + working = cl.count() # pseudo rev for the working directory + if a is None: + a = working + if b is None: + b = working + + side = {a: -1, b: 1} + visit = [-a, -b] + heapq.heapify(visit) + interesting = len(visit) + limit = working + + while interesting: + r = -heapq.heappop(visit) + if r == working: + parents = [cl.rev(p) for p in repo.dirstate.parents()] + else: + parents = cl.parentrevs(r) + for p in parents: + if p not in side: + # first time we see p; add it to visit + side[p] = side[r] + if side[p]: + interesting += 1 + heapq.heappush(visit, -p) + elif side[p] and side[p] != side[r]: + # p was interesting but now we know better + side[p] = 0 + interesting -= 1 + if side[r]: + limit = r # lowest rev visited + interesting -= 1 + return limit + +def copies(repo, c1, c2, ca, checkdirs=False): + """ + Find moves and copies between context c1 and c2 + """ + # avoid silly behavior for update from empty dir + if not c1 or not c2 or c1 == c2: + return {}, {} + + limit = _findlimit(repo, c1.rev(), c2.rev()) + m1 = c1.manifest() + m2 = c2.manifest() + ma = ca.manifest() + + def makectx(f, n): + if len(n) != 20: # in a working context? + if c1.rev() is None: + return c1.filectx(f) + return c2.filectx(f) + return repo.filectx(f, fileid=n) + ctx = util.cachefunc(makectx) + + copy = {} + fullcopy = {} + diverge = {} + + def checkcopies(f, m1, m2): + '''check possible copies of f from m1 to m2''' + c1 = ctx(f, m1[f]) + for of in _findoldnames(c1, limit): + fullcopy[f] = of # remember for dir rename detection + if of in m2: # original file not in other manifest? + # if the original file is unchanged on the other branch, + # no merge needed + if m2[of] != ma.get(of): + c2 = ctx(of, m2[of]) + ca = c1.ancestor(c2) + # related and named changed on only one side? + if ca and (ca.path() == f or ca.path() == c2.path()): + if c1 != ca or c2 != ca: # merge needed? + copy[f] = of + elif of in ma: + diverge.setdefault(of, []).append(f) + + repo.ui.debug(_(" searching for copies back to rev %d\n") % limit) + + u1 = _nonoverlap(m1, m2, ma) + u2 = _nonoverlap(m2, m1, ma) + + if u1: + repo.ui.debug(_(" unmatched files in local:\n %s\n") + % "\n ".join(u1)) + if u2: + repo.ui.debug(_(" unmatched files in other:\n %s\n") + % "\n ".join(u2)) + + for f in u1: + checkcopies(f, m1, m2) + for f in u2: + checkcopies(f, m2, m1) + + diverge2 = {} + for of, fl in diverge.items(): + if len(fl) == 1: + del diverge[of] # not actually divergent + else: + diverge2.update(dict.fromkeys(fl)) # reverse map for below + + if fullcopy: + repo.ui.debug(_(" all copies found (* = to merge, ! = divergent):\n")) + for f in fullcopy: + note = "" + if f in copy: note += "*" + if f in diverge2: note += "!" + repo.ui.debug(_(" %s -> %s %s\n") % (f, fullcopy[f], note)) + del diverge2 + + if not fullcopy or not checkdirs: + return copy, diverge + + repo.ui.debug(_(" checking for directory renames\n")) + + # generate a directory move map + d1, d2 = _dirs(m1), _dirs(m2) + invalid = {} + dirmove = {} + + # examine each file copy for a potential directory move, which is + # when all the files in a directory are moved to a new directory + for dst, src in fullcopy.items(): + dsrc, ddst = _dirname(src), _dirname(dst) + if dsrc in invalid: + # already seen to be uninteresting + continue + elif dsrc in d1 and ddst in d1: + # directory wasn't entirely moved locally + invalid[dsrc] = True + elif dsrc in d2 and ddst in d2: + # directory wasn't entirely moved remotely + invalid[dsrc] = True + elif dsrc in dirmove and dirmove[dsrc] != ddst: + # files from the same directory moved to two different places + invalid[dsrc] = True + else: + # looks good so far + dirmove[dsrc + "/"] = ddst + "/" + + for i in invalid: + if i in dirmove: + del dirmove[i] + del d1, d2, invalid + + if not dirmove: + return copy, diverge + + for d in dirmove: + repo.ui.debug(_(" dir %s -> %s\n") % (d, dirmove[d])) + + # check unaccounted nonoverlapping files against directory moves + for f in u1 + u2: + if f not in fullcopy: + for d in dirmove: + if f.startswith(d): + # new file added in a directory that was moved, move it + df = dirmove[d] + f[len(d):] + if df not in copy: + copy[f] = df + repo.ui.debug(_(" file %s -> %s\n") % (f, copy[f])) + break + + return copy, diverge
--- a/mercurial/demandimport.py Wed Apr 09 15:27:57 2008 -0700 +++ b/mercurial/demandimport.py Wed Apr 09 15:28:30 2008 -0700 @@ -77,7 +77,7 @@ self._load() setattr(self._module, attr, val) -def _demandimport(name, globals=None, locals=None, fromlist=None): +def _demandimport(name, globals=None, locals=None, fromlist=None, level=None): if not locals or name in ignore or fromlist == ('*',): # these cases we can't really delay return _origimport(name, globals, locals, fromlist) @@ -95,6 +95,9 @@ return locals[base] return _demandmod(name, globals, locals) else: + if level is not None: + # from . import b,c,d or from .a import b,c,d + return _origimport(name, globals, locals, fromlist, level) # from a import b,c,d mod = _origimport(name, globals, locals) # recurse down the module chain
--- a/mercurial/dirstate.py Wed Apr 09 15:27:57 2008 -0700 +++ b/mercurial/dirstate.py Wed Apr 09 15:28:30 2008 -0700 @@ -7,10 +7,10 @@ of the GNU General Public License, incorporated herein by reference. """ -from node import * +from node import nullid from i18n import _ -import struct, os, time, bisect, stat, strutil, util, re, errno, ignore -import cStringIO, osutil +import struct, os, bisect, stat, strutil, util, errno, ignore +import cStringIO, osutil, sys _unknown = ('?', 0, 0, 0) _format = ">cllll" @@ -63,6 +63,9 @@ elif name == '_slash': self._slash = self._ui.configbool('ui', 'slash') and os.sep != '/' return self._slash + elif name == '_checkexec': + self._checkexec = util.checkexec(self._root) + return self._checkexec else: raise AttributeError, name @@ -74,7 +77,7 @@ if cwd == self._root: return '' # self._root ends with a path separator if self._root is '/' or 'C:\' rootsep = self._root - if not rootsep.endswith(os.sep): + if not util.endswithsep(rootsep): rootsep += os.sep if cwd.startswith(rootsep): return cwd[len(rootsep):] @@ -87,7 +90,7 @@ cwd = self.getcwd() path = util.pathto(self._root, cwd, f) if self._slash: - return path.replace(os.sep, '/') + return util.normpath(path) return path def __getitem__(self, key): @@ -197,7 +200,8 @@ def _incpathcheck(self, f): if '\r' in f or '\n' in f: - raise util.Abort(_("'\\n' and '\\r' disallowed in filenames")) + raise util.Abort(_("'\\n' and '\\r' disallowed in filenames: %r") + % f) # shadows if f in self._dirs: raise util.Abort(_('directory %r already in dirstate') % f) @@ -235,11 +239,26 @@ self._changepath(f, 'n', True) s = os.lstat(self._join(f)) self._map[f] = ('n', s.st_mode, s.st_size, s.st_mtime, 0) - if self._copymap.has_key(f): + if f in self._copymap: del self._copymap[f] def normallookup(self, f): 'mark a file normal, but possibly dirty' + if self._pl[1] != nullid and f in self._map: + # if there is a merge going on and the file was either + # in state 'm' or dirty before being removed, restore that state. + entry = self._map[f] + if entry[0] == 'r' and entry[2] in (-1, -2): + source = self._copymap.get(f) + if entry[2] == -1: + self.merge(f) + elif entry[2] == -2: + self.normaldirty(f) + if source: + self.copy(source, f) + return + if entry[0] == 'm' or entry[0] == 'n' and entry[2] == -2: + return self._dirty = True self._changepath(f, 'n', True) self._map[f] = ('n', 0, -1, -1, 0) @@ -266,8 +285,15 @@ 'mark a file removed' self._dirty = True self._changepath(f, 'r') - self._map[f] = ('r', 0, 0, 0, 0) - if f in self._copymap: + size = 0 + if self._pl[1] != nullid and f in self._map: + entry = self._map[f] + if entry[0] == 'm': + size = -1 + elif entry[0] == 'n' and entry[2] == -2: + size = -2 + self._map[f] = ('r', 0, size, 0, 0) + if size == 0 and f in self._copymap: del self._copymap[f] def merge(self, f): @@ -286,7 +312,7 @@ self._changepath(f, '?') del self._map[f] except KeyError: - self._ui.warn(_("not in dirstate: %s!\n") % f) + self._ui.warn(_("not in dirstate: %s\n") % f) def clear(self): self._map = {} @@ -309,6 +335,16 @@ def write(self): if not self._dirty: return + st = self._opener("dirstate", "w", atomictemp=True) + + try: + gran = int(self._ui.config('dirstate', 'granularity', 1)) + except ValueError: + gran = 1 + limit = sys.maxint + if gran > 0: + limit = util.fstat(st).st_mtime - gran + cs = cStringIO.StringIO() copymap = self._copymap pack = struct.pack @@ -317,10 +353,11 @@ for f, e in self._map.iteritems(): if f in copymap: f = "%s\0%s" % (f, copymap[f]) + if e[3] > limit and e[0] == 'n': + e = (e[0], 0, -1, -1, 0) e = pack(_format, e[0], e[1], e[2], e[3], len(f)) write(e) write(f) - st = self._opener("dirstate", "w", atomictemp=True) st.write(cs.getvalue()) st.rename() self._dirty = self._dirtypl = False @@ -369,13 +406,23 @@ % (self.pathto(f), kind)) return False + def _dirignore(self, f): + if f == '.': + return False + if self._ignore(f): + return True + for c in strutil.findall(f, '/'): + if self._ignore(f[:c]): + return True + return False + def walk(self, files=None, match=util.always, badmatch=None): # filter out the stat for src, f, st in self.statwalk(files, match, badmatch=badmatch): yield src, f - def statwalk(self, files=None, match=util.always, ignored=False, - badmatch=None, directories=False): + def statwalk(self, files=None, match=util.always, unknown=True, + ignored=False, badmatch=None, directories=False): ''' walk recursively through the directory tree, finding all files matched by the match function @@ -403,14 +450,17 @@ return False return match(file_) + # TODO: don't walk unknown directories if unknown and ignored are False ignore = self._ignore + dirignore = self._dirignore if ignored: imatch = match ignore = util.never + dirignore = util.never # self._root may end with a path separator when self._root == '/' common_prefix_len = len(self._root) - if not self._root.endswith(os.sep): + if not util.endswithsep(self._root): common_prefix_len += 1 normpath = util.normpath @@ -492,8 +542,9 @@ yield 'b', ff, None continue if s_isdir(st.st_mode): - for f, src, st in findfiles(f): - yield src, f, st + if not dirignore(nf): + for f, src, st in findfiles(f): + yield src, f, st else: if nf in known: continue @@ -515,10 +566,11 @@ if imatch(k): yield 'm', k, None - def status(self, files, match, list_ignored, list_clean): + def status(self, files, match, list_ignored, list_clean, list_unknown=True): lookup, modified, added, unknown, ignored = [], [], [], [], [] removed, deleted, clean = [], [], [] + files = files or [] _join = self._join lstat = os.lstat cmap = self._copymap @@ -532,13 +584,15 @@ dadd = deleted.append cadd = clean.append - for src, fn, st in self.statwalk(files, match, ignored=list_ignored): + for src, fn, st in self.statwalk(files, match, unknown=list_unknown, + ignored=list_ignored): if fn in dmap: type_, mode, size, time, foo = dmap[fn] else: - if list_ignored and self._ignore(fn): - iadd(fn) - else: + if (list_ignored or fn in files) and self._dirignore(fn): + if list_ignored: + iadd(fn) + elif list_unknown: uadd(fn) continue if src == 'm': @@ -555,15 +609,16 @@ nonexistent = False # XXX: what to do with file no longer present in the fs # who are not removed in the dirstate ? - if nonexistent and type_ in "nm": + if nonexistent and type_ in "nma": dadd(fn) continue # check the common case first if type_ == 'n': if not st: st = lstat(_join(fn)) - if (size >= 0 and (size != st.st_size - or (mode ^ st.st_mode) & 0100) + if (size >= 0 and + (size != st.st_size + or ((mode ^ st.st_mode) & 0100 and self._checkexec)) or size == -2 or fn in self._copymap): madd(fn)
--- a/mercurial/dispatch.py Wed Apr 09 15:27:57 2008 -0700 +++ b/mercurial/dispatch.py Wed Apr 09 15:28:30 2008 -0700 @@ -5,8 +5,8 @@ # This software may be used and distributed according to the terms # of the GNU General Public License, incorporated herein by reference. -from node import * from i18n import _ +from repo import RepoError import os, sys, atexit, signal, pdb, traceback, socket, errno, shlex, time import util, commands, hg, lock, fancyopts, revlog, version, extensions, hook import cmdutil @@ -65,7 +65,7 @@ except cmdutil.UnknownCommand, inst: ui.warn(_("hg: unknown command '%s'\n") % inst.args[0]) commands.help_(ui, 'shortlist') - except hg.RepoError, inst: + except RepoError, inst: ui.warn(_("abort: %s!\n") % inst) except lock.LockHeld, inst: if inst.errno == errno.ETIMEDOUT: @@ -150,8 +150,7 @@ return -1 -def _findrepo(): - p = os.getcwd() +def _findrepo(p): while not os.path.isdir(os.path.join(p, ".hg")): oldp, p = p, os.path.dirname(p) if p == oldp: @@ -254,7 +253,7 @@ os.chdir(cwd[-1]) # read the local repository .hgrc into a local ui object - path = _findrepo() or "" + path = _findrepo(os.getcwd()) or "" if not path: lui = ui if path: @@ -275,6 +274,15 @@ for name, module in extensions.extensions(): if name in _loaded: continue + + # setup extensions + # TODO this should be generalized to scheme, where extensions can + # redepend on other extensions. then we should toposort them, and + # do initialization in correct order + extsetup = getattr(module, 'extsetup', None) + if extsetup: + extsetup() + cmdtable = getattr(module, 'cmdtable', {}) overrides = [cmd for cmd in cmdtable if cmd in commands.table] if overrides: @@ -331,26 +339,31 @@ try: repo = hg.repository(ui, path=path) ui = repo.ui - ui.setconfig("bundle", "mainreporoot", repo.root) if not repo.local(): raise util.Abort(_("repository '%s' is not local") % path) - except hg.RepoError: + ui.setconfig("bundle", "mainreporoot", repo.root) + except RepoError: if cmd not in commands.optionalrepo.split(): + if args and not path: # try to infer -R from command args + repos = map(_findrepo, args) + guess = repos[0] + if guess and repos.count(guess) == len(repos): + return _dispatch(ui, ['--repository', guess] + fullargs) if not path: - raise hg.RepoError(_("There is no Mercurial repository here" - " (.hg not found)")) + raise RepoError(_("There is no Mercurial repository here" + " (.hg not found)")) raise d = lambda: func(ui, repo, *args, **cmdoptions) else: d = lambda: func(ui, *args, **cmdoptions) # run pre-hook, and abort if it fails - ret = hook.hook(ui, repo, "pre-%s" % cmd, False, args=" ".join(fullargs)) + ret = hook.hook(lui, repo, "pre-%s" % cmd, False, args=" ".join(fullargs)) if ret: return ret ret = _runcommand(ui, options, cmd, d) # run post-hook, passing command result - hook.hook(ui, repo, "post-%s" % cmd, False, args=" ".join(fullargs), + hook.hook(lui, repo, "post-%s" % cmd, False, args=" ".join(fullargs), result = ret) return ret
--- a/mercurial/extensions.py Wed Apr 09 15:27:57 2008 -0700 +++ b/mercurial/extensions.py Wed Apr 09 15:28:30 2008 -0700 @@ -6,7 +6,7 @@ # of the GNU General Public License, incorporated herein by reference. import imp, os -import util, sys +import util from i18n import _ _extensions = {} @@ -78,8 +78,12 @@ except (util.SignalInterrupt, KeyboardInterrupt): raise except Exception, inst: - ui.warn(_("*** failed to import extension %s: %s\n") % - (name, inst)) + if path: + ui.warn(_("*** failed to import extension %s from %s: %s\n") + % (name, path, inst)) + else: + ui.warn(_("*** failed to import extension %s: %s\n") + % (name, inst)) if ui.print_exc(): return 1
--- a/mercurial/fancyopts.py Wed Apr 09 15:27:57 2008 -0700 +++ b/mercurial/fancyopts.py Wed Apr 09 15:28:30 2008 -0700 @@ -38,7 +38,6 @@ if isinstance(default, list): state[name] = default[:] elif callable(default): - print "whoa", name, default state[name] = None else: state[name] = default
--- a/mercurial/filelog.py Wed Apr 09 15:27:57 2008 -0700 +++ b/mercurial/filelog.py Wed Apr 09 15:28:30 2008 -0700 @@ -5,8 +5,8 @@ # This software may be used and distributed according to the terms # of the GNU General Public License, incorporated herein by reference. -from revlog import * -import os +from node import bin, nullid +from revlog import revlog class filelog(revlog): def __init__(self, opener, path): @@ -58,7 +58,7 @@ if self.parents(node)[0] != nullid: return False m = self._readmeta(node) - if m and m.has_key("copy"): + if m and "copy" in m: return (m["copy"], bin(m["copyrev"])) return False
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/mercurial/filemerge.py Wed Apr 09 15:28:30 2008 -0700 @@ -0,0 +1,217 @@ +# filemerge.py - file-level merge handling for Mercurial +# +# Copyright 2006, 2007, 2008 Matt Mackall <mpm@selenic.com> +# +# This software may be used and distributed according to the terms +# of the GNU General Public License, incorporated herein by reference. + +from node import nullrev +from i18n import _ +import util, os, tempfile, simplemerge, re, filecmp + +def _toolstr(ui, tool, part, default=""): + return ui.config("merge-tools", tool + "." + part, default) + +def _toolbool(ui, tool, part, default=False): + return ui.configbool("merge-tools", tool + "." + part, default) + +def _findtool(ui, tool): + k = _toolstr(ui, tool, "regkey") + if k: + p = util.lookup_reg(k, _toolstr(ui, tool, "regname")) + if p: + p = util.find_exe(p + _toolstr(ui, tool, "regappend")) + if p: + return p + return util.find_exe(_toolstr(ui, tool, "executable", tool)) + +def _picktool(repo, ui, path, binary, symlink): + def check(tool, pat, symlink, binary): + tmsg = tool + if pat: + tmsg += " specified for " + pat + if pat and not _findtool(ui, tool): # skip search if not matching + ui.warn(_("couldn't find merge tool %s\n") % tmsg) + elif symlink and not _toolbool(ui, tool, "symlink"): + ui.warn(_("tool %s can't handle symlinks\n") % tmsg) + elif binary and not _toolbool(ui, tool, "binary"): + ui.warn(_("tool %s can't handle binary\n") % tmsg) + elif not util.gui() and _toolbool(ui, tool, "gui"): + ui.warn(_("tool %s requires a GUI\n") % tmsg) + else: + return True + return False + + # HGMERGE takes precedence + hgmerge = os.environ.get("HGMERGE") + if hgmerge: + return (hgmerge, hgmerge) + + # then patterns + for pat, tool in ui.configitems("merge-patterns"): + mf = util.matcher(repo.root, "", [pat], [], [])[1] + if mf(path) and check(tool, pat, symlink, False): + toolpath = _findtool(ui, tool) + return (tool, '"' + toolpath + '"') + + # then merge tools + tools = {} + for k,v in ui.configitems("merge-tools"): + t = k.split('.')[0] + if t not in tools: + tools[t] = int(_toolstr(ui, t, "priority", "0")) + names = tools.keys() + tools = [(-p,t) for t,p in tools.items()] + tools.sort() + uimerge = ui.config("ui", "merge") + if uimerge: + if uimerge not in names: + return (uimerge, uimerge) + tools.insert(0, (None, uimerge)) # highest priority + tools.append((None, "hgmerge")) # the old default, if found + for p,t in tools: + toolpath = _findtool(ui, t) + if toolpath and check(t, None, symlink, binary): + return (t, '"' + toolpath + '"') + # internal merge as last resort + return (not (symlink or binary) and "internal:merge" or None, None) + +def _eoltype(data): + "Guess the EOL type of a file" + if '\0' in data: # binary + return None + if '\r\n' in data: # Windows + return '\r\n' + if '\r' in data: # Old Mac + return '\r' + if '\n' in data: # UNIX + return '\n' + return None # unknown + +def _matcheol(file, origfile): + "Convert EOL markers in a file to match origfile" + tostyle = _eoltype(open(origfile, "rb").read()) + if tostyle: + data = open(file, "rb").read() + style = _eoltype(data) + if style: + newdata = data.replace(style, tostyle) + if newdata != data: + open(file, "wb").write(newdata) + +def filemerge(repo, fw, fd, fo, wctx, mctx): + """perform a 3-way merge in the working directory + + fw = original filename in the working directory + fd = destination filename in the working directory + fo = filename in other parent + wctx, mctx = working and merge changecontexts + """ + + def temp(prefix, ctx): + pre = "%s~%s." % (os.path.basename(ctx.path()), prefix) + (fd, name) = tempfile.mkstemp(prefix=pre) + data = repo.wwritedata(ctx.path(), ctx.data()) + f = os.fdopen(fd, "wb") + f.write(data) + f.close() + return name + + def isbin(ctx): + try: + return util.binary(ctx.data()) + except IOError: + return False + + fco = mctx.filectx(fo) + if not fco.cmp(wctx.filectx(fd).data()): # files identical? + return None + + ui = repo.ui + fcm = wctx.filectx(fw) + fca = fcm.ancestor(fco) or repo.filectx(fw, fileid=nullrev) + binary = isbin(fcm) or isbin(fco) or isbin(fca) + symlink = fcm.islink() or fco.islink() + tool, toolpath = _picktool(repo, ui, fw, binary, symlink) + ui.debug(_("picked tool '%s' for %s (binary %s symlink %s)\n") % + (tool, fw, binary, symlink)) + + if not tool: + tool = "internal:local" + if ui.prompt(_(" no tool found to merge %s\n" + "keep (l)ocal or take (o)ther?") % fw, + _("[lo]"), _("l")) != _("l"): + tool = "internal:other" + if tool == "internal:local": + return 0 + if tool == "internal:other": + repo.wwrite(fd, fco.data(), fco.fileflags()) + return 0 + if tool == "internal:fail": + return 1 + + # do the actual merge + a = repo.wjoin(fd) + b = temp("base", fca) + c = temp("other", fco) + out = "" + back = a + ".orig" + util.copyfile(a, back) + + if fw != fo: + repo.ui.status(_("merging %s and %s\n") % (fw, fo)) + else: + repo.ui.status(_("merging %s\n") % fw) + repo.ui.debug(_("my %s other %s ancestor %s\n") % (fcm, fco, fca)) + + # do we attempt to simplemerge first? + if _toolbool(ui, tool, "premerge", not (binary or symlink)): + r = simplemerge.simplemerge(a, b, c, quiet=True) + if not r: + ui.debug(_(" premerge successful\n")) + os.unlink(back) + os.unlink(b) + os.unlink(c) + return 0 + util.copyfile(back, a) # restore from backup and try again + + env = dict(HG_FILE=fd, + HG_MY_NODE=str(wctx.parents()[0]), + HG_OTHER_NODE=str(mctx), + HG_MY_ISLINK=fcm.islink(), + HG_OTHER_ISLINK=fco.islink(), + HG_BASE_ISLINK=fca.islink()) + + if tool == "internal:merge": + r = simplemerge.simplemerge(a, b, c, label=['local', 'other']) + else: + args = _toolstr(ui, tool, "args", '$local $base $other') + if "$output" in args: + out, a = a, back # read input from backup, write to original + replace = dict(local=a, base=b, other=c, output=out) + args = re.sub("\$(local|base|other|output)", + lambda x: '"%s"' % replace[x.group()[1:]], args) + r = util.system(toolpath + ' ' + args, cwd=repo.root, environ=env) + + if not r and _toolbool(ui, tool, "checkconflicts"): + if re.match("^(<<<<<<< .*|=======|>>>>>>> .*)$", fcm.data()): + r = 1 + + if not r and _toolbool(ui, tool, "checkchanged"): + if filecmp.cmp(repo.wjoin(fd), back): + if ui.prompt(_(" output file %s appears unchanged\n" + "was merge successful (yn)?") % fd, + _("[yn]"), _("n")) != _("y"): + r = 1 + + if _toolbool(ui, tool, "fixeol"): + _matcheol(repo.wjoin(fd), back) + + if r: + repo.ui.warn(_("merging %s failed!\n") % fd) + else: + os.unlink(back) + + os.unlink(b) + os.unlink(c) + return r
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/mercurial/hbisect.py Wed Apr 09 15:28:30 2008 -0700 @@ -0,0 +1,106 @@ +# changelog bisection for mercurial +# +# Copyright 2007 Matt Mackall +# Copyright 2005, 2006 Benoit Boissinot <benoit.boissinot@ens-lyon.org> +# Inspired by git bisect, extension skeleton taken from mq.py. +# +# This software may be used and distributed according to the terms +# of the GNU General Public License, incorporated herein by reference. + +from i18n import _ +from node import short +import util + +def bisect(changelog, state): + clparents = changelog.parentrevs + skip = dict.fromkeys([changelog.rev(n) for n in state['skip']]) + + def buildancestors(bad, good): + # only the earliest bad revision matters + badrev = min([changelog.rev(n) for n in bad]) + goodrevs = [changelog.rev(n) for n in good] + # build ancestors array + ancestors = [[]] * (changelog.count() + 1) # an extra for [-1] + + # clear good revs from array + for node in goodrevs: + ancestors[node] = None + for rev in xrange(changelog.count(), -1, -1): + if ancestors[rev] is None: + for prev in clparents(rev): + ancestors[prev] = None + + if ancestors[badrev] is None: + return badrev, None + return badrev, ancestors + + good = 0 + badrev, ancestors = buildancestors(state['bad'], state['good']) + if not ancestors: # looking for bad to good transition? + good = 1 + badrev, ancestors = buildancestors(state['good'], state['bad']) + bad = changelog.node(badrev) + if not ancestors: # now we're confused + raise util.Abort(_("Inconsistent state, %s:%s is good and bad") + % (badrev, short(bad))) + + # build children dict + children = {} + visit = [badrev] + candidates = [] + while visit: + rev = visit.pop(0) + if ancestors[rev] == []: + candidates.append(rev) + for prev in clparents(rev): + if prev != -1: + if prev in children: + children[prev].append(rev) + else: + children[prev] = [rev] + visit.append(prev) + + candidates.sort() + # have we narrowed it down to one entry? + tot = len(candidates) + if tot == 1: + return (bad, 0, good) + perfect = tot / 2 + + # find the best node to test + best_rev = None + best_len = -1 + poison = {} + for rev in candidates: + if rev in poison: + for c in children.get(rev, []): + poison[c] = True # poison children + continue + + a = ancestors[rev] or [rev] + ancestors[rev] = None + + x = len(a) # number of ancestors + y = tot - x # number of non-ancestors + value = min(x, y) # how good is this test? + if value > best_len and rev not in skip: + best_len = value + best_rev = rev + if value == perfect: # found a perfect candidate? quit early + break + + if y < perfect: # all downhill from here? + for c in children.get(rev, []): + poison[c] = True # poison children + continue + + for c in children.get(rev, []): + if ancestors[c]: + ancestors[c] = dict.fromkeys(ancestors[c] + a).keys() + else: + ancestors[c] = a + [c] + + assert best_rev is not None + best_node = changelog.node(best_rev) + + return (best_node, tot, good)
--- a/mercurial/help.py Wed Apr 09 15:27:57 2008 -0700 +++ b/mercurial/help.py Wed Apr 09 15:28:30 2008 -0700 @@ -8,8 +8,11 @@ helptable = { "dates|Date Formats": r''' - Some commands (backout, commit, tag) allow the user to specify a date. - Many date formats are acceptible. Here are some examples: + Some commands allow the user to specify a date: + backout, commit, import, tag: Specify the commit date. + log, revert, update: Select revision(s) by date. + + Many date formats are valid. Here are some examples: "Wed Dec 6 13:18:29 2006" (local timezone assumed) "Dec 6 13:18 -0600" (year assumed, time offset provided) @@ -33,6 +36,13 @@ the number of seconds since the epoch (1970-01-01 00:00 UTC). offset is the offset of the local timezone, in seconds west of UTC (negative if the timezone is east of UTC). + + The log command also accepts date ranges: + + "<{date}" - on or before a given date + ">{date}" - on or after a given date + "{date} to {date}" - a date range, inclusive + "-{days}" - within a given number of days of today ''', 'environment|env|Environment Variables': @@ -66,9 +76,6 @@ will be executed with three arguments: local file, remote file, ancestor file. - The default program is "hgmerge", which is a shell script provided - by Mercurial with some sensible defaults. - (deprecated, use .hgrc) HGRCPATH:: @@ -97,11 +104,11 @@ This is the name of the editor to use when committing. See EDITOR. EDITOR:: - Sometimes Mercurial needs to open a text file in an editor for a user - to modify, for example when writing commit messages or when using the - hgmerge script. The editor it uses is determined by looking at the - environment variables HGEDITOR, VISUAL and EDITOR, in that order. The - first non-empty one is chosen. If all of them are empty, the editor + Sometimes Mercurial needs to open a text file in an editor + for a user to modify, for example when writing commit messages. + The editor it uses is determined by looking at the environment + variables HGEDITOR, VISUAL and EDITOR, in that order. The first + non-empty one is chosen. If all of them are empty, the editor defaults to 'vi'. PYTHONPATH::
--- a/mercurial/hg.py Wed Apr 09 15:27:57 2008 -0700 +++ b/mercurial/hg.py Wed Apr 09 15:28:30 2008 -0700 @@ -6,8 +6,6 @@ # This software may be used and distributed according to the terms # of the GNU General Public License, incorporated herein by reference. -from node import * -from repo import * from i18n import _ import localrepo, bundlerepo, httprepo, sshrepo, statichttprepo import errno, lock, os, shutil, util, extensions @@ -105,20 +103,22 @@ destination is local repository """ - origsource = source - source, rev, checkout = parseurl(ui.expandpath(source), rev) - if isinstance(source, str): + origsource = ui.expandpath(source) + source, rev, checkout = parseurl(origsource, rev) src_repo = repository(ui, source) else: src_repo = source - source = src_repo.url() + origsource = source = src_repo.url() + checkout = None if dest is None: dest = defaultdest(source) ui.status(_("destination directory: %s\n") % dest) def localpath(path): + if path.startswith('file://localhost/'): + return path[16:] if path.startswith('file://'): return path[7:] if path.startswith('file:'): @@ -148,7 +148,7 @@ abspath = origsource copy = False - if src_repo.local() and islocal(dest): + if src_repo.cancopy() and islocal(dest): abspath = os.path.abspath(util.drop_scheme('file', origsource)) copy = not pull and not rev @@ -164,11 +164,10 @@ if copy: def force_copy(src, dst): - try: - util.copyfiles(src, dst) - except OSError, inst: - if inst.errno != errno.ENOENT: - raise + if not os.path.exists(src): + # Tolerate empty source repository and optional files + return + util.copyfiles(src, dst) src_store = os.path.realpath(src_repo.spath) if not os.path.exists(dest): @@ -244,6 +243,7 @@ fp.close() if update: + dest_repo.ui.status(_("updating working directory\n")) if not checkout: try: checkout = dest_repo.lookup("default")
--- a/mercurial/hgweb/common.py Wed Apr 09 15:27:57 2008 -0700 +++ b/mercurial/hgweb/common.py Wed Apr 09 15:28:30 2008 -0700 @@ -8,6 +8,11 @@ import errno, mimetypes, os +HTTP_OK = 200 +HTTP_BAD_REQUEST = 400 +HTTP_NOT_FOUND = 404 +HTTP_SERVER_ERROR = 500 + class ErrorResponse(Exception): def __init__(self, code, message=None): Exception.__init__(self) @@ -21,7 +26,7 @@ from BaseHTTPServer import BaseHTTPRequestHandler responses = BaseHTTPRequestHandler.responses return responses.get(code, ('Error', 'Unknown error'))[0] - + def statusmessage(code): return '%d %s' % (code, _statusmessage(code)) @@ -36,11 +41,11 @@ return os.stat(store_path).st_mtime def staticfile(directory, fname, req): - """return a file inside directory with guessed content-type header + """return a file inside directory with guessed Content-Type header fname always uses '/' as directory separator and isn't allowed to contain unusual path components. - Content-type is guessed using the mimetypes module. + Content-Type is guessed using the mimetypes module. Return an empty string if fname is illegal or file not found. """ @@ -54,16 +59,15 @@ try: os.stat(path) ct = mimetypes.guess_type(path)[0] or "text/plain" - req.header([('Content-type', ct), - ('Content-length', str(os.path.getsize(path)))]) + req.respond(HTTP_OK, ct, length = os.path.getsize(path)) return file(path, 'rb').read() except TypeError: - raise ErrorResponse(500, 'illegal file name') + raise ErrorResponse(HTTP_SERVER_ERROR, 'illegal file name') except OSError, err: if err.errno == errno.ENOENT: - raise ErrorResponse(404) + raise ErrorResponse(HTTP_NOT_FOUND) else: - raise ErrorResponse(500, err.strerror) + raise ErrorResponse(HTTP_SERVER_ERROR, err.strerror) def style_map(templatepath, style): """Return path to mapfile for a given style. @@ -97,3 +101,18 @@ parity = 1 - parity count = 0 +def countgen(start=0, step=1): + """count forever -- useful for line numbers""" + while True: + yield start + start += step + +def get_contact(config): + """Return repo contact information or empty string. + + web.contact is the primary source, but if that is not set, try + ui.username or $EMAIL as a fallback to display something useful. + """ + return (config("web", "contact") or + config("ui", "username") or + os.environ.get("EMAIL") or "")
--- a/mercurial/hgweb/hgweb_mod.py Wed Apr 09 15:27:57 2008 -0700 +++ b/mercurial/hgweb/hgweb_mod.py Wed Apr 09 15:28:30 2008 -0700 @@ -6,13 +6,15 @@ # This software may be used and distributed according to the terms # of the GNU General Public License, incorporated herein by reference. -import os, mimetypes, re, mimetools, cStringIO -from mercurial.node import * -from mercurial import mdiff, ui, hg, util, archival, patch -from mercurial import revlog, templater -from common import ErrorResponse, get_mtime, style_map, paritygen +import os, mimetypes +from mercurial.node import hex, nullid +from mercurial.repo import RepoError +from mercurial import mdiff, ui, hg, util, patch, hook +from mercurial import revlog, templater, templatefilters, changegroup +from common import get_mtime, style_map, paritygen, countgen, ErrorResponse +from common import HTTP_OK, HTTP_BAD_REQUEST, HTTP_NOT_FOUND, HTTP_SERVER_ERROR from request import wsgirequest -import webcommands, protocol +import webcommands, protocol, webutil shortcuts = { 'cl': [('cmd', ['changelog']), ('rev', None)], @@ -29,54 +31,6 @@ 'static': [('cmd', ['static']), ('file', None)] } -def _up(p): - if p[0] != "/": - p = "/" + p - if p[-1] == "/": - p = p[:-1] - up = os.path.dirname(p) - if up == "/": - return "/" - return up + "/" - -def revnavgen(pos, pagelen, limit, nodefunc): - def seq(factor, limit=None): - if limit: - yield limit - if limit >= 20 and limit <= 40: - yield 50 - else: - yield 1 * factor - yield 3 * factor - for f in seq(factor * 10): - yield f - - def nav(**map): - l = [] - last = 0 - for f in seq(1, pagelen): - if f < pagelen or f <= last: - continue - if f > limit: - break - last = f - if pos + f < limit: - l.append(("+%d" % f, hex(nodefunc(pos + f).node()))) - if pos - f >= 0: - l.insert(0, ("-%d" % f, hex(nodefunc(pos - f).node()))) - - try: - yield {"label": "(0)", "node": hex(nodefunc('0').node())} - - for label, node in l: - yield {"label": label, "node": node} - - yield {"label": "tip", "node": "tip"} - except hg.RepoError: - pass - - return nav - class hgweb(object): def __init__(self, repo, name=None): if isinstance(repo, str): @@ -85,10 +39,12 @@ else: self.repo = repo + hook.redirect(True) self.mtime = -1 self.reponame = name self.archives = 'zip', 'gz', 'bz2' self.stripecount = 1 + self._capabilities = None # 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", @@ -120,6 +76,18 @@ self.maxfiles = int(self.config("web", "maxfiles", 10)) self.allowpull = self.configbool("web", "allowpull", True) self.encoding = self.config("web", "encoding", util._encoding) + self._capabilities = None + + def capabilities(self): + if self._capabilities is not None: + return self._capabilities + caps = ['lookup', 'changegroupsubset'] + if self.configbool('server', 'uncompressed'): + caps.append('stream=%d' % self.repo.changelog.version) + if changegroup.bundlepriority: + caps.append('unbundle=%s' % ','.join(changegroup.bundlepriority)) + self._capabilities = caps + return caps def run(self): if not os.environ.get('GATEWAY_INTERFACE', '').startswith("CGI/1."): @@ -152,11 +120,11 @@ req.url = req.env['SCRIPT_NAME'] if not req.url.endswith('/'): req.url += '/' - if req.env.has_key('REPO_NAME'): + if 'REPO_NAME' in req.env: req.url += req.env['REPO_NAME'] + '/' - if req.env.get('PATH_INFO'): - parts = req.env.get('PATH_INFO').strip('/').split('/') + if 'PATH_INFO' in req.env: + parts = req.env['PATH_INFO'].strip('/').split('/') repo_parts = req.env.get('REPO_NAME', '').split('/') if parts[:len(repo_parts)] == repo_parts: parts = parts[len(repo_parts):] @@ -196,33 +164,51 @@ req.form['node'] = [fn[:-len(ext)]] req.form['type'] = [type_] - # actually process the request + # process this if it's a protocol request + + cmd = req.form.get('cmd', [''])[0] + if cmd in protocol.__all__: + method = getattr(protocol, cmd) + method(self, req) + return + + # process the web interface request try: - cmd = req.form.get('cmd', [''])[0] - if hasattr(protocol, cmd): - method = getattr(protocol, cmd) - method(self, req) + tmpl = self.templater(req) + ctype = tmpl('mimetype', encoding=self.encoding) + ctype = templater.stringify(ctype) + + if cmd == '': + req.form['cmd'] = [tmpl.cache['default']] + cmd = req.form['cmd'][0] + + if cmd not in webcommands.__all__: + msg = 'no such method: %s' % cmd + raise ErrorResponse(HTTP_BAD_REQUEST, msg) + elif cmd == 'file' and 'raw' in req.form.get('style', []): + self.ctype = ctype + content = webcommands.rawfile(self, req, tmpl) else: - tmpl = self.templater(req) - if cmd == '': - req.form['cmd'] = [tmpl.cache['default']] - cmd = req.form['cmd'][0] - method = getattr(webcommands, cmd) - method(self, req, tmpl) - del tmpl + content = getattr(webcommands, cmd)(self, req, tmpl) + req.respond(HTTP_OK, ctype) + + req.write(content) + del tmpl except revlog.LookupError, err: - req.respond(404, tmpl( - 'error', error='revision not found: %s' % err.name)) - except (hg.RepoError, revlog.RevlogError), inst: - req.respond('500 Internal Server Error', - tmpl('error', error=str(inst))) + req.respond(HTTP_NOT_FOUND, ctype) + msg = str(err) + if 'manifest' not in msg: + msg = 'revision not found: %s' % err.name + req.write(tmpl('error', error=msg)) + except (RepoError, revlog.RevlogError), inst: + req.respond(HTTP_SERVER_ERROR, ctype) + req.write(tmpl('error', error=str(inst))) except ErrorResponse, inst: - req.respond(inst.code, tmpl('error', error=inst.message)) - except AttributeError: - req.respond(400, tmpl('error', error='No such method: ' + cmd)) + req.respond(inst.code, ctype) + req.write(tmpl('error', error=inst.message)) def templater(self, req): @@ -247,17 +233,7 @@ # some functions for the templater def header(**map): - header_file = cStringIO.StringIO( - ''.join(tmpl("header", encoding=self.encoding, **map))) - msg = mimetools.Message(header_file, 0) - req.header(msg.items()) - yield header_file.read() - - def rawfileheader(**map): - req.header([('Content-type', map['mimetype']), - ('Content-disposition', 'filename=%s' % map['file']), - ('Content-length', str(len(map['raw'])))]) - yield '' + yield tmpl('header', encoding=self.encoding, **map) def footer(**map): yield tmpl("footer", **map) @@ -267,7 +243,7 @@ def sessionvars(**map): fields = [] - if req.form.has_key('style'): + if 'style' in req.form: style = req.form['style'][0] if style != self.config('web', 'style', ''): fields.append(('style', style)) @@ -280,7 +256,7 @@ # figure out which style to use style = self.config("web", "style", "") - if req.form.has_key('style'): + if 'style' in req.form: style = req.form['style'][0] mapfile = style_map(self.templatepath, style) @@ -291,7 +267,7 @@ # create the templater - tmpl = templater.templater(mapfile, templater.common_filters, + tmpl = templater.templater(mapfile, templatefilters.filters, defaults={"url": req.url, "staticurl": staticurl, "urlbase": urlbase, @@ -299,7 +275,6 @@ "header": header, "footer": footer, "motd": motd, - "rawfileheader": rawfileheader, "sessionvars": sessionvars }) return tmpl @@ -316,40 +291,6 @@ if len(files) > self.maxfiles: yield tmpl("fileellipses") - def siblings(self, siblings=[], hiderev=None, **args): - siblings = [s for s in siblings if s.node() != nullid] - if len(siblings) == 1 and siblings[0].rev() == hiderev: - return - for s in siblings: - d = {'node': hex(s.node()), 'rev': s.rev()} - if hasattr(s, 'path'): - d['file'] = s.path() - d.update(args) - yield d - - def renamelink(self, fl, node): - r = fl.renamed(node) - if r: - return [dict(file=r[0], node=hex(r[1]))] - return [] - - def nodetagsdict(self, node): - return [{"name": i} for i in self.repo.nodetags(node)] - - def nodebranchdict(self, ctx): - branches = [] - branch = ctx.branch() - # If this is an empty repo, ctx.node() == nullid, - # ctx.branch() == 'default', but branchtags() is - # an empty dict. Using dict.get avoids a traceback. - if self.repo.branchtags().get(branch) == ctx.node(): - branches.append({"name": branch}) - return branches - - def showtag(self, tmpl, t1, node=nullid, **args): - for t in self.repo.nodetags(node): - yield tmpl(t1, tag=t, **args) - def diff(self, tmpl, node1, node2, files): def filterfiles(filters, files): l = [x for x in files if x in filters] @@ -368,16 +309,26 @@ file=f, filenode=hex(fn or nullid)) + blockcount = countgen() def prettyprintlines(diff): - for l in diff.splitlines(1): + blockno = blockcount.next() + for lineno, l in enumerate(diff.splitlines(1)): + if blockno == 0: + lineno = lineno + 1 + else: + lineno = "%d.%d" % (blockno, lineno + 1) if l.startswith('+'): - yield tmpl("difflineplus", line=l) + ltype = "difflineplus" elif l.startswith('-'): - yield tmpl("difflineminus", line=l) + ltype = "difflineminus" elif l.startswith('@'): - yield tmpl("difflineat", line=l) + ltype = "difflineat" else: - yield tmpl("diffline", line=l) + ltype = "diffline" + yield tmpl(ltype, + line=l, + lineid="l%s" % lineno, + linenumber="% 8s" % lineno) r = self.repo c1 = r.changectx(node1) @@ -407,495 +358,12 @@ yield diffblock(mdiff.unidiff(to, date1, tn, date2, f, f, opts=diffopts), f, tn) - def changelog(self, tmpl, ctx, shortlog=False): - def changelist(limit=0,**map): - cl = self.repo.changelog - l = [] # build a list in forward order for efficiency - for i in xrange(start, end): - ctx = self.repo.changectx(i) - n = ctx.node() - - l.insert(0, {"parity": parity.next(), - "author": ctx.user(), - "parent": self.siblings(ctx.parents(), i - 1), - "child": self.siblings(ctx.children(), i + 1), - "changelogtag": self.showtag("changelogtag",n), - "desc": ctx.description(), - "date": ctx.date(), - "files": self.listfilediffs(tmpl, ctx.files(), n), - "rev": i, - "node": hex(n), - "tags": self.nodetagsdict(n), - "branches": self.nodebranchdict(ctx)}) - - if limit > 0: - l = l[:limit] - - for e in l: - yield e - - maxchanges = shortlog and self.maxshortchanges or self.maxchanges - cl = self.repo.changelog - count = cl.count() - pos = ctx.rev() - start = max(0, pos - maxchanges + 1) - end = min(count, start + maxchanges) - pos = end - 1 - parity = paritygen(self.stripecount, offset=start-end) - - changenav = revnavgen(pos, maxchanges, count, self.repo.changectx) - - yield tmpl(shortlog and 'shortlog' or 'changelog', - changenav=changenav, - node=hex(cl.tip()), - rev=pos, changesets=count, - entries=lambda **x: changelist(limit=0,**x), - latestentry=lambda **x: changelist(limit=1,**x), - archives=self.archivelist("tip")) - - def search(self, tmpl, query): - - def changelist(**map): - cl = self.repo.changelog - count = 0 - qw = query.lower().split() - - def revgen(): - for i in xrange(cl.count() - 1, 0, -100): - l = [] - for j in xrange(max(0, i - 100), i): - ctx = self.repo.changectx(j) - l.append(ctx) - l.reverse() - for e in l: - yield e - - for ctx in revgen(): - miss = 0 - for q in qw: - if not (q in ctx.user().lower() or - q in ctx.description().lower() or - q in " ".join(ctx.files()).lower()): - miss = 1 - break - if miss: - continue - - count += 1 - n = ctx.node() - - yield tmpl('searchentry', - parity=parity.next(), - author=ctx.user(), - parent=self.siblings(ctx.parents()), - child=self.siblings(ctx.children()), - changelogtag=self.showtag("changelogtag",n), - desc=ctx.description(), - date=ctx.date(), - files=self.listfilediffs(tmpl, ctx.files(), n), - rev=ctx.rev(), - node=hex(n), - tags=self.nodetagsdict(n), - branches=self.nodebranchdict(ctx)) - - if count >= self.maxchanges: - break - - cl = self.repo.changelog - parity = paritygen(self.stripecount) - - yield tmpl('search', - query=query, - node=hex(cl.tip()), - entries=changelist, - archives=self.archivelist("tip")) - - def changeset(self, tmpl, ctx): - n = ctx.node() - parents = ctx.parents() - p1 = parents[0].node() - - files = [] - parity = paritygen(self.stripecount) - for f in ctx.files(): - files.append(tmpl("filenodelink", - node=hex(n), file=f, - parity=parity.next())) - - def diff(**map): - yield self.diff(tmpl, p1, n, None) - - yield tmpl('changeset', - diff=diff, - rev=ctx.rev(), - node=hex(n), - parent=self.siblings(parents), - child=self.siblings(ctx.children()), - changesettag=self.showtag("changesettag",n), - author=ctx.user(), - desc=ctx.description(), - date=ctx.date(), - files=files, - archives=self.archivelist(hex(n)), - tags=self.nodetagsdict(n), - branches=self.nodebranchdict(ctx)) - - def filelog(self, tmpl, fctx): - f = fctx.path() - fl = fctx.filelog() - count = fl.count() - pagelen = self.maxshortchanges - pos = fctx.filerev() - start = max(0, pos - pagelen + 1) - end = min(count, start + pagelen) - pos = end - 1 - parity = paritygen(self.stripecount, offset=start-end) - - def entries(limit=0, **map): - l = [] - - for i in xrange(start, end): - ctx = fctx.filectx(i) - n = fl.node(i) - - l.insert(0, {"parity": parity.next(), - "filerev": i, - "file": f, - "node": hex(ctx.node()), - "author": ctx.user(), - "date": ctx.date(), - "rename": self.renamelink(fl, n), - "parent": self.siblings(fctx.parents()), - "child": self.siblings(fctx.children()), - "desc": ctx.description()}) - - if limit > 0: - l = l[:limit] - - for e in l: - yield e - - nodefunc = lambda x: fctx.filectx(fileid=x) - nav = revnavgen(pos, pagelen, count, nodefunc) - yield tmpl("filelog", file=f, node=hex(fctx.node()), nav=nav, - entries=lambda **x: entries(limit=0, **x), - latestentry=lambda **x: entries(limit=1, **x)) - - def filerevision(self, tmpl, fctx): - f = fctx.path() - text = fctx.data() - fl = fctx.filelog() - n = fctx.filenode() - parity = paritygen(self.stripecount) - - mt = mimetypes.guess_type(f)[0] - rawtext = text - if util.binary(text): - mt = mt or 'application/octet-stream' - text = "(binary:%s)" % mt - mt = mt or 'text/plain' - - def lines(): - for l, t in enumerate(text.splitlines(1)): - yield {"line": t, - "linenumber": "% 6d" % (l + 1), - "parity": parity.next()} - - yield tmpl("filerevision", - file=f, - path=_up(f), - text=lines(), - raw=rawtext, - mimetype=mt, - rev=fctx.rev(), - node=hex(fctx.node()), - author=fctx.user(), - date=fctx.date(), - desc=fctx.description(), - parent=self.siblings(fctx.parents()), - child=self.siblings(fctx.children()), - rename=self.renamelink(fl, n), - permissions=fctx.manifest().flags(f)) - - def fileannotate(self, tmpl, fctx): - f = fctx.path() - n = fctx.filenode() - fl = fctx.filelog() - parity = paritygen(self.stripecount) - - def annotate(**map): - last = None - for f, l in fctx.annotate(follow=True): - fnode = f.filenode() - name = self.repo.ui.shortuser(f.user()) - - if last != fnode: - last = fnode - - yield {"parity": parity.next(), - "node": hex(f.node()), - "rev": f.rev(), - "author": name, - "file": f.path(), - "line": l} - - yield tmpl("fileannotate", - file=f, - annotate=annotate, - path=_up(f), - rev=fctx.rev(), - node=hex(fctx.node()), - author=fctx.user(), - date=fctx.date(), - desc=fctx.description(), - rename=self.renamelink(fl, n), - parent=self.siblings(fctx.parents()), - child=self.siblings(fctx.children()), - permissions=fctx.manifest().flags(f)) - - def manifest(self, tmpl, ctx, path): - mf = ctx.manifest() - node = ctx.node() - - files = {} - parity = paritygen(self.stripecount) - - if path and path[-1] != "/": - path += "/" - l = len(path) - abspath = "/" + path - - for f, n in mf.items(): - if f[:l] != path: - continue - remain = f[l:] - if "/" in remain: - short = remain[:remain.index("/") + 1] # bleah - files[short] = (f, None) - else: - short = os.path.basename(remain) - files[short] = (f, n) - - if not files: - raise ErrorResponse(404, 'Path not found: ' + path) - - def filelist(**map): - fl = files.keys() - fl.sort() - for f in fl: - full, fnode = files[f] - if not fnode: - continue - - fctx = ctx.filectx(full) - yield {"file": full, - "parity": parity.next(), - "basename": f, - "date": fctx.changectx().date(), - "size": fctx.size(), - "permissions": mf.flags(full)} - - def dirlist(**map): - fl = files.keys() - fl.sort() - for f in fl: - full, fnode = files[f] - if fnode: - continue - - yield {"parity": parity.next(), - "path": "%s%s" % (abspath, f), - "basename": f[:-1]} - - yield tmpl("manifest", - rev=ctx.rev(), - node=hex(node), - path=abspath, - up=_up(abspath), - upparity=parity.next(), - fentries=filelist, - dentries=dirlist, - archives=self.archivelist(hex(node)), - tags=self.nodetagsdict(node), - branches=self.nodebranchdict(ctx)) - - def tags(self, tmpl): - i = self.repo.tagslist() - i.reverse() - parity = paritygen(self.stripecount) - - def entries(notip=False,limit=0, **map): - count = 0 - for k, n in i: - if notip and k == "tip": - continue - if limit > 0 and count >= limit: - continue - count = count + 1 - yield {"parity": parity.next(), - "tag": k, - "date": self.repo.changectx(n).date(), - "node": hex(n)} - - yield tmpl("tags", - node=hex(self.repo.changelog.tip()), - entries=lambda **x: entries(False,0, **x), - entriesnotip=lambda **x: entries(True,0, **x), - latestentry=lambda **x: entries(True,1, **x)) - - def summary(self, tmpl): - i = self.repo.tagslist() - i.reverse() - - def tagentries(**map): - parity = paritygen(self.stripecount) - count = 0 - for k, n in i: - if k == "tip": # skip tip - continue; - - count += 1 - if count > 10: # limit to 10 tags - break; - - yield tmpl("tagentry", - parity=parity.next(), - tag=k, - node=hex(n), - date=self.repo.changectx(n).date()) - - - def branches(**map): - parity = paritygen(self.stripecount) - - b = self.repo.branchtags() - l = [(-self.repo.changelog.rev(n), n, t) for t, n in b.items()] - l.sort() - - for r,n,t in l: - ctx = self.repo.changectx(n) - - yield {'parity': parity.next(), - 'branch': t, - 'node': hex(n), - 'date': ctx.date()} - - def changelist(**map): - parity = paritygen(self.stripecount, offset=start-end) - l = [] # build a list in forward order for efficiency - for i in xrange(start, end): - ctx = self.repo.changectx(i) - n = ctx.node() - hn = hex(n) - - l.insert(0, tmpl( - 'shortlogentry', - parity=parity.next(), - author=ctx.user(), - desc=ctx.description(), - date=ctx.date(), - rev=i, - node=hn, - tags=self.nodetagsdict(n), - branches=self.nodebranchdict(ctx))) - - yield l - - cl = self.repo.changelog - count = cl.count() - start = max(0, count - self.maxchanges) - end = min(count, start + self.maxchanges) - - yield tmpl("summary", - desc=self.config("web", "description", "unknown"), - owner=(self.config("ui", "username") or # preferred - self.config("web", "contact") or # deprecated - self.config("web", "author", "unknown")), # also - lastchange=cl.read(cl.tip())[2], - tags=tagentries, - branches=branches, - shortlog=changelist, - node=hex(cl.tip()), - archives=self.archivelist("tip")) - - def filediff(self, tmpl, fctx): - n = fctx.node() - path = fctx.path() - parents = fctx.parents() - p1 = parents and parents[0].node() or nullid - - def diff(**map): - yield self.diff(tmpl, p1, n, [path]) - - yield tmpl("filediff", - file=path, - node=hex(n), - rev=fctx.rev(), - parent=self.siblings(parents), - child=self.siblings(fctx.children()), - diff=diff) - archive_specs = { 'bz2': ('application/x-tar', 'tbz2', '.tar.bz2', None), 'gz': ('application/x-tar', 'tgz', '.tar.gz', None), 'zip': ('application/zip', 'zip', '.zip', None), } - def archive(self, tmpl, req, key, type_): - reponame = re.sub(r"\W+", "-", os.path.basename(self.reponame)) - cnode = self.repo.lookup(key) - arch_version = key - if cnode == key or key == 'tip': - arch_version = short(cnode) - name = "%s-%s" % (reponame, arch_version) - mimetype, artype, extension, encoding = self.archive_specs[type_] - headers = [('Content-type', mimetype), - ('Content-disposition', 'attachment; filename=%s%s' % - (name, extension))] - if encoding: - headers.append(('Content-encoding', encoding)) - req.header(headers) - archival.archive(self.repo, req.out, cnode, artype, prefix=name) - - # add tags to things - # tags -> list of changesets corresponding to tags - # find tag, changeset, file - - def cleanpath(self, path): - path = path.lstrip('/') - return util.canonpath(self.repo.root, '', path) - - def changectx(self, req): - if req.form.has_key('node'): - changeid = req.form['node'][0] - elif req.form.has_key('manifest'): - changeid = req.form['manifest'][0] - else: - changeid = self.repo.changelog.count() - 1 - - try: - ctx = self.repo.changectx(changeid) - except hg.RepoError: - man = self.repo.manifest - mn = man.lookup(changeid) - ctx = self.repo.changectx(man.linkrev(mn)) - - return ctx - - def filectx(self, req): - path = self.cleanpath(req.form['file'][0]) - if req.form.has_key('node'): - changeid = req.form['node'][0] - else: - changeid = req.form['filenode'][0] - try: - ctx = self.repo.changectx(changeid) - fctx = ctx.filectx(path) - except hg.RepoError: - fctx = self.repo.filectx(path, fileid=changeid) - - return fctx - def check_perm(self, req, op, default): '''check permission for operation based on user auth. return true if op allowed, else false.
--- a/mercurial/hgweb/hgwebdir_mod.py Wed Apr 09 15:27:57 2008 -0700 +++ b/mercurial/hgweb/hgwebdir_mod.py Wed Apr 09 15:28:30 2008 -0700 @@ -6,10 +6,12 @@ # This software may be used and distributed according to the terms # of the GNU General Public License, incorporated herein by reference. -import os, mimetools, cStringIO +import os from mercurial.i18n import gettext as _ -from mercurial import ui, hg, util, templater -from common import ErrorResponse, get_mtime, staticfile, style_map, paritygen +from mercurial.repo import RepoError +from mercurial import ui, hg, util, templater, templatefilters +from common import ErrorResponse, get_mtime, staticfile, style_map, paritygen,\ + get_contact, HTTP_OK, HTTP_NOT_FOUND, HTTP_SERVER_ERROR from hgweb_mod import hgweb from request import wsgirequest @@ -26,6 +28,7 @@ self.style = None self.stripecount = None self.repos_sorted = ('name', False) + self._baseurl = None if isinstance(config, (list, tuple)): self.repos = cleannames(config) self.repos_sorted = ('', False) @@ -46,11 +49,13 @@ self.style = cp.get('web', 'style') if cp.has_option('web', 'stripes'): self.stripecount = int(cp.get('web', 'stripes')) + if cp.has_option('web', 'baseurl'): + self._baseurl = cp.get('web', 'baseurl') if cp.has_section('paths'): self.repos.extend(cleannames(cp.items('paths'))) if cp.has_section('collections'): for prefix, root in cp.items('collections'): - for path in util.walkrepos(root): + for path in util.walkrepos(root, followsym=True): repo = os.path.normpath(path) name = repo if name.startswith(prefix): @@ -75,7 +80,10 @@ try: virtual = req.env.get("PATH_INFO", "").strip('/') - + tmpl = self.templater(req) + ctype = tmpl('mimetype', encoding=util._encoding) + ctype = templater.stringify(ctype) + # a static file if virtual.startswith('static/') or 'static' in req.form: static = os.path.join(templater.templatepath(), 'static') @@ -88,11 +96,12 @@ # top-level index elif not virtual: - tmpl = self.templater(req) - self.makeindex(req, tmpl) + req.respond(HTTP_OK, ctype) + req.write(self.makeindex(req, tmpl)) return # nested indexes and hgwebs + repos = dict(self.repos) while virtual: real = repos.get(virtual) @@ -103,15 +112,16 @@ hgweb(repo).run_wsgi(req) return except IOError, inst: - raise ErrorResponse(500, inst.strerror) - except hg.RepoError, inst: - raise ErrorResponse(500, str(inst)) + msg = inst.strerror + raise ErrorResponse(HTTP_SERVER_ERROR, msg) + except RepoError, inst: + raise ErrorResponse(HTTP_SERVER_ERROR, str(inst)) # browse subdirectories subdir = virtual + '/' if [r for r in repos if r.startswith(subdir)]: - tmpl = self.templater(req) - self.makeindex(req, tmpl, subdir) + req.respond(HTTP_OK, ctype) + req.write(self.makeindex(req, tmpl, subdir)) return up = virtual.rfind('/') @@ -120,12 +130,12 @@ virtual = virtual[:up] # prefixes not found - tmpl = self.templater(req) - req.respond(404, tmpl("notfound", repo=virtual)) - + req.respond(HTTP_NOT_FOUND, ctype) + req.write(tmpl("notfound", repo=virtual)) + except ErrorResponse, err: - tmpl = self.templater(req) - req.respond(err.code, tmpl('error', error=err.message or '')) + req.respond(err.code, ctype) + req.write(tmpl('error', error=err.message or '')) finally: tmpl = None @@ -142,7 +152,7 @@ def entries(sortcolumn="", descending=False, subdir="", **map): def sessionvars(**map): fields = [] - if req.form.has_key('style'): + if 'style' in req.form: style = req.form['style'][0] if style != get('web', 'style', ''): fields.append(('style', style)) @@ -171,9 +181,11 @@ if u.configbool("web", "hidden", untrusted=True): continue - parts = [req.env['PATH_INFO'], name] + parts = [name] + if 'PATH_INFO' in req.env: + parts.insert(0, req.env['PATH_INFO'].rstrip('/')) if req.env['SCRIPT_NAME']: - parts.insert(0, req.env['SCRIPT_NAME']) + parts.insert(0, req.env['SCRIPT_NAME']) url = ('/'.join(parts).replace("//", "/")) + '/' # update time with local timezone @@ -182,9 +194,7 @@ except OSError: continue - contact = (get("ui", "username") or # preferred - get("web", "contact") or # deprecated - get("web", "author", "")) # also + contact = get_contact(get) description = get("web", "description", "") name = get("web", "name", name) row = dict(contact=contact or "unknown", @@ -215,7 +225,7 @@ sortable = ["name", "description", "contact", "lastchange"] sortcolumn, descending = self.repos_sorted - if req.form.has_key('sort'): + if 'sort' in req.form: sortcolumn = req.form['sort'][0] descending = sortcolumn.startswith('-') if descending: @@ -227,18 +237,18 @@ "%s%s" % ((not descending and column == sortcolumn) and "-" or "", column)) for column in sortable] - req.write(tmpl("index", entries=entries, subdir=subdir, - sortcolumn=sortcolumn, descending=descending, - **dict(sort))) + + if self._baseurl is not None: + req.env['SCRIPT_NAME'] = self._baseurl + + return tmpl("index", entries=entries, subdir=subdir, + sortcolumn=sortcolumn, descending=descending, + **dict(sort)) def templater(self, req): def header(**map): - header_file = cStringIO.StringIO( - ''.join(tmpl("header", encoding=util._encoding, **map))) - msg = mimetools.Message(header_file, 0) - req.header(msg.items()) - yield header_file.read() + yield tmpl('header', encoding=util._encoding, **map) def footer(**map): yield tmpl("footer", **map) @@ -252,6 +262,9 @@ def config(section, name, default=None, untrusted=True): return self.parentui.config(section, name, default, untrusted) + if self._baseurl is not None: + req.env['SCRIPT_NAME'] = self._baseurl + url = req.env.get('SCRIPT_NAME', '') if not url.endswith('/'): url += '/' @@ -263,12 +276,12 @@ style = self.style if style is None: style = config('web', 'style', '') - if req.form.has_key('style'): + if 'style' in req.form: style = req.form['style'][0] if self.stripecount is None: self.stripecount = int(config('web', 'stripes', 1)) mapfile = style_map(templater.templatepath(), style) - tmpl = templater.templater(mapfile, templater.common_filters, + tmpl = templater.templater(mapfile, templatefilters.filters, defaults={"header": header, "footer": footer, "motd": motd,
--- a/mercurial/hgweb/protocol.py Wed Apr 09 15:27:57 2008 -0700 +++ b/mercurial/hgweb/protocol.py Wed Apr 09 15:28:30 2008 -0700 @@ -5,10 +5,21 @@ # This software may be used and distributed according to the terms # of the GNU General Public License, incorporated herein by reference. -import cStringIO, zlib, bz2, tempfile, errno, os, sys +import cStringIO, zlib, tempfile, errno, os, sys from mercurial import util, streamclone -from mercurial.i18n import gettext as _ -from mercurial.node import * +from mercurial.node import bin, hex +from mercurial import changegroup as changegroupmod +from common import HTTP_OK, HTTP_NOT_FOUND, HTTP_SERVER_ERROR + +# __all__ is populated with the allowed commands. Be sure to add to it if +# you're adding a new command, or the new command won't work. + +__all__ = [ + 'lookup', 'heads', 'branches', 'between', 'changegroup', + 'changegroupsubset', 'capabilities', 'unbundle', 'stream_out', +] + +HGTYPE = 'application/mercurial-0.1' def lookup(web, req): try: @@ -18,43 +29,43 @@ r = str(inst) success = 0 resp = "%s %s\n" % (success, r) - req.httphdr("application/mercurial-0.1", length=len(resp)) + req.respond(HTTP_OK, HGTYPE, length=len(resp)) req.write(resp) def heads(web, req): resp = " ".join(map(hex, web.repo.heads())) + "\n" - req.httphdr("application/mercurial-0.1", length=len(resp)) + req.respond(HTTP_OK, HGTYPE, length=len(resp)) req.write(resp) def branches(web, req): nodes = [] - if req.form.has_key('nodes'): + if 'nodes' in req.form: nodes = map(bin, req.form['nodes'][0].split(" ")) resp = cStringIO.StringIO() for b in web.repo.branches(nodes): resp.write(" ".join(map(hex, b)) + "\n") resp = resp.getvalue() - req.httphdr("application/mercurial-0.1", length=len(resp)) + req.respond(HTTP_OK, HGTYPE, length=len(resp)) req.write(resp) def between(web, req): - if req.form.has_key('pairs'): + if 'pairs' in req.form: pairs = [map(bin, p.split("-")) for p in req.form['pairs'][0].split(" ")] resp = cStringIO.StringIO() for b in web.repo.between(pairs): resp.write(" ".join(map(hex, b)) + "\n") resp = resp.getvalue() - req.httphdr("application/mercurial-0.1", length=len(resp)) + req.respond(HTTP_OK, HGTYPE, length=len(resp)) req.write(resp) def changegroup(web, req): - req.httphdr("application/mercurial-0.1") + req.respond(HTTP_OK, HGTYPE) nodes = [] if not web.allowpull: return - if req.form.has_key('roots'): + if 'roots' in req.form: nodes = map(bin, req.form['roots'][0].split(" ")) z = zlib.compressobj() @@ -68,15 +79,15 @@ req.write(z.flush()) def changegroupsubset(web, req): - req.httphdr("application/mercurial-0.1") + req.respond(HTTP_OK, HGTYPE) bases = [] heads = [] if not web.allowpull: return - if req.form.has_key('bases'): + if 'bases' in req.form: bases = [bin(x) for x in req.form['bases'][0].split(' ')] - if req.form.has_key('heads'): + if 'heads' in req.form: heads = [bin(x) for x in req.form['heads'][0].split(' ')] z = zlib.compressobj() @@ -90,34 +101,37 @@ req.write(z.flush()) def capabilities(web, req): - caps = ['lookup', 'changegroupsubset'] - if web.configbool('server', 'uncompressed'): - caps.append('stream=%d' % web.repo.changelog.version) - # XXX: make configurable and/or share code with do_unbundle: - unbundleversions = ['HG10GZ', 'HG10BZ', 'HG10UN'] - if unbundleversions: - caps.append('unbundle=%s' % ','.join(unbundleversions)) - resp = ' '.join(caps) - req.httphdr("application/mercurial-0.1", length=len(resp)) + resp = ' '.join(web.capabilities()) + req.respond(HTTP_OK, HGTYPE, length=len(resp)) req.write(resp) def unbundle(web, req): + def bail(response, headers={}): - length = int(req.env['CONTENT_LENGTH']) + length = int(req.env.get('CONTENT_LENGTH', 0)) for s in util.filechunkiter(req, limit=length): # drain incoming bundle, else client will not see # response when run outside cgi script pass - req.httphdr("application/mercurial-0.1", headers=headers) + + status = headers.pop('status', HTTP_OK) + req.header(headers.items()) + req.respond(status, HGTYPE) req.write('0\n') req.write(response) + # enforce that you can only unbundle with POST requests + if req.env['REQUEST_METHOD'] != 'POST': + headers = {'status': '405 Method Not Allowed'} + bail('unbundle requires POST request\n', headers) + return + # require ssl by default, auth info cannot be sniffed and # replayed ssl_req = web.configbool('web', 'push_ssl', True) if ssl_req: if req.env.get('wsgi.url_scheme') != 'https': - bail(_('ssl required\n')) + bail('ssl required\n') return proto = 'https' else: @@ -125,8 +139,7 @@ # do not allow push unless explicitly allowed if not web.check_perm(req, 'push', False): - bail(_('push not authorized\n'), - headers={'status': '401 Unauthorized'}) + bail('push not authorized\n', headers={'status': '401 Unauthorized'}) return their_heads = req.form['heads'][0].split(' ') @@ -137,10 +150,10 @@ # fail early if possible if not check_heads(): - bail(_('unsynced changes\n')) + bail('unsynced changes\n') return - req.httphdr("application/mercurial-0.1") + req.respond(HTTP_OK, HGTYPE) # do not lock repo until all changegroup data is # streamed. save to temporary file. @@ -157,63 +170,40 @@ try: if not check_heads(): req.write('0\n') - req.write(_('unsynced changes\n')) + req.write('unsynced changes\n') return fp.seek(0) header = fp.read(6) - if not header.startswith("HG"): - # old client with uncompressed bundle - def generator(f): - yield header - for chunk in f: - yield chunk - elif not header.startswith("HG10"): - req.write("0\n") - req.write(_("unknown bundle version\n")) - return - elif header == "HG10GZ": - def generator(f): - zd = zlib.decompressobj() - for chunk in f: - yield zd.decompress(chunk) - elif header == "HG10BZ": - def generator(f): - zd = bz2.BZ2Decompressor() - zd.decompress("BZ") - for chunk in f: - yield zd.decompress(chunk) - elif header == "HG10UN": - def generator(f): - for chunk in f: - yield chunk - else: - req.write("0\n") - req.write(_("unknown bundle compression type\n")) - return - gen = generator(util.filechunkiter(fp, 4096)) + if header.startswith('HG') and not header.startswith('HG10'): + raise ValueError('unknown bundle version') + elif header not in changegroupmod.bundletypes: + raise ValueError('unknown bundle compression type') + gen = changegroupmod.unbundle(header, fp) # send addchangegroup output to client - old_stdout = sys.stdout - sys.stdout = cStringIO.StringIO() + oldio = sys.stdout, sys.stderr + sys.stderr = sys.stdout = cStringIO.StringIO() try: url = 'remote:%s:%s' % (proto, req.env.get('REMOTE_HOST', '')) try: - ret = web.repo.addchangegroup( - util.chunkbuffer(gen), 'serve', url) + ret = web.repo.addchangegroup(gen, 'serve', url) except util.Abort, inst: sys.stdout.write("abort: %s\n" % inst) ret = 0 finally: val = sys.stdout.getvalue() - sys.stdout = old_stdout + sys.stdout, sys.stderr = oldio req.write('%d\n' % ret) req.write(val) finally: del lock + except ValueError, inst: + req.write('0\n') + req.write(str(inst) + '\n') except (OSError, IOError), inst: req.write('0\n') filename = getattr(inst, 'filename', '') @@ -224,14 +214,15 @@ filename = '' error = getattr(inst, 'strerror', 'Unknown error') if inst.errno == errno.ENOENT: - code = 404 + code = HTTP_NOT_FOUND else: - code = 500 - req.respond(code, '%s: %s\n' % (error, filename)) + code = HTTP_SERVER_ERROR + req.respond(code) + req.write('%s: %s\n' % (error, filename)) finally: fp.close() os.unlink(tempname) def stream_out(web, req): - req.httphdr("application/mercurial-0.1") + req.respond(HTTP_OK, HGTYPE) streamclone.stream_out(web.repo, req, untrusted=True)
--- a/mercurial/hgweb/request.py Wed Apr 09 15:27:57 2008 -0700 +++ b/mercurial/hgweb/request.py Wed Apr 09 15:28:30 2008 -0700 @@ -7,7 +7,6 @@ # of the GNU General Public License, incorporated herein by reference. import socket, cgi, errno -from mercurial.i18n import gettext as _ from common import ErrorResponse, statusmessage class wsgirequest(object): @@ -17,50 +16,55 @@ raise RuntimeError("Unknown and unsupported WSGI version %d.%d" % version) self.inp = wsgienv['wsgi.input'] - self.server_write = None self.err = wsgienv['wsgi.errors'] self.threaded = wsgienv['wsgi.multithread'] self.multiprocess = wsgienv['wsgi.multiprocess'] self.run_once = wsgienv['wsgi.run_once'] self.env = wsgienv self.form = cgi.parse(self.inp, self.env, keep_blank_values=1) - self.start_response = start_response + self._start_response = start_response + self.server_write = None self.headers = [] - out = property(lambda self: self) - def __iter__(self): return iter([]) def read(self, count=-1): return self.inp.read(count) - def respond(self, status, *things): - for thing in things: - if hasattr(thing, "__iter__"): - for part in thing: - self.respond(status, part) - else: - thing = str(thing) - if self.server_write is None: - if not self.headers: - raise RuntimeError("request.write called before headers sent (%s)." % thing) - if isinstance(status, ErrorResponse): - status = statusmessage(status.code) - elif isinstance(status, int): - status = statusmessage(status) - self.server_write = self.start_response(status, - self.headers) - self.start_response = None - self.headers = [] - try: - self.server_write(thing) - except socket.error, inst: - if inst[0] != errno.ECONNRESET: - raise - - def write(self, *things): - self.respond('200 Script output follows', *things) + def respond(self, status, type=None, filename=None, length=0): + if self._start_response is not None: + + self.httphdr(type, filename, length) + if not self.headers: + raise RuntimeError("request.write called before headers sent") + + for k, v in self.headers: + if not isinstance(v, str): + raise TypeError('header value must be string: %r' % v) + + if isinstance(status, ErrorResponse): + status = statusmessage(status.code) + elif status == 200: + status = '200 Script output follows' + elif isinstance(status, int): + status = statusmessage(status) + + self.server_write = self._start_response(status, self.headers) + self._start_response = None + self.headers = [] + + def write(self, thing): + if hasattr(thing, "__iter__"): + for part in thing: + self.write(part) + else: + thing = str(thing) + try: + self.server_write(thing) + except socket.error, inst: + if inst[0] != errno.ECONNRESET: + raise def writelines(self, lines): for line in lines: @@ -72,21 +76,26 @@ def close(self): return None - def header(self, headers=[('Content-type','text/html')]): + def header(self, headers=[('Content-Type','text/html')]): self.headers.extend(headers) - def httphdr(self, type, filename=None, length=0, headers={}): + def httphdr(self, type=None, filename=None, length=0, headers={}): headers = headers.items() - headers.append(('Content-type', type)) + if type is not None: + headers.append(('Content-Type', type)) if filename: - headers.append(('Content-disposition', 'attachment; filename=%s' % - filename)) + filename = (filename.split('/')[-1] + .replace('\\', '\\\\').replace('"', '\\"')) + headers.append(('Content-Disposition', + 'inline; filename="%s"' % filename)) if length: - headers.append(('Content-length', str(length))) + headers.append(('Content-Length', str(length))) self.header(headers) def wsgiapplication(app_maker): - application = app_maker() - def run_wsgi(env, respond): - application(env, respond) - return run_wsgi + '''For compatibility with old CGI scripts. A plain hgweb() or hgwebdir() + can and should now be used as a WSGI application.''' + application = app_maker() + def run_wsgi(env, respond): + return application(env, respond) + return run_wsgi
--- a/mercurial/hgweb/server.py Wed Apr 09 15:27:57 2008 -0700 +++ b/mercurial/hgweb/server.py Wed Apr 09 15:28:30 2008 -0700 @@ -7,7 +7,8 @@ # of the GNU General Public License, incorporated herein by reference. import os, sys, errno, urllib, BaseHTTPServer, socket, SocketServer, traceback -from mercurial import ui, hg, util, templater +from mercurial import hg, util +from mercurial.repo import RepoError from hgweb_mod import hgweb from hgwebdir_mod import hgwebdir from mercurial.i18n import gettext as _ @@ -76,7 +77,7 @@ self.do_POST() def do_hgweb(self): - path_info, query = _splitURI(self.path) + path, query = _splitURI(self.path) env = {} env['GATEWAY_INTERFACE'] = 'CGI/1.1' @@ -84,8 +85,8 @@ env['SERVER_NAME'] = self.server.server_name env['SERVER_PORT'] = str(self.server.server_port) env['REQUEST_URI'] = self.path - env['SCRIPT_NAME'] = '' - env['PATH_INFO'] = path_info + env['SCRIPT_NAME'] = self.server.prefix + env['PATH_INFO'] = path[len(self.server.prefix):] env['REMOTE_HOST'] = self.client_address[0] env['REMOTE_ADDR'] = self.client_address[0] if query: @@ -197,7 +198,7 @@ def openlog(opt, default): if opt and opt != '-': - return open(opt, 'w') + return open(opt, 'a') return default if repo is None: @@ -206,6 +207,9 @@ myui = repo.ui address = myui.config("web", "address", "") port = int(myui.config("web", "port", 8000)) + prefix = myui.config("web", "prefix", "") + if prefix: + prefix = "/" + prefix.strip("/") use_ipv6 = myui.configbool("web", "ipv6") webdir_conf = myui.config("web", "webdir_conf") ssl_cert = myui.config("web", "certificate") @@ -244,17 +248,11 @@ elif repo is not None: hgwebobj = hgweb(hg.repository(repo.ui, repo.root)) else: - raise hg.RepoError(_("There is no Mercurial repository here" - " (.hg not found)")) + raise RepoError(_("There is no Mercurial repository here" + " (.hg not found)")) return hgwebobj self.application = make_handler() - addr = address - if addr in ('', '::'): - addr = socket.gethostname() - - self.addr, self.port = addr, port - if ssl_cert: try: from OpenSSL import SSL @@ -268,12 +266,16 @@ self.server_bind() self.server_activate() + self.addr, self.port = self.socket.getsockname()[0:2] + self.prefix = prefix + self.fqaddr = socket.getfqdn(address) + class IPv6HTTPServer(MercurialHTTPServer): address_family = getattr(socket, 'AF_INET6', None) def __init__(self, *args, **kwargs): if self.address_family is None: - raise hg.RepoError(_('IPv6 not available on this system')) + raise RepoError(_('IPv6 not available on this system')) super(IPv6HTTPServer, self).__init__(*args, **kwargs) if ssl_cert: @@ -287,4 +289,5 @@ else: return MercurialHTTPServer((address, port), handler) except socket.error, inst: - raise util.Abort(_('cannot start server: %s') % inst.args[1]) + raise util.Abort(_("cannot start server at '%s:%d': %s") + % (address, port, inst.args[1]))
--- a/mercurial/hgweb/webcommands.py Wed Apr 09 15:27:57 2008 -0700 +++ b/mercurial/hgweb/webcommands.py Wed Apr 09 15:28:30 2008 -0700 @@ -5,82 +5,566 @@ # This software may be used and distributed according to the terms # of the GNU General Public License, incorporated herein by reference. -import os -from mercurial import revlog -from common import staticfile +import os, mimetypes, re +import webutil +from mercurial import revlog, archival +from mercurial.node import short, hex, nullid +from mercurial.util import binary +from mercurial.repo import RepoError +from common import paritygen, staticfile, get_contact, ErrorResponse +from common import HTTP_OK, HTTP_NOT_FOUND + +# __all__ is populated with the allowed commands. Be sure to add to it if +# you're adding a new command, or the new command won't work. + +__all__ = [ + 'log', 'rawfile', 'file', 'changelog', 'shortlog', 'changeset', 'rev', + 'manifest', 'tags', 'summary', 'filediff', 'diff', 'annotate', 'filelog', + 'archive', 'static', +] def log(web, req, tmpl): - if req.form.has_key('file') and req.form['file'][0]: - filelog(web, req, tmpl) + if 'file' in req.form and req.form['file'][0]: + return filelog(web, req, tmpl) else: - changelog(web, req, tmpl) + return changelog(web, req, tmpl) + +def rawfile(web, req, tmpl): + path = webutil.cleanpath(web.repo, req.form.get('file', [''])[0]) + if not path: + content = manifest(web, req, tmpl) + req.respond(HTTP_OK, web.ctype) + return content + + try: + fctx = webutil.filectx(web.repo, req) + except revlog.LookupError, inst: + try: + content = manifest(web, req, tmpl) + req.respond(HTTP_OK, web.ctype) + return content + except ErrorResponse: + raise inst + + path = fctx.path() + text = fctx.data() + mt = mimetypes.guess_type(path)[0] + if mt is None or binary(text): + mt = mt or 'application/octet-stream' + + req.respond(HTTP_OK, mt, path, len(text)) + return [text] + +def _filerevision(web, tmpl, fctx): + f = fctx.path() + text = fctx.data() + fl = fctx.filelog() + n = fctx.filenode() + parity = paritygen(web.stripecount) + + if binary(text): + mt = mimetypes.guess_type(f)[0] or 'application/octet-stream' + text = '(binary:%s)' % mt + + def lines(): + for lineno, t in enumerate(text.splitlines(1)): + yield {"line": t, + "lineid": "l%d" % (lineno + 1), + "linenumber": "% 6d" % (lineno + 1), + "parity": parity.next()} + + return tmpl("filerevision", + file=f, + path=webutil.up(f), + text=lines(), + rev=fctx.rev(), + node=hex(fctx.node()), + author=fctx.user(), + date=fctx.date(), + desc=fctx.description(), + branch=webutil.nodebranchnodefault(fctx), + parent=webutil.siblings(fctx.parents()), + child=webutil.siblings(fctx.children()), + rename=webutil.renamelink(fctx), + permissions=fctx.manifest().flags(f)) def file(web, req, tmpl): - path = web.cleanpath(req.form.get('file', [''])[0]) + path = webutil.cleanpath(web.repo, req.form.get('file', [''])[0]) if path: try: - req.write(web.filerevision(tmpl, web.filectx(req))) - return - except revlog.LookupError: + return _filerevision(web, tmpl, webutil.filectx(web.repo, req)) + except revlog.LookupError, inst: pass - req.write(web.manifest(tmpl, web.changectx(req), path)) + try: + return manifest(web, req, tmpl) + except ErrorResponse: + raise inst + +def _search(web, tmpl, query): + + def changelist(**map): + cl = web.repo.changelog + count = 0 + qw = query.lower().split() + + def revgen(): + for i in xrange(cl.count() - 1, 0, -100): + l = [] + for j in xrange(max(0, i - 100), i + 1): + ctx = web.repo.changectx(j) + l.append(ctx) + l.reverse() + for e in l: + yield e + + for ctx in revgen(): + miss = 0 + for q in qw: + if not (q in ctx.user().lower() or + q in ctx.description().lower() or + q in " ".join(ctx.files()).lower()): + miss = 1 + break + if miss: + continue + + count = 1 + n = ctx.node() + showtags = webutil.showtag(web.repo, tmpl, 'changelogtag', n) + + yield tmpl('searchentry', + parity=parity.next(), + author=ctx.user(), + parent=webutil.siblings(ctx.parents()), + child=webutil.siblings(ctx.children()), + changelogtag=showtags, + desc=ctx.description(), + date=ctx.date(), + files=web.listfilediffs(tmpl, ctx.files(), n), + rev=ctx.rev(), + node=hex(n), + tags=webutil.nodetagsdict(web.repo, n), + inbranch=webutil.nodeinbranch(web.repo, ctx), + branches=webutil.nodebranchdict(web.repo, ctx)) + + if count >= web.maxchanges: + break + + cl = web.repo.changelog + parity = paritygen(web.stripecount) + + return tmpl('search', + query=query, + node=hex(cl.tip()), + entries=changelist, + archives=web.archivelist("tip")) def changelog(web, req, tmpl, shortlog = False): - if req.form.has_key('node'): - ctx = web.changectx(req) + if 'node' in req.form: + ctx = webutil.changectx(web.repo, req) else: - if req.form.has_key('rev'): + if 'rev' in req.form: hi = req.form['rev'][0] else: hi = web.repo.changelog.count() - 1 try: ctx = web.repo.changectx(hi) - except hg.RepoError: - req.write(web.search(tmpl, hi)) # XXX redirect to 404 page? - return + except RepoError: + return _search(web, tmpl, hi) # XXX redirect to 404 page? + + def changelist(limit=0, **map): + cl = web.repo.changelog + l = [] # build a list in forward order for efficiency + for i in xrange(start, end): + ctx = web.repo.changectx(i) + n = ctx.node() + showtags = webutil.showtag(web.repo, tmpl, 'changelogtag', n) - req.write(web.changelog(tmpl, ctx, shortlog = shortlog)) + l.insert(0, {"parity": parity.next(), + "author": ctx.user(), + "parent": webutil.siblings(ctx.parents(), i - 1), + "child": webutil.siblings(ctx.children(), i + 1), + "changelogtag": showtags, + "desc": ctx.description(), + "date": ctx.date(), + "files": web.listfilediffs(tmpl, ctx.files(), n), + "rev": i, + "node": hex(n), + "tags": webutil.nodetagsdict(web.repo, n), + "inbranch": webutil.nodeinbranch(web.repo, ctx), + "branches": webutil.nodebranchdict(web.repo, ctx) + }) + + if limit > 0: + l = l[:limit] + + for e in l: + yield e + + maxchanges = shortlog and web.maxshortchanges or web.maxchanges + cl = web.repo.changelog + count = cl.count() + pos = ctx.rev() + start = max(0, pos - maxchanges + 1) + end = min(count, start + maxchanges) + pos = end - 1 + parity = paritygen(web.stripecount, offset=start-end) + + changenav = webutil.revnavgen(pos, maxchanges, count, web.repo.changectx) + + return tmpl(shortlog and 'shortlog' or 'changelog', + changenav=changenav, + node=hex(ctx.node()), + rev=pos, changesets=count, + entries=lambda **x: changelist(limit=0,**x), + latestentry=lambda **x: changelist(limit=1,**x), + archives=web.archivelist("tip")) def shortlog(web, req, tmpl): - changelog(web, req, tmpl, shortlog = True) + return changelog(web, req, tmpl, shortlog = True) def changeset(web, req, tmpl): - req.write(web.changeset(tmpl, web.changectx(req))) + ctx = webutil.changectx(web.repo, req) + n = ctx.node() + showtags = webutil.showtag(web.repo, tmpl, 'changesettag', n) + parents = ctx.parents() + p1 = parents[0].node() + + files = [] + parity = paritygen(web.stripecount) + for f in ctx.files(): + files.append(tmpl("filenodelink", + node=hex(n), file=f, + parity=parity.next())) + + diffs = web.diff(tmpl, p1, n, None) + return tmpl('changeset', + diff=diffs, + rev=ctx.rev(), + node=hex(n), + parent=webutil.siblings(parents), + child=webutil.siblings(ctx.children()), + changesettag=showtags, + author=ctx.user(), + desc=ctx.description(), + date=ctx.date(), + files=files, + archives=web.archivelist(hex(n)), + tags=webutil.nodetagsdict(web.repo, n), + branch=webutil.nodebranchnodefault(ctx), + inbranch=webutil.nodeinbranch(web.repo, ctx), + branches=webutil.nodebranchdict(web.repo, ctx)) rev = changeset def manifest(web, req, tmpl): - req.write(web.manifest(tmpl, web.changectx(req), - web.cleanpath(req.form['path'][0]))) + ctx = webutil.changectx(web.repo, req) + path = webutil.cleanpath(web.repo, req.form.get('file', [''])[0]) + mf = ctx.manifest() + node = ctx.node() + + files = {} + parity = paritygen(web.stripecount) + + if path and path[-1] != "/": + path += "/" + l = len(path) + abspath = "/" + path + + for f, n in mf.items(): + if f[:l] != path: + continue + remain = f[l:] + if "/" in remain: + short = remain[:remain.index("/") + 1] # bleah + files[short] = (f, None) + else: + short = os.path.basename(remain) + files[short] = (f, n) + + if not files: + raise ErrorResponse(HTTP_NOT_FOUND, 'path not found: ' + path) + + def filelist(**map): + fl = files.keys() + fl.sort() + for f in fl: + full, fnode = files[f] + if not fnode: + continue + + fctx = ctx.filectx(full) + yield {"file": full, + "parity": parity.next(), + "basename": f, + "date": fctx.changectx().date(), + "size": fctx.size(), + "permissions": mf.flags(full)} + + def dirlist(**map): + fl = files.keys() + fl.sort() + for f in fl: + full, fnode = files[f] + if fnode: + continue + + yield {"parity": parity.next(), + "path": "%s%s" % (abspath, f), + "basename": f[:-1]} + + return tmpl("manifest", + rev=ctx.rev(), + node=hex(node), + path=abspath, + up=webutil.up(abspath), + upparity=parity.next(), + fentries=filelist, + dentries=dirlist, + archives=web.archivelist(hex(node)), + tags=webutil.nodetagsdict(web.repo, node), + inbranch=webutil.nodeinbranch(web.repo, ctx), + branches=webutil.nodebranchdict(web.repo, ctx)) def tags(web, req, tmpl): - req.write(web.tags(tmpl)) + i = web.repo.tagslist() + i.reverse() + parity = paritygen(web.stripecount) + + def entries(notip=False,limit=0, **map): + count = 0 + for k, n in i: + if notip and k == "tip": + continue + if limit > 0 and count >= limit: + continue + count = count + 1 + yield {"parity": parity.next(), + "tag": k, + "date": web.repo.changectx(n).date(), + "node": hex(n)} + + return tmpl("tags", + node=hex(web.repo.changelog.tip()), + entries=lambda **x: entries(False,0, **x), + entriesnotip=lambda **x: entries(True,0, **x), + latestentry=lambda **x: entries(True,1, **x)) def summary(web, req, tmpl): - req.write(web.summary(tmpl)) + i = web.repo.tagslist() + i.reverse() + + def tagentries(**map): + parity = paritygen(web.stripecount) + count = 0 + for k, n in i: + if k == "tip": # skip tip + continue + + count = 1 + if count > 10: # limit to 10 tags + break + + yield tmpl("tagentry", + parity=parity.next(), + tag=k, + node=hex(n), + date=web.repo.changectx(n).date()) + + def branches(**map): + parity = paritygen(web.stripecount) + + b = web.repo.branchtags() + l = [(-web.repo.changelog.rev(n), n, t) for t, n in b.items()] + l.sort() + + for r,n,t in l: + ctx = web.repo.changectx(n) + yield {'parity': parity.next(), + 'branch': t, + 'node': hex(n), + 'date': ctx.date()} + + def changelist(**map): + parity = paritygen(web.stripecount, offset=start-end) + l = [] # build a list in forward order for efficiency + for i in xrange(start, end): + ctx = web.repo.changectx(i) + n = ctx.node() + hn = hex(n) + + l.insert(0, tmpl( + 'shortlogentry', + parity=parity.next(), + author=ctx.user(), + desc=ctx.description(), + date=ctx.date(), + rev=i, + node=hn, + tags=webutil.nodetagsdict(web.repo, n), + inbranch=webutil.nodeinbranch(web.repo, ctx), + branches=webutil.nodebranchdict(web.repo, ctx))) + + yield l + + cl = web.repo.changelog + count = cl.count() + start = max(0, count - web.maxchanges) + end = min(count, start + web.maxchanges) + + return tmpl("summary", + desc=web.config("web", "description", "unknown"), + owner=get_contact(web.config) or "unknown", + lastchange=cl.read(cl.tip())[2], + tags=tagentries, + branches=branches, + shortlog=changelist, + node=hex(cl.tip()), + archives=web.archivelist("tip")) def filediff(web, req, tmpl): - req.write(web.filediff(tmpl, web.filectx(req))) + fctx = webutil.filectx(web.repo, req) + n = fctx.node() + path = fctx.path() + parents = fctx.parents() + p1 = parents and parents[0].node() or nullid + + diffs = web.diff(tmpl, p1, n, [path]) + return tmpl("filediff", + file=path, + node=hex(n), + rev=fctx.rev(), + date=fctx.date(), + desc=fctx.description(), + author=fctx.user(), + rename=webutil.renamelink(fctx), + branch=webutil.nodebranchnodefault(fctx), + parent=webutil.siblings(parents), + child=webutil.siblings(fctx.children()), + diff=diffs) diff = filediff def annotate(web, req, tmpl): - req.write(web.fileannotate(tmpl, web.filectx(req))) + fctx = webutil.filectx(web.repo, req) + f = fctx.path() + n = fctx.filenode() + fl = fctx.filelog() + parity = paritygen(web.stripecount) + + def annotate(**map): + last = None + if binary(fctx.data()): + mt = (mimetypes.guess_type(fctx.path())[0] + or 'application/octet-stream') + lines = enumerate([((fctx.filectx(fctx.filerev()), 1), + '(binary:%s)' % mt)]) + else: + lines = enumerate(fctx.annotate(follow=True, linenumber=True)) + for lineno, ((f, targetline), l) in lines: + fnode = f.filenode() + name = web.repo.ui.shortuser(f.user()) + + if last != fnode: + last = fnode + + yield {"parity": parity.next(), + "node": hex(f.node()), + "rev": f.rev(), + "author": name, + "file": f.path(), + "targetline": targetline, + "line": l, + "lineid": "l%d" % (lineno + 1), + "linenumber": "% 6d" % (lineno + 1)} + + return tmpl("fileannotate", + file=f, + annotate=annotate, + path=webutil.up(f), + rev=fctx.rev(), + node=hex(fctx.node()), + author=fctx.user(), + date=fctx.date(), + desc=fctx.description(), + rename=webutil.renamelink(fctx), + branch=webutil.nodebranchnodefault(fctx), + parent=webutil.siblings(fctx.parents()), + child=webutil.siblings(fctx.children()), + permissions=fctx.manifest().flags(f)) def filelog(web, req, tmpl): - req.write(web.filelog(tmpl, web.filectx(req))) + fctx = webutil.filectx(web.repo, req) + f = fctx.path() + fl = fctx.filelog() + count = fl.count() + pagelen = web.maxshortchanges + pos = fctx.filerev() + start = max(0, pos - pagelen + 1) + end = min(count, start + pagelen) + pos = end - 1 + parity = paritygen(web.stripecount, offset=start-end) + + def entries(limit=0, **map): + l = [] + + for i in xrange(start, end): + ctx = fctx.filectx(i) + n = fl.node(i) + + l.insert(0, {"parity": parity.next(), + "filerev": i, + "file": f, + "node": hex(ctx.node()), + "author": ctx.user(), + "date": ctx.date(), + "rename": webutil.renamelink(fctx), + "parent": webutil.siblings(fctx.parents()), + "child": webutil.siblings(fctx.children()), + "desc": ctx.description()}) + + if limit > 0: + l = l[:limit] + + for e in l: + yield e + + nodefunc = lambda x: fctx.filectx(fileid=x) + nav = webutil.revnavgen(pos, pagelen, count, nodefunc) + return tmpl("filelog", file=f, node=hex(fctx.node()), nav=nav, + entries=lambda **x: entries(limit=0, **x), + latestentry=lambda **x: entries(limit=1, **x)) + def archive(web, req, tmpl): type_ = req.form['type'][0] allowed = web.configlist("web", "allow_archive") - if (type_ in web.archives and (type_ in allowed or + key = req.form['node'][0] + + if not (type_ in web.archives and (type_ in allowed or web.configbool("web", "allow" + type_, False))): - web.archive(tmpl, req, req.form['node'][0], type_) - return + msg = 'Unsupported archive type: %s' % type_ + raise ErrorResponse(HTTP_NOT_FOUND, msg) - req.respond(400, tmpl('error', - error='Unsupported archive type: %s' % type_)) + reponame = re.sub(r"\W+", "-", os.path.basename(web.reponame)) + cnode = web.repo.lookup(key) + arch_version = key + if cnode == key or key == 'tip': + arch_version = short(cnode) + name = "%s-%s" % (reponame, arch_version) + mimetype, artype, extension, encoding = web.archive_specs[type_] + headers = [ + ('Content-Type', mimetype), + ('Content-Disposition', 'attachment; filename=%s%s' % (name, extension)) + ] + if encoding: + headers.append(('Content-Encoding', encoding)) + req.header(headers) + req.respond(HTTP_OK) + archival.archive(web.repo, req, cnode, artype, prefix=name) + return [] + def static(web, req, tmpl): fname = req.form['file'][0] @@ -89,4 +573,4 @@ static = web.config("web", "static", os.path.join(web.templatepath, "static"), untrusted=False) - req.write(staticfile(static, fname, req)) + return [staticfile(static, fname, req)]
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/mercurial/hgweb/webutil.py Wed Apr 09 15:28:30 2008 -0700 @@ -0,0 +1,143 @@ +# hgweb/webutil.py - utility library for the web interface. +# +# Copyright 21 May 2005 - (c) 2005 Jake Edge <jake@edge2.net> +# Copyright 2005-2007 Matt Mackall <mpm@selenic.com> +# +# This software may be used and distributed according to the terms +# of the GNU General Public License, incorporated herein by reference. + +import os +from mercurial.node import hex, nullid +from mercurial.repo import RepoError +from mercurial import util + +def up(p): + if p[0] != "/": + p = "/" + p + if p[-1] == "/": + p = p[:-1] + up = os.path.dirname(p) + if up == "/": + return "/" + return up + "/" + +def revnavgen(pos, pagelen, limit, nodefunc): + def seq(factor, limit=None): + if limit: + yield limit + if limit >= 20 and limit <= 40: + yield 50 + else: + yield 1 * factor + yield 3 * factor + for f in seq(factor * 10): + yield f + + def nav(**map): + l = [] + last = 0 + for f in seq(1, pagelen): + if f < pagelen or f <= last: + continue + if f > limit: + break + last = f + if pos + f < limit: + l.append(("+%d" % f, hex(nodefunc(pos + f).node()))) + if pos - f >= 0: + l.insert(0, ("-%d" % f, hex(nodefunc(pos - f).node()))) + + try: + yield {"label": "(0)", "node": hex(nodefunc('0').node())} + + for label, node in l: + yield {"label": label, "node": node} + + yield {"label": "tip", "node": "tip"} + except RepoError: + pass + + return nav + +def siblings(siblings=[], hiderev=None, **args): + siblings = [s for s in siblings if s.node() != nullid] + if len(siblings) == 1 and siblings[0].rev() == hiderev: + return + for s in siblings: + d = {'node': hex(s.node()), 'rev': s.rev()} + if hasattr(s, 'path'): + d['file'] = s.path() + d.update(args) + yield d + +def renamelink(fctx): + r = fctx.renamed() + if r: + return [dict(file=r[0], node=hex(r[1]))] + return [] + +def nodetagsdict(repo, node): + return [{"name": i} for i in repo.nodetags(node)] + +def nodebranchdict(repo, ctx): + branches = [] + branch = ctx.branch() + # If this is an empty repo, ctx.node() == nullid, + # ctx.branch() == 'default', but branchtags() is + # an empty dict. Using dict.get avoids a traceback. + if repo.branchtags().get(branch) == ctx.node(): + branches.append({"name": branch}) + return branches + +def nodeinbranch(repo, ctx): + branches = [] + branch = ctx.branch() + if branch != 'default' and repo.branchtags().get(branch) != ctx.node(): + branches.append({"name": branch}) + return branches + +def nodebranchnodefault(ctx): + branches = [] + branch = ctx.branch() + if branch != 'default': + branches.append({"name": branch}) + return branches + +def showtag(repo, tmpl, t1, node=nullid, **args): + for t in repo.nodetags(node): + yield tmpl(t1, tag=t, **args) + +def cleanpath(repo, path): + path = path.lstrip('/') + return util.canonpath(repo.root, '', path) + +def changectx(repo, req): + if 'node' in req.form: + changeid = req.form['node'][0] + elif 'manifest' in req.form: + changeid = req.form['manifest'][0] + else: + changeid = repo.changelog.count() - 1 + + try: + ctx = repo.changectx(changeid) + except RepoError: + man = repo.manifest + mn = man.lookup(changeid) + ctx = repo.changectx(man.linkrev(mn)) + + return ctx + +def filectx(repo, req): + path = cleanpath(repo, req.form['file'][0]) + if 'node' in req.form: + changeid = req.form['node'][0] + else: + changeid = req.form['filenode'][0] + try: + ctx = repo.changectx(changeid) + fctx = ctx.filectx(path) + except RepoError: + fctx = repo.filectx(path, fileid=changeid) + + return fctx
--- a/mercurial/hook.py Wed Apr 09 15:27:57 2008 -0700 +++ b/mercurial/hook.py Wed Apr 09 15:28:30 2008 -0700 @@ -6,7 +6,7 @@ # of the GNU General Public License, incorporated herein by reference. from i18n import _ -import util +import util, os, sys def _pythonhook(ui, repo, name, hname, funcname, args, throw): '''call python hook. hook is callable object, looked up as @@ -71,7 +71,11 @@ def _exthook(ui, repo, name, cmd, args, throw): ui.note(_("running hook %s: %s\n") % (name, cmd)) env = dict([('HG_' + k.upper(), v) for k, v in args.iteritems()]) - r = util.system(cmd, environ=env, cwd=repo.root) + if repo: + cwd = repo.root + else: + cwd = os.getcwd() + r = util.system(cmd, environ=env, cwd=cwd) if r: desc, r = util.explain_exit(r) if throw: @@ -79,8 +83,19 @@ ui.warn(_('warning: %s hook %s\n') % (name, desc)) return r +_redirect = False +def redirect(state): + global _redirect + _redirect = state + def hook(ui, repo, name, throw=False, **args): r = False + + if _redirect: + # temporarily redirect stdout to stderr + oldstdout = os.dup(sys.__stdout__.fileno()) + os.dup2(sys.__stderr__.fileno(), sys.__stdout__.fileno()) + hooks = [(hname, cmd) for hname, cmd in ui.configitems("hooks") if hname.split(".", 1)[0] == name and cmd] hooks.sort() @@ -92,5 +107,9 @@ args, throw) or r else: r = _exthook(ui, repo, hname, cmd, args, throw) or r + + if _redirect: + os.dup2(oldstdout, sys.__stdout__.fileno()) + os.close(oldstdout) + return r -
--- a/mercurial/httprepo.py Wed Apr 09 15:27:57 2008 -0700 +++ b/mercurial/httprepo.py Wed Apr 09 15:28:30 2008 -0700 @@ -6,11 +6,10 @@ # This software may be used and distributed according to the terms # of the GNU General Public License, incorporated herein by reference. -from node import * -from remoterepo import * +from node import bin, hex from i18n import _ import repo, os, urllib, urllib2, urlparse, zlib, util, httplib -import errno, keepalive, tempfile, socket, changegroup +import errno, keepalive, socket, changegroup class passwordmgr(urllib2.HTTPPasswordMgrWithDefaultRealm): def __init__(self, ui): @@ -103,10 +102,13 @@ # must be able to send big bundle as stream. send = _gen_sendfile(keepalive.HTTPConnection) -class basehttphandler(keepalive.HTTPHandler): +class httphandler(keepalive.HTTPHandler): def http_open(self, req): return self.do_open(httpconnection, req) + def __del__(self): + self.close_all() + has_https = hasattr(urllib2, 'HTTPSHandler') if has_https: class httpsconnection(httplib.HTTPSConnection): @@ -114,12 +116,9 @@ # must be able to send big bundle as stream. send = _gen_sendfile(httplib.HTTPSConnection) - class httphandler(basehttphandler, urllib2.HTTPSHandler): + class httpshandler(keepalive.KeepAliveHandler, urllib2.HTTPSHandler): def https_open(self, req): return self.do_open(httpsconnection, req) -else: - class httphandler(basehttphandler): - pass # In python < 2.5 AbstractDigestAuthHandler raises a ValueError if # it doesn't know about the auth type requested. This can happen if @@ -181,7 +180,7 @@ l[i] = '%%%02X' % ord(c) return ''.join(l) -class httprepository(remoterepository): +class httprepository(repo.repository): def __init__(self, ui, path): self.path = path self.caps = None @@ -203,8 +202,9 @@ proxyurl = ui.config("http_proxy", "host") or os.getenv('http_proxy') # XXX proxyauthinfo = None - self.handler = httphandler() - handlers = [self.handler] + handlers = [httphandler()] + if has_https: + handlers.append(httpshandler()) if proxyurl: # proxy can be proper url or host[:port] @@ -247,7 +247,7 @@ # will take precedence if found, so drop them for env in ["HTTP_PROXY", "http_proxy", "no_proxy"]: try: - if os.environ.has_key(env): + if env in os.environ: del os.environ[env] except OSError: pass @@ -270,11 +270,6 @@ opener.addheaders = [('User-agent', 'mercurial/proto-1.0')] urllib2.install_opener(opener) - def __del__(self): - if self.handler: - self.handler.close_all() - self.handler = None - def url(self): return self.path @@ -343,7 +338,7 @@ version = proto.split('-', 1)[1] version_info = tuple([int(n) for n in version.split('.')]) except ValueError: - raise repo.RepoError(_("'%s' sent a broken Content-type " + raise repo.RepoError(_("'%s' sent a broken Content-Type " "header (%s)") % (self._url, proto)) if version_info > (0, 1): raise repo.RepoError(_("'%s' uses newer protocol %s") % @@ -428,7 +423,7 @@ try: rfp = self.do_cmd( 'unbundle', data=fp, - headers={'content-type': 'application/octet-stream'}, + headers={'Content-Type': 'application/octet-stream'}, heads=' '.join(map(hex, heads))) try: ret = int(rfp.readline())
--- a/mercurial/keepalive.py Wed Apr 09 15:27:57 2008 -0700 +++ b/mercurial/keepalive.py Wed Apr 09 15:28:30 2008 -0700 @@ -19,6 +19,8 @@ # Modified by Benoit Boissinot: # - fix for digest auth (inspired from urllib2.py @ Python v2.4) +# Modified by Dirkjan Ochtman: +# - import md5 function from a local util module """An HTTP handler for urllib2 that supports HTTP 1.1 and keepalive. @@ -129,7 +131,7 @@ def add(self, host, connection, ready): self._lock.acquire() try: - if not self._hostmap.has_key(host): self._hostmap[host] = [] + if not host in self._hostmap: self._hostmap[host] = [] self._hostmap[host].append(connection) self._connmap[connection] = host self._readymap[connection] = ready @@ -159,7 +161,7 @@ conn = None self._lock.acquire() try: - if self._hostmap.has_key(host): + if host in self._hostmap: for c in self._hostmap[host]: if self._readymap[c]: self._readymap[c] = 0 @@ -175,7 +177,7 @@ else: return dict(self._hostmap) -class HTTPHandler(urllib2.HTTPHandler): +class KeepAliveHandler: def __init__(self): self._cm = ConnectionManager() @@ -314,6 +316,9 @@ except socket.error, err: # XXX what error? raise urllib2.URLError(err) +class HTTPHandler(KeepAliveHandler, urllib2.HTTPHandler): + pass + class HTTPResponse(httplib.HTTPResponse): # we need to subclass HTTPResponse in order to # 1) add readline() and readlines() methods @@ -447,7 +452,7 @@ keepalive_handler.close_all() def continuity(url): - import md5 + from util import md5 format = '%25s: %s' # first fetch the file with the normal http handler
--- a/mercurial/localrepo.py Wed Apr 09 15:27:57 2008 -0700 +++ b/mercurial/localrepo.py Wed Apr 09 15:28:30 2008 -0700 @@ -5,12 +5,12 @@ # This software may be used and distributed according to the terms # of the GNU General Public License, incorporated herein by reference. -from node import * +from node import bin, hex, nullid, nullrev, short from i18n import _ import repo, changegroup import changelog, dirstate, filelog, manifest, context, weakref -import re, lock, transaction, tempfile, stat, errno, ui -import os, revlog, time, util, extensions, hook +import lock, transaction, stat, errno, ui +import os, revlog, time, util, extensions, hook, inspect class localrepository(repo.repository): capabilities = util.set(('lookup', 'changegroupsubset')) @@ -68,8 +68,21 @@ self.encodefn = lambda x: x self.decodefn = lambda x: x self.spath = self.path - self.sopener = util.encodedopener(util.opener(self.spath), - self.encodefn) + + try: + # files in .hg/ will be created using this mode + mode = os.stat(self.spath).st_mode + # avoid some useless chmods + if (0777 & ~util._umask) == (0777 & mode): + mode = None + except OSError: + mode = None + + self._createmode = mode + self.opener.createmode = mode + sopener = util.opener(self.spath) + sopener.createmode = mode + self.sopener = util.encodedopener(sopener, self.encodefn) self.ui = ui.ui(parentui=parentui) try: @@ -81,8 +94,11 @@ self.tagscache = None self._tagstypecache = None self.branchcache = None + self._ubranchcache = None # UTF-8 version of branchcache + self._branchcachetip = None self.nodetagscache = None self.filterpats = {} + self._datafilters = {} self._transref = self._lockref = self._wlockref = None def __getattr__(self, name): @@ -108,22 +124,30 @@ tag_disallowed = ':\r\n' - def _tag(self, name, node, message, local, user, date, parent=None, + def _tag(self, names, node, message, local, user, date, parent=None, extra={}): use_dirstate = parent is None + if isinstance(names, str): + allchars = names + names = (names,) + else: + allchars = ''.join(names) for c in self.tag_disallowed: - if c in name: + if c in allchars: raise util.Abort(_('%r cannot be used in a tag name') % c) - self.hook('pretag', throw=True, node=hex(node), tag=name, local=local) + for name in names: + self.hook('pretag', throw=True, node=hex(node), tag=name, + local=local) - def writetag(fp, name, munge, prevtags): + def writetags(fp, names, munge, prevtags): + fp.seek(0, 2) if prevtags and prevtags[-1] != '\n': fp.write('\n') - fp.write('%s %s\n' % (hex(node), munge and munge(name) or name)) + for name in names: + fp.write('%s %s\n' % (hex(node), munge and munge(name) or name)) fp.close() - self.hook('tag', node=hex(node), tag=name, local=local) prevtags = '' if local: @@ -135,7 +159,9 @@ prevtags = fp.read() # local tags are stored in the current charset - writetag(fp, name, None, prevtags) + writetags(fp, names, None, prevtags) + for name in names: + self.hook('tag', node=hex(node), tag=name, local=local) return if use_dirstate: @@ -155,7 +181,7 @@ fp.write(prevtags) # committed tags are stored in UTF-8 - writetag(fp, name, util.fromlocal, prevtags) + writetags(fp, names, util.fromlocal, prevtags) if use_dirstate and '.hgtags' not in self.dirstate: self.add(['.hgtags']) @@ -163,20 +189,24 @@ tagnode = self.commit(['.hgtags'], message, user, date, p1=parent, extra=extra) - self.hook('tag', node=hex(node), tag=name, local=local) + for name in names: + self.hook('tag', node=hex(node), tag=name, local=local) return tagnode - def tag(self, name, node, message, local, user, date): - '''tag a revision with a symbolic name. + def tag(self, names, node, message, local, user, date): + '''tag a revision with one or more symbolic names. - if local is True, the tag is stored in a per-repository file. - otherwise, it is stored in the .hgtags file, and a new + names is a list of strings or, when adding a single tag, names may be a + string. + + if local is True, the tags are stored in a per-repository file. + otherwise, they are stored in the .hgtags file, and a new changeset is committed with the change. keyword arguments: - local: whether to store tag in non-version-controlled file + local: whether to store tags in non-version-controlled file (default False) message: commit message to use if committing @@ -190,8 +220,7 @@ raise util.Abort(_('working copy of .hgtags is changed ' '(please commit .hgtags manually)')) - - self._tag(name, node, message, local, user, date) + self._tag(names, node, message, local, user, date) def tags(self): '''return a mapping of tag to node''' @@ -288,7 +317,7 @@ ''' self.tags() - + return self._tagstypecache.get(tagname) def _hgtagsnodes(self): @@ -329,9 +358,7 @@ self.nodetagscache.setdefault(n, []).append(t) return self.nodetagscache.get(node, []) - def _branchtags(self): - partial, last, lrev = self._readbranchcache() - + def _branchtags(self, partial, lrev): tiprev = self.changelog.count() - 1 if lrev != tiprev: self._updatebranchcache(partial, lrev+1, tiprev+1) @@ -340,16 +367,29 @@ return partial def branchtags(self): - if self.branchcache is not None: + tip = self.changelog.tip() + if self.branchcache is not None and self._branchcachetip == tip: return self.branchcache - self.branchcache = {} # avoid recursion in changectx - partial = self._branchtags() + oldtip = self._branchcachetip + self._branchcachetip = tip + if self.branchcache is None: + self.branchcache = {} # avoid recursion in changectx + else: + self.branchcache.clear() # keep using the same dict + if oldtip is None or oldtip not in self.changelog.nodemap: + partial, last, lrev = self._readbranchcache() + else: + lrev = self.changelog.rev(oldtip) + partial = self._ubranchcache + + self._branchtags(partial, lrev) # the branch cache is stored on disk as UTF-8, but in the local # charset internally for k, v in partial.items(): self.branchcache[util.tolocal(k)] = v + self._ubranchcache = partial return self.branchcache def _readbranchcache(self): @@ -367,7 +407,7 @@ if not (lrev < self.changelog.count() and self.changelog.node(lrev) == last): # sanity check # invalidate the cache - raise ValueError('Invalid branch cache: unknown tip') + raise ValueError('invalidating branch cache (tip differs)') for l in lines: if not l: continue node, label = l.split(" ", 1) @@ -423,9 +463,6 @@ pass raise repo.RepoError(_("unknown revision '%s'") % key) - def dev(self): - return os.lstat(self.path).st_dev - def local(self): return True @@ -485,17 +522,33 @@ l = [] for pat, cmd in self.ui.configitems(filter): mf = util.matcher(self.root, "", [pat], [], [])[1] - l.append((mf, cmd)) + fn = None + params = cmd + for name, filterfn in self._datafilters.iteritems(): + if cmd.startswith(name): + fn = filterfn + params = cmd[len(name):].lstrip() + break + if not fn: + fn = lambda s, c, **kwargs: util.filter(s, c) + # Wrap old filters not supporting keyword arguments + if not inspect.getargspec(fn)[2]: + oldfn = fn + fn = lambda s, c, **kwargs: oldfn(s, c) + l.append((mf, fn, params)) self.filterpats[filter] = l - for mf, cmd in self.filterpats[filter]: + for mf, fn, cmd in self.filterpats[filter]: if mf(filename): self.ui.debug(_("filtering %s through %s\n") % (filename, cmd)) - data = util.filter(data, cmd) + data = fn(data, cmd, ui=self.ui, repo=self, filename=filename) break return data + def adddatafilter(self, name, filter): + self._datafilters[name] = filter + def wread(self, filename): if self._link(filename): data = os.readlink(self.wjoin(filename)) @@ -505,16 +558,12 @@ def wwrite(self, filename, data, flags): data = self._filter("decode", filename, data) - if "l" in flags: - self.wopener.symlink(data, filename) - else: - try: - if self._link(filename): - os.unlink(self.wjoin(filename)) - except OSError: - pass - self.wopener(filename, 'w').write(data) - util.set_exec(self.wjoin(filename), "x" in flags) + try: + os.unlink(self.wjoin(filename)) + except OSError: + pass + self.wopener(filename, 'w').write(data) + util.set_flags(self.wjoin(filename), flags) def wwritedata(self, filename, data): return self._filter("decode", filename, data) @@ -523,18 +572,25 @@ if self._transref and self._transref(): return self._transref().nest() + # abort here if the journal already exists + if os.path.exists(self.sjoin("journal")): + raise repo.RepoError(_("journal already exists - run hg recover")) + # save dirstate for rollback try: ds = self.opener("dirstate").read() except IOError: ds = "" self.opener("journal.dirstate", "w").write(ds) + self.opener("journal.branch", "w").write(self.dirstate.branch()) renames = [(self.sjoin("journal"), self.sjoin("undo")), - (self.join("journal.dirstate"), self.join("undo.dirstate"))] + (self.join("journal.dirstate"), self.join("undo.dirstate")), + (self.join("journal.branch"), self.join("undo.branch"))] tr = transaction.transaction(self.ui.warn, self.sopener, - self.sjoin("journal"), - aftertrans(renames)) + self.sjoin("journal"), + aftertrans(renames), + self._createmode) self._transref = weakref.ref(tr) return tr @@ -561,6 +617,13 @@ self.ui.status(_("rolling back last transaction\n")) transaction.rollback(self.sopener, self.sjoin("undo")) util.rename(self.join("undo.dirstate"), self.join("dirstate")) + try: + branch = self.opener("undo.branch").read() + self.dirstate.setbranch(branch) + except IOError: + self.ui.warn(_("Named branch could not be reset, " + "current branch still is: %s\n") + % util.tolocal(self.dirstate.branch())) self.invalidate() self.dirstate.invalidate() else: @@ -570,11 +633,14 @@ def invalidate(self): for a in "changelog manifest".split(): - if hasattr(self, a): - self.__delattr__(a) + if a in self.__dict__: + delattr(self, a) self.tagscache = None self._tagstypecache = None self.nodetagscache = None + self.branchcache = None + self._ubranchcache = None + self._branchcachetip = None def _lock(self, lockname, wait, releasefn, acquirefn, desc): try: @@ -683,7 +749,11 @@ p1=None, p2=None, extra={}, empty_ok=False): wlock = lock = tr = None valid = 0 # don't save the dirstate if this isn't set + if files: + files = util.unique(files) try: + wlock = self.wlock() + lock = self.lock() commit = [] remove = [] changed = [] @@ -711,6 +781,11 @@ if use_dirstate: p1, p2 = self.dirstate.parents() update_dirstate = True + + if (not force and p2 != nullid and + (files or match != util.always)): + raise util.Abort(_('cannot partially commit a merge ' + '(do not specify files or patterns)')) else: p1, p2 = p1, p2 or nullid update_dirstate = (self.dirstate.parents()[0] == p1) @@ -742,8 +817,6 @@ self.hook("precommit", throw=True, parent1=xp1, parent2=xp2) - wlock = self.wlock() - lock = self.lock() tr = self.transaction() trp = weakref.proxy(tr) @@ -803,11 +876,14 @@ if text: edittext.append(text) edittext.append("") + edittext.append(_("HG: Enter commit message." + " Lines beginning with 'HG:' are removed.")) + edittext.append("HG: --") edittext.append("HG: user: %s" % user) if p2 != nullid: edittext.append("HG: branch merge") if branchname: - edittext.append("HG: branch %s" % util.tolocal(branchname)) + edittext.append("HG: branch '%s'" % util.tolocal(branchname)) edittext.extend(["HG: changed %s" % f for f in changed]) edittext.extend(["HG: removed %s" % f for f in removed]) if not changed and not remove: @@ -822,13 +898,12 @@ if branchname: extra["branch"] = branchname - if use_dirstate: - lines = [line.rstrip() for line in text.rstrip().splitlines()] - while lines and not lines[0]: - del lines[0] - if not lines: - return None - text = '\n'.join(lines) + lines = [line.rstrip() for line in text.rstrip().splitlines()] + while lines and not lines[0]: + del lines[0] + if not lines and use_dirstate: + raise util.Abort(_("empty commit message")) + text = '\n'.join(lines) n = self.changelog.add(mn, changed + removed, text, trp, p1, p2, user, date, extra) @@ -836,8 +911,8 @@ parent2=xp2) tr.close() - if self.branchcache and "branch" in extra: - self.branchcache[util.tolocal(extra["branch"])] = n + if self.branchcache: + self.branchtags() if use_dirstate or update_dirstate: self.dirstate.setparents(n) @@ -896,7 +971,7 @@ yield src, fn def status(self, node1=None, node2=None, files=[], match=util.always, - list_ignored=False, list_clean=False): + list_ignored=False, list_clean=False, list_unknown=True): """return status of files between two nodes or node and working directory If node1 is None, use the first dirstate parent instead. @@ -932,7 +1007,8 @@ if not node2: (lookup, modified, added, removed, deleted, unknown, ignored, clean) = self.dirstate.status(files, match, - list_ignored, list_clean) + list_ignored, list_clean, + list_unknown) # are we comparing working dir against its parent? if compareworking: @@ -940,8 +1016,15 @@ fixup = [] # do a full compare of any files that might have changed ctx = self.changectx() + mexec = lambda f: 'x' in ctx.fileflags(f) + mlink = lambda f: 'l' in ctx.fileflags(f) + is_exec = util.execfunc(self.root, mexec) + is_link = util.linkfunc(self.root, mlink) + def flags(f): + return is_link(f) and 'l' or is_exec(f) and 'x' or '' for f in lookup: - if f not in ctx or ctx[f].cmp(self.wread(f)): + if (f not in ctx or flags(f) != ctx.fileflags(f) + or ctx[f].cmp(self.wread(f))): modified.append(f) else: fixup.append(f) @@ -989,7 +1072,7 @@ mf2keys.sort() getnode = lambda fn: mf1.get(fn, nullid) for fn in mf2keys: - if mf1.has_key(fn): + if fn in mf1: if (mf1.flags(fn) != mf2.flags(fn) or (mf1[fn] != mf2[fn] and (mf2[fn] != "" or fcmp(fn, getnode)))): @@ -1010,12 +1093,14 @@ def add(self, list): wlock = self.wlock() try: + rejected = [] for f in list: p = self.wjoin(f) try: st = os.lstat(p) except: self.ui.warn(_("%s does not exist!\n") % f) + rejected.append(f) continue if st.st_size > 10000000: self.ui.warn(_("%s: files over 10MB may cause memory and" @@ -1025,12 +1110,14 @@ if not (stat.S_ISREG(st.st_mode) or stat.S_ISLNK(st.st_mode)): self.ui.warn(_("%s not added: only files and symlinks " "supported currently\n") % f) + rejected.append(p) elif self.dirstate[f] in 'amn': self.ui.warn(_("%s already tracked!\n") % f) elif self.dirstate[f] == 'r': self.dirstate.normallookup(f) else: self.dirstate.add(f) + return rejected finally: del wlock @@ -1453,10 +1540,10 @@ warn = 1 if warn: - self.ui.warn(_("abort: push creates new remote branches!\n")) + self.ui.warn(_("abort: push creates new remote heads!\n")) self.ui.status(_("(did you forget to merge?" " use push -f to force)\n")) - return None, 1 + return None, 0 elif inc: self.ui.warn(_("note: unsynced remote changes!\n")) @@ -1491,14 +1578,15 @@ return remote.unbundle(cg, remote_heads, 'push') return ret[1] - def changegroupinfo(self, nodes): - self.ui.note(_("%d changesets found\n") % len(nodes)) + def changegroupinfo(self, nodes, source): + if self.ui.verbose or source == 'bundle': + self.ui.status(_("%d changesets found\n") % len(nodes)) if self.ui.debugflag: self.ui.debug(_("List of changesets:\n")) for node in nodes: self.ui.debug("%s\n" % hex(node)) - def changegroupsubset(self, bases, heads, source): + def changegroupsubset(self, bases, heads, source, extranodes=None): """This function generates a changegroup consisting of all the nodes that are descendents of any of the bases, and ancestors of any of the heads. @@ -1508,7 +1596,15 @@ is non-trivial. Another wrinkle is doing the reverse, figuring out which changeset in - the changegroup a particular filenode or manifestnode belongs to.""" + the changegroup a particular filenode or manifestnode belongs to. + + The caller can specify some nodes that must be included in the + changegroup using the extranodes argument. It should be a dict + where the keys are the filenames (or 1 for the manifest), and the + values are lists of (node, linknode) tuples, where node is a wanted + node and linknode is the changelog node that should be transmitted as + the linkrev. + """ self.hook('preoutgoing', throw=True, source=source) @@ -1518,7 +1614,7 @@ # msng is short for missing - compute the list of changesets in this # changegroup. msng_cl_lst, bases, heads = cl.nodesbetween(bases, heads) - self.changegroupinfo(msng_cl_lst) + self.changegroupinfo(msng_cl_lst, source) # Some bases may turn out to be superfluous, and some heads may be # too. nodesbetween will return the minimal set of bases and heads # necessary to re-create the changegroup. @@ -1701,6 +1797,15 @@ return msngset[fnode] return lookup_filenode_link + # Add the nodes that were explicitly requested. + def add_extra_nodes(name, nodes): + if not extranodes or name not in extranodes: + return + + for node, linknode in extranodes[name]: + if node not in nodes: + nodes[node] = linknode + # Now that we have all theses utility functions to help out and # logically divide up the task, generate the group. def gengroup(): @@ -1716,6 +1821,7 @@ # The list of manifests has been collected by the generator # calling our functions back. prune_manifests() + add_extra_nodes(1, msng_mnfst_set) msng_mnfst_lst = msng_mnfst_set.keys() # Sort the manifestnodes by revision number. msng_mnfst_lst.sort(cmp_by_rev_func(mnfst)) @@ -1731,6 +1837,13 @@ msng_mnfst_lst = None msng_mnfst_set.clear() + if extranodes: + for fname in extranodes: + if isinstance(fname, int): + continue + add_extra_nodes(fname, + msng_filenode_set.setdefault(fname, {})) + changedfiles[fname] = 1 changedfiles = changedfiles.keys() changedfiles.sort() # Go through all our files in order sorted by name. @@ -1740,7 +1853,7 @@ raise util.Abort(_("empty or missing revlog for %s") % fname) # Toss out the filenodes that the recipient isn't really # missing. - if msng_filenode_set.has_key(fname): + if fname in msng_filenode_set: prune_filenodes(fname, filerevlog) msng_filenode_lst = msng_filenode_set[fname].keys() else: @@ -1759,7 +1872,7 @@ lookup_filenode_link_func(fname)) for chnk in group: yield chnk - if msng_filenode_set.has_key(fname): + if fname in msng_filenode_set: # Don't need this anymore, toss it to free memory. del msng_filenode_set[fname] # Signal that no more groups are left. @@ -1782,7 +1895,7 @@ cl = self.changelog nodes = cl.nodesbetween(basenodes, None)[0] revset = dict.fromkeys([cl.rev(n) for n in nodes]) - self.changegroupinfo(nodes) + self.changegroupinfo(nodes, source) def identity(x): return x @@ -1840,7 +1953,7 @@ return util.chunkbuffer(gengroup()) - def addchangegroup(self, source, srctype, url): + def addchangegroup(self, source, srctype, url, emptyok=False): """add changegroup to repo. return values: @@ -1876,7 +1989,7 @@ self.ui.status(_("adding changesets\n")) cor = cl.count() - 1 chunkiter = changegroup.chunkiter(source) - if cl.addgroup(chunkiter, csmap, trp, 1) is None: + if cl.addgroup(chunkiter, csmap, trp, 1) is None and not emptyok: raise util.Abort(_("received changelog group is empty")) cnr = cl.count() - 1 changesets = cnr - cor @@ -1927,6 +2040,9 @@ del tr if changesets > 0: + # forcefully update the on-disk branch cache + self.ui.debug(_("updating the branch cache\n")) + self.branchtags() self.hook("changegroup", node=hex(self.changelog.node(cor+1)), source=srctype, url=url) @@ -1959,7 +2075,7 @@ l = fp.readline() try: total_files, total_bytes = map(int, l.split(' ', 1)) - except ValueError, TypeError: + except (ValueError, TypeError): raise util.UnexpectedOutput( _('Unexpected response from remote server:'), l) self.ui.status(_('%d files to transfer, %s of data\n') %
--- a/mercurial/lsprof.py Wed Apr 09 15:27:57 2008 -0700 +++ b/mercurial/lsprof.py Wed Apr 09 15:28:30 2008 -0700 @@ -1,28 +1,19 @@ -# this is copied from the lsprof distro because somehow -# it is not installed by distutils -# -# small modifications made +#! /usr/bin/env python import sys -try: - from _lsprof import Profiler, profiler_entry, profiler_subentry -except ImportError, inst: - import packagescan - if packagescan.scan_in_progress: - raise packagescan.SkipPackage('_lsprof not available') - raise +from _lsprof import Profiler, profiler_entry __all__ = ['profile', 'Stats'] def profile(f, *args, **kwds): """XXX docstring""" p = Profiler() - p.enable(subcalls=True) + p.enable(subcalls=True, builtins=True) try: - ret = f(*args, **kwds) + f(*args, **kwds) finally: p.disable() - return ret, Stats(p.getstats()) + return Stats(p.getstats()) class Stats(object): @@ -49,14 +40,14 @@ d = self.data if top is not None: d = d[:top] - cols = "% 12s %11.4f %11.4f %s\n" - hcols = "% 12s %12s %12s %s\n" - cols2 = "+%12s %11.4f %11.4f + %s\n" - file.write(hcols % ("CallCount", "Total(s)", - "Inline(s)", "module:lineno(function)")) + cols = "% 12s %12s %11.4f %11.4f %s\n" + hcols = "% 12s %12s %12s %12s %s\n" + cols2 = "+%12s %12s %11.4f %11.4f + %s\n" + file.write(hcols % ("CallCount", "Recursive", "Total(ms)", + "Inline(ms)", "module:lineno(function)")) count = 0 for e in d: - file.write(cols % (e.callcount, e.totaltime, + file.write(cols % (e.callcount, e.reccallcount, e.totaltime, e.inlinetime, label(e.code))) count += 1 if limit is not None and count == limit: @@ -64,7 +55,7 @@ ccount = 0 if e.calls: for se in e.calls: - file.write(cols % ("+%s" % se.callcount, + file.write(cols % ("+%s" % se.callcount, se.reccallcount, se.totaltime, se.inlinetime, "+%s" % label(se.code))) count += 1 @@ -83,11 +74,11 @@ e = self.data[i] if not isinstance(e.code, str): self.data[i] = type(e)((label(e.code),) + e[1:]) - if e.calls: - for j in range(len(e.calls)): - se = e.calls[j] - if not isinstance(se.code, str): - e.calls[j] = type(se)((label(se.code),) + se[1:]) + if e.calls: + for j in range(len(e.calls)): + se = e.calls[j] + if not isinstance(se.code, str): + e.calls[j] = type(se)((label(se.code),) + se[1:]) _fn2mod = {} @@ -97,7 +88,7 @@ try: mname = _fn2mod[code.co_filename] except KeyError: - for k, v in sys.modules.iteritems(): + for k, v in sys.modules.items(): if v is None: continue if not hasattr(v, '__file__'):
--- a/mercurial/mail.py Wed Apr 09 15:27:57 2008 -0700 +++ b/mercurial/mail.py Wed Apr 09 15:28:30 2008 -0700 @@ -6,11 +6,10 @@ # of the GNU General Public License, incorporated herein by reference. from i18n import _ -import os, smtplib, templater, util, socket +import os, smtplib, util, socket def _smtp(ui): - '''send mail using smtp.''' - + '''build an smtp connection and return a function to send mail''' local_hostname = ui.config('smtp', 'local_hostname') s = smtplib.SMTP(local_hostname=local_hostname) mailhost = ui.config('smtp', 'host') @@ -30,50 +29,48 @@ s.ehlo() username = ui.config('smtp', 'username') password = ui.config('smtp', 'password') + if username and not password: + password = ui.getpass() if username and password: ui.note(_('(authenticating to mail server as %s)\n') % (username)) s.login(username, password) - return s -class _sendmail(object): - '''send mail using sendmail.''' + def send(sender, recipients, msg): + try: + return s.sendmail(sender, recipients, msg) + except smtplib.SMTPRecipientsRefused, inst: + recipients = [r[1] for r in inst.recipients.values()] + raise util.Abort('\n' + '\n'.join(recipients)) + except smtplib.SMTPException, inst: + raise util.Abort(inst) - def __init__(self, ui, program): - self.ui = ui - self.program = program + return send - def sendmail(self, sender, recipients, msg): - cmdline = '%s -f %s %s' % ( - self.program, templater.email(sender), - ' '.join(map(templater.email, recipients))) - self.ui.note(_('sending mail: %s\n') % cmdline) - fp = os.popen(cmdline, 'w') - fp.write(msg) - ret = fp.close() - if ret: - raise util.Abort('%s %s' % ( - os.path.basename(self.program.split(None, 1)[0]), - util.explain_exit(ret)[0])) +def _sendmail(ui, sender, recipients, msg): + '''send mail using sendmail.''' + program = ui.config('email', 'method') + cmdline = '%s -f %s %s' % (program, util.email(sender), + ' '.join(map(util.email, recipients))) + ui.note(_('sending mail: %s\n') % cmdline) + fp = os.popen(cmdline, 'w') + fp.write(msg) + ret = fp.close() + if ret: + raise util.Abort('%s %s' % ( + os.path.basename(program.split(None, 1)[0]), + util.explain_exit(ret)[0])) def connect(ui): - '''make a mail connection. object returned has one method, sendmail. + '''make a mail connection. return a function to send mail. call as sendmail(sender, list-of-recipients, msg).''' - - method = ui.config('email', 'method', 'smtp') - if method == 'smtp': + if ui.config('email', 'method', 'smtp') == 'smtp': return _smtp(ui) - - return _sendmail(ui, method) + return lambda s, r, m: _sendmail(ui, s, r, m) def sendmail(ui, sender, recipients, msg): - try: - return connect(ui).sendmail(sender, recipients, msg) - except smtplib.SMTPRecipientsRefused, inst: - recipients = [r[1] for r in inst.recipients.values()] - raise util.Abort('\n' + '\n'.join(recipients)) - except smtplib.SMTPException, inst: - raise util.Abort(inst) + send = connect(ui) + return send(sender, recipients, msg) def validateconfig(ui): '''determine if we have enough config data to try sending email.'''
--- a/mercurial/manifest.py Wed Apr 09 15:27:57 2008 -0700 +++ b/mercurial/manifest.py Wed Apr 09 15:28:30 2008 -0700 @@ -5,9 +5,10 @@ # This software may be used and distributed according to the terms # of the GNU General Public License, incorporated herein by reference. -from revlog import * +from node import bin, hex, nullid +from revlog import revlog, RevlogError from i18n import _ -import array, bisect, struct, mdiff +import array, struct, mdiff, parsers class manifestdict(dict): def __init__(self, mapping=None, flags=None): @@ -38,14 +39,7 @@ def parse(self, lines): mfdict = manifestdict() - fdict = mfdict._flags - for l in lines.splitlines(): - f, n = l.split('\0') - if len(n) > 40: - fdict[f] = n[40:] - mfdict[f] = bin(n[:40]) - else: - mfdict[f] = bin(n) + parsers.parse_manifest(mfdict, mfdict._flags, lines) return mfdict def readdelta(self, node):
--- a/mercurial/mdiff.py Wed Apr 09 15:27:57 2008 -0700 +++ b/mercurial/mdiff.py Wed Apr 09 15:28:30 2008 -0700 @@ -5,7 +5,8 @@ # This software may be used and distributed according to the terms # of the GNU General Public License, incorporated herein by reference. -import bdiff, mpatch, re, struct, util, md5 +from i18n import _ +import bdiff, mpatch, re, struct, util def splitnewlines(text): '''like str.splitlines, but only split on newlines.''' @@ -30,7 +31,7 @@ defaults = { 'context': 3, 'text': False, - 'showfunc': True, + 'showfunc': False, 'git': False, 'nodates': False, 'ignorews': False, @@ -47,6 +48,12 @@ v = self.defaults[k] setattr(self, k, v) + try: + self.context = int(self.context) + except ValueError: + raise util.Abort(_('diff context lines count must be ' + 'an integer, not %r') % self.context) + defaultopts = diffopts() def wsclean(opts, text): @@ -73,7 +80,7 @@ if not opts.text and (util.binary(a) or util.binary(b)): def h(v): # md5 is used instead of sha1 because md5 is supposedly faster - return md5.new(v).digest() + return util.md5(v).digest() if a and b and len(a) == len(b) and h(a) == h(b): return "" l = ['Binary file %s has changed\n' % fn1]
--- a/mercurial/merge.py Wed Apr 09 15:27:57 2008 -0700 +++ b/mercurial/merge.py Wed Apr 09 15:28:30 2008 -0700 @@ -5,85 +5,28 @@ # This software may be used and distributed according to the terms # of the GNU General Public License, incorporated herein by reference. -from node import * +from node import nullid, nullrev from i18n import _ -import errno, util, os, tempfile, context, heapq - -def filemerge(repo, fw, fd, fo, wctx, mctx): - """perform a 3-way merge in the working directory - - fw = original filename in the working directory - fd = destination filename in the working directory - fo = filename in other parent - wctx, mctx = working and merge changecontexts - """ - - def temp(prefix, ctx): - pre = "%s~%s." % (os.path.basename(ctx.path()), prefix) - (fd, name) = tempfile.mkstemp(prefix=pre) - data = repo.wwritedata(ctx.path(), ctx.data()) - f = os.fdopen(fd, "wb") - f.write(data) - f.close() - return name - - fcm = wctx.filectx(fw) - fcmdata = wctx.filectx(fd).data() - fco = mctx.filectx(fo) - - if not fco.cmp(fcmdata): # files identical? - return None +import errno, util, os, filemerge, copies - fca = fcm.ancestor(fco) - if not fca: - fca = repo.filectx(fw, fileid=nullrev) - a = repo.wjoin(fd) - b = temp("base", fca) - c = temp("other", fco) - - if fw != fo: - repo.ui.status(_("merging %s and %s\n") % (fw, fo)) - else: - repo.ui.status(_("merging %s\n") % fw) - - repo.ui.debug(_("my %s other %s ancestor %s\n") % (fcm, fco, fca)) +def _checkunknown(wctx, mctx): + "check for collisions between unknown files and files in mctx" + for f in wctx.unknown(): + if f in mctx and mctx[f].cmp(wctx[f].data()): + raise util.Abort(_("untracked file in working directory differs" + " from file in requested revision: '%s'") % f) - cmd = (os.environ.get("HGMERGE") or repo.ui.config("ui", "merge") - or "hgmerge") - r = util.system('%s "%s" "%s" "%s"' % (cmd, a, b, c), cwd=repo.root, - environ={'HG_FILE': fd, - 'HG_MY_NODE': str(wctx.parents()[0]), - 'HG_OTHER_NODE': str(mctx), - 'HG_MY_ISLINK': fcm.islink(), - 'HG_OTHER_ISLINK': fco.islink(), - 'HG_BASE_ISLINK': fca.islink(),}) - if r: - repo.ui.warn(_("merging %s failed!\n") % fd) - - os.unlink(b) - os.unlink(c) - return r - -def checkunknown(wctx, mctx): - "check for collisions between unknown files and files in mctx" - man = mctx.manifest() - for f in wctx.unknown(): - if f in man: - if mctx.filectx(f).cmp(wctx.filectx(f).data()): - raise util.Abort(_("untracked local file '%s' differs" - " from remote version") % f) - -def checkcollision(mctx): +def _checkcollision(mctx): "check for case folding collisions in the destination context" folded = {} - for fn in mctx.manifest(): + for fn in mctx: fold = fn.lower() if fold in folded: raise util.Abort(_("case-folding collision between %s and %s") % (fn, folded[fold])) folded[fold] = fn -def forgetremoved(wctx, mctx): +def _forgetremoved(wctx, mctx, branchmerge): """ Forget removed files @@ -92,244 +35,25 @@ then we need to remove it from the dirstate, to prevent the dirstate from listing the file when it is no longer in the manifest. + + If we're merging, and the other revision has removed a file + that is not present in the working directory, we need to mark it + as removed. """ action = [] - man = mctx.manifest() - for f in wctx.deleted() + wctx.removed(): - if f not in man: - action.append((f, "f")) + state = branchmerge and 'r' or 'f' + for f in wctx.deleted(): + if f not in mctx: + action.append((f, state)) + + if not branchmerge: + for f in wctx.removed(): + if f not in mctx: + action.append((f, "f")) return action -def findcopies(repo, m1, m2, ma, limit): - """ - Find moves and copies between m1 and m2 back to limit linkrev - """ - - def nonoverlap(d1, d2, d3): - "Return list of elements in d1 not in d2 or d3" - l = [d for d in d1 if d not in d3 and d not in d2] - l.sort() - return l - - def dirname(f): - s = f.rfind("/") - if s == -1: - return "" - return f[:s] - - def dirs(files): - d = {} - for f in files: - f = dirname(f) - while f not in d: - d[f] = True - f = dirname(f) - return d - - wctx = repo.workingctx() - - def makectx(f, n): - if len(n) == 20: - return repo.filectx(f, fileid=n) - return wctx.filectx(f) - ctx = util.cachefunc(makectx) - - def findold(fctx): - "find files that path was copied from, back to linkrev limit" - old = {} - seen = {} - orig = fctx.path() - visit = [fctx] - while visit: - fc = visit.pop() - s = str(fc) - if s in seen: - continue - seen[s] = 1 - if fc.path() != orig and fc.path() not in old: - old[fc.path()] = 1 - if fc.rev() < limit: - continue - visit += fc.parents() - - old = old.keys() - old.sort() - return old - - copy = {} - fullcopy = {} - diverge = {} - - def checkcopies(c, man, aman): - '''check possible copies for filectx c''' - for of in findold(c): - fullcopy[c.path()] = of # remember for dir rename detection - if of not in man: # original file not in other manifest? - if of in ma: - diverge.setdefault(of, []).append(c.path()) - continue - # if the original file is unchanged on the other branch, - # no merge needed - if man[of] == aman.get(of): - continue - c2 = ctx(of, man[of]) - ca = c.ancestor(c2) - if not ca: # unrelated? - continue - # named changed on only one side? - if ca.path() == c.path() or ca.path() == c2.path(): - if c == ca and c2 == ca: # no merge needed, ignore copy - continue - copy[c.path()] = of - - if not repo.ui.configbool("merge", "followcopies", True): - return {}, {} - - # avoid silly behavior for update from empty dir - if not m1 or not m2 or not ma: - return {}, {} - - repo.ui.debug(_(" searching for copies back to rev %d\n") % limit) - - u1 = nonoverlap(m1, m2, ma) - u2 = nonoverlap(m2, m1, ma) - - if u1: - repo.ui.debug(_(" unmatched files in local:\n %s\n") - % "\n ".join(u1)) - if u2: - repo.ui.debug(_(" unmatched files in other:\n %s\n") - % "\n ".join(u2)) - - for f in u1: - checkcopies(ctx(f, m1[f]), m2, ma) - - for f in u2: - checkcopies(ctx(f, m2[f]), m1, ma) - - d2 = {} - for of, fl in diverge.items(): - for f in fl: - fo = list(fl) - fo.remove(f) - d2[f] = (of, fo) - - if fullcopy: - repo.ui.debug(_(" all copies found (* = to merge, ! = divergent):\n")) - for f in fullcopy: - note = "" - if f in copy: note += "*" - if f in diverge: note += "!" - repo.ui.debug(_(" %s -> %s %s\n") % (f, fullcopy[f], note)) - - if not fullcopy or not repo.ui.configbool("merge", "followdirs", True): - return copy, diverge - - repo.ui.debug(_(" checking for directory renames\n")) - - # generate a directory move map - d1, d2 = dirs(m1), dirs(m2) - invalid = {} - dirmove = {} - - # examine each file copy for a potential directory move, which is - # when all the files in a directory are moved to a new directory - for dst, src in fullcopy.items(): - dsrc, ddst = dirname(src), dirname(dst) - if dsrc in invalid: - # already seen to be uninteresting - continue - elif dsrc in d1 and ddst in d1: - # directory wasn't entirely moved locally - invalid[dsrc] = True - elif dsrc in d2 and ddst in d2: - # directory wasn't entirely moved remotely - invalid[dsrc] = True - elif dsrc in dirmove and dirmove[dsrc] != ddst: - # files from the same directory moved to two different places - invalid[dsrc] = True - else: - # looks good so far - dirmove[dsrc + "/"] = ddst + "/" - - for i in invalid: - if i in dirmove: - del dirmove[i] - - del d1, d2, invalid - - if not dirmove: - return copy, diverge - - for d in dirmove: - repo.ui.debug(_(" dir %s -> %s\n") % (d, dirmove[d])) - - # check unaccounted nonoverlapping files against directory moves - for f in u1 + u2: - if f not in fullcopy: - for d in dirmove: - if f.startswith(d): - # new file added in a directory that was moved, move it - copy[f] = dirmove[d] + f[len(d):] - repo.ui.debug(_(" file %s -> %s\n") % (f, copy[f])) - break - - return copy, diverge - -def symmetricdifference(repo, rev1, rev2): - """symmetric difference of the sets of ancestors of rev1 and rev2 - - I.e. revisions that are ancestors of rev1 or rev2, but not both. - """ - # basic idea: - # - mark rev1 and rev2 with different colors - # - walk the graph in topological order with the help of a heap; - # for each revision r: - # - if r has only one color, we want to return it - # - add colors[r] to its parents - # - # We keep track of the number of revisions in the heap that - # we may be interested in. We stop walking the graph as soon - # as this number reaches 0. - WHITE = 1 - BLACK = 2 - ALLCOLORS = WHITE | BLACK - colors = {rev1: WHITE, rev2: BLACK} - - cl = repo.changelog - - visit = [-rev1, -rev2] - heapq.heapify(visit) - n_wanted = len(visit) - ret = [] - - while n_wanted: - r = -heapq.heappop(visit) - wanted = colors[r] != ALLCOLORS - n_wanted -= wanted - if wanted: - ret.append(r) - - for p in cl.parentrevs(r): - if p == nullrev: - continue - if p not in colors: - # first time we see p; add it to visit - n_wanted += wanted - colors[p] = colors[r] - heapq.heappush(visit, -p) - elif colors[p] != ALLCOLORS and colors[p] != colors[r]: - # at first we thought we wanted p, but now - # we know we don't really want it - n_wanted -= 1 - colors[p] |= colors[r] - - del colors[r] - - return ret - def manifestmerge(repo, p1, p2, pa, overwrite, partial): """ Merge p1 and p2 with ancestor ma and generate merge action list @@ -347,65 +71,73 @@ ma = pa.manifest() backwards = (pa == p2) action = [] - copy = {} - diverge = {} + copy, copied, diverge = {}, {}, {} def fmerge(f, f2=None, fa=None): """merge flags""" if not f2: f2 = f fa = f - a, b, c = ma.execf(fa), m1.execf(f), m2.execf(f2) - if ((a^b) | (a^c)) ^ a: - return 'x' - a, b, c = ma.linkf(fa), m1.linkf(f), m2.linkf(f2) - if ((a^b) | (a^c)) ^ a: - return 'l' - return '' + a, m, n = ma.flags(fa), m1.flags(f), m2.flags(f2) + if m == n: # flags agree + return m # unchanged + if m and n: # flags are set but don't agree + if not a: # both differ from parent + r = repo.ui.prompt( + _(" conflicting flags for %s\n" + "(n)one, e(x)ec or sym(l)ink?") % f, "[nxl]", "n") + return r != "n" and r or '' + if m == a: + return n # changed from m to n + return m # changed from n to m + if m and m != a: # changed from a to m + return m + if n and n != a: # changed from a to n + return n + return '' # flag was cleared def act(msg, m, f, *args): repo.ui.debug(" %s: %s -> %s\n" % (f, msg, m)) action.append((f, m) + args) - if not (backwards or overwrite): - rev1 = p1.rev() - if rev1 is None: - # p1 is a workingctx - rev1 = p1.parents()[0].rev() - limit = min(symmetricdifference(repo, rev1, p2.rev())) - copy, diverge = findcopies(repo, m1, m2, ma, limit) - - for of, fl in diverge.items(): - act("divergent renames", "dr", of, fl) - - copied = dict.fromkeys(copy.values()) + if pa and not (backwards or overwrite): + if repo.ui.configbool("merge", "followcopies", True): + dirs = repo.ui.configbool("merge", "followdirs", True) + copy, diverge = copies.copies(repo, p1, p2, pa, dirs) + copied = dict.fromkeys(copy.values()) + for of, fl in diverge.items(): + act("divergent renames", "dr", of, fl) # Compare manifests for f, n in m1.iteritems(): if partial and not partial(f): continue if f in m2: + if overwrite or backwards: + rflags = m2.flags(f) + else: + rflags = fmerge(f) # are files different? if n != m2[f]: a = ma.get(f, nullid) + # are we clobbering? + if overwrite: + act("clobbering", "g", f, rflags) + # or are we going back in time and clean? + elif backwards and not n[20:]: + act("reverting", "g", f, rflags) # are both different from the ancestor? - if not overwrite and n != a and m2[f] != a: - act("versions differ", "m", f, f, f, fmerge(f), False) - # are we clobbering? + elif n != a and m2[f] != a: + act("versions differ", "m", f, f, f, rflags, False) # is remote's version newer? - # or are we going back in time and clean? - elif overwrite or m2[f] != a or (backwards and not n[20:]): - act("remote is newer", "g", f, m2.flags(f)) + elif m2[f] != a: + act("remote is newer", "g", f, rflags) # local is newer, not overwrite, check mode bits - elif fmerge(f) != m1.flags(f): - act("update permissions", "e", f, m2.flags(f)) + elif m1.flags(f) != rflags: + act("update permissions", "e", f, rflags) # contents same, check mode bits - elif m1.flags(f) != m2.flags(f): - # are we clobbering? - # is remote's version newer? - # or are we going back? - if overwrite or fmerge(f) != m1.flags(f) or backwards: - act("update permissions", "e", f, m2.flags(f)) + elif m1.flags(f) != rflags: + act("update permissions", "e", f, rflags) elif f in copied: continue elif f in copy: @@ -422,8 +154,9 @@ elif f in ma: if n != ma[f] and not overwrite: if repo.ui.prompt( - (_(" local changed %s which remote deleted\n") % f) + - _("(k)eep or (d)elete?"), _("[kd]"), _("k")) == _("d"): + _(" local changed %s which remote deleted\n" + "use (c)hanged version or (d)elete?") % f, + _("[cd]"), _("c")) == _("d"): act("prompt delete", "r", f) else: act("other deleted", "r", f) @@ -455,8 +188,9 @@ act("recreating", "g", f, m2.flags(f)) elif n != ma[f]: if repo.ui.prompt( - (_("remote changed %s which local deleted\n") % f) + - _("(k)eep or (d)elete?"), _("[kd]"), _("k")) == _("k"): + _("remote changed %s which local deleted\n" + "use (c)hanged version or leave (d)eleted?") % f, + _("[cd]"), _("c")) == _("c"): act("prompt recreating", "g", f, m2.flags(f)) else: act("remote created", "g", f, m2.flags(f)) @@ -495,7 +229,7 @@ removed += 1 elif m == "m": # merge f2, fd, flags, move = a[2:] - r = filemerge(repo, f, fd, f2, wctx, mctx) + r = filemerge.filemerge(repo, f, fd, f2, wctx, mctx) if r > 0: unresolved += 1 else: @@ -503,7 +237,7 @@ updated += 1 else: merged += 1 - util.set_exec(repo.wjoin(fd), "x" in flags) + util.set_flags(repo.wjoin(fd), flags) if f != fd and move and util.lexists(repo.wjoin(f)): repo.ui.debug(_("removing %s\n") % f) os.unlink(repo.wjoin(f)) @@ -532,7 +266,7 @@ repo.ui.warn(" %s\n" % nf) elif m == "e": # exec flags = a[2] - util.set_exec(repo.wjoin(f), flags) + util.set_flags(repo.wjoin(f), flags) return updated, merged, removed, unresolved @@ -614,7 +348,6 @@ else: raise util.Abort(_("branch %s not found") % wc.branch()) overwrite = force and not branchmerge - forcemerge = force and branchmerge pl = wc.parents() p1, p2 = pl[0], repo.changectx(node) pa = p1.ancestor(p2) @@ -624,28 +357,40 @@ ### check phase if not overwrite and len(pl) > 1: raise util.Abort(_("outstanding uncommitted merges")) - if pa == p1 or pa == p2: # is there a linear path from p1 to p2? - if branchmerge: - if p1.branch() != p2.branch() and pa != p2: + if branchmerge: + if pa == p2: + raise util.Abort(_("can't merge with ancestor")) + elif pa == p1: + if p1.branch() != p2.branch(): fastforward = True else: - raise util.Abort(_("there is nothing to merge, just use " - "'hg update' or look at 'hg heads'")) - elif not (overwrite or branchmerge): - raise util.Abort(_("update spans branches, use 'hg merge' " - "or 'hg update -C' to lose changes")) - if branchmerge and not forcemerge: - if wc.files(): + raise util.Abort(_("nothing to merge (use 'hg update'" + " or check 'hg heads')")) + if not force and (wc.files() or wc.deleted()): raise util.Abort(_("outstanding uncommitted changes")) + elif not overwrite: + if pa == p1 or pa == p2: # linear + pass # all good + elif p1.branch() == p2.branch(): + if wc.files() or wc.deleted(): + raise util.Abort(_("crosses branches (use 'hg merge' or " + "'hg update -C' to discard changes)")) + raise util.Abort(_("crosses branches (use 'hg merge' " + "or 'hg update -C')")) + elif wc.files() or wc.deleted(): + raise util.Abort(_("crosses named branches (use " + "'hg update -C' to discard changes)")) + else: + # Allow jumping branches if there are no changes + overwrite = True ### calculate phase action = [] if not force: - checkunknown(wc, p2) + _checkunknown(wc, p2) if not util.checkfolding(repo.path): - checkcollision(p2) - if not branchmerge: - action += forgetremoved(wc, p2) + _checkcollision(p2) + action += _forgetremoved(wc, p2, branchmerge) action += manifestmerge(repo, wc, p2, pa, overwrite, partial) ### apply phase
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/mercurial/parsers.c Wed Apr 09 15:28:30 2008 -0700 @@ -0,0 +1,169 @@ +/* + parsers.c - efficient content parsing + + Copyright 2008 Matt Mackall <mpm@selenic.com> and others + + This software may be used and distributed according to the terms of + the GNU General Public License, incorporated herein by reference. +*/ + +#include <Python.h> +#include <ctype.h> +#include <string.h> + +static int hexdigit(char c) +{ + if (c >= '0' && c <= '9') + return c - '0'; + + if (c >= 'A' && c <= 'F') + return c - 'A' + 10; + + if (c >= 'a' && c <= 'f') + return c - 'a' + 10; + + return -1; +} + +/* + * Turn a hex-encoded string into binary. + */ +static PyObject *unhexlify(const char *str, int len) +{ + PyObject *ret = NULL; + const char *c; + char *d; + + if (len % 2) { + PyErr_SetString(PyExc_ValueError, + "input is not even in length"); + goto bail; + } + + ret = PyString_FromStringAndSize(NULL, len / 2); + if (!ret) + goto bail; + + d = PyString_AsString(ret); + if (!d) + goto bail; + + for (c = str; c < str + len;) { + int hi = hexdigit(*c++); + int lo = hexdigit(*c++); + + if (hi == -1 || lo == -1) { + PyErr_SetString(PyExc_ValueError, + "input contains non-hex character"); + goto bail; + } + + *d++ = (hi << 4) | lo; + } + + goto done; + +bail: + Py_XDECREF(ret); + ret = NULL; +done: + return ret; +} + +/* + * This code assumes that a manifest is stitched together with newline + * ('\n') characters. + */ +static PyObject *parse_manifest(PyObject *self, PyObject *args) +{ + PyObject *mfdict, *fdict; + char *str, *cur, *start, *zero; + int len; + + if (!PyArg_ParseTuple(args, "O!O!s#:parse_manifest", + &PyDict_Type, &mfdict, + &PyDict_Type, &fdict, + &str, &len)) + goto quit; + + for (start = cur = str, zero = NULL; cur < str + len; cur++) { + PyObject *file = NULL, *node = NULL; + PyObject *flags = NULL; + int nlen; + + if (!*cur) { + zero = cur; + continue; + } + else if (*cur != '\n') + continue; + + if (!zero) { + PyErr_SetString(PyExc_ValueError, + "manifest entry has no separator"); + goto quit; + } + + file = PyString_FromStringAndSize(start, zero - start); + if (!file) + goto bail; + + nlen = cur - zero - 1; + + node = unhexlify(zero + 1, nlen > 40 ? 40 : nlen); + if (!node) + goto bail; + + if (nlen > 40) { + PyObject *flags; + + flags = PyString_FromStringAndSize(zero + 41, + nlen - 40); + if (!flags) + goto bail; + + if (PyDict_SetItem(fdict, file, flags) == -1) + goto bail; + } + + if (PyDict_SetItem(mfdict, file, node) == -1) + goto bail; + + start = cur + 1; + zero = NULL; + + Py_XDECREF(flags); + Py_XDECREF(node); + Py_XDECREF(file); + continue; + bail: + Py_XDECREF(flags); + Py_XDECREF(node); + Py_XDECREF(file); + goto quit; + } + + if (len > 0 && *(cur - 1) != '\n') { + PyErr_SetString(PyExc_ValueError, + "manifest contains trailing garbage"); + goto quit; + } + + Py_INCREF(Py_None); + return Py_None; + +quit: + return NULL; +} + +static char parsers_doc[] = "Efficient content parsing."; + +static PyMethodDef methods[] = { + {"parse_manifest", parse_manifest, METH_VARARGS, "parse a manifest\n"}, + {NULL, NULL} +}; + +PyMODINIT_FUNC initparsers(void) +{ + Py_InitModule3("parsers", methods, parsers_doc); +}
--- a/mercurial/patch.py Wed Apr 09 15:27:57 2008 -0700 +++ b/mercurial/patch.py Wed Apr 09 15:28:30 2008 -0700 @@ -7,9 +7,9 @@ # of the GNU General Public License, incorporated herein by reference. from i18n import _ -from node import * -import base85, cmdutil, mdiff, util, context, revlog, diffhelpers -import cStringIO, email.Parser, os, popen2, re, sha, errno +from node import hex, nullid, short +import base85, cmdutil, mdiff, util, context, revlog, diffhelpers, copies +import cStringIO, email.Parser, os, popen2, re, errno import sys, tempfile, zlib class PatchError(Exception): @@ -505,7 +505,7 @@ return -1 class hunk: - def __init__(self, desc, num, lr, context): + def __init__(self, desc, num, lr, context, create=False, remove=False): self.number = num self.desc = desc self.hunk = [ desc ] @@ -515,6 +515,8 @@ self.read_context_hunk(lr) else: self.read_unified_hunk(lr) + self.create = create + self.remove = remove and not create def read_unified_hunk(self, lr): m = unidesc.match(self.desc) @@ -639,6 +641,7 @@ self.hunk[0] = self.desc def reverse(self): + self.create, self.remove = self.remove, self.create origlena = self.lena origstarta = self.starta self.lena = self.lenb @@ -669,10 +672,10 @@ return len(self.a) == self.lena and len(self.b) == self.lenb def createfile(self): - return self.starta == 0 and self.lena == 0 + return self.starta == 0 and self.lena == 0 and self.create def rmfile(self): - return self.startb == 0 and self.lenb == 0 + return self.startb == 0 and self.lenb == 0 and self.remove def fuzzit(self, l, fuzz, toponly): # this removes context lines from the top and bottom of list 'l'. It @@ -773,7 +776,7 @@ def parsefilename(str): # --- filename \t|space stuff - s = str[4:] + s = str[4:].rstrip('\r\n') i = s.find('\t') if i < 0: i = s.find(' ') @@ -797,13 +800,13 @@ while i < pathlen - 1 and path[i] == '/': i += 1 count -= 1 - return path[i:].rstrip() + return path[:i].lstrip(), path[i:].rstrip() nulla = afile_orig == "/dev/null" nullb = bfile_orig == "/dev/null" - afile = pathstrip(afile_orig, strip) + abase, afile = pathstrip(afile_orig, strip) gooda = not nulla and os.path.exists(afile) - bfile = pathstrip(bfile_orig, strip) + bbase, bfile = pathstrip(bfile_orig, strip) if afile == bfile: goodb = gooda else: @@ -812,21 +815,25 @@ if reverse: createfunc = hunk.rmfile missing = not goodb and not gooda and not createfunc() + # If afile is "a/b/foo" and bfile is "a/b/foo.orig" we assume the + # diff is between a file and its backup. In this case, the original + # file should be patched (see original mpatch code). + isbackup = (abase == bbase and bfile.startswith(afile)) fname = None if not missing: if gooda and goodb: - fname = (afile in bfile) and afile or bfile + fname = isbackup and afile or bfile elif gooda: fname = afile - + if not fname: if not nullb: - fname = (afile in bfile) and afile or bfile + fname = isbackup and afile or bfile elif not nulla: fname = afile else: raise PatchError(_("undefined source and destination files")) - + return fname, missing class linereader: @@ -887,6 +894,9 @@ context = None lr = linereader(fp) dopatch = True + # gitworkdone is True if a git operation (copy, rename, ...) was + # performed already for the current file. Useful when the file + # section may have no hunk. gitworkdone = False while True: @@ -905,7 +915,10 @@ try: if context == None and x.startswith('***************'): context = True - current_hunk = hunk(x, hunknum + 1, lr, context) + gpatch = changed.get(bfile[2:], (None, None))[1] + create = afile == '/dev/null' or gpatch and gpatch.op == 'ADD' + remove = bfile == '/dev/null' or gpatch and gpatch.op == 'DELETE' + current_hunk = hunk(x, hunknum + 1, lr, context, create, remove) except PatchError, err: ui.debug(err) current_hunk = None @@ -934,8 +947,8 @@ changed[gp.path] = (gp.op, gp) # else error? # copy/rename + modify should modify target, not source - if changed.get(bfile[2:], (None, None))[0] in ('COPY', - 'RENAME'): + gitop = changed.get(bfile[2:], (None, None))[0] + if gitop in ('COPY', 'DELETE', 'RENAME'): afile = bfile gitworkdone = True newfile = True @@ -1029,7 +1042,7 @@ for gp in gitpatches: if gp.op in ('COPY', 'RENAME'): copyfile(gp.oldpath, gp.path) - changed[gp.path] = (gp.op, gp) + changed[gp.path] = (gp.op, gp) else: raise util.Abort(_('unsupported parser state: %s') % state) @@ -1042,9 +1055,9 @@ return err def diffopts(ui, opts={}, untrusted=False): - def get(key, name=None): + def get(key, name=None, getter=ui.configbool): return (opts.get(key) or - ui.configbool('diff', name or key, None, untrusted=untrusted)) + getter('diff', name or key, None, untrusted=untrusted)) return mdiff.diffopts( text=opts.get('text'), git=get('git'), @@ -1052,7 +1065,8 @@ showfunc=get('show_function', 'showfunc'), ignorews=get('ignore_all_space', 'ignorews'), ignorewsamount=get('ignore_space_change', 'ignorewsamount'), - ignoreblanklines=get('ignore_blank_lines', 'ignoreblanklines')) + ignoreblanklines=get('ignore_blank_lines', 'ignoreblanklines'), + context=get('unified', getter=ui.config)) def updatedir(ui, repo, patches): '''Update dirstate after patch application according to metadata''' @@ -1082,16 +1096,17 @@ for f in patches: ctype, gp = patches[f] if gp and gp.mode: - x = gp.mode & 0100 != 0 - l = gp.mode & 020000 != 0 + flags = '' + if gp.mode & 0100: + flags = 'x' + elif gp.mode & 020000: + flags = 'l' dst = os.path.join(repo.root, gp.path) # patch won't create empty files if ctype == 'ADD' and not os.path.exists(dst): - repo.wwrite(gp.path, '', x and 'x' or '') + repo.wwrite(gp.path, '', flags) else: - util.set_link(dst, l) - if not l: - util.set_exec(dst, x) + util.set_flags(dst, flags) cmdutil.addremove(repo, cfiles) files = patches.keys() files.extend([r for r in removes if r not in files]) @@ -1105,7 +1120,7 @@ if not text: return '0' * 40 l = len(text) - s = sha.new('blob %d\0' % l) + s = util.sha1('blob %d\0' % l) s.update(text) return s.hexdigest() @@ -1193,36 +1208,6 @@ execf2 = mc.execf linkf2 = mc.linkf - # returns False if there was no rename between ctx1 and ctx2 - # returns None if the file was created between ctx1 and ctx2 - # returns the (file, node) present in ctx1 that was renamed to f in ctx2 - # This will only really work if c1 is the Nth 1st parent of c2. - def renamed(c1, c2, man, f): - startrev = c1.rev() - c = c2 - crev = c.rev() - if crev is None: - crev = repo.changelog.count() - orig = f - files = (f,) - while crev > startrev: - if f in files: - try: - src = getfilectx(f, c).renamed() - except revlog.LookupError: - return None - if src: - f = src[0] - crev = c.parents()[0].rev() - # try to reuse - c = getctx(crev) - files = c.files() - if f not in man: - return None - if f == orig: - return False - return f - if repo.ui.quiet: r = None else: @@ -1230,28 +1215,9 @@ r = [hexfunc(node) for node in [node1, node2] if node] if opts.git: - copied = {} - c1, c2 = ctx1, ctx2 - files = added - man = man1 - if node2 and ctx1.rev() >= ctx2.rev(): - # renamed() starts at c2 and walks back in history until c1. - # Since ctx1.rev() >= ctx2.rev(), invert ctx2 and ctx1 to - # detect (inverted) copies. - c1, c2 = ctx2, ctx1 - files = removed - man = ctx2.manifest() - for f in files: - src = renamed(c1, c2, man, f) - if src: - copied[f] = src - if ctx1 == c2: - # invert the copied dict - copied = dict([(v, k) for (k, v) in copied.iteritems()]) - # If we've renamed file foo to bar (copied['bar'] = 'foo'), - # avoid showing a diff for foo if we're going to show - # the rename to bar. - srcs = [x[1] for x in copied.iteritems() if x[0] in added] + copy, diverge = copies.copies(repo, ctx1, ctx2, repo.changectx(nullid)) + for k, v in copy.items(): + copy[v] = k all = modified + added + removed all.sort() @@ -1277,8 +1243,8 @@ if f in added: mode = gitmode(execf2(f), linkf2(f)) - if f in copied: - a = copied[f] + if f in copy: + a = copy[f] omode = gitmode(man1.execf(a), man1.linkf(a)) addmodehdr(header, omode, mode) if a in removed and a not in gone: @@ -1294,7 +1260,8 @@ if util.binary(tn): dodiff = 'binary' elif f in removed: - if f in srcs: + # have we already reported a copy above? + if f in copy and copy[f] in added and copy[copy[f]] == f: dodiff = False else: mode = gitmode(man1.execf(f), man1.linkf(f)) @@ -1367,7 +1334,8 @@ try: p = popen2.Popen3('diffstat -p1 -w79 2>/dev/null > ' + name) try: - for line in patchlines: print >> p.tochild, line + for line in patchlines: + p.tochild.write(line + "\n") p.tochild.close() if p.wait(): return fp = os.fdopen(fd, 'r') @@ -1376,7 +1344,6 @@ last = stat.pop() stat.insert(0, last) stat = ''.join(stat) - if stat.startswith('0 files'): raise ValueError return stat except: raise finally:
--- a/mercurial/remoterepo.py Wed Apr 09 15:27:57 2008 -0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,25 +0,0 @@ -# remoterepo - remote repository proxy classes for mercurial -# -# Copyright 2005, 2006 Matt Mackall <mpm@selenic.com> -# -# This software may be used and distributed according to the terms -# of the GNU General Public License, incorporated herein by reference. - -import repo - -class remoterepository(repo.repository): - def dev(self): - return -1 - - def local(self): - return False - -class remotelock(object): - def __init__(self, repo): - self.repo = repo - def release(self): - self.repo.unlock() - self.repo = None - def __del__(self): - if self.repo: - self.release()
--- a/mercurial/repair.py Wed Apr 09 15:27:57 2008 -0700 +++ b/mercurial/repair.py Wed Apr 09 15:28:30 2008 -0700 @@ -6,122 +6,130 @@ # This software may be used and distributed according to the terms # of the GNU General Public License, incorporated herein by reference. -import changegroup, revlog, os, commands +import changegroup, os +from node import nullrev, short + +def _bundle(repo, bases, heads, node, suffix, extranodes=None): + """create a bundle with the specified revisions as a backup""" + cg = repo.changegroupsubset(bases, heads, 'strip', extranodes) + backupdir = repo.join("strip-backup") + if not os.path.isdir(backupdir): + os.mkdir(backupdir) + name = os.path.join(backupdir, "%s-%s" % (short(node), suffix)) + repo.ui.warn("saving bundle to %s\n" % name) + return changegroup.writebundle(cg, name, "HG10BZ") + +def _collectfiles(repo, striprev): + """find out the filelogs affected by the strip""" + files = {} -def strip(ui, repo, rev, backup="all"): - def limitheads(chlog, stop): - """return the list of all nodes that have no children""" - p = {} - h = [] - stoprev = 0 - if stop in chlog.nodemap: - stoprev = chlog.rev(stop) + for x in xrange(striprev, repo.changelog.count()): + for name in repo.changectx(x).files(): + if name in files: + continue + files[name] = 1 + + files = files.keys() + files.sort() + return files - for r in xrange(chlog.count() - 1, -1, -1): - n = chlog.node(r) - if n not in p: - h.append(n) - if n == stop: +def _collectextranodes(repo, files, link): + """return the nodes that have to be saved before the strip""" + def collectone(revlog): + extra = [] + startrev = count = revlog.count() + # find the truncation point of the revlog + for i in xrange(0, count): + node = revlog.node(i) + lrev = revlog.linkrev(node) + if lrev >= link: + startrev = i + 1 break - if r < stoprev: - break - for pn in chlog.parents(n): - p[pn] = 1 - return h - def bundle(repo, bases, heads, rev, suffix): - cg = repo.changegroupsubset(bases, heads, 'strip') - backupdir = repo.join("strip-backup") - if not os.path.isdir(backupdir): - os.mkdir(backupdir) - name = os.path.join(backupdir, "%s-%s" % (revlog.short(rev), suffix)) - ui.warn("saving bundle to %s\n" % name) - return changegroup.writebundle(cg, name, "HG10BZ") + # see if any revision after that point has a linkrev less than link + # (we have to manually save these guys) + for i in xrange(startrev, count): + node = revlog.node(i) + lrev = revlog.linkrev(node) + if lrev < link: + extra.append((node, cl.node(lrev))) - def stripall(revnum): - mm = repo.changectx(rev).manifest() - seen = {} + return extra - for x in xrange(revnum, repo.changelog.count()): - for f in repo.changectx(x).files(): - if f in seen: - continue - seen[f] = 1 - if f in mm: - filerev = mm[f] - else: - filerev = 0 - seen[f] = filerev - # we go in two steps here so the strip loop happens in a - # sensible order. When stripping many files, this helps keep - # our disk access patterns under control. - seen_list = seen.keys() - seen_list.sort() - for f in seen_list: - ff = repo.file(f) - filerev = seen[f] - if filerev != 0: - if filerev in ff.nodemap: - filerev = ff.rev(filerev) - else: - filerev = 0 - ff.strip(filerev, revnum) + extranodes = {} + cl = repo.changelog + extra = collectone(repo.manifest) + if extra: + extranodes[1] = extra + for fname in files: + f = repo.file(fname) + extra = collectone(f) + if extra: + extranodes[fname] = extra - chlog = repo.changelog + return extranodes + +def strip(ui, repo, node, backup="all"): + cl = repo.changelog # TODO delete the undo files, and handle undo of merge sets - pp = chlog.parents(rev) - revnum = chlog.rev(rev) + striprev = cl.rev(node) - # save is a list of all the branches we are truncating away - # that we actually want to keep. changegroup will be used - # to preserve them and add them back after the truncate - saveheads = [] - savebases = {} - - heads = limitheads(chlog, rev) - seen = {} + # Some revisions with rev > striprev may not be descendants of striprev. + # We have to find these revisions and put them in a bundle, so that + # we can restore them after the truncations. + # To create the bundle we use repo.changegroupsubset which requires + # the list of heads and bases of the set of interesting revisions. + # (head = revision in the set that has no descendant in the set; + # base = revision in the set that has no ancestor in the set) + tostrip = {striprev: 1} + saveheads = {} + savebases = [] + for r in xrange(striprev + 1, cl.count()): + parents = cl.parentrevs(r) + if parents[0] in tostrip or parents[1] in tostrip: + # r is a descendant of striprev + tostrip[r] = 1 + # if this is a merge and one of the parents does not descend + # from striprev, mark that parent as a savehead. + if parents[1] != nullrev: + for p in parents: + if p not in tostrip and p > striprev: + saveheads[p] = 1 + else: + # if no parents of this revision will be stripped, mark it as + # a savebase + if parents[0] < striprev and parents[1] < striprev: + savebases.append(cl.node(r)) - # search through all the heads, finding those where the revision - # we want to strip away is an ancestor. Also look for merges - # that might be turned into new heads by the strip. - while heads: - h = heads.pop() - n = h - while True: - seen[n] = 1 - pp = chlog.parents(n) - if pp[1] != revlog.nullid: - for p in pp: - if chlog.rev(p) > revnum and p not in seen: - heads.append(p) - if pp[0] == revlog.nullid: - break - if chlog.rev(pp[0]) < revnum: - break - n = pp[0] - if n == rev: - break - r = chlog.reachable(h, rev) - if rev not in r: - saveheads.append(h) - for x in r: - if chlog.rev(x) > revnum: - savebases[x] = 1 + for p in parents: + if p in saveheads: + del saveheads[p] + saveheads[r] = 1 + + saveheads = [cl.node(r) for r in saveheads] + files = _collectfiles(repo, striprev) + + extranodes = _collectextranodes(repo, files, striprev) # create a changegroup for all the branches we need to keep if backup == "all": - bundle(repo, [rev], chlog.heads(), rev, 'backup') - if saveheads: - chgrpfile = bundle(repo, savebases.keys(), saveheads, rev, 'temp') - - stripall(revnum) + _bundle(repo, [node], cl.heads(), node, 'backup') + if saveheads or extranodes: + chgrpfile = _bundle(repo, savebases, saveheads, node, 'temp', + extranodes) - change = chlog.read(rev) - chlog.strip(revnum, revnum) - repo.manifest.strip(repo.manifest.rev(change[0]), revnum) - if saveheads: + cl.strip(striprev) + repo.manifest.strip(striprev) + for name in files: + f = repo.file(name) + f.strip(striprev) + + if saveheads or extranodes: ui.status("adding branch\n") - commands.unbundle(ui, repo, "file:%s" % chgrpfile, update=False) + f = open(chgrpfile, "rb") + gen = changegroup.readbundle(f, chgrpfile) + repo.addchangegroup(gen, 'strip', 'bundle:' + chgrpfile, True) + f.close() if backup != "strip": os.unlink(chgrpfile)
--- a/mercurial/repo.py Wed Apr 09 15:27:57 2008 -0700 +++ b/mercurial/repo.py Wed Apr 09 15:28:30 2008 -0700 @@ -34,3 +34,9 @@ raise NoCapability(_('cannot %s; remote repository does not ' 'support the %r capability') % (purpose, name)) + + def local(self): + return False + + def cancopy(self): + return self.local()
--- a/mercurial/revlog.py Wed Apr 09 15:27:57 2008 -0700 +++ b/mercurial/revlog.py Wed Apr 09 15:28:30 2008 -0700 @@ -10,16 +10,16 @@ of the GNU General Public License, incorporated herein by reference. """ -from node import * +from node import bin, hex, nullid, nullrev, short from i18n import _ -import binascii, changegroup, errno, ancestor, mdiff, os -import sha, struct, util, zlib +import changegroup, errno, ancestor, mdiff +import struct, util, zlib _pack = struct.pack _unpack = struct.unpack _compress = zlib.compress _decompress = zlib.decompress -_sha = sha.new +_sha = util.sha1 # revlog flags REVLOGV0 = 0 @@ -33,11 +33,11 @@ pass class LookupError(RevlogError): - def __init__(self, name, message=None): - if message is None: - message = _('not found: %s') % name - RevlogError.__init__(self, message) + def __init__(self, name, index, message): self.name = name + if isinstance(name, str) and len(name) == 20: + name = short(name) + RevlogError.__init__(self, _('%s@%s: %s') % (index, name, message)) def getoffset(q): return int(q >> 16) @@ -519,7 +519,7 @@ try: return self.nodemap[node] except KeyError: - raise LookupError(hex(node), _('%s: no node %s') % (self.indexfile, hex(node))) + raise LookupError(node, self.indexfile, _('no node')) def node(self, rev): return self.index[rev][7] def linkrev(self, node): @@ -839,8 +839,8 @@ for n in self.nodemap: if n.startswith(bin_id) and hex(n).startswith(id): if node is not None: - raise LookupError(hex(node), - _("Ambiguous identifier")) + raise LookupError(id, self.indexfile, + _('ambiguous identifier')) node = n if node is not None: return node @@ -859,7 +859,7 @@ if n: return n - raise LookupError(id, _("No match found")) + raise LookupError(id, self.indexfile, _('no match found')) def cmp(self, node, text): """compare text with a given file revision""" @@ -933,19 +933,19 @@ raise RevlogError(_('incompatible revision flag %x') % (self.index[rev][0] & 0xFFFF)) - if self._inline: - # we probably have the whole chunk cached - df = None - else: - df = self.opener(self.datafile) + df = None # do we have useful data cached? if self._cache and self._cache[1] >= base and self._cache[1] < rev: base = self._cache[1] text = str(self._cache[2]) self._loadindex(base, rev + 1) + if not self._inline and rev > base + 1: + df = self.opener(self.datafile) else: self._loadindex(base, rev + 1) + if not self._inline and rev > base: + df = self.opener(self.datafile) text = self.chunk(base, df=df) bins = [self.chunk(r, df) for r in xrange(base + 1, rev + 1)] @@ -977,15 +977,18 @@ tr.add(self.datafile, dataoff) df = self.opener(self.datafile, 'w') - calc = self._io.size - for r in xrange(self.count()): - start = self.start(r) + (r + 1) * calc - length = self.length(r) - fp.seek(start) - d = fp.read(length) - df.write(d) + try: + calc = self._io.size + for r in xrange(self.count()): + start = self.start(r) + (r + 1) * calc + length = self.length(r) + fp.seek(start) + d = fp.read(length) + df.write(d) + finally: + df.close() + fp.close() - df.close() fp = self.opener(self.indexfile, 'w', atomictemp=True) self.version &= ~(REVLOGNGINLINEDATA) self._inline = False @@ -1013,7 +1016,12 @@ if not self._inline: dfh = self.opener(self.datafile, "a") ifh = self.opener(self.indexfile, "a+") - return self._addrevision(text, transaction, link, p1, p2, d, ifh, dfh) + try: + return self._addrevision(text, transaction, link, p1, p2, d, ifh, dfh) + finally: + if dfh: + dfh.close() + ifh.close() def _addrevision(self, text, transaction, link, p1, p2, d, ifh, dfh): node = hash(text, p1, p2) @@ -1154,104 +1162,119 @@ transaction.add(self.datafile, end) dfh = self.opener(self.datafile, "a") - # loop through our set of deltas - chain = None - for chunk in revs: - node, p1, p2, cs = struct.unpack("20s20s20s20s", chunk[:80]) - link = linkmapper(cs) - if node in self.nodemap: - # this can happen if two branches make the same change - # if unique: - # raise RevlogError(_("already have %s") % hex(node[:4])) - chain = node - continue - delta = buffer(chunk, 80) - del chunk + try: + # loop through our set of deltas + chain = None + for chunk in revs: + node, p1, p2, cs = struct.unpack("20s20s20s20s", chunk[:80]) + link = linkmapper(cs) + if node in self.nodemap: + # this can happen if two branches make the same change + # if unique: + # raise RevlogError(_("already have %s") % hex(node[:4])) + chain = node + continue + delta = buffer(chunk, 80) + del chunk - for p in (p1, p2): - if not p in self.nodemap: - raise LookupError(hex(p), _("unknown parent %s") % short(p)) - - if not chain: - # retrieve the parent revision of the delta chain - chain = p1 - if not chain in self.nodemap: - raise LookupError(hex(chain), _("unknown base %s") % short(chain[:4])) + for p in (p1, p2): + if not p in self.nodemap: + raise LookupError(p, self.indexfile, _('unknown parent')) - # full versions are inserted when the needed deltas become - # comparable to the uncompressed text or when the previous - # version is not the one we have a delta against. We use - # the size of the previous full rev as a proxy for the - # current size. - - if chain == prev: - cdelta = compress(delta) - cdeltalen = len(cdelta[0]) + len(cdelta[1]) - textlen = mdiff.patchedsize(textlen, delta) + if not chain: + # retrieve the parent revision of the delta chain + chain = p1 + if not chain in self.nodemap: + raise LookupError(chain, self.indexfile, _('unknown base')) - if chain != prev or (end - start + cdeltalen) > textlen * 2: - # flush our writes here so we can read it in revision - if dfh: - dfh.flush() - ifh.flush() - text = self.revision(chain) - if len(text) == 0: - # skip over trivial delta header - text = buffer(delta, 12) - else: - text = mdiff.patches(text, [delta]) - del delta - chk = self._addrevision(text, transaction, link, p1, p2, None, - ifh, dfh) - if not dfh and not self._inline: - # addrevision switched from inline to conventional - # reopen the index - dfh = self.opener(self.datafile, "a") - ifh = self.opener(self.indexfile, "a") - if chk != node: - raise RevlogError(_("consistency error adding group")) - textlen = len(text) - else: - e = (offset_type(end, 0), cdeltalen, textlen, base, - link, self.rev(p1), self.rev(p2), node) - self.index.insert(-1, e) - self.nodemap[node] = r - entry = self._io.packentry(e, self.node, self.version, r) - if self._inline: - ifh.write(entry) - ifh.write(cdelta[0]) - ifh.write(cdelta[1]) - self.checkinlinesize(transaction, ifh) - if not self._inline: + # full versions are inserted when the needed deltas become + # comparable to the uncompressed text or when the previous + # version is not the one we have a delta against. We use + # the size of the previous full rev as a proxy for the + # current size. + + if chain == prev: + cdelta = compress(delta) + cdeltalen = len(cdelta[0]) + len(cdelta[1]) + textlen = mdiff.patchedsize(textlen, delta) + + if chain != prev or (end - start + cdeltalen) > textlen * 2: + # flush our writes here so we can read it in revision + if dfh: + dfh.flush() + ifh.flush() + text = self.revision(chain) + if len(text) == 0: + # skip over trivial delta header + text = buffer(delta, 12) + else: + text = mdiff.patches(text, [delta]) + del delta + chk = self._addrevision(text, transaction, link, p1, p2, None, + ifh, dfh) + if not dfh and not self._inline: + # addrevision switched from inline to conventional + # reopen the index dfh = self.opener(self.datafile, "a") ifh = self.opener(self.indexfile, "a") + if chk != node: + raise RevlogError(_("consistency error adding group")) + textlen = len(text) else: - dfh.write(cdelta[0]) - dfh.write(cdelta[1]) - ifh.write(entry) + e = (offset_type(end, 0), cdeltalen, textlen, base, + link, self.rev(p1), self.rev(p2), node) + self.index.insert(-1, e) + self.nodemap[node] = r + entry = self._io.packentry(e, self.node, self.version, r) + if self._inline: + ifh.write(entry) + ifh.write(cdelta[0]) + ifh.write(cdelta[1]) + self.checkinlinesize(transaction, ifh) + if not self._inline: + dfh = self.opener(self.datafile, "a") + ifh = self.opener(self.indexfile, "a") + else: + dfh.write(cdelta[0]) + dfh.write(cdelta[1]) + ifh.write(entry) - t, r, chain, prev = r, r + 1, node, node - base = self.base(t) - start = self.start(base) - end = self.end(t) + t, r, chain, prev = r, r + 1, node, node + base = self.base(t) + start = self.start(base) + end = self.end(t) + finally: + if dfh: + dfh.close() + ifh.close() return node - def strip(self, rev, minlink): - if self.count() == 0 or rev >= self.count(): + def strip(self, minlink): + """truncate the revlog on the first revision with a linkrev >= minlink + + This function is called when we're stripping revision minlink and + its descendants from the repository. + + We have to remove all revisions with linkrev >= minlink, because + the equivalent changelog revisions will be renumbered after the + strip. + + So we truncate the revlog on the first of these revisions, and + trust that the caller has saved the revisions that shouldn't be + removed and that it'll readd them after this truncation. + """ + if self.count() == 0: return if isinstance(self.index, lazyindex): self._loadindexmap() - # When stripping away a revision, we need to make sure it - # does not actually belong to an older changeset. - # The minlink parameter defines the oldest revision - # we're allowed to strip away. - while minlink > self.index[rev][4]: - rev += 1 - if rev >= self.count(): - return + for rev in xrange(0, self.count()): + if self.index[rev][4] >= minlink: + break + else: + return # first truncate the files on disk end = self.start(rev)
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/mercurial/simplemerge.py Wed Apr 09 15:28:30 2008 -0700 @@ -0,0 +1,456 @@ +#!/usr/bin/env python +# Copyright (C) 2004, 2005 Canonical Ltd +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +# mbp: "you know that thing where cvs gives you conflict markers?" +# s: "i hate that." + +from i18n import _ +import util, mdiff, sys, os + +class CantReprocessAndShowBase(Exception): + pass + +def warn(message): + sys.stdout.flush() + sys.stderr.write(message) + sys.stderr.flush() + +def intersect(ra, rb): + """Given two ranges return the range where they intersect or None. + + >>> intersect((0, 10), (0, 6)) + (0, 6) + >>> intersect((0, 10), (5, 15)) + (5, 10) + >>> intersect((0, 10), (10, 15)) + >>> intersect((0, 9), (10, 15)) + >>> intersect((0, 9), (7, 15)) + (7, 9) + """ + assert ra[0] <= ra[1] + assert rb[0] <= rb[1] + + sa = max(ra[0], rb[0]) + sb = min(ra[1], rb[1]) + if sa < sb: + return sa, sb + else: + return None + +def compare_range(a, astart, aend, b, bstart, bend): + """Compare a[astart:aend] == b[bstart:bend], without slicing. + """ + if (aend-astart) != (bend-bstart): + return False + for ia, ib in zip(xrange(astart, aend), xrange(bstart, bend)): + if a[ia] != b[ib]: + return False + else: + return True + +class Merge3Text(object): + """3-way merge of texts. + + Given strings BASE, OTHER, THIS, tries to produce a combined text + incorporating the changes from both BASE->OTHER and BASE->THIS.""" + def __init__(self, basetext, atext, btext, base=None, a=None, b=None): + self.basetext = basetext + self.atext = atext + self.btext = btext + if base is None: + base = mdiff.splitnewlines(basetext) + if a is None: + a = mdiff.splitnewlines(atext) + if b is None: + b = mdiff.splitnewlines(btext) + self.base = base + self.a = a + self.b = b + + def merge_lines(self, + name_a=None, + name_b=None, + name_base=None, + start_marker='<<<<<<<', + mid_marker='=======', + end_marker='>>>>>>>', + base_marker=None, + reprocess=False): + """Return merge in cvs-like form. + """ + self.conflicts = False + newline = '\n' + if len(self.a) > 0: + if self.a[0].endswith('\r\n'): + newline = '\r\n' + elif self.a[0].endswith('\r'): + newline = '\r' + if base_marker and reprocess: + raise CantReprocessAndShowBase() + if name_a: + start_marker = start_marker + ' ' + name_a + if name_b: + end_marker = end_marker + ' ' + name_b + if name_base and base_marker: + base_marker = base_marker + ' ' + name_base + merge_regions = self.merge_regions() + if reprocess is True: + merge_regions = self.reprocess_merge_regions(merge_regions) + for t in merge_regions: + what = t[0] + if what == 'unchanged': + for i in range(t[1], t[2]): + yield self.base[i] + elif what == 'a' or what == 'same': + for i in range(t[1], t[2]): + yield self.a[i] + elif what == 'b': + for i in range(t[1], t[2]): + yield self.b[i] + elif what == 'conflict': + self.conflicts = True + yield start_marker + newline + for i in range(t[3], t[4]): + yield self.a[i] + if base_marker is not None: + yield base_marker + newline + for i in range(t[1], t[2]): + yield self.base[i] + yield mid_marker + newline + for i in range(t[5], t[6]): + yield self.b[i] + yield end_marker + newline + else: + raise ValueError(what) + + def merge_annotated(self): + """Return merge with conflicts, showing origin of lines. + + Most useful for debugging merge. + """ + for t in self.merge_regions(): + what = t[0] + if what == 'unchanged': + for i in range(t[1], t[2]): + yield 'u | ' + self.base[i] + elif what == 'a' or what == 'same': + for i in range(t[1], t[2]): + yield what[0] + ' | ' + self.a[i] + elif what == 'b': + for i in range(t[1], t[2]): + yield 'b | ' + self.b[i] + elif what == 'conflict': + yield '<<<<\n' + for i in range(t[3], t[4]): + yield 'A | ' + self.a[i] + yield '----\n' + for i in range(t[5], t[6]): + yield 'B | ' + self.b[i] + yield '>>>>\n' + else: + raise ValueError(what) + + def merge_groups(self): + """Yield sequence of line groups. Each one is a tuple: + + 'unchanged', lines + Lines unchanged from base + + 'a', lines + Lines taken from a + + 'same', lines + Lines taken from a (and equal to b) + + 'b', lines + Lines taken from b + + 'conflict', base_lines, a_lines, b_lines + Lines from base were changed to either a or b and conflict. + """ + for t in self.merge_regions(): + what = t[0] + if what == 'unchanged': + yield what, self.base[t[1]:t[2]] + elif what == 'a' or what == 'same': + yield what, self.a[t[1]:t[2]] + elif what == 'b': + yield what, self.b[t[1]:t[2]] + elif what == 'conflict': + yield (what, + self.base[t[1]:t[2]], + self.a[t[3]:t[4]], + self.b[t[5]:t[6]]) + else: + raise ValueError(what) + + def merge_regions(self): + """Return sequences of matching and conflicting regions. + + This returns tuples, where the first value says what kind we + have: + + 'unchanged', start, end + Take a region of base[start:end] + + 'same', astart, aend + b and a are different from base but give the same result + + 'a', start, end + Non-clashing insertion from a[start:end] + + Method is as follows: + + The two sequences align only on regions which match the base + and both descendents. These are found by doing a two-way diff + of each one against the base, and then finding the + intersections between those regions. These "sync regions" + are by definition unchanged in both and easily dealt with. + + The regions in between can be in any of three cases: + conflicted, or changed on only one side. + """ + + # section a[0:ia] has been disposed of, etc + iz = ia = ib = 0 + + for zmatch, zend, amatch, aend, bmatch, bend in self.find_sync_regions(): + #print 'match base [%d:%d]' % (zmatch, zend) + + matchlen = zend - zmatch + assert matchlen >= 0 + assert matchlen == (aend - amatch) + assert matchlen == (bend - bmatch) + + len_a = amatch - ia + len_b = bmatch - ib + len_base = zmatch - iz + assert len_a >= 0 + assert len_b >= 0 + assert len_base >= 0 + + #print 'unmatched a=%d, b=%d' % (len_a, len_b) + + if len_a or len_b: + # try to avoid actually slicing the lists + equal_a = compare_range(self.a, ia, amatch, + self.base, iz, zmatch) + equal_b = compare_range(self.b, ib, bmatch, + self.base, iz, zmatch) + same = compare_range(self.a, ia, amatch, + self.b, ib, bmatch) + + if same: + yield 'same', ia, amatch + elif equal_a and not equal_b: + yield 'b', ib, bmatch + elif equal_b and not equal_a: + yield 'a', ia, amatch + elif not equal_a and not equal_b: + yield 'conflict', iz, zmatch, ia, amatch, ib, bmatch + else: + raise AssertionError("can't handle a=b=base but unmatched") + + ia = amatch + ib = bmatch + iz = zmatch + + # if the same part of the base was deleted on both sides + # that's OK, we can just skip it. + + + if matchlen > 0: + assert ia == amatch + assert ib == bmatch + assert iz == zmatch + + yield 'unchanged', zmatch, zend + iz = zend + ia = aend + ib = bend + + def reprocess_merge_regions(self, merge_regions): + """Where there are conflict regions, remove the agreed lines. + + Lines where both A and B have made the same changes are + eliminated. + """ + for region in merge_regions: + if region[0] != "conflict": + yield region + continue + type, iz, zmatch, ia, amatch, ib, bmatch = region + a_region = self.a[ia:amatch] + b_region = self.b[ib:bmatch] + matches = mdiff.get_matching_blocks(''.join(a_region), + ''.join(b_region)) + next_a = ia + next_b = ib + for region_ia, region_ib, region_len in matches[:-1]: + region_ia += ia + region_ib += ib + reg = self.mismatch_region(next_a, region_ia, next_b, + region_ib) + if reg is not None: + yield reg + yield 'same', region_ia, region_len+region_ia + next_a = region_ia + region_len + next_b = region_ib + region_len + reg = self.mismatch_region(next_a, amatch, next_b, bmatch) + if reg is not None: + yield reg + + def mismatch_region(next_a, region_ia, next_b, region_ib): + if next_a < region_ia or next_b < region_ib: + return 'conflict', None, None, next_a, region_ia, next_b, region_ib + mismatch_region = staticmethod(mismatch_region) + + def find_sync_regions(self): + """Return a list of sync regions, where both descendents match the base. + + Generates a list of (base1, base2, a1, a2, b1, b2). There is + always a zero-length sync region at the end of all the files. + """ + + ia = ib = 0 + amatches = mdiff.get_matching_blocks(self.basetext, self.atext) + bmatches = mdiff.get_matching_blocks(self.basetext, self.btext) + len_a = len(amatches) + len_b = len(bmatches) + + sl = [] + + while ia < len_a and ib < len_b: + abase, amatch, alen = amatches[ia] + bbase, bmatch, blen = bmatches[ib] + + # there is an unconflicted block at i; how long does it + # extend? until whichever one ends earlier. + i = intersect((abase, abase+alen), (bbase, bbase+blen)) + if i: + intbase = i[0] + intend = i[1] + intlen = intend - intbase + + # found a match of base[i[0], i[1]]; this may be less than + # the region that matches in either one + assert intlen <= alen + assert intlen <= blen + assert abase <= intbase + assert bbase <= intbase + + asub = amatch + (intbase - abase) + bsub = bmatch + (intbase - bbase) + aend = asub + intlen + bend = bsub + intlen + + assert self.base[intbase:intend] == self.a[asub:aend], \ + (self.base[intbase:intend], self.a[asub:aend]) + + assert self.base[intbase:intend] == self.b[bsub:bend] + + sl.append((intbase, intend, + asub, aend, + bsub, bend)) + + # advance whichever one ends first in the base text + if (abase + alen) < (bbase + blen): + ia += 1 + else: + ib += 1 + + intbase = len(self.base) + abase = len(self.a) + bbase = len(self.b) + sl.append((intbase, intbase, abase, abase, bbase, bbase)) + + return sl + + def find_unconflicted(self): + """Return a list of ranges in base that are not conflicted.""" + am = mdiff.get_matching_blocks(self.basetext, self.atext) + bm = mdiff.get_matching_blocks(self.basetext, self.btext) + + unc = [] + + while am and bm: + # there is an unconflicted block at i; how long does it + # extend? until whichever one ends earlier. + a1 = am[0][0] + a2 = a1 + am[0][2] + b1 = bm[0][0] + b2 = b1 + bm[0][2] + i = intersect((a1, a2), (b1, b2)) + if i: + unc.append(i) + + if a2 < b2: + del am[0] + else: + del bm[0] + + return unc + +def simplemerge(local, base, other, **opts): + def readfile(filename): + f = open(filename, "rb") + text = f.read() + f.close() + if util.binary(text): + msg = _("%s looks like a binary file.") % filename + if not opts.get('text'): + raise util.Abort(msg) + elif not opts.get('quiet'): + warn(_('warning: %s\n') % msg) + return text + + name_a = local + name_b = other + labels = opts.get('label', []) + if labels: + name_a = labels.pop(0) + if labels: + name_b = labels.pop(0) + if labels: + raise util.Abort(_("can only specify two labels.")) + + localtext = readfile(local) + basetext = readfile(base) + othertext = readfile(other) + + orig = local + local = os.path.realpath(local) + if not opts.get('print'): + opener = util.opener(os.path.dirname(local)) + out = opener(os.path.basename(local), "w", atomictemp=True) + else: + out = sys.stdout + + reprocess = not opts.get('no_minimal') + + m3 = Merge3Text(basetext, localtext, othertext) + for line in m3.merge_lines(name_a=name_a, name_b=name_b, + reprocess=reprocess): + out.write(line) + + if not opts.get('print'): + out.rename() + + if m3.conflicts: + if not opts.get('quiet'): + warn(_("warning: conflicts during merge.\n")) + return 1
--- a/mercurial/sshrepo.py Wed Apr 09 15:27:57 2008 -0700 +++ b/mercurial/sshrepo.py Wed Apr 09 15:28:30 2008 -0700 @@ -5,12 +5,21 @@ # This software may be used and distributed according to the terms # of the GNU General Public License, incorporated herein by reference. -from node import * -from remoterepo import * +from node import bin, hex from i18n import _ -import repo, os, re, stat, util +import repo, os, re, util -class sshrepository(remoterepository): +class remotelock(object): + def __init__(self, repo): + self.repo = repo + def release(self): + self.repo.unlock() + self.repo = None + def __del__(self): + if self.repo: + self.release() + +class sshrepository(repo.repository): def __init__(self, ui, path, create=0): self._url = path self.ui = ui @@ -114,14 +123,25 @@ return self.pipei def call(self, cmd, **args): - r = self.do_cmd(cmd, **args) - l = r.readline() + self.do_cmd(cmd, **args) + return self._recv() + + def _recv(self): + l = self.pipei.readline() self.readerr() try: l = int(l) except: self.raise_(util.UnexpectedOutput(_("unexpected response:"), l)) - return r.read(l) + return self.pipei.read(l) + + def _send(self, data, flush=False): + self.pipeo.write("%d\n" % len(data)) + if data: + self.pipeo.write(data) + if flush: + self.pipeo.flush() + self.readerr() def lock(self): self.call("lock") @@ -182,25 +202,22 @@ while 1: d = cg.read(4096) - if not d: break - self.pipeo.write(str(len(d)) + '\n') - self.pipeo.write(d) - self.readerr() + if not d: + break + self._send(d) - self.pipeo.write('0\n') - self.pipeo.flush() + self._send("", flush=True) - self.readerr() - l = int(self.pipei.readline()) - r = self.pipei.read(l) + r = self._recv() if r: # remote may send "unsynced changes" - self.raise_(hg.RepoError(_("push failed: %s") % r)) + self.raise_(repo.RepoError(_("push failed: %s") % r)) - self.readerr() - l = int(self.pipei.readline()) - r = self.pipei.read(l) - return int(r) + r = self._recv() + try: + return int(r) + except: + self.raise_(util.UnexpectedOutput(_("unexpected response:"), r)) def addchangegroup(self, cg, source, url): d = self.call("addchangegroup") @@ -208,18 +225,21 @@ self.raise_(repo.RepoError(_("push refused: %s") % d)) while 1: d = cg.read(4096) - if not d: break + if not d: + break self.pipeo.write(d) self.readerr() self.pipeo.flush() self.readerr() - l = int(self.pipei.readline()) - r = self.pipei.read(l) + r = self._recv() if not r: return 1 - return int(r) + try: + return int(r) + except: + self.raise_(util.UnexpectedOutput(_("unexpected response:"), r)) def stream_out(self): return self.do_cmd('stream_out')
--- a/mercurial/sshserver.py Wed Apr 09 15:27:57 2008 -0700 +++ b/mercurial/sshserver.py Wed Apr 09 15:28:30 2008 -0700 @@ -7,8 +7,8 @@ # of the GNU General Public License, incorporated herein by reference. from i18n import _ -from node import * -import os, streamclone, sys, tempfile, util +from node import bin, hex +import os, streamclone, sys, tempfile, util, hook class sshserver(object): def __init__(self, ui, repo): @@ -18,6 +18,7 @@ self.fin = sys.stdin self.fout = sys.stdout + hook.redirect(True) sys.stdout = sys.stderr # Prevent insertion/deletion of CRs
--- a/mercurial/statichttprepo.py Wed Apr 09 15:27:57 2008 -0700 +++ b/mercurial/statichttprepo.py Wed Apr 09 15:28:30 2008 -0700 @@ -8,15 +8,17 @@ # of the GNU General Public License, incorporated herein by reference. from i18n import _ -import changelog, filelog, httprangereader -import repo, localrepo, manifest, os, urllib, urllib2, util +import changelog, httprangereader +import repo, localrepo, manifest, util +import urllib, urllib2, errno class rangereader(httprangereader.httprangereader): def read(self, size=None): try: return httprangereader.httprangereader.read(self, size) except urllib2.HTTPError, inst: - raise IOError(None, inst) + num = inst.code == 404 and errno.ENOENT or None + raise IOError(num, inst) except urllib2.URLError, inst: raise IOError(None, inst.reason[1]) @@ -35,11 +37,17 @@ self.path = path.rstrip('/') + "/.hg" self.opener = opener(self.path) + # find requirements try: requirements = self.opener("requires").read().splitlines() - except IOError: - requirements = [] + except IOError, inst: + if inst.errno == errno.ENOENT: + msg = _("'%s' does not appear to be an hg repository") % path + raise repo.RepoError(msg) + else: + requirements = [] + # check them for r in requirements: if r not in self.supported: @@ -66,9 +74,6 @@ def url(self): return 'static-' + self._url - def dev(self): - return -1 - def local(self): return False
--- a/mercurial/streamclone.py Wed Apr 09 15:27:57 2008 -0700 +++ b/mercurial/streamclone.py Wed Apr 09 15:28:30 2008 -0700 @@ -5,7 +5,6 @@ # This software may be used and distributed according to the terms # of the GNU General Public License, incorporated herein by reference. -from i18n import _ import os, osutil, stat, util, lock # if server supports streaming clone, it advertises "stream"
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/mercurial/templatefilters.py Wed Apr 09 15:28:30 2008 -0700 @@ -0,0 +1,154 @@ +# template-filters.py - common template expansion filters +# +# Copyright 2005-2008 Matt Mackall <mpm@selenic.com> +# +# This software may be used and distributed according to the terms +# of the GNU General Public License, incorporated herein by reference. + +import cgi, re, os, time, urllib, textwrap +import util, templater + +agescales = [("second", 1), + ("minute", 60), + ("hour", 3600), + ("day", 3600 * 24), + ("week", 3600 * 24 * 7), + ("month", 3600 * 24 * 30), + ("year", 3600 * 24 * 365)] + +agescales.reverse() + +def age(date): + '''turn a (timestamp, tzoff) tuple into an age string.''' + + def plural(t, c): + if c == 1: + return t + return t + "s" + def fmt(t, c): + return "%d %s" % (c, plural(t, c)) + + now = time.time() + then = date[0] + delta = max(1, int(now - then)) + + for t, s in agescales: + n = delta / s + if n >= 2 or s == 1: + return fmt(t, n) + +para_re = None +space_re = None + +def fill(text, width): + '''fill many paragraphs.''' + global para_re, space_re + if para_re is None: + para_re = re.compile('(\n\n|\n\\s*[-*]\\s*)', re.M) + space_re = re.compile(r' +') + + def findparas(): + start = 0 + while True: + m = para_re.search(text, start) + if not m: + w = len(text) + while w > start and text[w-1].isspace(): w -= 1 + yield text[start:w], text[w:] + break + yield text[start:m.start(0)], m.group(1) + start = m.end(1) + + return "".join([space_re.sub(' ', textwrap.fill(para, width)) + rest + for para, rest in findparas()]) + +def firstline(text): + '''return the first line of text''' + try: + return text.splitlines(1)[0].rstrip('\r\n') + except IndexError: + return '' + +def nl2br(text): + '''replace raw newlines with xhtml line breaks.''' + return text.replace('\n', '<br/>\n') + +def obfuscate(text): + text = unicode(text, util._encoding, 'replace') + return ''.join(['&#%d;' % ord(c) for c in text]) + +def domain(author): + '''get domain of author, or empty string if none.''' + f = author.find('@') + if f == -1: return '' + author = author[f+1:] + f = author.find('>') + if f >= 0: author = author[:f] + return author + +def person(author): + '''get name of author, or else username.''' + f = author.find('<') + if f == -1: return util.shortuser(author) + return author[:f].rstrip() + +def indent(text, prefix): + '''indent each non-empty line of text after first with prefix.''' + lines = text.splitlines() + num_lines = len(lines) + def indenter(): + for i in xrange(num_lines): + l = lines[i] + if i and l.strip(): + yield prefix + yield l + if i < num_lines - 1 or text.endswith('\n'): + yield '\n' + return "".join(indenter()) + +def permissions(flags): + if "l" in flags: + return "lrwxrwxrwx" + if "x" in flags: + return "-rwxr-xr-x" + return "-rw-r--r--" + +def xmlescape(text): + text = (text + .replace('&', '&') + .replace('<', '<') + .replace('>', '>') + .replace('"', '"') + .replace("'", ''')) # ' invalid in HTML + return re.sub('[\x00-\x08\x0B\x0C\x0E-\x1F]', ' ', text) + +filters = { + "addbreaks": nl2br, + "basename": os.path.basename, + "age": age, + "date": lambda x: util.datestr(x), + "domain": domain, + "email": util.email, + "escape": lambda x: cgi.escape(x, True), + "fill68": lambda x: fill(x, width=68), + "fill76": lambda x: fill(x, width=76), + "firstline": firstline, + "tabindent": lambda x: indent(x, '\t'), + "hgdate": lambda x: "%d %d" % x, + "isodate": lambda x: util.datestr(x, '%Y-%m-%d %H:%M %1%2'), + "isodatesec": lambda x: util.datestr(x, '%Y-%m-%d %H:%M:%S %1%2'), + "obfuscate": obfuscate, + "permissions": permissions, + "person": person, + "rfc822date": lambda x: util.datestr(x, "%a, %d %b %Y %H:%M:%S %1%2"), + "rfc3339date": lambda x: util.datestr(x, "%Y-%m-%dT%H:%M:%S%1:%2"), + "short": lambda x: x[:12], + "shortdate": util.shortdate, + "stringify": templater.stringify, + "strip": lambda x: x.strip(), + "urlescape": lambda x: urllib.quote(x), + "user": lambda x: util.shortuser(x), + "stringescape": lambda x: x.encode('string_escape'), + "xmlescape": xmlescape, + } +
--- a/mercurial/templater.py Wed Apr 09 15:27:57 2008 -0700 +++ b/mercurial/templater.py Wed Apr 09 15:28:30 2008 -0700 @@ -6,8 +6,8 @@ # of the GNU General Public License, incorporated herein by reference. from i18n import _ -from node import * -import cgi, re, sys, os, time, urllib, util, textwrap +import re, sys, os +from mercurial import util def parsestring(s, quoted=True): '''parse a string using simple c-like syntax. @@ -56,6 +56,9 @@ if not mapfile: return + if not os.path.exists(mapfile): + raise util.Abort(_('style not found: %s') % mapfile) + i = 0 for l in file(mapfile): l = l.strip() @@ -82,7 +85,7 @@ '''perform expansion. t is name of map element to expand. map is added elements to use during expansion.''' - if not self.cache.has_key(t): + if not t in self.cache: try: self.cache[t] = file(self.map[t]).read() except IOError, inst: @@ -111,7 +114,7 @@ v = v(**map) if format: if not hasattr(v, '__iter__'): - raise SyntaxError(_("Error expanding '%s%s'") + raise SyntaxError(_("Error expanding '%s%%%s'") % (key, format)) lm = map.copy() for i in v: @@ -123,163 +126,6 @@ v = self.filters[f](v) yield v -agescales = [("second", 1), - ("minute", 60), - ("hour", 3600), - ("day", 3600 * 24), - ("week", 3600 * 24 * 7), - ("month", 3600 * 24 * 30), - ("year", 3600 * 24 * 365)] - -agescales.reverse() - -def age(date): - '''turn a (timestamp, tzoff) tuple into an age string.''' - - def plural(t, c): - if c == 1: - return t - return t + "s" - def fmt(t, c): - return "%d %s" % (c, plural(t, c)) - - now = time.time() - then = date[0] - delta = max(1, int(now - then)) - - for t, s in agescales: - n = delta / s - if n >= 2 or s == 1: - return fmt(t, n) - -def stringify(thing): - '''turn nested template iterator into string.''' - if hasattr(thing, '__iter__'): - return "".join([stringify(t) for t in thing if t is not None]) - return str(thing) - -para_re = None -space_re = None - -def fill(text, width): - '''fill many paragraphs.''' - global para_re, space_re - if para_re is None: - para_re = re.compile('(\n\n|\n\\s*[-*]\\s*)', re.M) - space_re = re.compile(r' +') - - def findparas(): - start = 0 - while True: - m = para_re.search(text, start) - if not m: - w = len(text) - while w > start and text[w-1].isspace(): w -= 1 - yield text[start:w], text[w:] - break - yield text[start:m.start(0)], m.group(1) - start = m.end(1) - - return "".join([space_re.sub(' ', textwrap.fill(para, width)) + rest - for para, rest in findparas()]) - -def firstline(text): - '''return the first line of text''' - try: - return text.splitlines(1)[0].rstrip('\r\n') - except IndexError: - return '' - -def isodate(date): - '''turn a (timestamp, tzoff) tuple into an iso 8631 date and time.''' - return util.datestr(date, format='%Y-%m-%d %H:%M') - -def hgdate(date): - '''turn a (timestamp, tzoff) tuple into an hg cset timestamp.''' - return "%d %d" % date - -def nl2br(text): - '''replace raw newlines with xhtml line breaks.''' - return text.replace('\n', '<br/>\n') - -def obfuscate(text): - text = unicode(text, util._encoding, 'replace') - return ''.join(['&#%d;' % ord(c) for c in text]) - -def domain(author): - '''get domain of author, or empty string if none.''' - f = author.find('@') - if f == -1: return '' - author = author[f+1:] - f = author.find('>') - if f >= 0: author = author[:f] - return author - -def email(author): - '''get email of author.''' - r = author.find('>') - if r == -1: r = None - return author[author.find('<')+1:r] - -def person(author): - '''get name of author, or else username.''' - f = author.find('<') - if f == -1: return util.shortuser(author) - return author[:f].rstrip() - -def shortdate(date): - '''turn (timestamp, tzoff) tuple into iso 8631 date.''' - return util.datestr(date, format='%Y-%m-%d', timezone=False) - -def indent(text, prefix): - '''indent each non-empty line of text after first with prefix.''' - lines = text.splitlines() - num_lines = len(lines) - def indenter(): - for i in xrange(num_lines): - l = lines[i] - if i and l.strip(): - yield prefix - yield l - if i < num_lines - 1 or text.endswith('\n'): - yield '\n' - return "".join(indenter()) - -def permissions(flags): - if "l" in flags: - return "lrwxrwxrwx" - if "x" in flags: - return "-rwxr-xr-x" - return "-rw-r--r--" - -common_filters = { - "addbreaks": nl2br, - "basename": os.path.basename, - "age": age, - "date": lambda x: util.datestr(x), - "domain": domain, - "email": email, - "escape": lambda x: cgi.escape(x, True), - "fill68": lambda x: fill(x, width=68), - "fill76": lambda x: fill(x, width=76), - "firstline": firstline, - "tabindent": lambda x: indent(x, '\t'), - "hgdate": hgdate, - "isodate": isodate, - "obfuscate": obfuscate, - "permissions": permissions, - "person": person, - "rfc822date": lambda x: util.datestr(x, "%a, %d %b %Y %H:%M:%S"), - "rfc3339date": lambda x: util.datestr(x, "%Y-%m-%dT%H:%M:%S", True, "%+03d:%02d"), - "short": lambda x: x[:12], - "shortdate": shortdate, - "stringify": stringify, - "strip": lambda x: x.strip(), - "urlescape": lambda x: urllib.quote(x), - "user": lambda x: util.shortuser(x), - "stringescape": lambda x: x.encode('string_escape'), - } - def templatepath(name=None): '''return location of template file or directory (if no name). returns None if not found.''' @@ -296,3 +142,9 @@ if (name and os.path.exists(p)) or os.path.isdir(p): return os.path.normpath(p) +def stringify(thing): + '''turn nested template iterator into string.''' + if hasattr(thing, '__iter__'): + return "".join([stringify(t) for t in thing if t is not None]) + return str(thing) +
--- a/mercurial/transaction.py Wed Apr 09 15:27:57 2008 -0700 +++ b/mercurial/transaction.py Wed Apr 09 15:28:30 2008 -0700 @@ -15,13 +15,9 @@ import os class transaction(object): - def __init__(self, report, opener, journal, after=None): + def __init__(self, report, opener, journal, after=None, createmode=None): self.journal = None - # abort here if the journal already exists - if os.path.exists(journal): - raise AssertionError(_("journal already exists - run hg recover")) - self.count = 1 self.report = report self.opener = opener @@ -31,6 +27,8 @@ self.journal = journal self.file = open(self.journal, "w") + if createmode is not None: + os.chmod(self.journal, createmode & 0666) def __del__(self): if self.journal: @@ -98,9 +96,13 @@ files = {} for l in open(file).readlines(): f, o = l.split('\0') - files[f] = o + files[f] = int(o) for f in files: o = files[f] - opener(f, "a").truncate(int(o)) + if o: + opener(f, "a").truncate(int(o)) + else: + fn = opener(f).name + os.unlink(fn) os.unlink(file)
--- a/mercurial/ui.py Wed Apr 09 15:27:57 2008 -0700 +++ b/mercurial/ui.py Wed Apr 09 15:28:30 2008 -0700 @@ -60,6 +60,11 @@ self.ucdata = dupconfig(self.parentui.ucdata) if self.parentui.overlay: self.overlay = dupconfig(self.parentui.overlay) + if self.parentui is not parentui and parentui.overlay is not None: + if self.overlay is None: + self.overlay = util.configparser() + updateconfig(parentui.overlay, self.overlay) + self.buffers = parentui.buffers def __getattr__(self, key): return getattr(self.parentui, key) @@ -204,7 +209,8 @@ pathsitems = items for n, path in pathsitems: if path and "://" not in path and not os.path.isabs(path): - cdata.set("paths", n, os.path.join(root, path)) + cdata.set("paths", n, + os.path.normpath(os.path.join(root, path))) # update verbosity/interactive/report_untrusted settings if section is None or section == 'ui': @@ -345,6 +351,8 @@ pass if not user: raise util.Abort(_("Please specify a username.")) + if "\n" in user: + raise util.Abort(_("username %s contains a newline\n") % `user`) return user def shortuser(self, user): @@ -410,16 +418,23 @@ line = line[:-1] return line - def prompt(self, msg, pat=None, default="y", matchflags=0): + def prompt(self, msg, pat=None, default="y"): + """Prompt user with msg, read response, and ensure it matches pat + + If not interactive -- the default is returned + """ if not self.interactive: return default - try: - r = self._readline(msg + ' ') - if not pat or re.match(pat, r, matchflags): - return r - else: - self.write(_("unrecognized response\n")) - except EOFError: - raise util.Abort(_('response expected')) + while True: + try: + r = self._readline(msg + ' ') + if not r: + return default + if not pat or re.match(pat, r): + return r + else: + self.write(_("unrecognized response\n")) + except EOFError: + raise util.Abort(_('response expected')) def getpass(self, prompt=None, default=None): if not self.interactive: return default @@ -469,4 +484,3 @@ self.config("ui", "editor") or os.environ.get("VISUAL") or os.environ.get("EDITOR", "vi")) -
--- a/mercurial/util.py Wed Apr 09 15:27:57 2008 -0700 +++ b/mercurial/util.py Wed Apr 09 15:28:30 2008 -0700 @@ -13,9 +13,11 @@ """ from i18n import _ -import cStringIO, errno, getpass, popen2, re, shutil, sys, tempfile, strutil +import cStringIO, errno, getpass, re, shutil, sys, tempfile import os, stat, threading, time, calendar, ConfigParser, locale, glob, osutil -import re, urlparse, imp +import imp, urlparse + +# Python compatibility try: set = set @@ -23,6 +25,30 @@ except NameError: from sets import Set as set, ImmutableSet as frozenset +_md5 = None +def md5(s): + global _md5 + if _md5 is None: + try: + import hashlib + _md5 = hashlib.md5 + except ImportError: + import md5 + _md5 = md5.md5 + return _md5(s) + +_sha1 = None +def sha1(s): + global _sha1 + if _sha1 is None: + try: + import hashlib + _sha1 = hashlib.sha1 + except ImportError: + import sha + _sha1 = sha.sha + return _sha1(s) + try: _encoding = os.environ.get("HGENCODING") if sys.platform == 'darwin' and not _encoding: @@ -81,18 +107,6 @@ """Find the length in characters of a local string""" return len(s.decode(_encoding, "replace")) -def localsub(s, a, b=None): - try: - u = s.decode(_encoding, _encodingmode) - if b is not None: - u = u[a:b] - else: - u = u[:a] - return u.encode(_encoding, _encodingmode) - except UnicodeDecodeError, inst: - sub = s[max(0, inst.start-10), inst.start+10] - raise Abort(_("decoding near '%s': %s!") % (sub, inst)) - # used by parsedate defaultdateformats = ( '%Y-%m-%d %H:%M:%S', @@ -236,13 +250,7 @@ def unique(g): """return the uniq elements of iterable g""" - seen = {} - l = [] - for f in g: - if f not in seen: - seen[f] = 1 - l.append(f) - return l + return dict.fromkeys(g).keys() class Abort(Exception): """Raised if a command needs to print an error and exit.""" @@ -280,7 +288,7 @@ "convert a glob pattern into a regexp" i, n = 0, len(pat) res = '' - group = False + group = 0 def peek(): return i < n and pat[i] while i < n: c = pat[i] @@ -310,11 +318,11 @@ stuff = '\\' + stuff res = '%s[%s]' % (res, stuff) elif c == '{': - group = True + group += 1 res += '(?:' elif c == '}' and group: res += ')' - group = False + group -= 1 elif c == ',' and group: res += '|' elif c == '\\': @@ -346,20 +354,20 @@ if os.path.splitdrive(root)[0] != os.path.splitdrive(n1)[0]: return os.path.join(root, localpath(n2)) n2 = '/'.join((pconvert(root), n2)) - a, b = n1.split(os.sep), n2.split('/') + a, b = splitpath(n1), n2.split('/') a.reverse() b.reverse() while a and b and a[-1] == b[-1]: a.pop() b.pop() b.reverse() - return os.sep.join((['..'] * len(a)) + b) + return os.sep.join((['..'] * len(a)) + b) or '.' def canonpath(root, cwd, myname): """return the canonical path of myname, given cwd and root""" if root == os.sep: rootsep = os.sep - elif root.endswith(os.sep): + elif endswithsep(root): rootsep = root else: rootsep = root + os.sep @@ -477,6 +485,8 @@ return try: pat = '(?:%s)' % '|'.join([regex(k, p, tail) for (k, p) in pats]) + if len(pat) > 20000: + raise OverflowError() return re.compile(pat).match except OverflowError: # We're using a Python with a tiny regex engine and we @@ -726,7 +736,7 @@ if path in self.audited: return normpath = os.path.normcase(path) - parts = normpath.split(os.sep) + parts = splitpath(normpath) if (os.path.splitdrive(path)[0] or parts[0] in ('.hg', '') or os.pardir in parts): raise Abort(_("path contains illegal component: %s") % path) @@ -747,14 +757,15 @@ os.path.isdir(os.path.join(curpath, '.hg'))): raise Abort(_('path %r is inside repo %r') % (path, prefix)) - + parts.pop() prefixes = [] - for c in strutil.rfindall(normpath, os.sep): - prefix = normpath[:c] + for n in range(len(parts)): + prefix = os.sep.join(parts) if prefix in self.auditeddir: break check(prefix) prefixes.append(prefix) + parts.pop() self.audited.add(path) # only add prefixes to the cache after checking everything: we don't @@ -864,18 +875,23 @@ Requires a directory (like /foo/.hg) """ + + # VFAT on some Linux versions can flip mode but it doesn't persist + # a FS remount. Frequently we can detect it if files are created + # with exec bit on. + try: EXECFLAGS = stat.S_IXUSR | stat.S_IXGRP | stat.S_IXOTH fh, fn = tempfile.mkstemp("", "", path) - os.close(fh) - m = os.stat(fn).st_mode - # VFAT on Linux can flip mode but it doesn't persist a FS remount. - # frequently we can detect it if files are created with exec bit on. - new_file_has_exec = m & EXECFLAGS - os.chmod(fn, m ^ EXECFLAGS) - exec_flags_cannot_flip = (os.stat(fn).st_mode == m) - os.unlink(fn) - except (IOError,OSError): + try: + os.close(fh) + m = os.stat(fn).st_mode & 0777 + new_file_has_exec = m & EXECFLAGS + os.chmod(fn, m ^ EXECFLAGS) + exec_flags_cannot_flip = ((os.stat(fn).st_mode & 0777) == m) + finally: + os.unlink(fn) + except (IOError, OSError): # we don't care, the user probably won't be able to commit anyway return False return not (new_file_has_exec or exec_flags_cannot_flip) @@ -911,6 +927,25 @@ """return True if patches should be applied in binary mode by default.""" return os.name == 'nt' +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) + +def splitpath(path): + '''Split path by os.sep. + Note that this function does not use os.altsep because this is + an alternative of simple "xxx.split(os.sep)". + It is recommended to use os.path.normpath() before using this + function if need.''' + return path.split(os.sep) + +def gui(): + '''Are we running in a GUI?''' + return os.name == "nt" or os.name == "mac" or os.environ.get("DISPLAY") + +def lookup_reg(key, name=None, scope=None): + return None + # Platform specific variants if os.name == 'nt': import msvcrt @@ -975,13 +1010,15 @@ def user_rcpath(): '''return os-specific hgrc search path to the user dir''' try: - userrc = user_rcpath_win32() + path = user_rcpath_win32() except: - userrc = os.path.join(os.path.expanduser('~'), 'mercurial.ini') - path = [userrc] + 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 parse_patch_output(output_line): @@ -1001,17 +1038,17 @@ '''return False if pid dead, True if running or not known''' return True - def set_exec(f, mode): - pass - - def set_link(f, mode): + def set_flags(f, flags): pass def set_binary(fd): - msvcrt.setmode(fd.fileno(), os.O_BINARY) + # When run without console, pipes may expose invalid + # fileno(), usually set to -1. + if hasattr(fd, 'fileno') and fd.fileno() >= 0: + msvcrt.setmode(fd.fileno(), os.O_BINARY) def pconvert(path): - return path.replace("\\", "/") + return '/'.join(splitpath(path)) def localpath(path): return path.replace('/', '\\') @@ -1148,37 +1185,34 @@ """check whether a file is executable""" return (os.lstat(f).st_mode & 0100 != 0) - def set_exec(f, mode): + def set_flags(f, flags): s = os.lstat(f).st_mode - if stat.S_ISLNK(s) or (s & 0100 != 0) == mode: + x = "x" in flags + l = "l" in flags + if l: + if not stat.S_ISLNK(s): + # switch file to link + data = file(f).read() + os.unlink(f) + os.symlink(data, f) + # no chmod needed at this point return - if mode: + if stat.S_ISLNK(s): + # switch link to file + data = os.readlink(f) + os.unlink(f) + file(f, "w").write(data) + s = 0666 & ~_umask # avoid restatting for chmod + + sx = s & 0100 + if x and not sx: # Turn on +x for every +r bit when making a file executable # and obey umask. os.chmod(f, s | (s & 0444) >> 2 & ~_umask) - else: + elif not x and sx: + # Turn off all +x bits os.chmod(f, s & 0666) - def set_link(f, mode): - """make a file a symbolic link/regular file - - if a file is changed to a link, its contents become the link data - if a link is changed to a file, its link data become its contents - """ - - m = os.path.islink(f) - if m == bool(mode): - return - - if mode: # switch file to link - data = file(f).read() - os.unlink(f) - os.symlink(data, f) - else: - data = os.readlink(f) - os.unlink(f) - file(f, "w").write(data) - def set_binary(fd): pass @@ -1313,7 +1347,7 @@ return openerfn(fn(path), *args, **kw) return o -def mktempcopy(name, emptyok=False): +def mktempcopy(name, emptyok=False, createmode=None): """Create a temporary file with the same contents from name The permission bits are copied from the original file. @@ -1330,11 +1364,14 @@ # what we want. If the original file already exists, just copy # its mode. Otherwise, manually obey umask. try: - st_mode = os.lstat(name).st_mode + st_mode = os.lstat(name).st_mode & 0777 except OSError, inst: if inst.errno != errno.ENOENT: raise - st_mode = 0666 & ~_umask + st_mode = createmode + if st_mode is None: + st_mode = ~_umask + st_mode &= 0666 os.chmod(temp, st_mode) if emptyok: return temp @@ -1365,9 +1402,10 @@ file. When rename is called, the copy is renamed to the original name, making the changes visible. """ - def __init__(self, name, mode): + def __init__(self, name, mode, createmode): self.__name = name - self.temp = mktempcopy(name, emptyok=('w' in mode)) + self.temp = mktempcopy(name, emptyok=('w' in mode), + createmode=createmode) posixfile.__init__(self, self.temp, mode) def rename(self): @@ -1382,6 +1420,22 @@ except: pass posixfile.close(self) +def makedirs(name, mode=None): + """recursive directory creation with parent mode inheritance""" + try: + os.mkdir(name) + if mode is not None: + os.chmod(name, mode) + return + except OSError, err: + if err.errno == errno.EEXIST: + return + if err.errno != errno.ENOENT: + raise + parent = os.path.abspath(os.path.dirname(name)) + makedirs(parent, mode) + makedirs(name, mode) + class opener(object): """Open files relative to a base directory @@ -1394,6 +1448,7 @@ self.audit_path = path_auditor(base) else: self.audit_path = always + self.createmode = None def __getattr__(self, name): if name == '_can_symlink': @@ -1401,6 +1456,11 @@ return self._can_symlink raise AttributeError(name) + def _fixfilemode(self, name): + if self.createmode is None: + return + os.chmod(name, self.createmode & 0666) + def __call__(self, path, mode="r", text=False, atomictemp=False): self.audit_path(path) f = os.path.join(self.base, path) @@ -1408,6 +1468,7 @@ if not text and "b" not in mode: mode += "b" # for that other OS + nlink = -1 if mode[0] != "r": try: nlink = nlinks(f) @@ -1415,12 +1476,15 @@ nlink = 0 d = os.path.dirname(f) if not os.path.isdir(d): - os.makedirs(d) + makedirs(d, self.createmode) if atomictemp: - return atomictempfile(f, mode) + return atomictempfile(f, mode, self.createmode) if nlink > 1: rename(mktempcopy(f), f) - return posixfile(f, mode) + fp = posixfile(f, mode) + if nlink == 0: + self._fixfilemode(f) + return fp def symlink(self, src, dst): self.audit_path(dst) @@ -1432,7 +1496,7 @@ dirname = os.path.dirname(linkname) if not os.path.exists(dirname): - os.makedirs(dirname) + makedirs(dirname, self.createmode) if self._can_symlink: try: @@ -1444,6 +1508,7 @@ f = self(dst, "w") f.write(src) f.close() + self._fixfilemode(dst) class chunkbuffer(object): """Allow arbitrary sized chunks of data to be efficiently read from an @@ -1504,26 +1569,34 @@ tz = time.timezone return time.mktime(lt), tz -def datestr(date=None, format='%a %b %d %H:%M:%S %Y', timezone=True, timezone_format=" %+03d%02d"): +def datestr(date=None, format='%a %b %d %H:%M:%S %Y %1%2'): """represent a (unixtime, offset) tuple as a localized time. unixtime is seconds since the epoch, and offset is the time zone's number of seconds away from UTC. if timezone is false, do not append time zone to string.""" t, tz = date or makedate() + if "%1" in format or "%2" in format: + sign = (tz > 0) and "-" or "+" + minutes = abs(tz) / 60 + format = format.replace("%1", "%c%02d" % (sign, minutes / 60)) + format = format.replace("%2", "%02d" % (minutes % 60)) s = time.strftime(format, time.gmtime(float(t) - tz)) - if timezone: - s += timezone_format % (-tz / 3600, ((-tz % 3600) / 60)) return s +def shortdate(date=None): + """turn (timestamp, tzoff) tuple into iso 8631 date.""" + return datestr(date, format='%Y-%m-%d') + def strdate(string, format, defaults=[]): """parse a localized time string and return a (unixtime, offset) tuple. if the string cannot be parsed, ValueError is raised.""" def timezone(string): tz = string.split()[-1] if tz[0] in "+-" and len(tz) == 5 and tz[1:].isdigit(): - tz = int(tz) - offset = - 3600 * (tz / 100) - 60 * (tz % 100) - return offset + sign = (tz[0] == "+") and 1 or -1 + hours = int(tz[1:3]) + minutes = int(tz[3:5]) + return -sign * (hours * 60 + minutes) * 60 if tz == "GMT" or tz == "UTC": return 0 return None @@ -1550,17 +1623,21 @@ unixtime = localunixtime + offset return unixtime, offset -def parsedate(string, formats=None, defaults=None): - """parse a localized time string and return a (unixtime, offset) tuple. +def parsedate(date, formats=None, defaults=None): + """parse a localized date/time string and return a (unixtime, offset) tuple. + The date may be a "unixtime offset" string or in one of the specified - formats.""" - if not string: + formats. If the date already is a (unixtime, offset) tuple, it is returned. + """ + if not date: return 0, 0 + if isinstance(date, tuple) and len(date) == 2: + return date if not formats: formats = defaultdateformats - string = string.strip() + date = date.strip() try: - when, offset = map(int, string.split(' ')) + when, offset = map(int, date.split(' ')) except ValueError: # fill out defaults if not defaults: @@ -1570,20 +1647,18 @@ if part not in defaults: if part[0] in "HMS": defaults[part] = "00" - elif part[0] in "dm": - defaults[part] = "1" else: - defaults[part] = datestr(now, "%" + part[0], False) + defaults[part] = datestr(now, "%" + part[0]) for format in formats: try: - when, offset = strdate(string, format, defaults) - except ValueError: + when, offset = strdate(date, format, defaults) + except (ValueError, OverflowError): pass else: break else: - raise Abort(_('invalid date: %r ') % string) + raise Abort(_('invalid date: %r ') % date) # validate explicit (probably user-specified) date and # time zone offset. values must fit in signed 32 bits for # current 32-bit linux runtimes. timezones go from UTC-12 @@ -1608,7 +1683,8 @@ """ def lower(date): - return parsedate(date, extendeddateformats)[0] + d = dict(mb="1", d="1") + return parsedate(date, extendeddateformats, d)[0] def upper(date): d = dict(mb="12", HI="23", M="59", S="59") @@ -1658,6 +1734,12 @@ user = user[:f] return user +def email(author): + '''get email of author.''' + r = author.find('>') + if r == -1: r = None + return author[author.find('<')+1:r] + def ellipsis(text, maxlength=400): """Trim string to at most maxlength (default: 400) characters.""" if len(text) <= maxlength: @@ -1665,18 +1747,47 @@ else: return "%s..." % (text[:maxlength-3]) -def walkrepos(path): +def walkrepos(path, followsym=False, seen_dirs=None): '''yield every hg repository under path, recursively.''' def errhandler(err): if err.filename == path: raise err + if followsym and hasattr(os.path, 'samestat'): + def _add_dir_if_not_there(dirlst, dirname): + match = False + samestat = os.path.samestat + dirstat = os.stat(dirname) + for lstdirstat in dirlst: + if samestat(dirstat, lstdirstat): + match = True + break + if not match: + dirlst.append(dirstat) + return not match + else: + followsym = False - for root, dirs, files in os.walk(path, onerror=errhandler): - for d in dirs: - if d == '.hg': - yield root - dirs[:] = [] - break + if (seen_dirs is None) and followsym: + seen_dirs = [] + _add_dir_if_not_there(seen_dirs, path) + for root, dirs, files in os.walk(path, topdown=True, onerror=errhandler): + if '.hg' in dirs: + dirs[:] = [] # don't descend further + yield root # found a repository + qroot = os.path.join(root, '.hg', 'patches') + if os.path.isdir(os.path.join(qroot, '.hg')): + yield qroot # we have a patch queue repo here + elif followsym: + newdirs = [] + for d in dirs: + fname = os.path.join(root, d) + if _add_dir_if_not_there(seen_dirs, fname): + if os.path.islink(fname): + for hgname in walkrepos(fname, True, seen_dirs): + yield hgname + else: + newdirs.append(d) + dirs[:] = newdirs _rcpath = None @@ -1743,31 +1854,13 @@ return repr(s).replace('\\\\', '\\') def hidepassword(url): - '''replaces the password in the url string by three asterisks (***) - - >>> hidepassword('http://www.example.com/some/path#fragment') - 'http://www.example.com/some/path#fragment' - >>> hidepassword('http://me@www.example.com/some/path#fragment') - 'http://me@www.example.com/some/path#fragment' - >>> hidepassword('http://me:simplepw@www.example.com/path#frag') - 'http://me:***@www.example.com/path#frag' - >>> hidepassword('http://me:complex:pw@www.example.com/path#frag') - 'http://me:***@www.example.com/path#frag' - >>> hidepassword('/path/to/repo') - '/path/to/repo' - >>> hidepassword('relative/path/to/repo') - 'relative/path/to/repo' - >>> hidepassword('c:\\\\path\\\\to\\\\repo') - 'c:\\\\path\\\\to\\\\repo' - >>> hidepassword('c:/path/to/repo') - 'c:/path/to/repo' - >>> hidepassword('bundle://path/to/bundle') - 'bundle://path/to/bundle' - ''' - url_parts = list(urlparse.urlparse(url)) - host_with_pw_pattern = re.compile('^([^:]*):([^@]*)@(.*)$') - if host_with_pw_pattern.match(url_parts[1]): - url_parts[1] = re.sub(host_with_pw_pattern, r'\1:***@\3', - url_parts[1]) - return urlparse.urlunparse(url_parts) + '''hide user credential in a url string''' + scheme, netloc, path, params, query, fragment = urlparse.urlparse(url) + netloc = re.sub('([^:]*):([^@]*)@(.*)', r'\1:***@\3', netloc) + return urlparse.urlunparse((scheme, netloc, path, params, query, fragment)) +def removeauth(url): + '''remove all authentication information from a url string''' + scheme, netloc, path, params, query, fragment = urlparse.urlparse(url) + netloc = netloc[netloc.find('@')+1:] + return urlparse.urlunparse((scheme, netloc, path, params, query, fragment))
--- a/mercurial/util_win32.py Wed Apr 09 15:27:57 2008 -0700 +++ b/mercurial/util_win32.py Wed Apr 09 15:28:30 2008 -0700 @@ -13,8 +13,7 @@ import win32api -from i18n import _ -import errno, os, pywintypes, win32con, win32file, win32process +import errno, os, sys, pywintypes, win32con, win32file, win32process import cStringIO, winerror import osutil from win32com.shell import shell,shellcon @@ -147,9 +146,18 @@ self.win_strerror) def os_link(src, dst): - # NB will only succeed on NTFS try: win32file.CreateHardLink(dst, src) + # CreateHardLink sometimes succeeds on mapped drives but + # following nlinks() returns 1. Check it now and bail out. + if nlinks(src) < 2: + try: + win32file.DeleteFile(dst) + except: + pass + # Fake hardlinking error + raise WinOSError((18, 'CreateHardLink', 'The system cannot ' + 'move the file to a different disk drive')) except pywintypes.error, details: raise WinOSError(details) @@ -178,6 +186,37 @@ return details[0] != winerror.ERROR_INVALID_PARAMETER return True +def lookup_reg(key, valname=None, scope=None): + ''' Look up a key/value name in the Windows registry. + + valname: value name. If unspecified, the default value for the key + is used. + scope: optionally specify scope for registry lookup, this can be + a sequence of scopes to look up in order. Default (CURRENT_USER, + LOCAL_MACHINE). + ''' + try: + from _winreg import HKEY_CURRENT_USER, HKEY_LOCAL_MACHINE, \ + QueryValueEx, OpenKey + except ImportError: + return None + + def query_val(scope, key, valname): + try: + keyhandle = OpenKey(scope, key) + return QueryValueEx(keyhandle, valname)[0] + except EnvironmentError: + return None + + if scope is None: + scope = (HKEY_CURRENT_USER, HKEY_LOCAL_MACHINE) + elif not isinstance(scope, (list, tuple)): + scope = (scope,) + for s in scope: + val = query_val(s, key, valname) + if val is not None: + return val + def system_rcpath_win32(): '''return default os-specific hgrc search path''' proc = win32api.GetCurrentProcess() @@ -215,7 +254,8 @@ appdir = shell.SHGetPathFromIDList( shell.SHGetSpecialFolderLocation(0, shellcon.CSIDL_APPDATA)) userdir = os.path.dirname(appdir) - return os.path.join(userdir, 'mercurial.ini') + return [os.path.join(userdir, 'mercurial.ini'), + os.path.join(userdir, '.hgrc')] class posixfile_nt(object): '''file object with posix-like semantics. on windows, normal @@ -227,6 +267,9 @@ # but does not work at all. wrap win32 file api instead. def __init__(self, name, mode='rb'): + self.closed = False + self.name = name + self.mode = mode access = 0 if 'r' in mode or '+' in mode: access |= win32file.GENERIC_READ @@ -250,9 +293,6 @@ 0) except pywintypes.error, err: raise WinIOError(err, name) - self.closed = False - self.name = name - self.mode = mode def __iter__(self): for line in self.read().splitlines(True): @@ -285,6 +325,10 @@ except pywintypes.error, err: raise WinIOError(err) + def writelines(self, sequence): + for s in sequence: + self.write(s) + def seek(self, pos, whence=0): try: win32file.SetFilePointer(self.handle, int(pos), whence) @@ -304,10 +348,8 @@ self.closed = True def flush(self): - try: - win32file.FlushFileBuffers(self.handle) - except pywintypes.error, err: - raise WinIOError(err) + # we have no application-level buffering + pass def truncate(self, pos=0): try:
--- a/mercurial/verify.py Wed Apr 09 15:27:57 2008 -0700 +++ b/mercurial/verify.py Wed Apr 09 15:28:30 2008 -0700 @@ -5,7 +5,7 @@ # This software may be used and distributed according to the terms # of the GNU General Public License, incorporated herein by reference. -from node import * +from node import nullid, short from i18n import _ import revlog
--- a/mercurial/version.py Wed Apr 09 15:27:57 2008 -0700 +++ b/mercurial/version.py Wed Apr 09 15:28:30 2008 -0700 @@ -1,4 +1,4 @@ -# Copyright (C) 2005, 2006 by Intevation GmbH +# Copyright (C) 2005, 2006, 2008 by Intevation GmbH # Author(s): # Thomas Arendsen Hein <thomas@intevation.de> # @@ -10,10 +10,8 @@ """ import os -import os.path import re import time -import util unknown_version = 'unknown' remembered_version = False @@ -50,7 +48,7 @@ """Store version information.""" global remembered_version if not version and os.path.isdir(".hg"): - f = util.popen("hg identify") # use real hg installation + f = os.popen("hg identify") # use real hg installation ident = f.read()[:-1] if not f.close() and ident: ids = ident.split(' ', 1)
--- a/setup.py Wed Apr 09 15:27:57 2008 -0700 +++ b/setup.py Wed Apr 09 15:28:30 2008 -0700 @@ -10,13 +10,48 @@ raise SystemExit, "Mercurial requires python 2.3 or later." import os +import shutil +import tempfile from distutils.core import setup, Extension from distutils.command.install_data import install_data +from distutils.ccompiler import new_compiler import mercurial.version extra = {} +# simplified version of distutils.ccompiler.CCompiler.has_function +# that actually removes its temporary files. +def has_function(cc, funcname): + tmpdir = tempfile.mkdtemp(prefix='hg-install-') + devnull = oldstderr = None + try: + try: + fname = os.path.join(tmpdir, 'funcname.c') + f = open(fname, 'w') + f.write('int main(void) {\n') + f.write(' %s();\n' % funcname) + f.write('}\n') + f.close() + # Redirect stderr to /dev/null to hide any error messages + # from the compiler. + # This will have to be changed if we ever have to check + # for a function on Windows. + devnull = open('/dev/null', 'w') + oldstderr = os.dup(sys.stderr.fileno()) + os.dup2(devnull.fileno(), sys.stderr.fileno()) + objects = cc.compile([fname]) + cc.link_executable(objects, os.path.join(tmpdir, "a.out")) + except: + return False + return True + finally: + if oldstderr is not None: + os.dup2(oldstderr, sys.stderr.fileno()) + if devnull is not None: + devnull.close() + shutil.rmtree(tmpdir) + # py2exe needs to be installed to work try: import py2exe @@ -40,11 +75,6 @@ except ImportError: pass -if os.name in ['nt']: - extra['scripts'] = ['hg'] -else: - extra['scripts'] = ['hg', 'hgmerge'] - # specify version string, otherwise 'hg identify' will be used: version = '' @@ -58,15 +88,27 @@ cmdclass = {'install_data': install_package_data} ext_modules=[ - Extension('mercurial.mpatch', ['mercurial/mpatch.c']), + Extension('mercurial.base85', ['mercurial/base85.c']), Extension('mercurial.bdiff', ['mercurial/bdiff.c']), - Extension('mercurial.base85', ['mercurial/base85.c']), - Extension('mercurial.diffhelpers', ['mercurial/diffhelpers.c']) + Extension('mercurial.diffhelpers', ['mercurial/diffhelpers.c']), + Extension('mercurial.mpatch', ['mercurial/mpatch.c']), + Extension('mercurial.parsers', ['mercurial/parsers.c']), ] +packages = ['mercurial', 'mercurial.hgweb', 'hgext', 'hgext.convert'] + try: import posix ext_modules.append(Extension('mercurial.osutil', ['mercurial/osutil.c'])) + + if sys.platform == 'linux2' and os.uname()[2] > '2.6': + # The inotify extension is only usable with Linux 2.6 kernels. + # You also need a reasonably recent C library. + cc = new_compiler() + if has_function(cc, 'inotify_add_watch'): + ext_modules.append(Extension('hgext.inotify.linux._inotify', + ['hgext/inotify/linux/_inotify.c'])) + packages.extend(['hgext.inotify', 'hgext.inotify.linux']) except ImportError: pass @@ -77,7 +119,8 @@ url='http://selenic.com/mercurial', description='Scalable distributed SCM', license='GNU GPL', - packages=['mercurial', 'mercurial.hgweb', 'hgext', 'hgext.convert'], + scripts=['hg'], + packages=packages, ext_modules=ext_modules, data_files=[(os.path.join('mercurial', root), [os.path.join(root, file_) for file_ in files])
--- a/templates/atom/header.tmpl Wed Apr 09 15:27:57 2008 -0700 +++ b/templates/atom/header.tmpl Wed Apr 09 15:28:30 2008 -0700 @@ -1,4 +1,2 @@ -Content-type: application/atom+xml; charset={encoding} - <?xml version="1.0" encoding="{encoding}"?> <feed xmlns="http://www.w3.org/2005/Atom"> \ No newline at end of file
--- a/templates/atom/map Wed Apr 09 15:27:57 2008 -0700 +++ b/templates/atom/map Wed Apr 09 15:28:30 2008 -0700 @@ -1,5 +1,6 @@ default = 'changelog' feedupdated = '<updated>#date|rfc3339date#</updated>' +mimetype = 'application/atom+xml; charset={encoding}' header = header.tmpl changelog = changelog.tmpl changelogentry = changelogentry.tmpl
--- a/templates/changelog.tmpl Wed Apr 09 15:27:57 2008 -0700 +++ b/templates/changelog.tmpl Wed Apr 09 15:28:30 2008 -0700 @@ -10,7 +10,7 @@ <div class="buttons"> <a href="#url#shortlog/#rev#{sessionvars%urlparameter}">shortlog</a> <a href="#url#tags{sessionvars%urlparameter}">tags</a> -<a href="#url#file/#node|short#{sessionvars%urlparameter}">manifest</a> +<a href="#url#file/#node|short#{sessionvars%urlparameter}">files</a> #archives%archiveentry# <a type="application/rss+xml" href="#url#rss-log">rss</a> <a type="application/atom+xml" href="#url#atom-log" title="Atom feed for #repo|escape#">atom</a>
--- a/templates/changeset.tmpl Wed Apr 09 15:27:57 2008 -0700 +++ b/templates/changeset.tmpl Wed Apr 09 15:28:30 2008 -0700 @@ -7,7 +7,7 @@ <a href="#url#log/#rev#{sessionvars%urlparameter}">changelog</a> <a href="#url#shortlog/#rev#{sessionvars%urlparameter}">shortlog</a> <a href="#url#tags{sessionvars%urlparameter}">tags</a> -<a href="#url#file/#node|short#{sessionvars%urlparameter}">manifest</a> +<a href="#url#file/#node|short#{sessionvars%urlparameter}">files</a> <a href="#url#raw-rev/#node|short#">raw</a> #archives%archiveentry# </div>
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/templates/coal/changeset.tmpl Wed Apr 09 15:28:30 2008 -0700 @@ -0,0 +1,71 @@ +{header} +<title>{repo|escape}: {node|short}</title> +</head> +<body> +<div class="container"> +<div class="menu"> +<div class="logo"> +<a href="http://www.selenic.com/mercurial/"> +<img src="{staticurl}hglogo.png" width=75 height=90 border=0 alt="mercurial"></a> +</div> +<ul> + <li><a href="{url}shortlog{sessionvars%urlparameter}">log</a></li> + <li><a href="{url}tags{sessionvars%urlparameter}">tags</a></li> +</ul> +<ul> + <li class="active">changeset</li> + <li><a href="{url}file/{node|short}{sessionvars%urlparameter}">browse</a></li> +</ul> +<ul> + {archives%archiveentry}</ul> +</ul> +</div> + +<div class="main"> + +<h2>{repo|escape}</h2> +<h3>changeset {rev}:{node|short} {changesettag}</h3> + +<form class="search" action="{url}log"> +{sessionvars%hiddenformentry} +<p><input name="rev" id="search1" type="text" size="30"></p> +</form> + +<div class="description">{desc|strip|escape|addbreaks}</div> + +<table id="changesetEntry"> +<tr> + <th class="author">author</th> + <td class="author">{author|obfuscate}</td> +</tr> +<tr> + <th class="date">date</th> + <td class="date">{date|date} ({date|age} ago)</td></tr> +<tr> + <th class="author">parents</th> + <td class="author">{parent%changesetparent}</td> +</tr> +<tr> + <th class="author">children</th> + <td class="author">{child%changesetchild}</td> +</tr> +<tr> + <th class="files">files</th> + <td class="files">{files}</td></tr> +</tr> +</table> +<tr> + +<div class="overflow"> +<table class="bigtable"> +<tr> + <th class="lineno">line</th> + <th class="source">diff</th> +</tr> +</table> +{diff} +</div> +</div> +{footer} + +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/templates/coal/error.tmpl Wed Apr 09 15:28:30 2008 -0700 @@ -0,0 +1,39 @@ +{header} +<title>{repo|escape}: error</title> +</head> +<body> + +<div class="content"> +<div class="menu"> +<div class="logo"> +<a href="http://www.selenic.com/mercurial/"> +<img src="{staticurl}hglogo.png" width=75 height=90 border=0 alt="mercurial"></a> +</div> +<ul> +<li><a href="{url}log{sessionvars%urlparameter}">log</a></li> +<li><a href="{url}tags{sessionvars%urlparameter}">tags</a></li> +</ul> +</div> + +<div class="main"> + +<h2>{repo|escape}</h2> +<h3>error</h3> + +<form class="search" action="{url}log"> +{sessionvars%hiddenformentry} +<p><input name="rev" id="search1" type="text" size="30"></p> +</form> + +<div class="description"> +<p> +An error occurred while processing your request: +</p> +<p> +{error|escape} +</p> +</div> +</div> +</div> + +{footer}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/templates/coal/fileannotate.tmpl Wed Apr 09 15:28:30 2008 -0700 @@ -0,0 +1,76 @@ +{header} +<title>{repo|escape}: {file|escape} annotate</title> +</head> +<body> + +<div class="container"> +<div class="menu"> +<div class="logo"> +<a href="http://www.selenic.com/mercurial/"> +<img src="{staticurl}hglogo.png" width=75 height=90 border=0 alt="mercurial"></a> +</div> +<ul> +<li><a href="{url}shortlog/{sessionvars%urlparameter}">log</a></li> +<li><a href="{url}tags{sessionvars%urlparameter}">tags</a></li> +</ul> + +<ul> +<li><a href="{url}rev/{node|short}{sessionvars%urlparameter}">changeset</a></li> +<li><a href="{url}file/{node|short}{path|urlescape}{sessionvars%urlparameter}">browse</a></li> +</ul> +<ul> +<li><a href="{url}file/{node|short}/{file|urlescape}{sessionvars%urlparameter}">file</a></li> +<li><a href="{url}diff/{node|short}/{file|urlescape}{sessionvars%urlparameter}">diff</a></li> +<li class="active">annotate</li> +<li><a href="{url}log/{node|short}/{file|urlescape}{sessionvars%urlparameter}">file log</a></li> +<li><a href="{url}raw-annotate/{node|short}/{file|urlescape}">raw</a></li> +</ul> +</div> + +<div class="main"> +<h2>{repo|escape}</h2> +<h3>annotate {file|escape} @ {rev}:{node|short}</h2> + +<form class="search" action="{url}log"> +{sessionvars%hiddenformentry} +<p><input name="rev" id="search1" type="text" size="30"></p> +</form> + +<div class="description">{desc|strip|escape|addbreaks}</div> + +<table id="changesetEntry"> +<tr> + <th class="author">author</th> + <td class="author">{author|obfuscate}</td> +</tr> +<tr> + <th class="date">date</th> + <td class="date">{date|date} ({date|age} ago)</td> +</tr> +<tr> + <th class="author">parents</th> + <td class="author">{parent%filerevparent}</td> +</tr> +<tr> + <th class="author">children</th> + <td class="author">{child%filerevchild}</td> +</tr> +{changesettag} +</table> + +<br/> + +<div class="overflow"> +<table class="bigtable"> +<tr> + <th class="annotate">rev</th> + <th class="lineno">line</th> + <th class="line">source</th> +</tr> +{annotate%annotateline} +</table> +</div> +</div> +</div> + +{footer}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/templates/coal/filediff.tmpl Wed Apr 09 15:28:30 2008 -0700 @@ -0,0 +1,74 @@ +{header} +<title>{repo|escape}: {file|escape} diff</title> +</head> +<body> + +<div class="container"> +<div class="menu"> +<div class="logo"> +<a href="http://www.selenic.com/mercurial/"> +<img src="{staticurl}hglogo.png" width=75 height=90 border=0 alt="mercurial"></a> +</div> +<ul> +<li><a href="{url}shortlog/{sessionvars%urlparameter}">log</a></li> +<li><a href="{url}tags{sessionvars%urlparameter}">tags</a></li> +</ul> +<ul> +<li><a href="{url}rev/{node|short}{sessionvars%urlparameter}">changeset</a></li> +<li><a href="{url}file/{node|short}{path|urlescape}{sessionvars%urlparameter}">browse</a></li> +</ul> +<ul> +<li><a href="{url}file/{node|short}/{file|urlescape}{sessionvars%urlparameter}">file</a></li> +<li class="active">diff</li> +<li><a href="{url}annotate/{node|short}/{file|urlescape}{sessionvars%urlparameter}">annotate</a></li> +<li><a href="{url}log/{node|short}/{file|urlescape}{sessionvars%urlparameter}">file log</a></li> +<li><a href="{url}raw-file/{node|short}/{file|urlescape}">raw</a></li> +</ul> +</div> + +<div class="main"> +<h2>{repo|escape}</h2> +<h3>diff {file|escape} @ {rev}:{node|short}</h3> + +<form class="search" action="{url}log"> +{sessionvars%hiddenformentry} +<p><input name="rev" id="search1" type="text" size="30"></p> +</form> + +<div class="description">{desc|strip|escape|addbreaks}</div> + +<table id="changesetEntry"> +<tr> + <th>author</th> + <td>{author|obfuscate}</td> +</tr> +<tr> + <th>date</th> + <td>{date|date} ({date|age} ago)</td> +</tr> +<tr> + <th>parents</th> + <td>{parent%filerevparent}</td> +</tr> +<tr> + <th>children</th> + <td>{child%filerevchild}</td> +</tr> +{changesettag} +</table> + +<div class="overflow"> +<table class="bigtable"> +<tr> + <th class="lineno">line</th> + <th class="source">diff</th> +</tr> +<table> +{diff} +</div> +</div> +</div> + +{footer} + +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/templates/coal/filelog.tmpl Wed Apr 09 15:28:30 2008 -0700 @@ -0,0 +1,58 @@ +{header} +<title>{repo|escape}: {file|escape} history</title> +<link rel="alternate" type="application/atom+xml" + href="{url}atom-log/tip/{file|urlescape}" title="Atom feed for {repo|escape}:{file}"> +<link rel="alternate" type="application/rss+xml" + href="{url}rss-log/tip/{file|urlescape}" title="RSS feed for {repo|escape}:{file}"> +</head> +</head> +<body> + +<div class="container"> +<div class="menu"> +<div class="logo"> +<a href="http://www.selenic.com/mercurial/"> +<img src="{staticurl}hglogo.png" width=75 height=90 border=0 alt="mercurial"></a> +</div> +<ul> +<li><a href="{url}shortlog/{sessionvars%urlparameter}">log</a></li> +<li><a href="{url}tags{sessionvars%urlparameter}">tags</a></li> +</ul> +<ul> +<li><a href="{url}rev/{node|short}{sessionvars%urlparameter}">changeset</a></li> +<li><a href="{url}file/{node|short}{path|urlescape}{sessionvars%urlparameter}">browse</a></li> +</ul> +<ul> +<li><a href="{url}file/{node|short}/{file|urlescape}{sessionvars%urlparameter}">file</a></li> +<li><a href="{url}diff/{node|short}/{file|urlescape}{sessionvars%urlparameter}">diff</a></li> +<li><a href="{url}annotate/{node|short}/{file|urlescape}{sessionvars%urlparameter}">annotate</a></li> +<li class="active">file log</li> +<li><a href="{url}raw-file/{node|short}/{file|urlescape}">raw</a></li> +</ul> +</div> + +<div class="main"> + +<h2>{repo|escape}</h2> +<h3>log {file|escape}</h3> + +<form class="search" action="{url}log"> +{sessionvars%hiddenformentry} +<p><input name="rev" id="search1" type="text" size="30"></p> +</form> + +<div class="navigate">{nav%filenaventry}</div> + +<table class="bigtable"> + <tr> + <th class="age">age</td> + <th class="author">author</td> + <th class="description">description</td> + </tr> +{entries%filelogentry} +</table> + +</div> +</div> + +{footer}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/templates/coal/filelogentry.tmpl Wed Apr 09 15:28:30 2008 -0700 @@ -0,0 +1,5 @@ + <tr class="parity{parity}"> + <td class="age">{date|age}</td> + <td class="author">{author|person}</td> + <td class="description"><a href="{url}rev/{node|short}{sessionvars%urlparameter}">{desc|strip|firstline|escape}</a></td> + </tr>
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/templates/coal/filerevision.tmpl Wed Apr 09 15:28:30 2008 -0700 @@ -0,0 +1,73 @@ +{header} +<title>{repo|escape}: {node|short} {file|escape}</title> +</head> +<body> + +<div class="container"> +<div class="menu"> +<div class="logo"> +<a href="http://www.selenic.com/mercurial/"> +<img src="{staticurl}hglogo.png" width=75 height=90 border=0 alt="mercurial"></a> +</div> +<ul> +<li><a href="{url}shortlog/{sessionvars%urlparameter}">log</a></li> +<li><a href="{url}tags{sessionvars%urlparameter}">tags</a></li> +</ul> +<ul> +<li><a href="{url}rev/{node|short}{sessionvars%urlparameter}">changeset</a></li> +<li><a href="{url}file/{node|short}{path|urlescape}{sessionvars%urlparameter}">browse</a></li> +</ul> +<ul> +<li class="active">file</li> +<li><a href="{url}diff/{node|short}/{file|urlescape}{sessionvars%urlparameter}">diff</a></li> +<li><a href="{url}annotate/{node|short}/{file|urlescape}{sessionvars%urlparameter}">annotate</a></li> +<li><a href="{url}log/{node|short}/{file|urlescape}{sessionvars%urlparameter}">file log</a></li> +<li><a href="{url}raw-file/{node|short}/{file|urlescape}">raw</a></li> +</ul> +</div> + +<div class="main"> + +<h2>{repo|escape}</h2> +<h3>view {file|escape} @ {rev}:{node|short}</h3> + +<form class="search" action="{url}log"> +{sessionvars%hiddenformentry} +<p><input name="rev" id="search1" type="text" size="30"></p> +</form> + +<div class="description">{desc|strip|escape|addbreaks}</div> + +<table id="changesetEntry"> +<tr> + <th class="author">author</th> + <td class="author">{author|obfuscate}</td> +</tr> +<tr> + <th class="date">date</th> + <td class="date">{date|date} ({date|age} ago)</td> +</tr> +<tr> + <th class="author">parents</th> + <td class="author">{parent%filerevparent}</td> +</tr> +<tr> + <th class="author">children</th> + <td class="author">{child%filerevchild}</td> +</tr> +{changesettag} +</table> + +<div class="overflow"> +<table class="bigtable"> +<tr> + <th class="lineno">line</th> + <th class="source">source</th> +</tr> +{text%fileline} +</table> +</div> +</div> +</div> + +{footer}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/templates/coal/footer.tmpl Wed Apr 09 15:28:30 2008 -0700 @@ -0,0 +1,4 @@ +{motd} + +</body> +</html>
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/templates/coal/header.tmpl Wed Apr 09 15:28:30 2008 -0700 @@ -0,0 +1,7 @@ +<!-- quirksmode --> +<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"> +<html> +<head> +<link rel="icon" href="{staticurl}hgicon.png" type="image/png"> +<meta name="robots" content="index, nofollow" /> +<link rel="stylesheet" href="{staticurl}style-coal.css" type="text/css" />
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/templates/coal/index.tmpl Wed Apr 09 15:28:30 2008 -0700 @@ -0,0 +1,19 @@ +{header} +<title>Mercurial repositories index</title> +</head> +<body> + +<h2>Mercurial Repositories</h2> + +<table> + <tr> + <td><a href="?sort={sort_name}">Name</a></td> + <td><a href="?sort={sort_description}">Description</a></td> + <td><a href="?sort={sort_contact}">Contact</a></td> + <td><a href="?sort={sort_lastchange}">Last change</a></td> + <td> </td> + <tr> + {entries%indexentry} +</table> + +{footer}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/templates/coal/manifest.tmpl Wed Apr 09 15:28:30 2008 -0700 @@ -0,0 +1,51 @@ +{header} +<title>{repo|escape}: {node|short} {path|escape}</title> +</head> +<body> + +<div class="container"> +<div class="menu"> +<div class="logo"> +<a href="http://www.selenic.com/mercurial/"> +<img src="{staticurl}hglogo.png" width=75 height=90 border=0 alt="mercurial"></a> +</div> +<ul> +<li><a href="{url}shortlog/{sessionvars%urlparameter}">log</a></li> +<li><a href="{url}tags{sessionvars%urlparameter}">tags</a></li> +</ul> +<ul> +<li><a href="{url}rev/{node|short}{sessionvars%urlparameter}">changeset</a></li> +<li class="active">browse</li> +</ul> +<ul> +{archives%archiveentry} +</ul> +</div> + +<div class="main"> + +<h2>{repo|escape}</h2> +<h3>directory {path|escape} @ {rev}:{node|short} {tags%changelogtag}</h3> + +<form class="search" action="{url}log"> +{sessionvars%hiddenformentry} +<p><input name="rev" id="search1" type="text" size="30"></p> +</form> + +<table class="bigtable"> +<tr> + <th class="name">name</th> + <th class="size">size</th> + <th class="permissions">permissions</th> +</tr> +<tr class="fileline parity{upparity}"> + <td class="name"><a href="{url}file/{node|short}{up|urlescape}{sessionvars%urlparameter}">[up]</a></td> + <td class="size"></td> + <td class="permissions">drwxr-xr-x</td> +</tr> +{dentries%direntry} +{fentries%fileentry} +</table> +</div> +</div> +{footer}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/templates/coal/map Wed Apr 09 15:28:30 2008 -0700 @@ -0,0 +1,69 @@ +default = 'shortlog' + +mimetype = 'text/html; charset={encoding}' +header = header.tmpl +footer = footer.tmpl +search = search.tmpl + +changelog = shortlog.tmpl +shortlog = shortlog.tmpl +shortlogentry = shortlogentry.tmpl + +naventry = '<a href="{url}log/{node|short}{sessionvars%urlparameter}">{label|escape}</a> ' +navshortentry = '<a href="{url}shortlog/{node|short}{sessionvars%urlparameter}">{label|escape}</a> ' +filenaventry = '<a href="{url}log/{node|short}/{file|urlescape}{sessionvars%urlparameter}">{label|escape}</a> ' +filedifflink = '<a href="{url}diff/{node|short}/{file|urlescape}{sessionvars%urlparameter}">{file|escape}</a> ' +filenodelink = '<a href="{url}file/{node|short}/{file|urlescape}{sessionvars%urlparameter}">{file|escape}</a> ' +fileellipses = '...' +changelogentry = shortlogentry.tmpl +searchentry = shortlogentry.tmpl +changeset = changeset.tmpl +manifest = manifest.tmpl + +direntry = '<tr class="fileline parity{parity}"><td class="name"><a href="{url}file/{node|short}{path|urlescape}{sessionvars%urlparameter}"><img src="{staticurl}coal-folder.png"> {basename|escape}/</a><td class="size"></td><td class="permissions">drwxr-xr-x</td></tr>' +fileentry = '<tr class="fileline parity{parity}"><td clase="filename"><a href="{url}file/{node|short}/{file|urlescape}#l1{sessionvars%urlparameter}"><img src="{staticurl}coal-file.png"> {basename|escape}</a></td><td class="size">{size}</td><td class="permissions">{permissions|permissions}</td></tr>' + +filerevision = filerevision.tmpl +fileannotate = fileannotate.tmpl +filediff = filediff.tmpl +filelog = filelog.tmpl +fileline = '<tr class="parity{parity}"><td class="lineno"><a href="#{lineid}" id="{lineid}">{linenumber}</a></td><td class="source">{line|escape}</td></tr>' +filelogentry = filelogentry.tmpl + +annotateline = '<tr class="parity{parity}"><td class="annotate"><a href="{url}annotate/{node|short}/{file|urlescape}{sessionvars%urlparameter}#{targetline}">{author|obfuscate}@{rev}</a></td><td class="lineno"><a href="#{lineid}" id="{lineid}">{linenumber}</a></td><td class="source">{line|escape}</td></tr>' + +diffblock = '<table class="bigtable parity{parity}">{lines}</table>' +difflineplus = '<tr><td class="lineno"><a href="#{lineid}" id="{lineid}">{linenumber}</a></td><td class="source plusline">{line|escape}</td></tr>' +difflineminus = '<tr><td class="lineno"><a href="#{lineid}" id="{lineid}">{linenumber}</a></td><td class="source minusline">{line|escape}</td></tr>' +difflineat = '<tr><td class="lineno"><a href="#{lineid}" id="{lineid}">{linenumber}</a></td><td class="source atline">{line|escape}</td></tr>' +diffline = '<tr><td class="lineno"><a href="#{lineid}" id="{lineid}">{linenumber}</a></td><td class="source">{line|escape}</td></tr>' + +changelogparent = '<tr><th class="parent">parent {rev}:</th><td class="parent"><a href="{url}rev/{node|short}{sessionvars%urlparameter}">{node|short}</a></td></tr>' + +changesetparent = '<a href="{url}rev/{node|short}{sessionvars%urlparameter}">{node|short}</a> ' + +filerevparent = '<a href="{url}file/{node|short}/{file|urlescape}{sessionvars%urlparameter}">{rename%filerename}{node|short}</a> ' +filerevchild = '<a href="{url}file/{node|short}/{file|urlescape}{sessionvars%urlparameter}">{node|short}</a> ' + +filerename = '{file|escape}@' +filelogrename = '<tr><th>base:</th><td><a href="{url}file/{node|short}/{file|urlescape}{sessionvars%urlparameter}">{file|escape}@{node|short}</a></td></tr>' +fileannotateparent = '<tr><td class="metatag">parent:</td><td><a href="{url}annotate/{node|short}/{file|urlescape}{sessionvars%urlparameter}">{rename%filerename}{node|short}</a></td></tr>' +changesetchild = '<a href="{url}rev/{node|short}{sessionvars%urlparameter}">{node|short}</a>' +changelogchild = '<tr><th class="child">child</th><td class="child"><a href="{url}rev/{node|short}{sessionvars%urlparameter}">{node|short}</a></td></tr>' +fileannotatechild = '<tr><td class="metatag">child:</td><td><a href="{url}annotate/{node|short}/{file|urlescape}{sessionvars%urlparameter}">{node|short}</a></td></tr>' +tags = tags.tmpl +tagentry = '<tr class="tagEntry parity{parity}"><td><a href="{url}rev/{node|short}{sessionvars%urlparameter}">{tag|escape}</a></td><td class="node">{node|short}</td></tr>' +changelogtag = '<tr><th class="tag">tag:</th><td class="tag">{tag|escape}</td></tr>' +changelogtag = '<span class="tag">{name|escape}</span> ' +changesettag = '<span class="tag">{tag|escape}</span> ' +filediffparent = '<tr><th class="parent">parent {rev}:</th><td class="parent"><a href="{url}rev/{node|short}{sessionvars%urlparameter}">{node|short}</a></td></tr>' +filelogparent = '<tr><th>parent {rev}:</th><td><a href="{url}file/{node|short}/{file|urlescape}{sessionvars%urlparameter}">{node|short}</a></td></tr>' +filediffchild = '<tr><th class="child">child {rev}:</th><td class="child"><a href="{url}rev/{node|short}{sessionvars%urlparameter}">{node|short}</a></td></tr>' +filelogchild = '<tr><th>child {rev}:</th><td><a href="{url}file/{node|short}/{file|urlescape}{sessionvars%urlparameter}">{node|short}</a></td></tr>' +indexentry = '<tr class="parity{parity}"><td><a href="{url}{sessionvars%urlparameter}">{name|escape}</a></td><td>{description}</td><td>{contact|obfuscate}</td><td class="age">{lastchange|age} ago</td><td class="indexlinks"><a href="{url}rss-log">RSS</a> <a href="{url}atom-log">Atom</a> {archives%archiveentry}</td></tr>' +index = index.tmpl +archiveentry = '<li><a href="{url}archive/{node|short}{extension|urlescape}">{type|escape}</a></li>' +notfound = notfound.tmpl +error = error.tmpl +urlparameter = '{separator}{name}={value|urlescape}' +hiddenformentry = '<input type="hidden" name="{name}" value="{value|escape}" />'
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/templates/coal/notfound.tmpl Wed Apr 09 15:28:30 2008 -0700 @@ -0,0 +1,12 @@ +{header} +<title>Mercurial repository not found</title> +</head> +<body> + +<h2>Mercurial repository not found</h2> + +The specified repository "{repo|escape}" is unknown, sorry. + +Please go back to the main repository list page. + +{footer}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/templates/coal/search.tmpl Wed Apr 09 15:28:30 2008 -0700 @@ -0,0 +1,40 @@ +{header} +<title>{repo|escape}: searching for {query|escape}</title> +</head> +<body> + +<div class="container"> +<div class="menu"> +<div class="logo"> +<a href="http://www.selenic.com/mercurial/"> +<img src="{staticurl}hglogo.png" width=75 height=90 border=0 alt="mercurial"></a> +</div> +<ul> +<li><a href="{url}shortlog{sessionvars%urlparameter}">log</a></li> +<li><a href="{url}tags{sessionvars%urlparameter}">tags</a></li> +</ul> +</div> + +<div class="main"> + +<h2>{repo|escape}</h2> +<h3>searching for '{query|escape}'</h3> + +<form class="search" action="{url}log"> +{sessionvars%hiddenformentry} +<p><input name="rev" id="search1" type="text" size="30"></p> +</form> + +<table class="bigtable"> + <tr> + <th class="age">age</td> + <th class="author">author</td> + <th class="description">description</td> + </tr> +{entries} +</table> + +</div> +</div> + +{footer}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/templates/coal/shortlog.tmpl Wed Apr 09 15:28:30 2008 -0700 @@ -0,0 +1,54 @@ +{header} +<title>{repo|escape}: log</title> +<link rel="alternate" type="application/atom+xml" + href="{url}atom-log" title="Atom feed for {repo|escape}"> +<link rel="alternate" type="application/rss+xml" + href="{url}rss-log" title="RSS feed for {repo|escape}"> +</head> +<body> + +<div class="container"> +<div class="menu"> +<div class="logo"> +<a href="http://www.selenic.com/mercurial/"> +<img src="{staticurl}hglogo.png" width=75 height=90 border=0 alt="mercurial"></a> +</div> +<ul> +<li class="active">log</li> +<li><a href="{url}tags{sessionvars%urlparameter}">tags</a></li> +</ul> +<ul> +<li><a href="{url}rev/{node|short}{sessionvars%urlparameter}">changeset</a></li> +<li><a href="{url}file/{node|short}{path|urlescape}{sessionvars%urlparameter}">browse</a></li> +</ul> +<ul> +{archives%archiveentry} +</ul> +</div> + +<div class="main"> + +<h2>{repo|escape}</h2> +<h3>log</h3> + +<form class="search" action="{url}log"> +{sessionvars%hiddenformentry} +<p><input name="rev" id="search1" type="text" size="30"></p> +</form> + +<div class="navigate">rev {rev}: {changenav%navshortentry}</div> + +<table class="bigtable"> + <tr> + <th class="age">age</td> + <th class="author">author</td> + <th class="description">description</td> + </tr> +{entries%shortlogentry} +</table> + +<div class="navigate">rev {rev}: {changenav%navshortentry}</div> +</div> +</div> + +{footer}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/templates/coal/shortlogentry.tmpl Wed Apr 09 15:28:30 2008 -0700 @@ -0,0 +1,5 @@ + <tr class="parity{parity}"> + <td class="age">{date|age}</td> + <td class="author">{author|person}</td> + <td class="description"><a href="{url}rev/{node|short}{sessionvars%urlparameter}">{desc|strip|firstline|escape}</a>{tags%changelogtag}</td> + </tr>
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/templates/coal/tags.tmpl Wed Apr 09 15:28:30 2008 -0700 @@ -0,0 +1,41 @@ +{header} +<title>{repo|escape}: tags</title> +<link rel="alternate" type="application/atom+xml" + href="{url}atom-tags" title="Atom feed for {repo|escape}: tags"> +<link rel="alternate" type="application/rss+xml" + href="{url}rss-tags" title="RSS feed for {repo|escape}: tags"> +</head> +<body> + +<div class="container"> +<div class="menu"> +<div class="logo"> +<a href="http://www.selenic.com/mercurial/"> +<img src="{staticurl}hglogo.png" width=75 height=90 border=0 alt="mercurial"></a> +</div> +<ul> +<li><a href="{url}shortlog{sessionvars%urlparameter}">log</a></li> +<li class="active">tags</li> +</ul> +</div> + +<div class="main"> +<h2>{repo|escape}</h2> +<h3>tags</h3> + +<form class="search" action="{url}log"> +{sessionvars%hiddenformentry} +<p><input name="rev" id="search1" type="text" size="30"></p> +</form> + +<table class="bigtable"> +<tr> + <th>tag</th> + <th>node</th> +</tr> +{entries%tagentry} +</table> +</div> +</div> + +{footer}
--- a/templates/error.tmpl Wed Apr 09 15:27:57 2008 -0700 +++ b/templates/error.tmpl Wed Apr 09 15:28:30 2008 -0700 @@ -6,7 +6,7 @@ <h2>Mercurial Error</h2> <p> -An error occured while processing your request: +An error occurred while processing your request: </p> <p> #error|escape#
--- a/templates/fileannotate.tmpl Wed Apr 09 15:27:57 2008 -0700 +++ b/templates/fileannotate.tmpl Wed Apr 09 15:28:30 2008 -0700 @@ -8,7 +8,7 @@ <a href="#url#shortlog/#rev#{sessionvars%urlparameter}">shortlog</a> <a href="#url#tags{sessionvars%urlparameter}">tags</a> <a href="#url#rev/#node|short#{sessionvars%urlparameter}">changeset</a> -<a href="#url#file/#node|short##path|urlescape#{sessionvars%urlparameter}">manifest</a> +<a href="#url#file/#node|short##path|urlescape#{sessionvars%urlparameter}">files</a> <a href="#url#file/#node|short#/#file|urlescape#{sessionvars%urlparameter}">file</a> <a href="#url#log/#node|short#/#file|urlescape#{sessionvars%urlparameter}">revisions</a> <a href="#url#raw-annotate/#node|short#/#file|urlescape#">raw</a>
--- a/templates/filerevision.tmpl Wed Apr 09 15:27:57 2008 -0700 +++ b/templates/filerevision.tmpl Wed Apr 09 15:28:30 2008 -0700 @@ -8,7 +8,7 @@ <a href="#url#shortlog/#rev#{sessionvars%urlparameter}">shortlog</a> <a href="#url#tags{sessionvars%urlparameter}">tags</a> <a href="#url#rev/#node|short#{sessionvars%urlparameter}">changeset</a> -<a href="#url#file/#node|short##path|urlescape#{sessionvars%urlparameter}">manifest</a> +<a href="#url#file/#node|short##path|urlescape#{sessionvars%urlparameter}">files</a> <a href="#url#log/#node|short#/#file|urlescape#{sessionvars%urlparameter}">revisions</a> <a href="#url#annotate/#node|short#/#file|urlescape#{sessionvars%urlparameter}">annotate</a> <a href="#url#raw-file/#node|short#/#file|urlescape#">raw</a>
--- a/templates/footer.tmpl Wed Apr 09 15:27:57 2008 -0700 +++ b/templates/footer.tmpl Wed Apr 09 15:28:30 2008 -0700 @@ -1,7 +1,7 @@ #motd# <div class="logo"> -powered by<br/> -<a href="http://www.selenic.com/mercurial/">mercurial</a> +<a href="http://www.selenic.com/mercurial/"> +<img src="#staticurl#hglogo.png" width=75 height=90 border=0 alt="mercurial"></a> </div> </body>
--- a/templates/gitweb/changelog.tmpl Wed Apr 09 15:27:57 2008 -0700 +++ b/templates/gitweb/changelog.tmpl Wed Apr 09 15:28:30 2008 -0700 @@ -1,14 +1,14 @@ #header# <title>#repo|escape#: Changelog</title> <link rel="alternate" type="application/atom+xml" - href="{url}atom-log" title="Atom feed for #repo|escape#"> + href="{url}atom-log" title="Atom feed for #repo|escape#"/> <link rel="alternate" type="application/rss+xml" - href="{url}rss-log" title="RSS feed for #repo|escape#"> + href="{url}rss-log" title="RSS feed for #repo|escape#"/> </head> <body> <div class="page_header"> -<a href="http://www.selenic.com/mercurial/" title="Mercurial"><div style="float:right;">Mercurial</div></a><a href="{url}summary{sessionvars%urlparameter}">#repo|escape#</a> / changelog +<a href="http://www.selenic.com/mercurial/" title="Mercurial" style="float: right;">Mercurial</a><a href="{url}summary{sessionvars%urlparameter}">#repo|escape#</a> / changelog </div> <form action="{url}log"> @@ -17,10 +17,9 @@ <input type="text" name="rev" /> </div> </form> -</div> <div class="page_nav"> -<a href="{url}summary{sessionvars%urlparameter}">summary</a> | <a href="{url}shortlog/#rev#{sessionvars%urlparameter}">shortlog</a> | changelog | <a href="{url}tags{sessionvars%urlparameter}">tags</a> | <a href="{url}file/#node|short#{sessionvars%urlparameter}">manifest</a>#archives%archiveentry# +<a href="{url}summary{sessionvars%urlparameter}">summary</a> | <a href="{url}shortlog/#rev#{sessionvars%urlparameter}">shortlog</a> | changelog | <a href="{url}tags{sessionvars%urlparameter}">tags</a> | <a href="{url}file/#node|short#{sessionvars%urlparameter}">files</a>#archives%archiveentry# <br/> #changenav%naventry#<br/> </div>
--- a/templates/gitweb/changelogentry.tmpl Wed Apr 09 15:27:57 2008 -0700 +++ b/templates/gitweb/changelogentry.tmpl Wed Apr 09 15:28:30 2008 -0700 @@ -1,5 +1,5 @@ <div> -<a class="title" href="{url}rev/#node|short#{sessionvars%urlparameter}"><span class="age">#date|age# ago</span>#desc|strip|firstline|escape#<span class="logtags"> {branches%branchtag}{tags%tagtag}</span></a> +<a class="title" href="{url}rev/#node|short#{sessionvars%urlparameter}"><span class="age">#date|age# ago</span>#desc|strip|firstline|escape#<span class="logtags"> {inbranch%inbranchtag}{branches%branchtag}{tags%tagtag}</span></a> </div> <div class="title_text"> <div class="log_link">
--- a/templates/gitweb/changeset.tmpl Wed Apr 09 15:27:57 2008 -0700 +++ b/templates/gitweb/changeset.tmpl Wed Apr 09 15:28:30 2008 -0700 @@ -1,27 +1,28 @@ #header# <title>{repo|escape}: changeset {rev}:{node|short}</title> <link rel="alternate" type="application/atom+xml" - href="{url}atom-log" title="Atom feed for #repo|escape#"> + href="{url}atom-log" title="Atom feed for #repo|escape#"/> <link rel="alternate" type="application/rss+xml" - href="{url}rss-log" title="RSS feed for #repo|escape#"> + href="{url}rss-log" title="RSS feed for #repo|escape#"/> </head> <body> <div class="page_header"> -<a href="http://www.selenic.com/mercurial/" title="Mercurial"><div style="float:right;">Mercurial</div></a><a href="#url#summary{sessionvars%urlparameter}">#repo|escape#</a> / changeset +<a href="http://www.selenic.com/mercurial/" title="Mercurial" style="float: right;">Mercurial</a><a href="#url#summary{sessionvars%urlparameter}">#repo|escape#</a> / changeset </div> <div class="page_nav"> -<a href="{url}summary{sessionvars%urlparameter}">summary</a> | <a href="{url}shortlog/#rev#{sessionvars%urlparameter}">shortlog</a> | <a href="{url}log/#rev#{sessionvars%urlparameter}">changelog</a> | <a href="{url}tags{sessionvars%urlparameter}">tags</a> | <a href="{url}file/#node|short#{sessionvars%urlparameter}">manifest</a> | changeset | <a href="{url}raw-rev/#node|short#">raw</a> #archives%archiveentry#<br/> +<a href="{url}summary{sessionvars%urlparameter}">summary</a> | <a href="{url}shortlog/#rev#{sessionvars%urlparameter}">shortlog</a> | <a href="{url}log/#rev#{sessionvars%urlparameter}">changelog</a> | <a href="{url}tags{sessionvars%urlparameter}">tags</a> | <a href="{url}file/#node|short#{sessionvars%urlparameter}">files</a> | changeset | <a href="{url}raw-rev/#node|short#">raw</a> #archives%archiveentry#<br/> </div> <div> -<a class="title" href="{url}raw-rev/#node|short#">#desc|strip|escape|firstline# <span class="logtags">{branches%branchtag}{tags%tagtag}</span></a> +<a class="title" href="{url}raw-rev/#node|short#">#desc|strip|escape|firstline# <span class="logtags">{inbranch%inbranchtag}{branches%branchtag}{tags%tagtag}</span></a> </div> <div class="title_text"> <table cellspacing="0"> <tr><td>author</td><td>#author|obfuscate#</td></tr> <tr><td></td><td>#date|date# (#date|age# ago)</td></tr> +#branch%changesetbranch# <tr><td>changeset {rev}</td><td style="font-family:monospace">{node|short}</td></tr> #parent%changesetparent# #child%changesetchild#
--- a/templates/gitweb/error.tmpl Wed Apr 09 15:27:57 2008 -0700 +++ b/templates/gitweb/error.tmpl Wed Apr 09 15:28:30 2008 -0700 @@ -1,23 +1,23 @@ #header# <title>#repo|escape#: Error</title> <link rel="alternate" type="application/atom+xml" - href="{url}atom-log" title="Atom feed for #repo|escape#"> + href="{url}atom-log" title="Atom feed for #repo|escape#"/> <link rel="alternate" type="application/rss+xml" - href="{url}rss-log" title="RSS feed for #repo|escape#"> + href="{url}rss-log" title="RSS feed for #repo|escape#"/> </head> <body> <div class="page_header"> -<a href="http://www.selenic.com/mercurial/" title="Mercurial"><div style="float:right;">Mercurial</div></a><a href="{url}summary{sessionvars%urlparameter}">#repo|escape#</a> / error +<a href="http://www.selenic.com/mercurial/" title="Mercurial" style="float: right;">Mercurial</a><a href="{url}summary{sessionvars%urlparameter}">#repo|escape#</a> / error </div> <div class="page_nav"> -<a href="{url}summary{sessionvars%urlparameter}">summary</a> | <a href="{url}shortlog{sessionvars%urlparameter}">shortlog</a> | <a href="{url}log{sessionvars%urlparameter}">changelog</a> | <a href="{url}tags{sessionvars%urlparameter}">tags</a> | <a href="{url}file/#node|short#{sessionvars%urlparameter}">manifest</a><br/> +<a href="{url}summary{sessionvars%urlparameter}">summary</a> | <a href="{url}shortlog{sessionvars%urlparameter}">shortlog</a> | <a href="{url}log{sessionvars%urlparameter}">changelog</a> | <a href="{url}tags{sessionvars%urlparameter}">tags</a> | <a href="{url}file/#node|short#{sessionvars%urlparameter}">files</a><br/> </div> <div class="page_body"> <br/> -<i>An error occured while processing your request</i><br/> +<i>An error occurred while processing your request</i><br/> <br/> {error|escape} </div>
--- a/templates/gitweb/fileannotate.tmpl Wed Apr 09 15:27:57 2008 -0700 +++ b/templates/gitweb/fileannotate.tmpl Wed Apr 09 15:28:30 2008 -0700 @@ -1,14 +1,14 @@ #header# <title>{repo|escape}: {file|escape}@{node|short} (annotated)</title> <link rel="alternate" type="application/atom+xml" - href="{url}atom-log" title="Atom feed for #repo|escape#"> + href="{url}atom-log" title="Atom feed for #repo|escape#"/> <link rel="alternate" type="application/rss+xml" - href="{url}rss-log" title="RSS feed for #repo|escape#"> + href="{url}rss-log" title="RSS feed for #repo|escape#"/> </head> <body> <div class="page_header"> -<a href="http://www.selenic.com/mercurial/" title="Mercurial"><div style="float:right;">Mercurial</div></a><a href="{url}summary{sessionvars%urlparameter}">#repo|escape#</a> / annotate +<a href="http://www.selenic.com/mercurial/" title="Mercurial" style="float: right;">Mercurial</a><a href="{url}summary{sessionvars%urlparameter}">#repo|escape#</a> / annotate </div> <div class="page_nav"> @@ -16,7 +16,7 @@ <a href="{url}shortlog{sessionvars%urlparameter}">shortlog</a> | <a href="{url}log{sessionvars%urlparameter}">changelog</a> | <a href="{url}tags{sessionvars%urlparameter}">tags</a> | -<a href="{url}file/#node|short##path|urlescape#{sessionvars%urlparameter}">manifest</a> | +<a href="{url}file/#node|short##path|urlescape#{sessionvars%urlparameter}">files</a> | <a href="{url}rev/#node|short#{sessionvars%urlparameter}">changeset</a> | <a href="{url}file/{node|short}/#file|urlescape#{sessionvars%urlparameter}">file</a> | <a href="{url}log/{node|short}/#file|urlescape#{sessionvars%urlparameter}">revisions</a> | @@ -35,6 +35,7 @@ <tr> <td></td> <td>#date|date# (#date|age# ago)</td></tr> +#branch%filerevbranch# <tr> <td>changeset {rev}</td> <td style="font-family:monospace"><a class="list" href="{url}rev/#node|short#{sessionvars%urlparameter}">#node|short#</a></td></tr>
--- a/templates/gitweb/filediff.tmpl Wed Apr 09 15:27:57 2008 -0700 +++ b/templates/gitweb/filediff.tmpl Wed Apr 09 15:28:30 2008 -0700 @@ -1,14 +1,14 @@ {header} <title>{repo|escape}: diff {file|escape}</title> <link rel="alternate" type="application/atom+xml" - href="{url}atom-log" title="Atom feed for #repo|escape#"> + href="{url}atom-log" title="Atom feed for #repo|escape#"/> <link rel="alternate" type="application/rss+xml" - href="{url}rss-log" title="RSS feed for {repo|escape}"> + href="{url}rss-log" title="RSS feed for {repo|escape}"/> </head> <body> <div class="page_header"> -<a href="http://www.selenic.com/mercurial/" title="Mercurial"><div style="float:right;">Mercurial</div></a><a href="{url}summary{sessionvars%urlparameter}">{repo|escape}</a> / diff +<a href="http://www.selenic.com/mercurial/" title="Mercurial" style="float: right;">Mercurial</a><a href="{url}summary{sessionvars%urlparameter}">{repo|escape}</a> / diff </div> <div class="page_nav"> @@ -16,7 +16,7 @@ <a href="{url}shortlog{sessionvars%urlparameter}">shortlog</a> | <a href="{url}log{sessionvars%urlparameter}">changelog</a> | <a href="{url}tags{sessionvars%urlparameter}">tags</a> | -<a href="{url}file/{node|short}{path|urlescape}{sessionvars%urlparameter}">manifest</a> | +<a href="{url}file/{node|short}{path|urlescape}{sessionvars%urlparameter}">files</a> | <a href="{url}rev/{node|short}{sessionvars%urlparameter}">changeset</a> | <a href="{url}file/{node|short}/{file|urlescape}{sessionvars%urlparameter}">file</a> | <a href="{url}log/{node|short}/{file|urlescape}{sessionvars%urlparameter}">revisions</a> | @@ -28,6 +28,7 @@ <div class="title">{file|escape}</div> <table> +{branch%filerevbranch} <tr> <td>changeset {rev}</td> <td style="font-family:monospace"><a class="list" href="{url}rev/{node|short}{sessionvars%urlparameter}">{node|short}</a></td></tr>
--- a/templates/gitweb/filelog.tmpl Wed Apr 09 15:27:57 2008 -0700 +++ b/templates/gitweb/filelog.tmpl Wed Apr 09 15:28:30 2008 -0700 @@ -1,14 +1,14 @@ #header# <title>#repo|escape#: File revisions</title> <link rel="alternate" type="application/atom+xml" - href="{url}atom-log" title="Atom feed for #repo|escape#"> + href="{url}atom-log" title="Atom feed for #repo|escape#"/> <link rel="alternate" type="application/rss+xml" - href="{url}rss-log" title="RSS feed for #repo|escape#"> + href="{url}rss-log" title="RSS feed for #repo|escape#"/> </head> <body> <div class="page_header"> -<a href="http://www.selenic.com/mercurial/" title="Mercurial"><div style="float:right;">Mercurial</div></a><a href="{url}summary{sessionvars%urlparameter}">#repo|escape#</a> / file revisions +<a href="http://www.selenic.com/mercurial/" title="Mercurial" style="float: right;">Mercurial</a><a href="{url}summary{sessionvars%urlparameter}">#repo|escape#</a> / file revisions </div> <div class="page_nav">
--- a/templates/gitweb/filerevision.tmpl Wed Apr 09 15:27:57 2008 -0700 +++ b/templates/gitweb/filerevision.tmpl Wed Apr 09 15:28:30 2008 -0700 @@ -1,14 +1,14 @@ #header# <title>{repo|escape}: {file|escape}@{node|short}</title> <link rel="alternate" type="application/atom+xml" - href="{url}atom-log" title="Atom feed for #repo|escape#"> + href="{url}atom-log" title="Atom feed for #repo|escape#"/> <link rel="alternate" type="application/rss+xml" - href="{url}rss-log" title="RSS feed for #repo|escape#"> + href="{url}rss-log" title="RSS feed for #repo|escape#"/> </head> <body> <div class="page_header"> -<a href="http://www.selenic.com/mercurial/" title="Mercurial"><div style="float:right;">Mercurial</div></a><a href="{url}summary{sessionvars%urlparameter}">#repo|escape#</a> / file revision +<a href="http://www.selenic.com/mercurial/" title="Mercurial" style="float: right;">Mercurial</a><a href="{url}summary{sessionvars%urlparameter}">#repo|escape#</a> / file revision </div> <div class="page_nav"> @@ -16,7 +16,7 @@ <a href="{url}shortlog{sessionvars%urlparameter}">shortlog</a> | <a href="{url}log{sessionvars%urlparameter}">changelog</a> | <a href="{url}tags{sessionvars%urlparameter}">tags</a> | -<a href="{url}file/#node|short##path|urlescape#{sessionvars%urlparameter}">manifest</a> | +<a href="{url}file/#node|short##path|urlescape#{sessionvars%urlparameter}">files</a> | <a href="{url}rev/#node|short#{sessionvars%urlparameter}">changeset</a> | file | <a href="{url}log/{node|short}/#file|urlescape#{sessionvars%urlparameter}">revisions</a> | @@ -35,6 +35,7 @@ <tr> <td></td> <td>#date|date# (#date|age# ago)</td></tr> +#branch%filerevbranch# <tr> <td>changeset {rev}</td> <td style="font-family:monospace"><a class="list" href="{url}rev/#node|short#{sessionvars%urlparameter}">#node|short#</a></td></tr>
--- a/templates/gitweb/footer.tmpl Wed Apr 09 15:27:57 2008 -0700 +++ b/templates/gitweb/footer.tmpl Wed Apr 09 15:28:30 2008 -0700 @@ -1,9 +1,11 @@ <div class="page_footer"> -<div class="page_footer_text">#repo|escape#</div> -<a class="rss_logo" href="#url#rss-log">RSS</a> -<a class="rss_logo" href="#url#atom-log">Atom</a> +<div class="page_footer_text">{repo|escape}</div> +<div class="rss_logo"> +<a href="{url}rss-log">RSS</a> +<a href="{url}atom-log">Atom</a> +</div> <br /> -#motd# +{motd} </div> </body> </html>
--- a/templates/gitweb/header.tmpl Wed Apr 09 15:27:57 2008 -0700 +++ b/templates/gitweb/header.tmpl Wed Apr 09 15:28:30 2008 -0700 @@ -1,10 +1,8 @@ -Content-type: text/html; charset={encoding} - <?xml version="1.0" encoding="{encoding}"?> <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en-US" lang="en-US"> <head> -<link rel="icon" href="{staticurl}hgicon.png" type="image/png"> +<link rel="icon" href="{staticurl}hgicon.png" type="image/png" /> <meta name="robots" content="index, nofollow"/> <link rel="stylesheet" href="{staticurl}style-gitweb.css" type="text/css" />
--- a/templates/gitweb/index.tmpl Wed Apr 09 15:27:57 2008 -0700 +++ b/templates/gitweb/index.tmpl Wed Apr 09 15:28:30 2008 -0700 @@ -4,7 +4,8 @@ <body> <div class="page_header"> -<a href="http://www.selenic.com/mercurial/" title="Mercurial"><div style="float:right;">Mercurial</div></a>Repositories list + <a href="http://www.selenic.com/mercurial/" title="Mercurial" style="float: right;">Mercurial</a> + Repositories list </div> <table cellspacing="0"> @@ -15,7 +16,7 @@ <td><a href="?sort=#sort_lastchange#">Last change</a></td> <td> </td> <td> </td> - <tr> + </tr> #entries%indexentry# </table> <div class="page_footer">
--- a/templates/gitweb/manifest.tmpl Wed Apr 09 15:27:57 2008 -0700 +++ b/templates/gitweb/manifest.tmpl Wed Apr 09 15:28:30 2008 -0700 @@ -1,14 +1,14 @@ #header# -<title>#repo|escape#: Manifest</title> +<title>#repo|escape#: files</title> <link rel="alternate" type="application/atom+xml" - href="{url}atom-log" title="Atom feed for #repo|escape#"> + href="{url}atom-log" title="Atom feed for #repo|escape#"/> <link rel="alternate" type="application/rss+xml" - href="{url}rss-log" title="RSS feed for #repo|escape#"> + href="{url}rss-log" title="RSS feed for #repo|escape#"/> </head> <body> <div class="page_header"> -<a href="http://www.selenic.com/mercurial/" title="Mercurial"><div style="float:right;">Mercurial</div></a><a href="{url}summary{sessionvars%urlparameter}">#repo|escape#</a> / manifest +<a href="http://www.selenic.com/mercurial/" title="Mercurial" style="float: right;">Mercurial</a><a href="{url}summary{sessionvars%urlparameter}">#repo|escape#</a> / files </div> <div class="page_nav"> @@ -16,11 +16,11 @@ <a href="{url}shortlog{sessionvars%urlparameter}">shortlog</a> | <a href="{url}log{sessionvars%urlparameter}">changelog</a> | <a href="{url}tags{sessionvars%urlparameter}">tags</a> | -manifest | +files | <a href="{url}rev/#node|short#{sessionvars%urlparameter}">changeset</a> #archives%archiveentry#<br/> </div> -<div class="title">#path|escape# <span class="logtags">{branches%branchtag}{tags%tagtag}</span></div> +<div class="title">#path|escape# <span class="logtags">{inbranch%inbranchtag}{branches%branchtag}{tags%tagtag}</span></div> <table cellspacing="0"> <tr class="parity#upparity#"> <td style="font-family:monospace">drwxr-xr-x</td> @@ -29,8 +29,8 @@ <td><a href="{url}file/#node|short##up|urlescape#{sessionvars%urlparameter}">[up]</a></td> <td class="link"> </td> </tr> -#dentries%manifestdirentry# -#fentries%manifestfileentry# +#dentries%direntry# +#fentries%fileentry# </table> #footer#
--- a/templates/gitweb/map Wed Apr 09 15:27:57 2008 -0700 +++ b/templates/gitweb/map Wed Apr 09 15:28:30 2008 -0700 @@ -1,4 +1,5 @@ default = 'summary' +mimetype = 'text/html; charset={encoding}' header = header.tmpl footer = footer.tmpl search = search.tmpl @@ -16,20 +17,22 @@ searchentry = changelogentry.tmpl changeset = changeset.tmpl manifest = manifest.tmpl -manifestdirentry = '<tr class="parity#parity#"><td style="font-family:monospace">drwxr-xr-x</td><td style="font-family:monospace"></td><td style="font-family:monospace"></td><td><a href="#url#file/#node|short##path|urlescape#{sessionvars%urlparameter}">#basename|escape#</a></td><td class="link"><a href="#url#file/#node|short##path|urlescape#{sessionvars%urlparameter}">manifest</a></td></tr>' -manifestfileentry = '<tr class="parity#parity#"><td style="font-family:monospace">#permissions|permissions#</td><td style="font-family:monospace" align=right>#date|isodate#</td><td style="font-family:monospace" align=right>#size#</td><td class="list"><a class="list" href="#url#file/#node|short#/#file|urlescape#{sessionvars%urlparameter}">#basename|escape#</a></td><td class="link"><a href="#url#file/#node|short#/#file|urlescape#{sessionvars%urlparameter}">file</a> | <a href="#url#log/#node|short#/#file|urlescape#{sessionvars%urlparameter}">revisions</a> | <a href="#url#annotate/#node|short#/#file|urlescape#{sessionvars%urlparameter}">annotate</a></td></tr>' +direntry = '<tr class="parity#parity#"><td style="font-family:monospace">drwxr-xr-x</td><td style="font-family:monospace"></td><td style="font-family:monospace"></td><td><a href="#url#file/#node|short##path|urlescape#{sessionvars%urlparameter}">#basename|escape#</a></td><td class="link"><a href="#url#file/#node|short##path|urlescape#{sessionvars%urlparameter}">files</a></td></tr>' +fileentry = '<tr class="parity#parity#"><td style="font-family:monospace">#permissions|permissions#</td><td style="font-family:monospace" align=right>#date|isodate#</td><td style="font-family:monospace" align=right>#size#</td><td class="list"><a class="list" href="#url#file/#node|short#/#file|urlescape#{sessionvars%urlparameter}">#basename|escape#</a></td><td class="link"><a href="#url#file/#node|short#/#file|urlescape#{sessionvars%urlparameter}">file</a> | <a href="#url#log/#node|short#/#file|urlescape#{sessionvars%urlparameter}">revisions</a> | <a href="#url#annotate/#node|short#/#file|urlescape#{sessionvars%urlparameter}">annotate</a></td></tr>' filerevision = filerevision.tmpl fileannotate = fileannotate.tmpl filediff = filediff.tmpl filelog = filelog.tmpl -fileline = '<div style="font-family:monospace" class="parity#parity#"><pre><span class="linenr"> #linenumber#</span> #line|escape#</pre></div>' -annotateline = '<tr style="font-family:monospace" class="parity#parity#"><td class="linenr" style="text-align: right;"><a href="#url#annotate/#node|short#/#file|urlescape#{sessionvars%urlparameter}">#author|obfuscate#@#rev#</a></td><td><pre>#line|escape#</pre></td></tr>' -difflineplus = '<div style="color:#008800;">#line|escape#</div>' -difflineminus = '<div style="color:#cc0000;">#line|escape#</div>' -difflineat = '<div style="color:#990099;">#line|escape#</div>' -diffline = '<div>#line|escape#</div>' +fileline = '<div style="font-family:monospace" class="parity#parity#"><pre><a class="linenr" href="##lineid#" id="#lineid#">#linenumber#</a> #line|escape#</pre></div>' +annotateline = '<tr style="font-family:monospace" class="parity#parity#"><td class="linenr" style="text-align: right;"><a href="#url#annotate/#node|short#/#file|urlescape#{sessionvars%urlparameter}#l{targetline}">#author|obfuscate#@#rev#</a></td><td><pre><a class="linenr" href="##lineid#" id="#lineid#">#linenumber#</a></pre></td><td><pre>#line|escape#</pre></td></tr>' +difflineplus = '<span style="color:#008800;"><a class="linenr" href="##lineid#" id="#lineid#">#linenumber#</a> #line|escape#</span>' +difflineminus = '<span style="color:#cc0000;"><a class="linenr" href="##lineid#" id="#lineid#">#linenumber#</a> #line|escape#</span>' +difflineat = '<span style="color:#990099;"><a class="linenr" href="##lineid#" id="#lineid#">#linenumber#</a> #line|escape#</span>' +diffline = '<span><a class="linenr" href="##lineid#" id="#lineid#">#linenumber#</a> #line|escape#</span>' changelogparent = '<tr><th class="parent">parent #rev#:</th><td class="parent"><a href="#url#rev/#node|short#{sessionvars%urlparameter}">#node|short#</a></td></tr>' +changesetbranch = '<tr><td>branch</td><td>{name}</td></tr>' changesetparent = '<tr><td>parent {rev}</td><td style="font-family:monospace"><a class="list" href="{url}rev/{node|short}{sessionvars%urlparameter}">{node|short}</a></td></tr>' +filerevbranch = '<tr><td>branch</td><td>{name}</td></tr>' filerevparent = '<tr><td>parent {rev}</td><td style="font-family:monospace"><a class="list" href="{url}file/{node|short}/{file|urlescape}{sessionvars%urlparameter}">{rename%filerename}{node|short}</a></td></tr>' filerename = '{file|escape}@' filelogrename = '| <a href="{url}file/#node|short#/#file|urlescape#{sessionvars%urlparameter}">base</a>' @@ -39,8 +42,8 @@ filerevchild = '<tr><td>child {rev}</td><td style="font-family:monospace"><a class="list" href="{url}file/{node|short}/{file|urlescape}{sessionvars%urlparameter}">{node|short}</a></td></tr>' fileannotatechild = '<tr><td>child {rev}</td><td style="font-family:monospace"><a class="list" href="{url}annotate/{node|short}/{file|urlescape}{sessionvars%urlparameter}">{node|short}</a></td></tr>' tags = tags.tmpl -tagentry = '<tr class="parity#parity#"><td class="age"><i>#date|age# ago</i></td><td><a class="list" href="{url}rev/{node|short}{sessionvars%urlparameter}"><b>#tag|escape#</b></a></td><td class="link"><a href="{url}rev/#node|short#{sessionvars%urlparameter}">changeset</a> | <a href="{url}log/#node|short#{sessionvars%urlparameter}">changelog</a> | <a href="{url}file/#node|short#{sessionvars%urlparameter}">manifest</a></td></tr>' -branchentry = '<tr class="parity{parity}"><td class="age"><i>{date|age} ago</i></td><td><a class="list" href="{url}rev/{node|short}{sessionvars%urlparameter}"><b>{node|short}</b></td><td>{branch|escape}</td><td class="link"><a href="{url}rev/{node|short}{sessionvars%urlparameter}">changeset</a> | <a href="{url}log/{node|short}{sessionvars%urlparameter}">changelog</a> | <a href="{url}file/{node|short}{sessionvars%urlparameter}">manifest</a></td></tr>' +tagentry = '<tr class="parity#parity#"><td class="age"><i>#date|age# ago</i></td><td><a class="list" href="{url}rev/{node|short}{sessionvars%urlparameter}"><b>#tag|escape#</b></a></td><td class="link"><a href="{url}rev/#node|short#{sessionvars%urlparameter}">changeset</a> | <a href="{url}log/#node|short#{sessionvars%urlparameter}">changelog</a> | <a href="{url}file/#node|short#{sessionvars%urlparameter}">files</a></td></tr>' +branchentry = '<tr class="parity{parity}"><td class="age"><i>{date|age} ago</i></td><td><a class="list" href="{url}rev/{node|short}{sessionvars%urlparameter}"><b>{node|short}</b></a></td><td>{branch|escape}</td><td class="link"><a href="{url}rev/{node|short}{sessionvars%urlparameter}">changeset</a> | <a href="{url}log/{node|short}{sessionvars%urlparameter}">changelog</a> | <a href="{url}file/{node|short}{sessionvars%urlparameter}">files</a></td></tr>' diffblock = '<pre>#lines#</pre>' filediffparent = '<tr><td>parent {rev}</td><td style="font-family:monospace"><a class="list" href="{url}diff/{node|short}/{file|urlescape}{sessionvars%urlparameter}">{node|short}</a></td></tr>' filelogparent = '<tr><td align="right">parent #rev#: </td><td><a href="{url}file/{node|short}/#file|urlescape#{sessionvars%urlparameter}">#node|short#</a></td></tr>' @@ -49,10 +52,12 @@ shortlog = shortlog.tmpl tagtag = '<span class="tagtag" title="{name}">{name}</span> ' branchtag = '<span class="branchtag" title="{name}">{name}</span> ' -shortlogentry = '<tr class="parity#parity#"><td class="age"><i>#date|age# ago</i></td><td><i>#author|person#</i></td><td><a class="list" href="{url}rev/#node|short#{sessionvars%urlparameter}"><b>#desc|strip|firstline|escape#</b> <span class="logtags">{branches%branchtag}{tags%tagtag}</span></a></td><td class="link" nowrap><a href="{url}rev/#node|short#{sessionvars%urlparameter}">changeset</a> | <a href="{url}file/#node|short#{sessionvars%urlparameter}">manifest</a></td></tr>' +inbranchtag = '<span class="inbranchtag" title="{name}">{name}</span> ' +shortlogentry = '<tr class="parity#parity#"><td class="age"><i>#date|age# ago</i></td><td><i>#author|person#</i></td><td><a class="list" href="{url}rev/#node|short#{sessionvars%urlparameter}"><b>#desc|strip|firstline|escape#</b> <span class="logtags">{inbranch%inbranchtag}{branches%branchtag}{tags%tagtag}</span></a></td><td class="link" nowrap><a href="{url}rev/#node|short#{sessionvars%urlparameter}">changeset</a> | <a href="{url}file/#node|short#{sessionvars%urlparameter}">files</a></td></tr>' filelogentry = '<tr class="parity#parity#"><td class="age"><i>#date|age# ago</i></td><td><a class="list" href="{url}rev/#node|short#{sessionvars%urlparameter}"><b>#desc|strip|firstline|escape#</b></a></td><td class="link"><a href="{url}file/#node|short#/#file|urlescape#{sessionvars%urlparameter}">file</a> | <a href="{url}diff/#node|short#/#file|urlescape#{sessionvars%urlparameter}">diff</a> | <a href="{url}annotate/#node|short#/#file|urlescape#{sessionvars%urlparameter}">annotate</a> #rename%filelogrename#</td></tr>' archiveentry = ' | <a href="{url}archive/{node|short}{extension}">#type|escape#</a> ' -indexentry = '<tr class="parity#parity#"><td><a class="list" href="#url#{sessionvars%urlparameter}"><b>#name|escape#</b></a></td><td>#description#</td><td>#contact|obfuscate#</td><td class="age">#lastchange|age# ago</td><td class="indexlinks">#archives%archiveentry#</td><td><a class="rss_logo" href="#url#rss-log">RSS</a> <a class="rss_logo" href="#url#atom-log">Atom</a></td></tr>' +indexentry = '<tr class="parity{parity}"><td><a class="list" href="{url}{sessionvars%urlparameter}"><b>{name|escape}</b></a></td><td>{description}</td><td>{contact|obfuscate}</td><td class="age">{lastchange|age} ago</td><td class="indexlinks">{archives%indexarchiveentry}</td><td><div class="rss_logo"><a href="{url}rss-log">RSS</a> <a href="{url}atom-log">Atom</a></div></td></tr>\n' +indexarchiveentry = ' <a href="{url}archive/{node|short}{extension}">{type|escape}</a> ' index = index.tmpl urlparameter = '#separator##name#=#value|urlescape#' hiddenformentry = '<input type="hidden" name="#name#" value="#value|escape#" />'
--- a/templates/gitweb/notfound.tmpl Wed Apr 09 15:27:57 2008 -0700 +++ b/templates/gitweb/notfound.tmpl Wed Apr 09 15:28:30 2008 -0700 @@ -5,15 +5,14 @@ <body> <div class="page_header"> -<a href="http://www.selenic.com/mercurial/" title="Mercurial"><div - style="float:right;">Mercurial</div></a> Not found: {repo|escape} +<a href="http://www.selenic.com/mercurial/" title="Mercurial" style="float: right;">Mercurial</a> Not found: {repo|escape} </div> <div class="page_body"> The specified repository "{repo|escape}" is unknown, sorry. <br/> <br/> -Please go back to the <a href="/">main repository list page</a>. +Please go back to the <a href="{url}">main repository list page</a>. </div> {footer}
--- a/templates/gitweb/search.tmpl Wed Apr 09 15:27:57 2008 -0700 +++ b/templates/gitweb/search.tmpl Wed Apr 09 15:28:30 2008 -0700 @@ -1,14 +1,14 @@ #header# <title>#repo|escape#: Search</title> <link rel="alternate" type="application/atom+xml" - href="{url}atom-log" title="Atom feed for #repo|escape#"> + href="{url}atom-log" title="Atom feed for #repo|escape#"/> <link rel="alternate" type="application/rss+xml" - href="{url}rss-log" title="RSS feed for #repo|escape#"> + href="{url}rss-log" title="RSS feed for #repo|escape#"/> </head> <body> <div class="page_header"> -<a href="http://www.selenic.com/mercurial/" title="Mercurial"><div style="float:right;">Mercurial</div></a><a href="{url}summary{sessionvars%urlparameter}">#repo|escape#</a> / search +<a href="http://www.selenic.com/mercurial/" title="Mercurial" style="float: right;">Mercurial</a><a href="{url}summary{sessionvars%urlparameter}">#repo|escape#</a> / search <form action="{url}log"> {sessionvars%hiddenformentry} @@ -23,7 +23,7 @@ <a href="{url}shortlog{sessionvars%urlparameter}">shortlog</a> | <a href="{url}log{sessionvars%urlparameter}">changelog</a> | <a href="{url}tags{sessionvars%urlparameter}">tags</a> | -<a href="{url}file/#node|short#{sessionvars%urlparameter}">manifest</a>#archives%archiveentry# +<a href="{url}file/#node|short#{sessionvars%urlparameter}">files</a>#archives%archiveentry# <br/> </div>
--- a/templates/gitweb/shortlog.tmpl Wed Apr 09 15:27:57 2008 -0700 +++ b/templates/gitweb/shortlog.tmpl Wed Apr 09 15:28:30 2008 -0700 @@ -1,14 +1,14 @@ #header# <title>#repo|escape#: Shortlog</title> <link rel="alternate" type="application/atom+xml" - href="{url}atom-log" title="Atom feed for #repo|escape#"> + href="{url}atom-log" title="Atom feed for #repo|escape#"/> <link rel="alternate" type="application/rss+xml" - href="{url}rss-log" title="RSS feed for #repo|escape#"> + href="{url}rss-log" title="RSS feed for #repo|escape#"/> </head> <body> <div class="page_header"> -<a href="http://www.selenic.com/mercurial/" title="Mercurial"><div style="float:right;">Mercurial</div></a><a href="{url}summary{sessionvars%urlparameter}">#repo|escape#</a> / shortlog +<a href="http://www.selenic.com/mercurial/" title="Mercurial" style="float: right;">Mercurial</a><a href="{url}summary{sessionvars%urlparameter}">#repo|escape#</a> / shortlog </div> <form action="{url}log"> @@ -17,13 +17,12 @@ <input type="text" name="rev" /> </div> </form> -</div> <div class="page_nav"> <a href="{url}summary{sessionvars%urlparameter}">summary</a> | shortlog | <a href="{url}log/#rev#{sessionvars%urlparameter}">changelog</a> | <a href="{url}tags{sessionvars%urlparameter}">tags</a> | -<a href="{url}file/#node|short#{sessionvars%urlparameter}">manifest</a>#archives%archiveentry# +<a href="{url}file/#node|short#{sessionvars%urlparameter}">files</a>#archives%archiveentry# <br/> #changenav%navshortentry#<br/> </div>
--- a/templates/gitweb/summary.tmpl Wed Apr 09 15:27:57 2008 -0700 +++ b/templates/gitweb/summary.tmpl Wed Apr 09 15:28:30 2008 -0700 @@ -1,14 +1,14 @@ #header# <title>#repo|escape#: Summary</title> <link rel="alternate" type="application/atom+xml" - href="{url}atom-log" title="Atom feed for #repo|escape#"> + href="{url}atom-log" title="Atom feed for #repo|escape#"/> <link rel="alternate" type="application/rss+xml" - href="{url}rss-log" title="RSS feed for #repo|escape#"> + href="{url}rss-log" title="RSS feed for #repo|escape#"/> </head> <body> <div class="page_header"> -<a href="http://www.selenic.com/mercurial/" title="Mercurial"><div style="float:right;">Mercurial</div></a><a href="{url}summary{sessionvars%urlparameter}">#repo|escape#</a> / summary +<a href="http://www.selenic.com/mercurial/" title="Mercurial" style="float: right;">Mercurial</a><a href="{url}summary{sessionvars%urlparameter}">#repo|escape#</a> / summary <form action="{url}log"> {sessionvars%hiddenformentry} @@ -23,14 +23,14 @@ <a href="{url}shortlog{sessionvars%urlparameter}">shortlog</a> | <a href="{url}log{sessionvars%urlparameter}">changelog</a> | <a href="{url}tags{sessionvars%urlparameter}">tags</a> | -<a href="{url}file/#node|short#{sessionvars%urlparameter}">manifest</a>#archives%archiveentry# +<a href="{url}file/#node|short#{sessionvars%urlparameter}">files</a>#archives%archiveentry# <br/> </div> <div class="title"> </div> <table cellspacing="0"> <tr><td>description</td><td>#desc#</td></tr> -<tr><td>owner</td><td>#owner|escape#</td></tr> +<tr><td>owner</td><td>#owner|obfuscate#</td></tr> <tr><td>last change</td><td>#lastchange|rfc822date#</td></tr> </table>
--- a/templates/gitweb/tags.tmpl Wed Apr 09 15:27:57 2008 -0700 +++ b/templates/gitweb/tags.tmpl Wed Apr 09 15:28:30 2008 -0700 @@ -1,14 +1,14 @@ #header# <title>#repo|escape#: Tags</title> <link rel="alternate" type="application/atom+xml" - href="{url}atom-log" title="Atom feed for #repo|escape#"> + href="{url}atom-log" title="Atom feed for #repo|escape#"/> <link rel="alternate" type="application/rss+xml" - href="{url}rss-log" title="RSS feed for #repo|escape#"> + href="{url}rss-log" title="RSS feed for #repo|escape#"/> </head> <body> <div class="page_header"> -<a href="http://www.selenic.com/mercurial/" title="Mercurial"><div style="float:right;">Mercurial</div></a><a href="{url}summary{sessionvars%urlparameter}">#repo|escape#</a> / tags +<a href="http://www.selenic.com/mercurial/" title="Mercurial" style="float: right;">Mercurial</a><a href="{url}summary{sessionvars%urlparameter}">#repo|escape#</a> / tags </div> <div class="page_nav"> @@ -16,7 +16,7 @@ <a href="{url}shortlog{sessionvars%urlparameter}">shortlog</a> | <a href="{url}log{sessionvars%urlparameter}">changelog</a> | tags | -<a href="{url}file/#node|short#{sessionvars%urlparameter}">manifest</a> +<a href="{url}file/#node|short#{sessionvars%urlparameter}">files</a> <br/> </div>
--- a/templates/header.tmpl Wed Apr 09 15:27:57 2008 -0700 +++ b/templates/header.tmpl Wed Apr 09 15:28:30 2008 -0700 @@ -1,5 +1,3 @@ -Content-type: text/html; charset={encoding} - <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"> <html> <head>
--- a/templates/manifest.tmpl Wed Apr 09 15:27:57 2008 -0700 +++ b/templates/manifest.tmpl Wed Apr 09 15:28:30 2008 -0700 @@ -1,5 +1,5 @@ #header# -<title>#repo|escape#: manifest for changeset #node|short#</title> +<title>#repo|escape#: files for changeset #node|short#</title> </head> <body> @@ -11,7 +11,7 @@ #archives%archiveentry# </div> -<h2>manifest for changeset #node|short#: #path|escape#</h2> +<h2>files for changeset #node|short#: #path|escape#</h2> <table cellpadding="0" cellspacing="0"> <tr class="parity#upparity#"> @@ -20,7 +20,7 @@ <td> <td><a href="#url#file/#node|short##up|urlescape#{sessionvars%urlparameter}">[up]</a> </tr> -#dentries%manifestdirentry# -#fentries%manifestfileentry# +#dentries%direntry# +#fentries%fileentry# </table> #footer#
--- a/templates/map Wed Apr 09 15:27:57 2008 -0700 +++ b/templates/map Wed Apr 09 15:28:30 2008 -0700 @@ -1,4 +1,5 @@ default = 'shortlog' +mimetype = 'text/html; charset={encoding}' header = header.tmpl footer = footer.tmpl search = search.tmpl @@ -15,19 +16,19 @@ searchentry = changelogentry.tmpl changeset = changeset.tmpl manifest = manifest.tmpl -manifestdirentry = '<tr class="parity#parity#"><td><tt>drwxr-xr-x</tt> <td> <td> <td><a href="#url#file/#node|short##path|urlescape#{sessionvars%urlparameter}">#basename|escape#/</a>' -manifestfileentry = '<tr class="parity#parity#"><td><tt>#permissions|permissions#</tt> <td align=right><tt class="date">#date|isodate#</tt> <td align=right><tt>#size#</tt> <td><a href="#url#file/#node|short#/#file|urlescape#{sessionvars%urlparameter}">#basename|escape#</a>' +direntry = '<tr class="parity#parity#"><td><tt>drwxr-xr-x</tt> <td> <td> <td><a href="#url#file/#node|short##path|urlescape#{sessionvars%urlparameter}">#basename|escape#/</a>' +fileentry = '<tr class="parity#parity#"><td><tt>#permissions|permissions#</tt> <td align=right><tt class="date">#date|isodate#</tt> <td align=right><tt>#size#</tt> <td><a href="#url#file/#node|short#/#file|urlescape#{sessionvars%urlparameter}">#basename|escape#</a>' filerevision = filerevision.tmpl fileannotate = fileannotate.tmpl filediff = filediff.tmpl filelog = filelog.tmpl -fileline = '<div class="parity#parity#"><span class="lineno">#linenumber#</span>#line|escape#</div>' +fileline = '<div class="parity#parity#"><a class="lineno" href="##lineid#" id="#lineid#">#linenumber#</a>#line|escape#</div>' filelogentry = filelogentry.tmpl -annotateline = '<tr class="parity#parity#"><td class="annotate"><a href="#url#annotate/#node|short#/#file|urlescape#{sessionvars%urlparameter}">#author|obfuscate#@#rev#</a></td><td><pre>#line|escape#</pre></td></tr>' -difflineplus = '<span class="plusline">#line|escape#</span>' -difflineminus = '<span class="minusline">#line|escape#</span>' -difflineat = '<span class="atline">#line|escape#</span>' -diffline = '#line|escape#' +annotateline = '<tr class="parity#parity#"><td class="annotate"><a href="#url#annotate/#node|short#/#file|urlescape#{sessionvars%urlparameter}#l{targetline}">#author|obfuscate#@#rev#</a></td><td><a class="lineno" href="##lineid#" id="#lineid#">#linenumber#</a></td><td><pre>#line|escape#</pre></td></tr>' +difflineplus = '<span class="plusline"><a class="lineno" href="##lineid#" id="#lineid#">#linenumber#</a>#line|escape#</span>' +difflineminus = '<span class="minusline"><a class="lineno" href="##lineid#" id="#lineid#">#linenumber#</a>#line|escape#</span>' +difflineat = '<span class="atline"><a class="lineno" href="##lineid#" id="#lineid#">#linenumber#</a>#line|escape#</span>' +diffline = '<a class="lineno" href="##lineid#" id="#lineid#">#linenumber#</a>#line|escape#' changelogparent = '<tr><th class="parent">parent #rev#:</th><td class="parent"><a href="#url#rev/#node|short#{sessionvars%urlparameter}">#node|short#</a></td></tr>' changesetparent = '<tr><th class="parent">parent #rev#:</th><td class="parent"><a href="#url#rev/#node|short#{sessionvars%urlparameter}">#node|short#</a></td></tr>' filerevparent = '<tr><td class="metatag">parent:</td><td><a href="{url}file/{node|short}/{file|urlescape}{sessionvars%urlparameter}">{rename%filerename}{node|short}</a></td></tr>'
--- a/templates/old/changelog.tmpl Wed Apr 09 15:27:57 2008 -0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,41 +0,0 @@ -#header# -<title>#repo|escape#: changelog</title> -<link rel="alternate" type="application/atom+xml" - href="#url#atom-log" title="Atom feed for #repo|escape#"> -<link rel="alternate" type="application/rss+xml" - href="?cmd=changelog;style=rss" title="RSS feed for #repo|escape#"> -</head> -<body> - -<div class="buttons"> -<a href="?sl=#rev#">shortlog</a> -<a href="?cmd=tags">tags</a> -<a href="?mf=#node|short#;path=/">manifest</a> -#archives%archiveentry# -<a type="application/rss+xml" href="?style=rss">rss</a> -<a type="application/atom+xml" href="#url#atom-log" title="Atom feed for #repo|escape#">atom</a> -</div> - -<h2>changelog for #repo|escape#</h2> - -<form action="#"> -<p> -<label for="search1">search:</label> -<input type="hidden" name="cmd" value="changelog"> -<input name="rev" id="search1" type="text" size="30"> -navigate: <small class="navigate">#changenav%naventry#</small> -</p> -</form> - -#entries%changelogentry# - -<form action="#"> -<p> -<label for="search2">search:</label> -<input type="hidden" name="cmd" value="changelog"> -<input name="rev" id="search2" type="text" size="30"> -navigate: <small class="navigate">#changenav%naventry#</small> -</p> -</form> - -#footer#
--- a/templates/old/changelogentry.tmpl Wed Apr 09 15:27:57 2008 -0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,25 +0,0 @@ -<table class="logEntry parity#parity#"> - <tr> - <th class="age">#date|age# ago:</th> - <th class="firstline">#desc|strip|firstline|escape#</th> - </tr> - <tr> - <th class="revision">changeset #rev#:</th> - <td class="node"><a href="?cs=#node|short#">#node|short#</a></td> - </tr> - #parent%changelogparent# - #child%changelogchild# - #changelogtag# - <tr> - <th class="author">author:</th> - <td class="author">#author|obfuscate#</td> - </tr> - <tr> - <th class="date">date:</th> - <td class="date">#date|date#</td> - </tr> - <tr> - <th class="files"><a href="?mf=#node|short#;path=/">files</a>:</th> - <td class="files">#files#</td> - </tr> -</table>
--- a/templates/old/changeset.tmpl Wed Apr 09 15:27:57 2008 -0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,47 +0,0 @@ -#header# -<title>#repo|escape#: changeset #node|short#</title> -</head> -<body> - -<div class="buttons"> -<a href="?cl=#rev#">changelog</a> -<a href="?sl=#rev#">shortlog</a> -<a href="?cmd=tags">tags</a> -<a href="?mf=#node|short#;path=/">manifest</a> -<a href="?cs=#node|short#;style=raw">raw</a> -#archives%archiveentry# -</div> - -<h2>changeset: #desc|strip|escape|firstline#</h2> - -<table id="changesetEntry"> -<tr> - <th class="changeset">changeset #rev#:</th> - <td class="changeset"><a href="?cs=#node|short#">#node|short#</a></td> -</tr> -#parent%changesetparent# -#child%changesetchild# -#changesettag# -<tr> - <th class="author">author:</th> - <td class="author">#author|obfuscate#</td> -</tr> -<tr> - <th class="date">date:</th> - <td class="date">#date|date# (#date|age# ago)</td></tr> -<tr> - <th class="files">files:</th> - <td class="files">#files#</td></tr> -<tr> - <th class="description">description:</th> - <td class="description">#desc|strip|escape|addbreaks#</td> -</tr> -</table> - -<div id="changesetDiff"> -#diff# -</div> - -#footer# - -
--- a/templates/old/fileannotate.tmpl Wed Apr 09 15:27:57 2008 -0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,43 +0,0 @@ -#header# -<title>#repo|escape#: #file|escape# annotate</title> -</head> -<body> - -<div class="buttons"> -<a href="?cl=#rev#">changelog</a> -<a href="?sl=#rev#">shortlog</a> -<a href="?tags=">tags</a> -<a href="?cs=#node|short#">changeset</a> -<a href="?mf=#node|short#;path=#path|urlescape#">manifest</a> -<a href="?f=#node|short#;file=#file|urlescape#">file</a> -<a href="?fl=#node|short#;file=#file|urlescape#">revisions</a> -<a href="?fa=#node|short#;file=#file|urlescape#;style=raw">raw</a> -</div> - -<h2>Annotate #file|escape#</h2> - -<table> -<tr> - <td class="metatag">changeset #rev#:</td> - <td><a href="?cs=#node|short#">#node|short#</a></td></tr> -#rename%filerename# -#parent%fileannotateparent# -#child%fileannotatechild# -<tr> - <td class="metatag">author:</td> - <td>#author|obfuscate#</td></tr> -<tr> - <td class="metatag">date:</td> - <td>#date|date# (#date|age# ago)</td></tr> -<tr> - <td class="metatag">permissions:</td> - <td>#permissions|permissions#</td></tr> -</table> - -<br/> - -<table cellspacing="0" cellpadding="0"> -#annotate%annotateline# -</table> - -#footer#
--- a/templates/old/filediff.tmpl Wed Apr 09 15:27:57 2008 -0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,34 +0,0 @@ -#header# -<title>#repo|escape#: #file|escape# diff</title> -</head> -<body> - -<div class="buttons"> -<a href="?cl=#rev#">changelog</a> -<a href="?sl=#rev#">shortlog</a> -<a href="?tags=">tags</a> -<a href="?cs=#node|short#">changeset</a> -<a href="?f=#node|short#;file=#file|urlescape#">file</a> -<a href="?fl=#node|short#;file=#file|urlescape#">revisions</a> -<a href="?fa=#node|short#;file=#file|urlescape#">annotate</a> -<a href="?fd=#node|short#;file=#file|urlescape#;style=raw">raw</a> -</div> - -<h2>#file|escape#</h2> - -<table id="filediffEntry"> -<tr> - <th class="revision">revision #rev#:</th> - <td class="revision"><a href="?cs=#node|short#">#node|short#</a></td> -</tr> -#parent%filediffparent# -#child%filediffchild# -</table> - -<div id="fileDiff"> -#diff# -</div> - -#footer# - -
--- a/templates/old/filelog.tmpl Wed Apr 09 15:27:57 2008 -0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,25 +0,0 @@ -#header# -<title>#repo|escape#: #file|escape# history</title> -<link rel="alternate" type="application/atom+xml" - href="#url#atom-log/tip/#file|urlescape#" title="Atom feed for #repo|escape#:#file#"> -<link rel="alternate" type="application/rss+xml" - href="?fl=0;file=#file|urlescape#;style=rss" title="RSS feed for #repo|escape#:#file#"> -</head> -</head> -<body> - -<div class="buttons"> -<a href="?cl=tip">changelog</a> -<a href="?sl=tip">shortlog</a> -<a href="?tags=">tags</a> -<a href="?f=#node|short#;file=#file|urlescape#">file</a> -<a href="?fa=#node|short#;file=#file|urlescape#">annotate</a> -<a type="application/rss+xml" href="?fl=0;file=#file|urlescape#;style=rss">rss</a> -<a type="application/atom+xml" href="#url#atom-log/tip/#file|urlescape#" title="Atom feed for #repo|escape#:#file#">atom</a> -</div> - -<h2>#file|escape# revision history</h2> - -#entries%filelogentry# - -#footer#
--- a/templates/old/filelogentry.tmpl Wed Apr 09 15:27:57 2008 -0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,25 +0,0 @@ -<table class="logEntry parity#parity#"> - <tr> - <th class="age">#date|age# ago:</th> - <th class="firstline"><a href="?cs=#node|short#">#desc|strip|firstline|escape#</a></th> - </tr> - <tr> - <th class="revision">revision #filerev#:</td> - <td class="node"> - <a href="?f=#node|short#;file=#file|urlescape#">#node|short#</a> - <a href="?fd=#node|short#;file=#file|urlescape#">(diff)</a> - <a href="?fa=#node|short#;file=#file|urlescape#">(annotate)</a> - </td> - </tr> - #rename%filelogrename# - <tr> - <th class="author">author:</th> - <td class="author">#author|obfuscate#</td> - </tr> - <tr> - <th class="date">date:</th> - <td class="date">#date|date#</td> - </tr> -</table> - -
--- a/templates/old/filerevision.tmpl Wed Apr 09 15:27:57 2008 -0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,41 +0,0 @@ -#header# -<title>#repo|escape#:#file|escape#</title> -</head> -<body> - -<div class="buttons"> -<a href="?cl=#rev#">changelog</a> -<a href="?sl=#rev#">shortlog</a> -<a href="?tags=">tags</a> -<a href="?cs=#node|short#">changeset</a> -<a href="?mf=#node|short#;path=#path|urlescape#">manifest</a> -<a href="?fl=#node|short#;file=#file|urlescape#">revisions</a> -<a href="?fa=#node|short#;file=#file|urlescape#">annotate</a> -<a href="?f=#node|short#;file=#file|urlescape#;style=raw">raw</a> -</div> - -<h2>#file|escape#</h2> - -<table> -<tr> - <td class="metatag">changeset #rev#:</td> - <td><a href="?cs=#node|short#">#node|short#</a></td></tr> -#rename%filerename# -#parent%filerevparent# -#child%filerevchild# -<tr> - <td class="metatag">author:</td> - <td>#author|obfuscate#</td></tr> -<tr> - <td class="metatag">date:</td> - <td>#date|date# (#date|age# ago)</td></tr> -<tr> - <td class="metatag">permissions:</td> - <td>#permissions|permissions#</td></tr> -</table> - -<pre> -#text%fileline# -</pre> - -#footer#
--- a/templates/old/footer.tmpl Wed Apr 09 15:27:57 2008 -0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,8 +0,0 @@ -#motd# -<div class="logo"> -powered by<br/> -<a href="http://www.selenic.com/mercurial/">mercurial</a> -</div> - -</body> -</html>
--- a/templates/old/header.tmpl Wed Apr 09 15:27:57 2008 -0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,8 +0,0 @@ -Content-type: text/html - -<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"> -<html> -<head> -<link rel="icon" href="?static=hgicon.png" type="image/png"> -<meta name="robots" content="index, nofollow" /> -<link rel="stylesheet" href="?static=style.css" type="text/css" />
--- a/templates/old/manifest.tmpl Wed Apr 09 15:27:57 2008 -0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,23 +0,0 @@ -#header# -<title>#repo|escape#: manifest for changeset #node|short#</title> -</head> -<body> - -<div class="buttons"> -<a href="?cl=#rev#">changelog</a> -<a href="?sl=#rev#">shortlog</a> -<a href="?tags=">tags</a> -<a href="?cs=#node|short#">changeset</a> -#archives%archiveentry# -</div> - -<h2>manifest for changeset #node|short#: #path|escape#</h2> - -<table cellpadding="0" cellspacing="0"> -<tr class="parity1"> - <td><tt>drwxr-xr-x</tt> - <td><a href="?mf=#node|short#;path=#up|urlescape#">[up]</a> -#dentries%manifestdirentry# -#fentries%manifestfileentry# -</table> -#footer#
--- a/templates/old/map Wed Apr 09 15:27:57 2008 -0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,53 +0,0 @@ -default = 'changelog' -header = header.tmpl -footer = footer.tmpl -search = search.tmpl -changelog = changelog.tmpl -shortlog = shortlog.tmpl -shortlogentry = shortlogentry.tmpl -naventry = '<a href="?cl={node|short}">{label|escape}</a> ' -navshortentry = '<a href="?sl={node|short}">{label|escape}</a> ' -filedifflink = '<a href="?fd=#node|short#;file=#file|urlescape#">#file|escape#</a> ' -filenodelink = '<a href="?f=#node|short#;file=#file|urlescape#">#file|escape#</a> ' -fileellipses = '...' -changelogentry = changelogentry.tmpl -searchentry = changelogentry.tmpl -changeset = changeset.tmpl -manifest = manifest.tmpl -manifestdirentry = '<tr class="parity#parity#"><td><tt>drwxr-xr-x</tt> <td><a href="?mf=#node|short#;path=#path|urlescape#">#basename|escape#/</a>' -manifestfileentry = '<tr class="parity#parity#"><td><tt>#permissions|permissions#</tt> <td><a href="?f=#node|short#;file=#file|urlescape#">#basename|escape#</a>' -filerevision = filerevision.tmpl -fileannotate = fileannotate.tmpl -filediff = filediff.tmpl -filelog = filelog.tmpl -fileline = '<div class="parity#parity#"><span class="lineno">#linenumber#</span>#line|escape#</div>' -filelogentry = filelogentry.tmpl -annotateline = '<tr class="parity#parity#"><td class="annotate"><a href="?fa=#node|short#;file=#file|urlescape#">#author|obfuscate#@#rev#</a></td><td><pre>#line|escape#</pre></td></tr>' -difflineplus = '<span class="plusline">#line|escape#</span>' -difflineminus = '<span class="minusline">#line|escape#</span>' -difflineat = '<span class="atline">#line|escape#</span>' -diffline = '#line|escape#' -changelogparent = '<tr><th class="parent">parent #rev#:</th><td class="parent"><a href="?cs=#node|short#">#node|short#</a></td></tr>' -changesetparent = '<tr><th class="parent">parent #rev#:</th><td class="parent"><a href="?cs=#node|short#">#node|short#</a></td></tr>' -filerevparent = '<tr><td class="metatag">parent:</td><td><a href="?f=#node|short#;file=#file|urlescape#">#node|short#</a></td></tr>' -filerename = '<tr><td class="metatag">parent:</td><td><a href="?f=#node|short#;file=#file|urlescape#">#file|escape#@#node|short#</a></td></tr>' -filelogrename = '<tr><th>base:</th><td><a href="?f=#node|short#;file=#file|urlescape#">#file|escape#@#node|short#</a></td></tr>' -fileannotateparent = '<tr><td class="metatag">parent:</td><td><a href="?fa=#node|short#;file=#file|urlescape#">#node|short#</a></td></tr>' -changesetchild = '<tr><th class="child">child #rev#:</th><td class="child"><a href="?cs=#node|short#">#node|short#</a></td></tr>' -changelogchild = '<tr><th class="child">child #rev#:</th><td class="child"><a href="?cs=#node|short#">#node|short#</a></td></tr>' -filerevchild = '<tr><td class="metatag">child:</td><td><a href="?f=#node|short#;file=#file|urlescape#">#node|short#</a></td></tr>' -fileannotatechild = '<tr><td class="metatag">child:</td><td><a href="?fa=#node|short#;file=#file|urlescape#">#node|short#</a></td></tr>' -tags = tags.tmpl -tagentry = '<li class="tagEntry parity#parity#"><tt class="node">#node#</tt> <a href="?cs=#node|short#">#tag|escape#</a></li>' -diffblock = '<pre class="parity#parity#">#lines#</pre>' -changelogtag = '<tr><th class="tag">tag:</th><td class="tag">#tag|escape#</td></tr>' -changesettag = '<tr><th class="tag">tag:</th><td class="tag">#tag|escape#</td></tr>' -filediffparent = '<tr><th class="parent">parent #rev#:</th><td class="parent"><a href="?cs=#node|short#">#node|short#</a></td></tr>' -filelogparent = '<tr><th>parent #rev#:</th><td><a href="?f=#node|short#;file=#file|urlescape#">#node|short#</a></td></tr>' -filediffchild = '<tr><th class="child">child #rev#:</th><td class="child"><a href="?cs=#node|short#">#node|short#</a></td></tr>' -filelogchild = '<tr><th>child #rev#:</th><td><a href="?f=#node|short#;file=#file|urlescape#">#node|short#</a></td></tr>' -indexentry = '<tr class="parity#parity#"><td><a href="#url#">#name|escape#</a></td><td>#description#</td><td>#contact|obfuscate#</td><td class="age">#lastchange|age# ago</td><td class="indexlinks"><a href="#url#?cl=tip;style=rss">RSS</a> <a href="#url#atom-log">Atom</a> #archives%archiveentry#</td></tr>' -index = index.tmpl -archiveentry = '<a href="#url#?ca=#node|short#;type=#type|urlescape#">#type|escape#</a> ' -notfound = notfound.tmpl -error = error.tmpl
--- a/templates/old/search.tmpl Wed Apr 09 15:27:57 2008 -0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,33 +0,0 @@ -#header# -<title>#repo|escape#: searching for #query|escape#</title> -</head> -<body> - -<div class="buttons"> -<a href="?cl=tip">changelog</a> -<a href="?sl=tip">shortlog</a> -<a href="?tags=">tags</a> -<a href="?mf=#node|short#;path=/">manifest</a> -</div> - -<h2>searching for #query|escape#</h2> - -<form> -<p> -search: -<input type="hidden" name="cmd" value="changelog"> -<input name="rev" type="text" width="30" value="#query|escape#"> -</p> -</form> - -#entries# - -<form> -<p> -search: -<input type="hidden" name="cmd" value="changelog"> -<input name="rev" type="text" width="30" value="#query|escape#"> -</p> -</form> - -#footer#
--- a/templates/old/shortlog.tmpl Wed Apr 09 15:27:57 2008 -0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,41 +0,0 @@ -#header# -<title>#repo|escape#: shortlog</title> -<link rel="alternate" type="application/atom+xml" - href="#url#atom-log" title="Atom feed for #repo|escape#"> -<link rel="alternate" type="application/rss+xml" - href="?cmd=changelog;style=rss" title="RSS feed for #repo|escape#"> -</head> -<body> - -<div class="buttons"> -<a href="?cl=#rev#">changelog</a> -<a href="?cmd=tags">tags</a> -<a href="?mf=#node|short#;path=/">manifest</a> -#archives%archiveentry# -<a type="application/rss+xml" href="?style=rss">rss</a> -<a type="application/atom+xml" href="#url#atom-log" title="Atom feed for #repo|escape#">atom</a> -</div> - -<h2>shortlog for #repo|escape#</h2> - -<form action="#"> -<p> -<label for="search1">search:</label> -<input type="hidden" name="cmd" value="changelog"> -<input name="rev" id="search1" type="text" size="30"> -navigate: <small class="navigate">#changenav%navshortentry#</small> -</p> -</form> - -#entries%shortlogentry# - -<form action="#"> -<p> -<label for="search2">search:</label> -<input type="hidden" name="cmd" value="changelog"> -<input name="rev" id="search2" type="text" size="30"> -navigate: <small class="navigate">#changenav%navshortentry#</small> -</p> -</form> - -#footer#
--- a/templates/old/shortlogentry.tmpl Wed Apr 09 15:27:57 2008 -0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,7 +0,0 @@ -<table class="slogEntry parity#parity#"> - <tr> - <td class="age">#date|age#</td> - <td class="author">#author|obfuscate#</td> - <td class="node"><a href="?cs=#node|short#">#desc|strip|firstline|escape#</a></td> - </tr> -</table>
--- a/templates/old/tags.tmpl Wed Apr 09 15:27:57 2008 -0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,24 +0,0 @@ -#header# -<title>#repo|escape#: tags</title> -<link rel="alternate" type="application/atom+xml" - href="#url#atom-tags" title="Atom feed for #repo|escape#: tags"> -<link rel="alternate" type="application/rss+xml" - href="?cmd=tags;style=rss" title="RSS feed for #repo|escape#: tags"> -</head> -<body> - -<div class="buttons"> -<a href="?cl=tip">changelog</a> -<a href="?sl=tip">shortlog</a> -<a href="?mf=#node|short#;path=/">manifest</a> -<a type="application/rss+xml" href="?cmd=tags;style=rss">rss</a> -<a type="application/atom+xml" href="#url#atom-tags">atom</a> -</div> - -<h2>tags:</h2> - -<ul id="tagEntries"> -#entries%tagentry# -</ul> - -#footer#
--- a/templates/raw/header.tmpl Wed Apr 09 15:27:57 2008 -0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,2 +0,0 @@ -Content-type: text/plain; charset={encoding} -
--- a/templates/raw/manifest.tmpl Wed Apr 09 15:27:57 2008 -0700 +++ b/templates/raw/manifest.tmpl Wed Apr 09 15:28:30 2008 -0700 @@ -1,3 +1,3 @@ {header} -{dentries%manifestdirentry}{fentries%manifestfileentry} +{dentries%direntry}{fentries%fileentry} {footer}
--- a/templates/raw/map Wed Apr 09 15:27:57 2008 -0700 +++ b/templates/raw/map Wed Apr 09 15:28:30 2008 -0700 @@ -1,4 +1,5 @@ -header = header.tmpl +mimetype = 'text/plain; charset={encoding}' +header = '' footer = '' changeset = changeset.tmpl difflineplus = '#line#' @@ -8,15 +9,14 @@ changesetparent = '# Parent #node#' changesetchild = '# Child #node#' filenodelink = '' -filerevision = '#rawfileheader##raw#' fileline = '#line#' diffblock = '#lines#' filediff = filediff.tmpl fileannotate = fileannotate.tmpl annotateline = '#author#@#rev#: #line#' manifest = manifest.tmpl -manifestdirentry = 'drwxr-xr-x {basename}\n' -manifestfileentry = '{permissions|permissions} {size} {basename}\n' +direntry = 'drwxr-xr-x {basename}\n' +fileentry = '{permissions|permissions} {size} {basename}\n' index = index.tmpl notfound = notfound.tmpl error = error.tmpl
--- a/templates/rss/header.tmpl Wed Apr 09 15:27:57 2008 -0700 +++ b/templates/rss/header.tmpl Wed Apr 09 15:28:30 2008 -0700 @@ -1,5 +1,3 @@ -Content-type: text/xml; charset={encoding} - <?xml version="1.0" encoding="{encoding}"?> <rss version="2.0"> <channel>
--- a/templates/rss/map Wed Apr 09 15:27:57 2008 -0700 +++ b/templates/rss/map Wed Apr 09 15:28:30 2008 -0700 @@ -1,4 +1,5 @@ default = 'changelog' +mimetype = 'text/xml; charset={encoding}' header = header.tmpl changelog = changelog.tmpl changelogentry = changelogentry.tmpl
--- a/templates/search.tmpl Wed Apr 09 15:27:57 2008 -0700 +++ b/templates/search.tmpl Wed Apr 09 15:28:30 2008 -0700 @@ -7,7 +7,7 @@ <a href="#url#log{sessionvars%urlparameter}">changelog</a> <a href="#url#shortlog{sessionvars%urlparameter}">shortlog</a> <a href="#url#tags{sessionvars%urlparameter}">tags</a> -<a href="#url#file/#node|short#{sessionvars%urlparameter}">manifest</a> +<a href="#url#file/#node|short#{sessionvars%urlparameter}">files</a> #archives%archiveentry# </div>
--- a/templates/shortlog.tmpl Wed Apr 09 15:27:57 2008 -0700 +++ b/templates/shortlog.tmpl Wed Apr 09 15:28:30 2008 -0700 @@ -10,7 +10,7 @@ <div class="buttons"> <a href="#url#log/#rev#{sessionvars%urlparameter}">changelog</a> <a href="#url#tags{sessionvars%urlparameter}">tags</a> -<a href="#url#file/#node|short#/{sessionvars%urlparameter}">manifest</a> +<a href="#url#file/#node|short#/{sessionvars%urlparameter}">files</a> #archives%archiveentry# <a type="application/rss+xml" href="#url#rss-log">rss</a> <a type="application/rss+xml" href="#url#atom-log" title="Atom feed for #repo|escape#">atom</a>
--- a/templates/static/highlight.css Wed Apr 09 15:27:57 2008 -0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,59 +0,0 @@ -.c { color: #808080 } /* Comment */ -.err { color: #F00000; background-color: #F0A0A0 } /* Error */ -.k { color: #008000; font-weight: bold } /* Keyword */ -.o { color: #303030 } /* Operator */ -.cm { color: #808080 } /* Comment.Multiline */ -.cp { color: #507090 } /* Comment.Preproc */ -.c1 { color: #808080 } /* Comment.Single */ -.cs { color: #cc0000; font-weight: bold } /* Comment.Special */ -.gd { color: #A00000 } /* Generic.Deleted */ -.ge { font-style: italic } /* Generic.Emph */ -.gr { color: #FF0000 } /* Generic.Error */ -.gh { color: #000080; font-weight: bold } /* Generic.Heading */ -.gi { color: #00A000 } /* Generic.Inserted */ -.go { color: #808080 } /* Generic.Output */ -.gp { color: #c65d09; font-weight: bold } /* Generic.Prompt */ -.gs { font-weight: bold } /* Generic.Strong */ -.gu { color: #800080; font-weight: bold } /* Generic.Subheading */ -.gt { color: #0040D0 } /* Generic.Traceback */ -.kc { color: #008000; font-weight: bold } /* Keyword.Constant */ -.kd { color: #008000; font-weight: bold } /* Keyword.Declaration */ -.kp { color: #003080; font-weight: bold } /* Keyword.Pseudo */ -.kr { color: #008000; font-weight: bold } /* Keyword.Reserved */ -.kt { color: #303090; font-weight: bold } /* Keyword.Type */ -.m { color: #6000E0; font-weight: bold } /* Literal.Number */ -.s { background-color: #fff0f0 } /* Literal.String */ -.na { color: #0000C0 } /* Name.Attribute */ -.nb { color: #007020 } /* Name.Builtin */ -.nc { color: #B00060; font-weight: bold } /* Name.Class */ -.no { color: #003060; font-weight: bold } /* Name.Constant */ -.nd { color: #505050; font-weight: bold } /* Name.Decorator */ -.ni { color: #800000; font-weight: bold } /* Name.Entity */ -.ne { color: #F00000; font-weight: bold } /* Name.Exception */ -.nf { color: #0060B0; font-weight: bold } /* Name.Function */ -.nl { color: #907000; font-weight: bold } /* Name.Label */ -.nn { color: #0e84b5; font-weight: bold } /* Name.Namespace */ -.nt { color: #007000 } /* Name.Tag */ -.nv { color: #906030 } /* Name.Variable */ -.ow { color: #000000; font-weight: bold } /* Operator.Word */ -.w { color: #bbbbbb } /* Text.Whitespace */ -.mf { color: #6000E0; font-weight: bold } /* Literal.Number.Float */ -.mh { color: #005080; font-weight: bold } /* Literal.Number.Hex */ -.mi { color: #0000D0; font-weight: bold } /* Literal.Number.Integer */ -.mo { color: #4000E0; font-weight: bold } /* Literal.Number.Oct */ -.sb { background-color: #fff0f0 } /* Literal.String.Backtick */ -.sc { color: #0040D0 } /* Literal.String.Char */ -.sd { color: #D04020 } /* Literal.String.Doc */ -.s2 { background-color: #fff0f0 } /* Literal.String.Double */ -.se { color: #606060; font-weight: bold; background-color: #fff0f0 } /* Literal.String.Escape */ -.sh { background-color: #fff0f0 } /* Literal.String.Heredoc */ -.si { background-color: #e0e0e0 } /* Literal.String.Interpol */ -.sx { color: #D02000; background-color: #fff0f0 } /* Literal.String.Other */ -.sr { color: #000000; background-color: #fff0ff } /* Literal.String.Regex */ -.s1 { background-color: #fff0f0 } /* Literal.String.Single */ -.ss { color: #A06000 } /* Literal.String.Symbol */ -.bp { color: #007020 } /* Name.Builtin.Pseudo */ -.vc { color: #306090 } /* Name.Variable.Class */ -.vg { color: #d07000; font-weight: bold } /* Name.Variable.Global */ -.vi { color: #3030B0 } /* Name.Variable.Instance */ -.il { color: #0000D0; font-weight: bold } /* Literal.Number.Integer.Long */
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/templates/static/style-coal.css Wed Apr 09 15:28:30 2008 -0700 @@ -0,0 +1,156 @@ +body { + margin: 0; + padding: 0; + background: black url(background.png) repeat-x; + font-family: sans-serif; +} + +.container { + padding-right: 150px; +} + +.main { + position: relative; + background: white; + padding: 2em; + border-right: 15px solid black; + border-bottom: 15px solid black; +} + +.overflow { + width: 100%; + overflow: auto; +} + +.menu { + background: #999; + padding: 10px; + width: 75px; + margin: 0; + font-size: 80%; + text-align: left; + position: fixed; + top: 27px; + left: auto; + right: 27px; +} + +.menu ul { + list-style: none; + padding: 0; + margin: 10px 0 0 0; +} + +.menu li { + margin-bottom: 3px; + padding: 2px 4px; + background: white; + color: black; + font-weight: normal; +} + +.menu li.active { + background: black; + color: white; +} + +.menu a { color: black; display: block; } + +.search { + position: absolute; + top: .7em; + right: 2em; +} + +a { text-decoration:none; } +.age { white-space:nowrap; } +.date { white-space:nowrap; } +.indexlinks { white-space:nowrap; } +.parity0 { background-color: #f5f5f5; } +.parity1 { background-color: white; } +.plusline { color: green; } +.minusline { color: red; } +.atline { color: purple; } + +.navigate { + text-align: right; + font-size: 60%; + margin: 1em 0 1em 0; +} + +.tag { + color: #999; + font-size: 70%; + font-weight: normal; + margin-left: .5em; + vertical-align: text-baseline; +} + +/* Common */ +pre { margin: 0; } + +h2 { font-size: 120%; border-bottom: 1px solid #999; } +h3 { + margin-top: -.7em; + font-size: 100%; +} + +/* log and tags tables */ +.bigtable { + border-bottom: 1px solid #999; + border-collapse: collapse; + font-size: 90%; + width: 100%; + font-weight: normal; + text-align: left; +} + +.bigtable td { + padding: 1px 4px 1px 4px; + vertical-align: top; +} + +.bigtable th { + padding: 1px 4px 1px 4px; + border-bottom: 1px solid #999; + font-size: smaller; +} +.bigtable tr { border: none; } +.bigtable .age { width: 6em; } +.bigtable .author { width: 10em; } +.bigtable .description { } +.bigtable .node { width: 5em; font-family: monospace;} +.bigtable .lineno { width: 2em; text-align: right;} +.bigtable .lineno a { color: #999; font-size: smaller; font-family: monospace;} +.bigtable td.source { font-family: monospace; white-space: pre; } +.bigtable .permissions { width: 8em; text-align: left;} +.bigtable .size { width: 5em; text-align: right; } +.bigtable .annotate { text-align: right; padding-right: } +.bigtable td.annotate { font-size: smaller; } + +.fileline { font-family: monospace; } +.fileline img { border: 0; } + +/* Changeset entry */ +#changesetEntry { + border-collapse: collapse; + font-size: 90%; + width: 100%; + margin-bottom: 1em; +} + +#changesetEntry th { + padding: 1px 4px 1px 4px; + width: 4em; + text-align: right; + font-weight: normal; + color: #999; + margin-right: .5em; + vertical-align: top; +} + +div.description { + border-left: 3px solid #999; + margin: 1em 0 1em 0; + padding: .3em; +}
--- a/templates/static/style-gitweb.css Wed Apr 09 15:27:57 2008 -0700 +++ b/templates/static/style-gitweb.css Wed Apr 09 15:28:30 2008 -0700 @@ -34,19 +34,30 @@ tr.dark:hover, .parity1:hover { background-color:#edece6; } td { padding:2px 5px; font-size:12px; vertical-align:top; } td.link { padding:2px 5px; font-family:sans-serif; font-size:10px; } +td.indexlinks { white-space: nowrap; } +td.indexlinks a { + padding: 2px 5px; line-height: 10px; + border: 1px solid; + color: #ffffff; background-color: #7777bb; + border-color: #aaaadd #333366 #333366 #aaaadd; + font-weight: bold; text-align: center; text-decoration: none; + font-size: 10px; +} +td.indexlinks a:hover { background-color: #6666aa; } div.pre { font-family:monospace; font-size:12px; white-space:pre; } div.diff_info { font-family:monospace; color:#000099; background-color:#edece6; font-style:italic; } div.index_include { border:solid #d9d8d1; border-width:0px 0px 1px; padding:12px 8px; } div.search { margin:4px 8px; position:absolute; top:56px; right:12px } .linenr { color:#999999; text-decoration:none } -a.rss_logo { - float:right; padding:3px 0px; width:35px; line-height:10px; +div.rss_logo { float: right; white-space: nowrap; } +div.rss_logo a { + padding:3px 6px; line-height:10px; border:1px solid; border-color:#fcc7a5 #7d3302 #3e1a01 #ff954e; color:#ffffff; background-color:#ff6600; font-weight:bold; font-family:sans-serif; font-size:10px; text-align:center; text-decoration:none; } -a.rss_logo:hover { background-color:#ee5500; } +div.rss_logo a:hover { background-color:#ee5500; } pre { margin: 0; } span.logtags span { padding: 0px 4px; @@ -64,3 +75,7 @@ background-color: #aaffaa; border-color: #ccffcc #00cc33 #00cc33 #ccffcc; } +span.logtags span.inbranchtag { + background-color: #d5dde6; + border-color: #e3ecf4 #9398f4 #9398f4 #e3ecf4; +}
--- a/templates/static/style.css Wed Apr 09 15:27:57 2008 -0700 +++ b/templates/static/style.css Wed Apr 09 15:28:30 2008 -0700 @@ -34,21 +34,8 @@ pre { margin: 0; } .logo { - background-color: #333; - padding: 4pt; - margin: 8pt 0 8pt 8pt; - font-family: sans; - font-size: 60%; - color: white; float: right; clear: right; - text-align: left; -} - -.logo a { - font-weight: bold; - font-size: 150%; - color: #999; } /* Changelog/Filelog entries */
--- a/templates/tags.tmpl Wed Apr 09 15:27:57 2008 -0700 +++ b/templates/tags.tmpl Wed Apr 09 15:28:30 2008 -0700 @@ -10,7 +10,7 @@ <div class="buttons"> <a href="#url#log{sessionvars%urlparameter}">changelog</a> <a href="#url#shortlog{sessionvars%urlparameter}">shortlog</a> -<a href="#url#file/#node|short#/{sessionvars%urlparameter}">manifest</a> +<a href="#url#file/#node|short#/{sessionvars%urlparameter}">files</a> <a type="application/rss+xml" href="#url#rss-tags">rss</a> <a type="application/atom+xml" href="#url#atom-tags">atom</a> </div>
--- a/tests/coverage.py Wed Apr 09 15:27:57 2008 -0700 +++ b/tests/coverage.py Wed Apr 09 15:28:30 2008 -0700 @@ -105,20 +105,20 @@ self.excluded = excluded self.suite_spots = suite_spots self.excluding_suite = 0 - + def doRecursive(self, node): for n in node.getChildNodes(): self.dispatch(n) visitStmt = visitModule = doRecursive - + def doCode(self, node): if hasattr(node, 'decorators') and node.decorators: self.dispatch(node.decorators) self.recordAndDispatch(node.code) else: self.doSuite(node, node.code) - + visitFunction = visitClass = doCode def getFirstLine(self, node): @@ -138,14 +138,14 @@ for n in node.getChildNodes(): lineno = max(lineno, self.getLastLine(n)) return lineno - + def doStatement(self, node): self.recordLine(self.getFirstLine(node)) visitAssert = visitAssign = visitAssTuple = visitPrint = \ visitPrintnl = visitRaise = visitSubscript = visitDecorators = \ doStatement - + def visitPass(self, node): # Pass statements have weird interactions with docstrings. If this # pass statement is part of one of those pairs, claim that the statement @@ -154,10 +154,10 @@ if l: lines = self.suite_spots.get(l, [l,l]) self.statements[lines[1]] = 1 - + def visitDiscard(self, node): # Discard nodes are statements that execute an expression, but then - # discard the results. This includes function calls, so we can't + # discard the results. This includes function calls, so we can't # ignore them all. But if the expression is a constant, the statement # won't be "executed", so don't count it now. if node.expr.__class__.__name__ != 'Const': @@ -171,7 +171,7 @@ return self.recordLine(self.getFirstLine(node)) else: return 0 - + def recordLine(self, lineno): # Returns a bool, whether the line is included or excluded. if lineno: @@ -186,18 +186,18 @@ return 0 # If this line is excluded, or suite_spots maps this line to # another line that is exlcuded, then we're excluded. - elif self.excluded.has_key(lineno) or \ - self.suite_spots.has_key(lineno) and \ - self.excluded.has_key(self.suite_spots[lineno][1]): + elif lineno in self.excluded or \ + lineno in self.suite_spots and \ + self.suite_spots[lineno][1] in self.excluded: return 0 # Otherwise, this is an executable line. else: self.statements[lineno] = 1 return 1 return 0 - + default = recordNodeLine - + def recordAndDispatch(self, node): self.recordNodeLine(node) self.dispatch(node) @@ -208,7 +208,7 @@ self.excluding_suite = 1 self.recordAndDispatch(body) self.excluding_suite = exsuite - + def doPlainWordSuite(self, prevsuite, suite): # Finding the exclude lines for else's is tricky, because they aren't # present in the compiler parse tree. Look at the previous suite, @@ -217,16 +217,16 @@ lastprev = self.getLastLine(prevsuite) firstelse = self.getFirstLine(suite) for l in range(lastprev+1, firstelse): - if self.suite_spots.has_key(l): - self.doSuite(None, suite, exclude=self.excluded.has_key(l)) + if l in self.suite_spots: + self.doSuite(None, suite, exclude=l in self.excluded) break else: self.doSuite(None, suite) - + def doElse(self, prevsuite, node): if node.else_: self.doPlainWordSuite(prevsuite, node.else_) - + def visitFor(self, node): self.doSuite(node, node.body) self.doElse(node.body, node) @@ -256,14 +256,14 @@ else: self.doSuite(a, h) self.doElse(node.handlers[-1][2], node) - + def visitTryFinally(self, node): self.doSuite(node, node.body) self.doPlainWordSuite(node.body, node.final) - + def visitWith(self, node): self.doSuite(node, node.body) - + def visitGlobal(self, node): # "global" statements don't execute like others (they don't call the # trace function), so don't record their line numbers. @@ -283,7 +283,7 @@ # A dictionary with an entry for (Python source file name, line number # in that file) if that line has been executed. c = {} - + # A map from canonical Python source file name to a dictionary in # which there's an entry for each line number that has been # executed. @@ -311,12 +311,12 @@ self.relative_dir = os.path.normcase(os.path.abspath(os.curdir)+os.sep) self.exclude('# *pragma[: ]*[nN][oO] *[cC][oO][vV][eE][rR]') - # t(f, x, y). This method is passed to sys.settrace as a trace function. - # See [van Rossum 2001-07-20b, 9.2] for an explanation of sys.settrace and + # t(f, x, y). This method is passed to sys.settrace as a trace function. + # See [van Rossum 2001-07-20b, 9.2] for an explanation of sys.settrace and # the arguments and return value of the trace function. # See [van Rossum 2001-07-20a, 3.2] for a description of frame and code # objects. - + def t(self, f, w, unused): #pragma: no cover if w == 'line': #print "Executing %s @ %d" % (f.f_code.co_filename, f.f_lineno) @@ -324,7 +324,7 @@ for c in self.cstack: c[(f.f_code.co_filename, f.f_lineno)] = 1 return self.t - + def help(self, error=None): #pragma: no cover if error: print error @@ -353,9 +353,9 @@ long_opts = optmap.values() options, args = getopt.getopt(argv, short_opts, long_opts) for o, a in options: - if optmap.has_key(o): + if o in optmap: settings[optmap[o]] = 1 - elif optmap.has_key(o + ':'): + elif o + ':' in optmap: settings[optmap[o + ':']] = a elif o[2:] in long_opts: settings[o[2:]] = 1 @@ -376,14 +376,14 @@ args_needed = (settings.get('execute') or settings.get('annotate') or settings.get('report')) - action = (settings.get('erase') + action = (settings.get('erase') or settings.get('collect') or args_needed) if not action: help_fn("You must specify at least one of -e, -x, -c, -r, or -a.") if not args_needed and args: help_fn("Unexpected arguments: %s" % " ".join(args)) - + self.parallel_mode = settings.get('parallel-mode') self.get_ready() @@ -401,7 +401,7 @@ self.collect() if not args: args = self.cexecuted.keys() - + ignore_errors = settings.get('ignore-errors') show_missing = settings.get('show-missing') directory = settings.get('directory=') @@ -412,6 +412,9 @@ else: omit = [] + omit = [os.path.normcase(os.path.abspath(os.path.realpath(p))) + for p in omit] + if settings.get('report'): self.report(args, show_missing, ignore_errors, omit_prefixes=omit) if settings.get('annotate'): @@ -421,7 +424,7 @@ self.usecache = usecache if cache_file and not self.cache: self.cache_default = cache_file - + def get_ready(self, parallel_mode=False): if self.usecache and not self.cache: self.cache = os.environ.get(self.cache_env, self.cache_default) @@ -429,7 +432,7 @@ self.cache += "." + gethostname() + "." + str(os.getpid()) self.restore() self.analysis_cache = {} - + def start(self, parallel_mode=False): self.get_ready() if self.nesting == 0: #pragma: no cover @@ -437,7 +440,7 @@ if hasattr(threading, 'settrace'): threading.settrace(self.t) self.nesting += 1 - + def stop(self): self.nesting -= 1 if self.nesting == 0: #pragma: no cover @@ -461,7 +464,7 @@ def begin_recursive(self): self.cstack.append(self.c) self.xstack.append(self.exclude_re) - + def end_recursive(self): self.c = self.cstack.pop() self.exclude_re = self.xstack.pop() @@ -512,14 +515,14 @@ def merge_data(self, new_data): for file_name, file_data in new_data.items(): - if self.cexecuted.has_key(file_name): + if file_name in self.cexecuted: self.merge_file_data(self.cexecuted[file_name], file_data) else: self.cexecuted[file_name] = file_data def merge_file_data(self, cache_data, new_data): for line_number in new_data.keys(): - if not cache_data.has_key(line_number): + if not line_number in cache_data: cache_data[line_number] = new_data[line_number] # canonical_filename(filename). Return a canonical filename for the @@ -527,7 +530,7 @@ # normalized case). See [GDR 2001-12-04b, 3.3]. def canonical_filename(self, filename): - if not self.canonical_filename_cache.has_key(filename): + if not filename in self.canonical_filename_cache: f = filename if os.path.isabs(f) and not os.path.exists(f): f = os.path.basename(f) @@ -537,11 +540,11 @@ if os.path.exists(g): f = g break - cf = os.path.normcase(os.path.abspath(f)) + cf = os.path.normcase(os.path.abspath(os.path.realpath(f))) self.canonical_filename_cache[filename] = cf return self.canonical_filename_cache[filename] - # canonicalize_filenames(). Copy results from "c" to "cexecuted", + # canonicalize_filenames(). Copy results from "c" to "cexecuted", # canonicalizing filenames on the way. Clear the "c" map. def canonicalize_filenames(self): @@ -550,7 +553,7 @@ # Can't do anything useful with exec'd strings, so skip them. continue f = self.canonical_filename(filename) - if not self.cexecuted.has_key(f): + if not f in self.cexecuted: self.cexecuted[f] = {} self.cexecuted[f][lineno] = 1 self.c = {} @@ -573,9 +576,9 @@ # in the source code, (3) a list of lines of excluded statements, # and (4), a map of line numbers to multi-line line number ranges, for # statements that cross lines. - + def analyze_morf(self, morf): - if self.analysis_cache.has_key(morf): + if morf in self.analysis_cache: return self.analysis_cache[morf] filename = self.morf_filename(morf) ext = os.path.splitext(filename)[1] @@ -600,26 +603,26 @@ if len(tree) == 3 and type(tree[2]) == type(1): return tree[2] tree = tree[1] - + def last_line_of_tree(self, tree): while True: if len(tree) == 3 and type(tree[2]) == type(1): return tree[2] tree = tree[-1] - + def find_docstring_pass_pair(self, tree, spots): for i in range(1, len(tree)): if self.is_string_constant(tree[i]) and self.is_pass_stmt(tree[i+1]): first_line = self.first_line_of_tree(tree[i]) last_line = self.last_line_of_tree(tree[i+1]) self.record_multiline(spots, first_line, last_line) - + def is_string_constant(self, tree): try: return tree[0] == symbol.stmt and tree[1][1][1][0] == symbol.expr_stmt except: return False - + def is_pass_stmt(self, tree): try: return tree[0] == symbol.stmt and tree[1][1][1][0] == symbol.pass_stmt @@ -629,7 +632,7 @@ def record_multiline(self, spots, i, j): for l in range(i, j+1): spots[l] = (i, j) - + def get_suite_spots(self, tree, spots): """ Analyze a parse tree to find suite introducers which span a number of lines. @@ -671,7 +674,7 @@ # treat them differently, especially in the common case of a # function with a doc string and a single pass statement. self.find_docstring_pass_pair(tree[i], spots) - + elif tree[i][0] == symbol.simple_stmt: first_line = self.first_line_of_tree(tree[i]) last_line = self.last_line_of_tree(tree[i]) @@ -696,7 +699,7 @@ tree = parser.suite(text+'\n\n').totuple(1) self.get_suite_spots(tree, suite_spots) #print "Suite spots:", suite_spots - + # Use the compiler module to parse the text and find the executable # statements. We add newlines to be impervious to final partial lines. statements = {} @@ -752,13 +755,13 @@ def analysis2(self, morf): filename, statements, excluded, line_map = self.analyze_morf(morf) self.canonicalize_filenames() - if not self.cexecuted.has_key(filename): + if not filename in self.cexecuted: self.cexecuted[filename] = {} missing = [] for line in statements: lines = line_map.get(line, [line, line]) for l in range(lines[0], lines[1]+1): - if self.cexecuted[filename].has_key(l): + if l in self.cexecuted[filename]: break else: missing.append(line) @@ -806,7 +809,7 @@ else: globbed.append(morf) morfs = globbed - + morfs = self.filter_by_prefix(morfs, omit_prefixes) morfs.sort(self.morf_name_compare) @@ -873,7 +876,7 @@ except: if not ignore_errors: raise - + def annotate_file(self, filename, statements, excluded, missing, directory=None): source = open(filename, 'r') if directory: @@ -901,7 +904,7 @@ if self.blank_re.match(line): dest.write(' ') elif self.else_re.match(line): - # Special logic for lines containing only 'else:'. + # Special logic for lines containing only 'else:'. # See [GDR 2001-12-04b, 3.2]. if i >= len(statements) and j >= len(missing): dest.write('! ') @@ -925,40 +928,40 @@ the_coverage = coverage() # Module functions call methods in the singleton object. -def use_cache(*args, **kw): +def use_cache(*args, **kw): return the_coverage.use_cache(*args, **kw) -def start(*args, **kw): +def start(*args, **kw): return the_coverage.start(*args, **kw) -def stop(*args, **kw): +def stop(*args, **kw): return the_coverage.stop(*args, **kw) -def erase(*args, **kw): +def erase(*args, **kw): return the_coverage.erase(*args, **kw) -def begin_recursive(*args, **kw): +def begin_recursive(*args, **kw): return the_coverage.begin_recursive(*args, **kw) -def end_recursive(*args, **kw): +def end_recursive(*args, **kw): return the_coverage.end_recursive(*args, **kw) -def exclude(*args, **kw): +def exclude(*args, **kw): return the_coverage.exclude(*args, **kw) -def analysis(*args, **kw): +def analysis(*args, **kw): return the_coverage.analysis(*args, **kw) -def analysis2(*args, **kw): +def analysis2(*args, **kw): return the_coverage.analysis2(*args, **kw) -def report(*args, **kw): +def report(*args, **kw): return the_coverage.report(*args, **kw) -def annotate(*args, **kw): +def annotate(*args, **kw): return the_coverage.annotate(*args, **kw) -def annotate_file(*args, **kw): +def annotate_file(*args, **kw): return the_coverage.annotate_file(*args, **kw) # Save coverage data when Python exits. (The atexit module wasn't @@ -1033,7 +1036,7 @@ # Thanks, Allen. # # 2005-12-02 NMB Call threading.settrace so that all threads are measured. -# Thanks Martin Fuzzey. Add a file argument to report so that reports can be +# Thanks Martin Fuzzey. Add a file argument to report so that reports can be # captured to a different destination. # # 2005-12-03 NMB coverage.py can now measure itself.
--- a/tests/hghave Wed Apr 09 15:27:57 2008 -0700 +++ b/tests/hghave Wed Apr 09 15:28:30 2008 -0700 @@ -21,6 +21,9 @@ ret = fh.close() return (ignorestatus or ret is None) and r.search(s) +def has_baz(): + return matchoutput('baz --version 2>&1', r'baz Bazaar version') + def has_cvs(): return matchoutput('cvs --version 2>&1', r'Concurrent Versions System') @@ -28,7 +31,11 @@ return matchoutput('cvsps -h -q 2>&1', r'cvsps version', True) def has_darcs(): - return matchoutput('darcs', 'darcs version', True) + return matchoutput('darcs', r'darcs version', True) + +def has_mtn(): + return matchoutput('mtn --version', r'monotone', True) and not matchoutput( + 'mtn --version', r'monotone 0\.(\d|[12]\d|3[01])[^\d]', True) def has_eol_in_paths(): try: @@ -85,7 +92,34 @@ def has_symlink(): return hasattr(os, "symlink") +def has_tla(): + return matchoutput('tla --version 2>&1', r'The GNU Arch Revision') + +def has_unix_permissions(): + d = tempfile.mkdtemp(prefix=tempprefix, dir=".") + try: + fname = os.path.join(d, 'foo') + for umask in (077, 007, 022): + os.umask(umask) + f = open(fname, 'w') + f.close() + mode = os.stat(fname).st_mode + os.unlink(fname) + if mode & 0777 != ~umask & 0666: + return False + return True + finally: + os.rmdir(d) + +def has_pygments(): + try: + import pygments + return True + except ImportError: + return False + checks = { + "baz": (has_baz, "GNU Arch baz client"), "cvs": (has_cvs, "cvs client"), "cvsps": (has_cvsps, "cvsps utility"), "darcs": (has_darcs, "darcs client"), @@ -95,9 +129,13 @@ "git": (has_git, "git command line client"), "hotshot": (has_hotshot, "python hotshot module"), "lsprof": (has_lsprof, "python lsprof module"), + "mtn": (has_mtn, "monotone client (> 0.31)"), "svn": (has_svn, "subversion client and admin tools"), "svn-bindings": (has_svn_bindings, "subversion python bindings"), "symlink": (has_symlink, "symbolic links"), + "tla": (has_tla, "GNU Arch tla client"), + "unix-permissions": (has_unix_permissions, "unix-style permissions"), + "pygments": (has_pygments, "Pygments source highlighting library"), } def list_features(): @@ -133,14 +171,14 @@ feature = feature[3:] if feature not in checks: - error('hghave: unknown feature: ' + feature) + error('skipped: unknown feature: ' + feature) continue check, desc = checks[feature] if not negate and not check(): - error('hghave: missing feature: ' + desc) + error('skipped: missing feature: ' + desc) elif negate and check(): - error('hghave: system supports %s' % desc) + error('skipped: system supports %s' % desc) if failures != 0: sys.exit(1)
--- a/tests/md5sum.py Wed Apr 09 15:27:57 2008 -0700 +++ b/tests/md5sum.py Wed Apr 09 15:28:30 2008 -0700 @@ -7,8 +7,11 @@ # GPL-compatible. import sys -import os -import md5 + +try: + from hashlib import md5 +except ImportError: + from md5 import md5 for filename in sys.argv[1:]: try: @@ -17,7 +20,7 @@ sys.stderr.write('%s: Can\'t open: %s\n' % (filename, msg)) sys.exit(1) - m = md5.new() + m = md5() try: while 1: data = fp.read(8192)
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tests/readlink.py Wed Apr 09 15:28:30 2008 -0700 @@ -0,0 +1,12 @@ +#!/usr/bin/env python + +import errno, os, sys + +for f in sys.argv[1:]: + try: + print f, '->', os.readlink(f) + except OSError, err: + if err.errno != errno.EINVAL: raise + print f, 'not a symlink' + +sys.exit(0)
--- a/tests/run-tests.py Wed Apr 09 15:27:57 2008 -0700 +++ b/tests/run-tests.py Wed Apr 09 15:28:30 2008 -0700 @@ -12,18 +12,24 @@ import optparse import os import popen2 -import re import shutil import signal import sys import tempfile import time -# hghave reserved exit code to skip test +# reserved exit code to skip test (used by hghave) SKIPPED_STATUS = 80 +SKIPPED_PREFIX = 'skipped: ' required_tools = ["python", "diff", "grep", "unzip", "gunzip", "bunzip2", "sed"] +defaults = { + 'jobs': ('HGTEST_JOBS', 1), + 'timeout': ('HGTEST_TIMEOUT', 180), + 'port': ('HGTEST_PORT', 20059), +} + parser = optparse.OptionParser("%prog [options] [tests]") parser.add_option("-C", "--annotate", action="store_true", help="output files annotated with coverage") @@ -36,17 +42,23 @@ parser.add_option("-i", "--interactive", action="store_true", help="prompt to accept changed output") parser.add_option("-j", "--jobs", type="int", - help="number of jobs to run in parallel") + help="number of jobs to run in parallel" + " (default: $%s or %d)" % defaults['jobs']) +parser.add_option("--keep-tmpdir", action="store_true", + help="keep temporary directory after running tests" + " (best used with --tmpdir)") parser.add_option("-R", "--restart", action="store_true", help="restart at last error") parser.add_option("-p", "--port", type="int", - help="port on which servers should listen") + help="port on which servers should listen" + " (default: $%s or %d)" % defaults['port']) parser.add_option("-r", "--retest", action="store_true", help="retest failed tests") parser.add_option("-s", "--cover_stdlib", action="store_true", help="print a test coverage report inc. standard libraries") parser.add_option("-t", "--timeout", type="int", - help="kill errant tests after TIMEOUT seconds") + help="kill errant tests after TIMEOUT seconds" + " (default: $%s or %d)" % defaults['timeout']) parser.add_option("--tmpdir", type="string", help="run tests in the given temporary directory") parser.add_option("-v", "--verbose", action="store_true", @@ -54,7 +66,9 @@ parser.add_option("--with-hg", type="string", help="test existing install at given location") -parser.set_defaults(jobs=1, port=20059, timeout=180) +for option, default in defaults.items(): + defaults[option] = os.environ.get(*default) +parser.set_defaults(**defaults) (options, args) = parser.parse_args() verbose = options.verbose coverage = options.cover or options.cover_stdlib or options.annotate @@ -67,6 +81,13 @@ print >> sys.stderr, 'ERROR: cannot mix -interactive and --jobs > 1' sys.exit(1) +def rename(src, dst): + """Like os.rename(), trade atomicity and opened files friendliness + for existing destination support. + """ + shutil.copy(src, dst) + os.remove(src) + def vlog(*msg): if verbose: for m in msg: @@ -92,10 +113,10 @@ '''Extract missing/unknown features log lines as a list''' missing = [] for line in lines: - if not line.startswith('hghave: '): + if not line.startswith(SKIPPED_PREFIX): continue line = line.splitlines()[0] - missing.append(line[8:]) + missing.append(line[len(SKIPPED_PREFIX):]) return missing @@ -125,9 +146,10 @@ print "WARNING: Did not find prerequisite tool: "+p def cleanup_exit(): - if verbose: - print "# Cleaning up HGTMP", HGTMP - shutil.rmtree(HGTMP, True) + if not options.keep_tmpdir: + if verbose: + print "# Cleaning up HGTMP", HGTMP + shutil.rmtree(HGTMP, True) def use_correct_python(): # some tests run python interpreter. they must use same @@ -257,7 +279,7 @@ % options.timeout) return ret, splitnewlines(output) -def run_one(test, skips): +def run_one(test, skips, fails): '''tristate output: None -> skipped True -> passed @@ -270,6 +292,11 @@ print "\nSkipping %s: %s" % (test, msg) return None + def fail(msg): + fails.append((test, msg)) + print "\nERROR: %s %s" % (test, msg) + return None + vlog("# Test", test) # create a fresh hgrc @@ -332,7 +359,6 @@ signal.alarm(0) skipped = (ret == SKIPPED_STATUS) - diffret = 0 # If reference output file exists, check test output against it if os.path.exists(ref): f = open(ref, "r") @@ -340,19 +366,20 @@ f.close() else: ref_out = [] - if not skipped and out != ref_out: - diffret = 1 - print "\nERROR: %s output changed" % (test) - show_diff(ref_out, out) if skipped: missing = extract_missing_features(out) if not missing: missing = ['irrelevant'] skip(missing[-1]) + elif out != ref_out: + if ret: + fail("output changed and returned error code %d" % ret) + else: + fail("output changed") + show_diff(ref_out, out) + ret = 1 elif ret: - print "\nERROR: %s failed with error code %d" % (test, ret) - elif diffret: - ret = diffret + fail("returned error code %d" % ret) if not verbose: sys.stdout.write(skipped and 's' or '.') @@ -390,7 +417,8 @@ pass os.chdir(TESTDIR) - shutil.rmtree(tmpd, True) + if not options.keep_tmpdir: + shutil.rmtree(tmpd, True) if skipped: return None return ret == 0 @@ -404,6 +432,7 @@ # the tests produce repeatable output. os.environ['LANG'] = os.environ['LC_ALL'] = 'C' os.environ['TZ'] = 'GMT' +os.environ["EMAIL"] = "Foo Bar <foo.bar@example.com>" TESTDIR = os.environ["TESTDIR"] = os.getcwd() HGTMP = os.environ['HGTMP'] = tempfile.mkdtemp('', 'hgtests.', options.tmpdir) @@ -411,9 +440,7 @@ HGRCPATH = None os.environ["HGEDITOR"] = sys.executable + ' -c "import sys; sys.exit(0)"' -os.environ["HGMERGE"] = ('python "%s" -L my -L other' - % os.path.join(TESTDIR, os.path.pardir, - 'contrib', 'simplemerge')) +os.environ["HGMERGE"] = "internal:merge" os.environ["HGUSER"] = "test" os.environ["HGENCODING"] = "ascii" os.environ["HGENCODINGMODE"] = "strict" @@ -464,13 +491,17 @@ failures = 0 tested, skipped, failed = 0, 0, 0 skips = [] + fails = [] while fps: pid, status = os.wait() fp = fps.pop(pid) l = fp.read().splitlines() test, skip, fail = map(int, l[:3]) - for s in l[3:]: + split = -fail or len(l) + for s in l[3:split]: skips.append(s.split(" ", 1)) + for s in l[split:]: + fails.append(s.split(" ", 1)) tested += test skipped += skip failed += fail @@ -479,6 +510,8 @@ print for s in skips: print "Skipped %s: %s" % (s[0], s[1]) + for s in fails: + print "Failed %s: %s" % (s[0], s[1]) print "# Ran %d tests, %d skipped, %d failed." % ( tested, skipped, failed) sys.exit(failures != 0) @@ -516,11 +549,12 @@ tests = orig skips = [] + fails = [] for test in tests: if options.retest and not os.path.exists(test + ".err"): skipped += 1 continue - ret = run_one(test, skips) + ret = run_one(test, skips, fails) if ret is None: skipped += 1 elif not ret: @@ -528,8 +562,9 @@ print "Accept this change? [n] ", answer = sys.stdin.readline().strip() if answer.lower() in "y yes".split(): - os.rename(test + ".err", test + ".out") + rename(test + ".err", test + ".out") tested += 1 + fails.pop() continue failed += 1 if options.first: @@ -540,12 +575,16 @@ fp = os.fdopen(options.child, 'w') fp.write('%d\n%d\n%d\n' % (tested, skipped, failed)) for s in skips: - fp.write("%s %s\n" % s) + fp.write("%s %s\n" % s) + for s in fails: + fp.write("%s %s\n" % s) fp.close() else: print for s in skips: print "Skipped %s: %s" % s + for s in fails: + print "Failed %s: %s" % s print "# Ran %d tests, %d skipped, %d failed." % ( tested, skipped, failed)
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tests/svn-safe-append.py Wed Apr 09 15:28:30 2008 -0700 @@ -0,0 +1,25 @@ +#!/usr/bin/env python + +__doc__ = """Same as `echo a >> b`, but ensures a changed mtime of b. +Without this svn will not detect workspace changes.""" + +import sys, os + +text = sys.argv[1] +fname = sys.argv[2] + +f = open(fname, "ab") +try: + before = os.fstat(f.fileno()).st_mtime + f.write(text) + f.write("\n") +finally: + f.close() +inc = 1 +now = os.stat(fname).st_mtime +while now == before: + t = now + inc + inc += 1 + os.utime(fname, (t, t)) + now = os.stat(fname).st_mtime +
--- a/tests/test-acl.out Wed Apr 09 15:27:57 2008 -0700 +++ b/tests/test-acl.out Wed Apr 09 15:28:30 2008 -0700 @@ -4,6 +4,7 @@ adding manifests adding file changes added 1 changesets with 3 changes to 3 files +updating working directory 3 files updated, 0 files merged, 0 files removed, 0 files unresolved Extension disabled for lack of a hook @@ -28,6 +29,7 @@ adding foo/file.txt revisions adding quux/file.py revisions added 3 changesets with 3 changes to 3 files +updating the branch cache rolling back last transaction 0:6675d58eff77 @@ -59,6 +61,7 @@ acl: acl.allow not enabled acl: acl.deny not enabled acl: changes have source "push" - skipping +updating the branch cache rolling back last transaction 0:6675d58eff77 @@ -94,6 +97,7 @@ acl: allowing changeset ef1ea85a6374 acl: allowing changeset f9cafe1212c8 acl: allowing changeset 911600dab2ae +updating the branch cache rolling back last transaction 0:6675d58eff77 @@ -383,6 +387,7 @@ acl: allowing changeset ef1ea85a6374 acl: allowing changeset f9cafe1212c8 acl: allowing changeset 911600dab2ae +updating the branch cache rolling back last transaction 0:6675d58eff77 @@ -578,6 +583,7 @@ acl: allowing changeset ef1ea85a6374 acl: allowing changeset f9cafe1212c8 acl: allowing changeset 911600dab2ae +updating the branch cache rolling back last transaction 0:6675d58eff77
--- a/tests/test-add Wed Apr 09 15:27:57 2008 -0700 +++ b/tests/test-add Wed Apr 09 15:28:30 2008 -0700 @@ -11,7 +11,7 @@ echo b > b hg add -n b hg st -hg add b +hg add b || echo "failed to add b" hg st echo % should fail hg add b @@ -40,3 +40,9 @@ echo a > a hg add a hg st + +hg add c && echo "unexpected addition of missing file" +echo c > c +hg add d c && echo "unexpected addition of missing file" +hg st +
--- a/tests/test-add.out Wed Apr 09 15:27:57 2008 -0700 +++ b/tests/test-add.out Wed Apr 09 15:28:30 2008 -0700 @@ -13,17 +13,27 @@ % should fail a already tracked! 1 files updated, 0 files merged, 0 files removed, 0 files unresolved +created new head +merging a warning: conflicts during merge. -merging a merging a failed! 0 files updated, 0 files merged, 0 files removed, 1 files unresolved There are unresolved merges, you can redo the full merge using: hg update -C 2 hg merge 1 M a +? a.orig % should fail a already tracked! M a +? a.orig % issue683 R a +? a.orig M a +? a.orig +c does not exist! +d does not exist! +M a +A c +? a.orig
--- a/tests/test-annotate.out Wed Apr 09 15:27:57 2008 -0700 +++ b/tests/test-annotate.out Wed Apr 09 15:28:30 2008 -0700 @@ -46,6 +46,7 @@ 3 b:5: b5 3 b:6: b6 1 files updated, 0 files merged, 0 files removed, 0 files unresolved +created new head merging b 0 files updated, 1 files merged, 0 files removed, 0 files unresolved (branch merge, don't forget to commit) @@ -64,6 +65,7 @@ 4 b:5: c 3 b:5: b5 0 files updated, 0 files merged, 1 files removed, 0 files unresolved +created new head merging b 0 files updated, 1 files merged, 0 files removed, 0 files unresolved (branch merge, don't forget to commit)
--- a/tests/test-archive Wed Apr 09 15:27:57 2008 -0700 +++ b/tests/test-archive Wed Apr 09 15:28:30 2008 -0700 @@ -13,7 +13,7 @@ echo "[web]" >> .hg/hgrc echo "name = test-archive" >> .hg/hgrc echo "allow_archive = gz bz2, zip" >> .hg/hgrc -hg serve -p $HGPORT -d --pid-file=hg.pid +hg serve -p $HGPORT -d --pid-file=hg.pid -E errors.log cat hg.pid >> $DAEMON_PIDS TIP=`hg id -v | cut -f1 -d' '` @@ -40,10 +40,11 @@ gzip -dc test-$QTIP.tar.gz | tar tf - | sed "s/$QTIP/TIP/" cat > md5comp.py <<EOF -import md5, sys +from mercurial.util import md5 +import sys f1, f2 = sys.argv[1:3] -h1 = md5.md5(file(f1, 'rb').read()).hexdigest() -h2 = md5.md5(file(f2, 'rb').read()).hexdigest() +h1 = md5(file(f1, 'rb').read()).hexdigest() +h2 = md5(file(f2, 'rb').read()).hexdigest() print h1 == h2 or "md5 differ: " + repr((h1, h2)) EOF @@ -59,7 +60,7 @@ hg archive -t zip -p /illegal test.zip hg archive -t zip -p very/../bad test.zip -hg archive -t zip -r 2 test.zip +hg archive --config ui.archivemeta=false -t zip -r 2 test.zip unzip -t test.zip hg archive -t tar - | tar tf - | sed "s/$QTIP/TIP/" @@ -69,8 +70,14 @@ echo 'rev-0.tar created' fi +hg archive -t bogus test.bogus + +echo % server errors +cat errors.log + echo '% empty repo' hg init ../empty cd ../empty hg archive ../test-empty + exit 0
--- a/tests/test-archive-symlinks Wed Apr 09 15:27:57 2008 -0700 +++ b/tests/test-archive-symlinks Wed Apr 09 15:28:30 2008 -0700 @@ -4,14 +4,6 @@ origdir=`pwd` -cat >> readlink.py <<EOF -import os -import sys - -for f in sys.argv[1:]: - print f, '->', os.readlink(f) -EOF - hg init repo cd repo ln -s nothing dangling @@ -25,16 +17,16 @@ echo '% files' cd "$origdir" cd archive -python ../readlink.py dangling +$TESTDIR/readlink.py dangling echo '% tar' cd "$origdir" tar xf archive.tar cd tar -python ../readlink.py dangling +$TESTDIR/readlink.py dangling echo '% zip' cd "$origdir" unzip archive.zip > /dev/null cd zip -python ../readlink.py dangling +$TESTDIR/readlink.py dangling
--- a/tests/test-archive.out Wed Apr 09 15:27:57 2008 -0700 +++ b/tests/test-archive.out Wed Apr 09 15:28:30 2008 -0700 @@ -29,7 +29,6 @@ True abort: archive prefix contains illegal components Archive: test.zip - testing: test/.hg_archival.txt OK testing: test/bar OK testing: test/baz/bletch OK testing: test/foo OK @@ -39,5 +38,7 @@ test-TIP/baz/bletch test-TIP/foo rev-0.tar created +abort: unknown archive type 'bogus' +% server errors % empty repo abort: repository has no revisions
--- a/tests/test-backout Wed Apr 09 15:27:57 2008 -0700 +++ b/tests/test-backout Wed Apr 09 15:28:30 2008 -0700 @@ -59,9 +59,10 @@ cd merge echo line 1 > a +echo line 2 >> a hg commit -d '0 0' -A -m a - -echo line 2 >> a +# remove line 1 +echo line 2 > a hg commit -d '1 0' -m b echo line 3 >> a @@ -69,6 +70,7 @@ hg backout --merge -d '3 0' 1 hg commit -d '4 0' -m d +# check line 1 is back cat a echo '# backout should not back out subsequent changesets' @@ -82,6 +84,8 @@ hg commit -d '2 0' -A -m c hg backout -d '3 0' 1 hg locate b +hg update -C tip +hg locate b cd .. hg init m @@ -119,4 +123,37 @@ hg backout -d '6 0' --parent 3 4 +cd .. + +echo '# named branches' + +hg init named_branches +cd named_branches + +echo default > default +hg ci -d '0 0' -Am default +hg branch branch1 +echo branch1 > file1 +hg ci -d '1 0' -Am file1 +hg branch branch2 +echo branch2 > file2 +hg ci -d '2 0' -Am file2 +hg backout -d '3 0' -r 1 -m 'backout on branch1' +# XXX maybe backout shouldn't suggest a merge here as it is a different branch? + +echo '% on branch2 with branch1 not merged, so file1 should still exist:' +hg id +hg st -A + +echo '% on branch2 with branch1 merged, so file1 should be gone:' +hg merge +hg ci -d '4 0' -m 'merge backout of branch1' +hg id +hg st -A + +echo '% on branch1, so no file1 and file2:' +hg co -C branch1 +hg id +hg st -A + exit 0
--- a/tests/test-backout.out Wed Apr 09 15:27:57 2008 -0700 +++ b/tests/test-backout.out Wed Apr 09 15:28:30 2008 -0700 @@ -21,28 +21,36 @@ 0 files updated, 0 files merged, 1 files removed, 0 files unresolved abort: cannot back out change on a different branch adding c +created new head abort: cannot back out change on a different branch # backout with merge adding a reverting a -changeset 3:3eb045e364a4 backs out changeset 1:314f55b1bf23 -merging with changeset 2:b66ea5b77abb +created new head +changeset 3:26b8ccb9ad91 backs out changeset 1:5a50a024c182 +merging with changeset 3:26b8ccb9ad91 merging a 0 files updated, 1 files merged, 0 files removed, 0 files unresolved (branch merge, don't forget to commit) line 1 +line 2 +line 3 # backout should not back out subsequent changesets adding a adding b reverting a +created new head changeset 3:3202beb76721 backs out changeset 1:22bca4c721e5 the backout changeset is a new head - do not forget to merge (use "backout --merge" if you want to auto-merge) +b +1 files updated, 0 files merged, 1 files removed, 0 files unresolved adding a adding b adding c 0 files updated, 0 files merged, 1 files removed, 0 files unresolved adding d +created new head 1 files updated, 0 files merged, 0 files removed, 0 files unresolved (branch merge, don't forget to commit) # backout of merge should fail @@ -58,3 +66,30 @@ 1 files updated, 0 files merged, 0 files removed, 0 files unresolved removing c changeset 5:033590168430 backs out changeset 4:b2f3bb92043e +# named branches +adding default +marked working directory as branch branch1 +adding file1 +marked working directory as branch branch2 +adding file2 +removing file1 +created new head +changeset 3:d4e8f6db59fb backs out changeset 1:bf1602f437f3 +the backout changeset is a new head - do not forget to merge +(use "backout --merge" if you want to auto-merge) +% on branch2 with branch1 not merged, so file1 should still exist: +45bbcd363bf0 (branch2) +C default +C file1 +C file2 +% on branch2 with branch1 merged, so file1 should be gone: +0 files updated, 0 files merged, 1 files removed, 0 files unresolved +(branch merge, don't forget to commit) +22149cdde76d (branch2) tip +C default +C file2 +% on branch1, so no file1 and file2: +1 files updated, 0 files merged, 1 files removed, 0 files unresolved +bf1602f437f3 (branch1) +C default +C file1
--- a/tests/test-bad-extension Wed Apr 09 15:27:57 2008 -0700 +++ b/tests/test-bad-extension Wed Apr 09 15:28:30 2008 -0700 @@ -7,5 +7,7 @@ echo "gpg =" >> $HGRCPATH echo "hgext.gpg =" >> $HGRCPATH echo "badext = $abspath" >> $HGRCPATH +echo "badext2 =" >> $HGRCPATH -hg -q help help +hg -q help help 2>&1 | python -c \ + "import sys; sys.stdout.write(sys.stdin.read().replace('$abspath', '.../badext.py'))"
--- a/tests/test-bad-extension.out Wed Apr 09 15:27:57 2008 -0700 +++ b/tests/test-bad-extension.out Wed Apr 09 15:28:30 2008 -0700 @@ -1,4 +1,5 @@ -*** failed to import extension badext: bit bucket overflow +*** failed to import extension badext from .../badext.py: bit bucket overflow +*** failed to import extension badext2: No module named badext2 hg help [COMMAND] show help for a command, extension, or list of commands
--- a/tests/test-bheads Wed Apr 09 15:27:57 2008 -0700 +++ b/tests/test-bheads Wed Apr 09 15:28:30 2008 -0700 @@ -1,108 +1,113 @@ #!/bin/sh +heads() +{ + hg heads --template '#rev#: #desc|firstline|strip#\n' "$@" +} + hg init a cd a echo 'root' >root hg add root -hg commit -d '0 0' -u test -m "Adding root node" -hg heads +hg commit -m "Adding root node" +heads echo '-------' -hg heads . +heads . echo '=======' echo 'a' >a hg add a hg branch a -hg commit -d '1 0' -u test -m "Adding a branch" -hg heads +hg commit -m "Adding a branch" +heads echo '-------' -hg heads . +heads . echo '=======' hg update -C 0 echo 'b' >b hg add b hg branch b -hg commit -d '2 0' -u test -m "Adding b branch" -hg heads +hg commit -m "Adding b branch" +heads echo '-------' -hg heads . +heads . echo '=======' echo 'bh1' >bh1 hg add bh1 -hg commit -d '3 0' -u test -m "Adding b branch head 1" -hg heads +hg commit -m "Adding b branch head 1" +heads echo '-------' -hg heads . +heads . echo '=======' hg update -C 2 echo 'bh2' >bh2 hg add bh2 -hg commit -d '4 0' -u test -m "Adding b branch head 2" -hg heads +hg commit -m "Adding b branch head 2" +heads echo '-------' -hg heads . +heads . echo '=======' hg update -C 2 echo 'bh3' >bh3 hg add bh3 -hg commit -d '5 0' -u test -m "Adding b branch head 3" -hg heads +hg commit -m "Adding b branch head 3" +heads echo '-------' -hg heads . +heads . echo '=======' hg merge 4 -hg commit -d '6 0' -u test -m "Merging b branch head 2 and b branch head 3" -hg heads +hg commit -m "Merging b branch head 2 and b branch head 3" +heads echo '-------' -hg heads . +heads . echo '=======' echo 'c' >c hg add c hg branch c -hg commit -d '7 0' -u test -m "Adding c branch" -hg heads +hg commit -m "Adding c branch" +heads echo '-------' -hg heads . +heads . echo '=======' -hg heads -r 3 . +heads -r 3 . echo $? echo '-------' -hg heads -r 2 . +heads -r 2 . echo $? echo '-------' hg update -C 4 echo $? echo '-------' -hg heads -r 3 . +heads -r 3 . echo $? echo '-------' -hg heads -r 2 . +heads -r 2 . echo $? echo '-------' -hg heads -r 7 . +heads -r 7 . echo $? echo '=======' for i in 0 1 2 3 4 5 6 7; do hg update -C "$i" - hg heads + heads echo '-------' - hg heads . + heads . echo '-------' done echo '=======' for i in a b c z; do - hg heads "$i" + heads "$i" echo '-------' done echo '=======' -hg heads 0 1 2 3 4 5 6 7 +heads 0 1 2 3 4 5 6 7
--- a/tests/test-bheads.out Wed Apr 09 15:27:57 2008 -0700 +++ b/tests/test-bheads.out Wed Apr 09 15:28:30 2008 -0700 @@ -1,608 +1,154 @@ -changeset: 0:19709c5a4e75 -tag: tip -user: test -date: Thu Jan 01 00:00:00 1970 +0000 -summary: Adding root node - +0: Adding root node ------- -changeset: 0:19709c5a4e75 -tag: tip -user: test -date: Thu Jan 01 00:00:00 1970 +0000 -summary: Adding root node - +0: Adding root node ======= marked working directory as branch a -changeset: 1:dd6b440dd85a -branch: a -tag: tip -user: test -date: Thu Jan 01 00:00:01 1970 +0000 -summary: Adding a branch - +1: Adding a branch ------- -changeset: 1:dd6b440dd85a -branch: a -tag: tip -user: test -date: Thu Jan 01 00:00:01 1970 +0000 -summary: Adding a branch - +1: Adding a branch ======= 0 files updated, 0 files merged, 1 files removed, 0 files unresolved marked working directory as branch b -changeset: 2:ac22033332d1 -branch: b -tag: tip -parent: 0:19709c5a4e75 -user: test -date: Thu Jan 01 00:00:02 1970 +0000 -summary: Adding b branch - -changeset: 1:dd6b440dd85a -branch: a -user: test -date: Thu Jan 01 00:00:01 1970 +0000 -summary: Adding a branch - +created new head +2: Adding b branch +1: Adding a branch ------- -changeset: 2:ac22033332d1 -branch: b -tag: tip -parent: 0:19709c5a4e75 -user: test -date: Thu Jan 01 00:00:02 1970 +0000 -summary: Adding b branch - +2: Adding b branch ======= -changeset: 3:aee39cd168d0 -branch: b -tag: tip -user: test -date: Thu Jan 01 00:00:03 1970 +0000 -summary: Adding b branch head 1 - -changeset: 1:dd6b440dd85a -branch: a -user: test -date: Thu Jan 01 00:00:01 1970 +0000 -summary: Adding a branch - +3: Adding b branch head 1 +1: Adding a branch ------- -changeset: 3:aee39cd168d0 -branch: b -tag: tip -user: test -date: Thu Jan 01 00:00:03 1970 +0000 -summary: Adding b branch head 1 - +3: Adding b branch head 1 ======= 0 files updated, 0 files merged, 1 files removed, 0 files unresolved -changeset: 4:22df7444f7c1 -branch: b -tag: tip -parent: 2:ac22033332d1 -user: test -date: Thu Jan 01 00:00:04 1970 +0000 -summary: Adding b branch head 2 - -changeset: 3:aee39cd168d0 -branch: b -user: test -date: Thu Jan 01 00:00:03 1970 +0000 -summary: Adding b branch head 1 - -changeset: 1:dd6b440dd85a -branch: a -user: test -date: Thu Jan 01 00:00:01 1970 +0000 -summary: Adding a branch - +created new head +4: Adding b branch head 2 +3: Adding b branch head 1 +1: Adding a branch ------- -changeset: 4:22df7444f7c1 -branch: b -tag: tip -parent: 2:ac22033332d1 -user: test -date: Thu Jan 01 00:00:04 1970 +0000 -summary: Adding b branch head 2 - -changeset: 3:aee39cd168d0 -branch: b -user: test -date: Thu Jan 01 00:00:03 1970 +0000 -summary: Adding b branch head 1 - +4: Adding b branch head 2 +3: Adding b branch head 1 ======= 0 files updated, 0 files merged, 1 files removed, 0 files unresolved -changeset: 5:0d57af4f9583 -branch: b -tag: tip -parent: 2:ac22033332d1 -user: test -date: Thu Jan 01 00:00:05 1970 +0000 -summary: Adding b branch head 3 - -changeset: 4:22df7444f7c1 -branch: b -parent: 2:ac22033332d1 -user: test -date: Thu Jan 01 00:00:04 1970 +0000 -summary: Adding b branch head 2 - -changeset: 3:aee39cd168d0 -branch: b -user: test -date: Thu Jan 01 00:00:03 1970 +0000 -summary: Adding b branch head 1 - -changeset: 1:dd6b440dd85a -branch: a -user: test -date: Thu Jan 01 00:00:01 1970 +0000 -summary: Adding a branch - +created new head +5: Adding b branch head 3 +4: Adding b branch head 2 +3: Adding b branch head 1 +1: Adding a branch ------- -changeset: 5:0d57af4f9583 -branch: b -tag: tip -parent: 2:ac22033332d1 -user: test -date: Thu Jan 01 00:00:05 1970 +0000 -summary: Adding b branch head 3 - -changeset: 4:22df7444f7c1 -branch: b -parent: 2:ac22033332d1 -user: test -date: Thu Jan 01 00:00:04 1970 +0000 -summary: Adding b branch head 2 - -changeset: 3:aee39cd168d0 -branch: b -user: test -date: Thu Jan 01 00:00:03 1970 +0000 -summary: Adding b branch head 1 - +5: Adding b branch head 3 +4: Adding b branch head 2 +3: Adding b branch head 1 ======= 1 files updated, 0 files merged, 0 files removed, 0 files unresolved (branch merge, don't forget to commit) -changeset: 6:00432327d822 -branch: b -tag: tip -parent: 5:0d57af4f9583 -parent: 4:22df7444f7c1 -user: test -date: Thu Jan 01 00:00:06 1970 +0000 -summary: Merging b branch head 2 and b branch head 3 - -changeset: 3:aee39cd168d0 -branch: b -user: test -date: Thu Jan 01 00:00:03 1970 +0000 -summary: Adding b branch head 1 - -changeset: 1:dd6b440dd85a -branch: a -user: test -date: Thu Jan 01 00:00:01 1970 +0000 -summary: Adding a branch - +6: Merging b branch head 2 and b branch head 3 +3: Adding b branch head 1 +1: Adding a branch ------- -changeset: 6:00432327d822 -branch: b -tag: tip -parent: 5:0d57af4f9583 -parent: 4:22df7444f7c1 -user: test -date: Thu Jan 01 00:00:06 1970 +0000 -summary: Merging b branch head 2 and b branch head 3 - -changeset: 3:aee39cd168d0 -branch: b -user: test -date: Thu Jan 01 00:00:03 1970 +0000 -summary: Adding b branch head 1 - +6: Merging b branch head 2 and b branch head 3 +3: Adding b branch head 1 ======= marked working directory as branch c -changeset: 7:9fb091bb9835 -branch: c -tag: tip -user: test -date: Thu Jan 01 00:00:07 1970 +0000 -summary: Adding c branch - -changeset: 3:aee39cd168d0 -branch: b -user: test -date: Thu Jan 01 00:00:03 1970 +0000 -summary: Adding b branch head 1 - -changeset: 1:dd6b440dd85a -branch: a -user: test -date: Thu Jan 01 00:00:01 1970 +0000 -summary: Adding a branch - +7: Adding c branch +3: Adding b branch head 1 +1: Adding a branch ------- -changeset: 7:9fb091bb9835 -branch: c -tag: tip -user: test -date: Thu Jan 01 00:00:07 1970 +0000 -summary: Adding c branch - +7: Adding c branch ======= no changes on branch c containing . are reachable from 3 1 ------- -changeset: 7:9fb091bb9835 -branch: c -tag: tip -user: test -date: Thu Jan 01 00:00:07 1970 +0000 -summary: Adding c branch - +7: Adding c branch 0 ------- 0 files updated, 0 files merged, 2 files removed, 0 files unresolved 0 ------- -changeset: 3:aee39cd168d0 -branch: b -user: test -date: Thu Jan 01 00:00:03 1970 +0000 -summary: Adding b branch head 1 - +3: Adding b branch head 1 0 ------- -changeset: 3:aee39cd168d0 -branch: b -user: test -date: Thu Jan 01 00:00:03 1970 +0000 -summary: Adding b branch head 1 - -changeset: 6:00432327d822 -branch: b -parent: 5:0d57af4f9583 -parent: 4:22df7444f7c1 -user: test -date: Thu Jan 01 00:00:06 1970 +0000 -summary: Merging b branch head 2 and b branch head 3 - +3: Adding b branch head 1 +6: Merging b branch head 2 and b branch head 3 0 ------- no changes on branch b containing . are reachable from 7 1 ======= 0 files updated, 0 files merged, 2 files removed, 0 files unresolved -changeset: 7:9fb091bb9835 -branch: c -tag: tip -user: test -date: Thu Jan 01 00:00:07 1970 +0000 -summary: Adding c branch - -changeset: 3:aee39cd168d0 -branch: b -user: test -date: Thu Jan 01 00:00:03 1970 +0000 -summary: Adding b branch head 1 - -changeset: 1:dd6b440dd85a -branch: a -user: test -date: Thu Jan 01 00:00:01 1970 +0000 -summary: Adding a branch - +7: Adding c branch +3: Adding b branch head 1 +1: Adding a branch ------- -changeset: 0:19709c5a4e75 -user: test -date: Thu Jan 01 00:00:00 1970 +0000 -summary: Adding root node - +0: Adding root node ------- 1 files updated, 0 files merged, 0 files removed, 0 files unresolved -changeset: 7:9fb091bb9835 -branch: c -tag: tip -user: test -date: Thu Jan 01 00:00:07 1970 +0000 -summary: Adding c branch - -changeset: 3:aee39cd168d0 -branch: b -user: test -date: Thu Jan 01 00:00:03 1970 +0000 -summary: Adding b branch head 1 - -changeset: 1:dd6b440dd85a -branch: a -user: test -date: Thu Jan 01 00:00:01 1970 +0000 -summary: Adding a branch - +7: Adding c branch +3: Adding b branch head 1 +1: Adding a branch ------- -changeset: 1:dd6b440dd85a -branch: a -user: test -date: Thu Jan 01 00:00:01 1970 +0000 -summary: Adding a branch - +1: Adding a branch ------- 1 files updated, 0 files merged, 1 files removed, 0 files unresolved -changeset: 7:9fb091bb9835 -branch: c -tag: tip -user: test -date: Thu Jan 01 00:00:07 1970 +0000 -summary: Adding c branch - -changeset: 3:aee39cd168d0 -branch: b -user: test -date: Thu Jan 01 00:00:03 1970 +0000 -summary: Adding b branch head 1 - -changeset: 1:dd6b440dd85a -branch: a -user: test -date: Thu Jan 01 00:00:01 1970 +0000 -summary: Adding a branch - +7: Adding c branch +3: Adding b branch head 1 +1: Adding a branch ------- -changeset: 6:00432327d822 -branch: b -parent: 5:0d57af4f9583 -parent: 4:22df7444f7c1 -user: test -date: Thu Jan 01 00:00:06 1970 +0000 -summary: Merging b branch head 2 and b branch head 3 - -changeset: 3:aee39cd168d0 -branch: b -user: test -date: Thu Jan 01 00:00:03 1970 +0000 -summary: Adding b branch head 1 - +6: Merging b branch head 2 and b branch head 3 +3: Adding b branch head 1 ------- 1 files updated, 0 files merged, 0 files removed, 0 files unresolved -changeset: 7:9fb091bb9835 -branch: c -tag: tip -user: test -date: Thu Jan 01 00:00:07 1970 +0000 -summary: Adding c branch - -changeset: 3:aee39cd168d0 -branch: b -user: test -date: Thu Jan 01 00:00:03 1970 +0000 -summary: Adding b branch head 1 - -changeset: 1:dd6b440dd85a -branch: a -user: test -date: Thu Jan 01 00:00:01 1970 +0000 -summary: Adding a branch - +7: Adding c branch +3: Adding b branch head 1 +1: Adding a branch ------- -changeset: 6:00432327d822 -branch: b -parent: 5:0d57af4f9583 -parent: 4:22df7444f7c1 -user: test -date: Thu Jan 01 00:00:06 1970 +0000 -summary: Merging b branch head 2 and b branch head 3 - -changeset: 3:aee39cd168d0 -branch: b -user: test -date: Thu Jan 01 00:00:03 1970 +0000 -summary: Adding b branch head 1 - +6: Merging b branch head 2 and b branch head 3 +3: Adding b branch head 1 ------- 1 files updated, 0 files merged, 1 files removed, 0 files unresolved -changeset: 7:9fb091bb9835 -branch: c -tag: tip -user: test -date: Thu Jan 01 00:00:07 1970 +0000 -summary: Adding c branch - -changeset: 3:aee39cd168d0 -branch: b -user: test -date: Thu Jan 01 00:00:03 1970 +0000 -summary: Adding b branch head 1 - -changeset: 1:dd6b440dd85a -branch: a -user: test -date: Thu Jan 01 00:00:01 1970 +0000 -summary: Adding a branch - +7: Adding c branch +3: Adding b branch head 1 +1: Adding a branch ------- -changeset: 6:00432327d822 -branch: b -parent: 5:0d57af4f9583 -parent: 4:22df7444f7c1 -user: test -date: Thu Jan 01 00:00:06 1970 +0000 -summary: Merging b branch head 2 and b branch head 3 - -changeset: 3:aee39cd168d0 -branch: b -user: test -date: Thu Jan 01 00:00:03 1970 +0000 -summary: Adding b branch head 1 - +6: Merging b branch head 2 and b branch head 3 +3: Adding b branch head 1 ------- 1 files updated, 0 files merged, 1 files removed, 0 files unresolved -changeset: 7:9fb091bb9835 -branch: c -tag: tip -user: test -date: Thu Jan 01 00:00:07 1970 +0000 -summary: Adding c branch - -changeset: 3:aee39cd168d0 -branch: b -user: test -date: Thu Jan 01 00:00:03 1970 +0000 -summary: Adding b branch head 1 - -changeset: 1:dd6b440dd85a -branch: a -user: test -date: Thu Jan 01 00:00:01 1970 +0000 -summary: Adding a branch - +7: Adding c branch +3: Adding b branch head 1 +1: Adding a branch ------- -changeset: 6:00432327d822 -branch: b -parent: 5:0d57af4f9583 -parent: 4:22df7444f7c1 -user: test -date: Thu Jan 01 00:00:06 1970 +0000 -summary: Merging b branch head 2 and b branch head 3 - -changeset: 3:aee39cd168d0 -branch: b -user: test -date: Thu Jan 01 00:00:03 1970 +0000 -summary: Adding b branch head 1 - +6: Merging b branch head 2 and b branch head 3 +3: Adding b branch head 1 ------- 1 files updated, 0 files merged, 0 files removed, 0 files unresolved -changeset: 7:9fb091bb9835 -branch: c -tag: tip -user: test -date: Thu Jan 01 00:00:07 1970 +0000 -summary: Adding c branch - -changeset: 3:aee39cd168d0 -branch: b -user: test -date: Thu Jan 01 00:00:03 1970 +0000 -summary: Adding b branch head 1 - -changeset: 1:dd6b440dd85a -branch: a -user: test -date: Thu Jan 01 00:00:01 1970 +0000 -summary: Adding a branch - +7: Adding c branch +3: Adding b branch head 1 +1: Adding a branch ------- -changeset: 6:00432327d822 -branch: b -parent: 5:0d57af4f9583 -parent: 4:22df7444f7c1 -user: test -date: Thu Jan 01 00:00:06 1970 +0000 -summary: Merging b branch head 2 and b branch head 3 - -changeset: 3:aee39cd168d0 -branch: b -user: test -date: Thu Jan 01 00:00:03 1970 +0000 -summary: Adding b branch head 1 - +6: Merging b branch head 2 and b branch head 3 +3: Adding b branch head 1 ------- 1 files updated, 0 files merged, 0 files removed, 0 files unresolved -changeset: 7:9fb091bb9835 -branch: c -tag: tip -user: test -date: Thu Jan 01 00:00:07 1970 +0000 -summary: Adding c branch - -changeset: 3:aee39cd168d0 -branch: b -user: test -date: Thu Jan 01 00:00:03 1970 +0000 -summary: Adding b branch head 1 - -changeset: 1:dd6b440dd85a -branch: a -user: test -date: Thu Jan 01 00:00:01 1970 +0000 -summary: Adding a branch - +7: Adding c branch +3: Adding b branch head 1 +1: Adding a branch ------- -changeset: 7:9fb091bb9835 -branch: c -tag: tip -user: test -date: Thu Jan 01 00:00:07 1970 +0000 -summary: Adding c branch - +7: Adding c branch ------- ======= -changeset: 1:dd6b440dd85a -branch: a -user: test -date: Thu Jan 01 00:00:01 1970 +0000 -summary: Adding a branch - +1: Adding a branch ------- -changeset: 6:00432327d822 -branch: b -parent: 5:0d57af4f9583 -parent: 4:22df7444f7c1 -user: test -date: Thu Jan 01 00:00:06 1970 +0000 -summary: Merging b branch head 2 and b branch head 3 - -changeset: 3:aee39cd168d0 -branch: b -user: test -date: Thu Jan 01 00:00:03 1970 +0000 -summary: Adding b branch head 1 - +6: Merging b branch head 2 and b branch head 3 +3: Adding b branch head 1 ------- -changeset: 7:9fb091bb9835 -branch: c -tag: tip -user: test -date: Thu Jan 01 00:00:07 1970 +0000 -summary: Adding c branch - +7: Adding c branch ------- abort: unknown revision 'z'! ------- ======= -changeset: 0:19709c5a4e75 -user: test -date: Thu Jan 01 00:00:00 1970 +0000 -summary: Adding root node - -changeset: 1:dd6b440dd85a -branch: a -user: test -date: Thu Jan 01 00:00:01 1970 +0000 -summary: Adding a branch - -changeset: 6:00432327d822 -branch: b -parent: 5:0d57af4f9583 -parent: 4:22df7444f7c1 -user: test -date: Thu Jan 01 00:00:06 1970 +0000 -summary: Merging b branch head 2 and b branch head 3 - -changeset: 3:aee39cd168d0 -branch: b -user: test -date: Thu Jan 01 00:00:03 1970 +0000 -summary: Adding b branch head 1 - -changeset: 7:9fb091bb9835 -branch: c -tag: tip -user: test -date: Thu Jan 01 00:00:07 1970 +0000 -summary: Adding c branch - +0: Adding root node +1: Adding a branch +6: Merging b branch head 2 and b branch head 3 +3: Adding b branch head 1 +7: Adding c branch
--- a/tests/test-bisect Wed Apr 09 15:27:57 2008 -0700 +++ b/tests/test-bisect Wed Apr 09 15:28:30 2008 -0700 @@ -2,9 +2,6 @@ set -e -echo "[extensions]" >> $HGRCPATH -echo "hbisect=" >> $HGRCPATH - echo % init hg init @@ -26,11 +23,34 @@ hg up -C echo % bisect test -hg bisect init -hg bisect bad -hg bisect good 1 -hg bisect good -hg bisect good -hg bisect good -hg bisect bad -hg bisect good +hg bisect -r +hg bisect -b +hg bisect -g 1 +hg bisect -g +echo skip +hg bisect -s +hg bisect -g +hg bisect -g +hg bisect -b +hg bisect -g + +echo % bisect reverse test +hg bisect -r +hg bisect -b null +hg bisect -g tip +hg bisect -g +echo skip +hg bisect -s +hg bisect -g +hg bisect -g +hg bisect -b +hg bisect -g + +hg bisect -r +hg bisect -g tip +hg bisect -b tip || echo error + +hg bisect -r +hg bisect -g null +hg bisect -bU tip +hg id \ No newline at end of file
--- a/tests/test-bisect.out Wed Apr 09 15:27:57 2008 -0700 +++ b/tests/test-bisect.out Wed Apr 09 15:28:30 2008 -0700 @@ -202,7 +202,10 @@ 1 files updated, 0 files merged, 0 files removed, 0 files unresolved Testing changeset 23:5ec79163bff4 (15 changesets remaining, ~3 tests) 1 files updated, 0 files merged, 0 files removed, 0 files unresolved -Testing changeset 27:288867a866e9 (8 changesets remaining, ~3 tests) +skip +Testing changeset 24:10e0acd3809e (15 changesets remaining, ~3 tests) +1 files updated, 0 files merged, 0 files removed, 0 files unresolved +Testing changeset 27:288867a866e9 (7 changesets remaining, ~2 tests) 1 files updated, 0 files merged, 0 files removed, 0 files unresolved Testing changeset 29:b5bd63375ab9 (4 changesets remaining, ~2 tests) 1 files updated, 0 files merged, 0 files removed, 0 files unresolved @@ -214,3 +217,27 @@ date: Thu Jan 01 00:00:29 1970 +0000 summary: msg 29 +% bisect reverse test +Testing changeset 15:e7fa0811edb0 (32 changesets remaining, ~5 tests) +1 files updated, 0 files merged, 0 files removed, 0 files unresolved +Testing changeset 7:03750880c6b5 (16 changesets remaining, ~4 tests) +1 files updated, 0 files merged, 0 files removed, 0 files unresolved +skip +Testing changeset 6:a3d5c6fdf0d3 (16 changesets remaining, ~4 tests) +1 files updated, 0 files merged, 0 files removed, 0 files unresolved +Testing changeset 2:db07c04beaca (7 changesets remaining, ~2 tests) +1 files updated, 0 files merged, 0 files removed, 0 files unresolved +Testing changeset 0:b99c7b9c8e11 (3 changesets remaining, ~1 tests) +1 files updated, 0 files merged, 0 files removed, 0 files unresolved +Testing changeset 1:5cd978ea5149 (2 changesets remaining, ~1 tests) +1 files updated, 0 files merged, 0 files removed, 0 files unresolved +The first good revision is: +changeset: 1:5cd978ea5149 +user: test +date: Thu Jan 01 00:00:01 1970 +0000 +summary: msg 1 + +abort: Inconsistent state, 31:58c80a7c8a40 is good and bad +error +Testing changeset 15:e7fa0811edb0 (32 changesets remaining, ~5 tests) +5cd978ea5149
--- a/tests/test-branches Wed Apr 09 15:27:57 2008 -0700 +++ b/tests/test-branches Wed Apr 09 15:28:30 2008 -0700 @@ -34,3 +34,9 @@ hg branches echo '-------' hg branches -a + +echo "--- Branch a" +hg log -b a + +echo "---- Branch b" +hg log -b b
--- a/tests/test-branches.out Wed Apr 09 15:27:57 2008 -0700 +++ b/tests/test-branches.out Wed Apr 09 15:28:30 2008 -0700 @@ -1,7 +1,9 @@ marked working directory as branch a 0 files updated, 0 files merged, 1 files removed, 0 files unresolved marked working directory as branch b +created new head 0 files updated, 0 files merged, 1 files removed, 0 files unresolved +created new head marked working directory as branch c c 5:5ca481e59b8c a 1:dd6b440dd85a @@ -10,3 +12,31 @@ ------- c 5:5ca481e59b8c a 1:dd6b440dd85a +--- Branch a +changeset: 1:dd6b440dd85a +branch: a +user: test +date: Thu Jan 01 00:00:01 1970 +0000 +summary: Adding a branch + +---- Branch b +changeset: 4:22df7444f7c1 +branch: b +parent: 2:ac22033332d1 +user: test +date: Thu Jan 01 00:00:04 1970 +0000 +summary: Adding b branch head 2 + +changeset: 3:aee39cd168d0 +branch: b +user: test +date: Thu Jan 01 00:00:03 1970 +0000 +summary: Adding b branch head 1 + +changeset: 2:ac22033332d1 +branch: b +parent: 0:19709c5a4e75 +user: test +date: Thu Jan 01 00:00:02 1970 +0000 +summary: Adding b branch +
--- a/tests/test-bundle Wed Apr 09 15:27:57 2008 -0700 +++ b/tests/test-bundle Wed Apr 09 15:28:30 2008 -0700 @@ -32,6 +32,8 @@ cd .. hg init empty +echo "====== Bundle --all" +hg -R test bundle --all all.hg echo "====== Bundle test to full.hg" hg -R test bundle full.hg empty echo "====== Unbundle full.hg in test" @@ -70,7 +72,14 @@ #doesn't work (yet ?) #hg -R bundle://../full.hg verify hg pull bundle://../full.hg +echo "====== Rollback empty" +hg rollback cd .. +echo "====== Log -R bundle:empty+full.hg" +hg -R bundle:empty+full.hg log --template="{rev} " +echo "" +echo "====== Pull full.hg into empty again (using -R; with hook)" +hg -R empty pull full.hg echo "====== Create partial clones" rm -r empty @@ -88,6 +97,11 @@ hg -R bundle://../does-not-exist.hg outgoing ../partial2 cd .. +echo "====== Direct clone from bundle (all-history)" +hg clone full.hg full-clone +hg -R full-clone heads +rm -r full-clone + # test for http://www.selenic.com/mercurial/bts/issue216 echo "====== Unbundle incremental bundles into fresh empty in one go" rm -r empty
--- a/tests/test-bundle-r.out Wed Apr 09 15:27:57 2008 -0700 +++ b/tests/test-bundle-r.out Wed Apr 09 15:28:30 2008 -0700 @@ -1,4 +1,5 @@ 1 files updated, 0 files merged, 0 files removed, 0 files unresolved +created new head 1 files updated, 0 files merged, 2 files removed, 0 files unresolved rev offset length base linkrev nodeid p1 p2 0 0 3 0 0 362fef284ce2 000000000000 000000000000 @@ -25,6 +26,7 @@ checking files 4 files, 9 changesets, 7 total revisions searching for changes +1 changesets found adding changesets adding manifests adding file changes @@ -37,6 +39,7 @@ 1 files, 1 changesets, 1 total revisions 0:5649c9d34dd8 searching for changes +2 changesets found adding changesets adding manifests adding file changes @@ -49,6 +52,7 @@ 1 files, 2 changesets, 2 total revisions 1:10b2180f755b searching for changes +3 changesets found adding changesets adding manifests adding file changes @@ -61,6 +65,7 @@ 1 files, 3 changesets, 3 total revisions 2:d62976ca1e50 searching for changes +4 changesets found adding changesets adding manifests adding file changes @@ -73,6 +78,7 @@ 1 files, 4 changesets, 4 total revisions 3:ac69c658229d searching for changes +2 changesets found adding changesets adding manifests adding file changes @@ -85,6 +91,7 @@ 1 files, 2 changesets, 2 total revisions 1:5f4f3ceb285e searching for changes +3 changesets found adding changesets adding manifests adding file changes @@ -97,6 +104,7 @@ 1 files, 3 changesets, 3 total revisions 2:024e4e7df376 searching for changes +4 changesets found adding changesets adding manifests adding file changes @@ -109,6 +117,7 @@ 2 files, 4 changesets, 5 total revisions 3:1e3f6b843bd6 searching for changes +5 changesets found adding changesets adding manifests adding file changes @@ -121,6 +130,7 @@ 3 files, 5 changesets, 6 total revisions 4:80fe151401c2 searching for changes +5 changesets found adding changesets adding manifests adding file changes @@ -148,13 +158,19 @@ % should fail abort: --base is incompatible with specifiying a destination abort: repository default-push not found! +2 changesets found +4 changesets found +6 changesets found +1 changesets found +1 changesets found +updating working directory 1 files updated, 0 files merged, 0 files removed, 0 files unresolved % 2 2:d62976ca1e50 adding changesets transaction abort! rollback completed -abort: unknown parent ac69c658229d! +abort: 00changelog.i@ac69c658229d: unknown parent! % 2 2:d62976ca1e50 adding changesets @@ -202,6 +218,8 @@ adifferentfile 2 files updated, 0 files merged, 0 files removed, 0 files unresolved (branch merge, don't forget to commit) +7 changesets found +updating working directory 1 files updated, 0 files merged, 0 files removed, 0 files unresolved adding changesets adding manifests
--- a/tests/test-bundle.out Wed Apr 09 15:27:57 2008 -0700 +++ b/tests/test-bundle.out Wed Apr 09 15:28:30 2008 -0700 @@ -1,13 +1,17 @@ ====== Setting up test 1 files updated, 0 files merged, 0 files removed, 0 files unresolved +created new head 1 files updated, 0 files merged, 2 files removed, 0 files unresolved checking changesets checking manifests crosschecking files in changesets and manifests checking files 4 files, 9 changesets, 7 total revisions +====== Bundle --all +9 changesets found ====== Bundle test to full.hg searching for changes +9 changesets found ====== Unbundle full.hg in test adding changesets adding manifests @@ -123,13 +127,28 @@ adding file changes added 9 changesets with 7 changes to 4 files (+1 heads) (run 'hg heads' to see heads, 'hg merge' to merge) +====== Rollback empty +rolling back last transaction +====== Log -R bundle:empty+full.hg +8 7 6 5 4 3 2 1 0 +====== Pull full.hg into empty again (using -R; with hook) +changegroup hook: HG_NODE=5649c9d34dd87d0ecb5fd39672128376e83b22e1 HG_SOURCE=pull HG_URL=bundle:empty+full.hg +pulling from full.hg +requesting all changes +adding changesets +adding manifests +adding file changes +added 9 changesets with 7 changes to 4 files (+1 heads) +(run 'hg heads' to see heads, 'hg merge' to merge) ====== Create partial clones requesting all changes adding changesets adding manifests adding file changes added 4 changesets with 4 changes to 1 files +updating working directory 1 files updated, 0 files merged, 0 files removed, 0 files unresolved +updating working directory 1 files updated, 0 files merged, 0 files removed, 0 files unresolved ====== Log -R full.hg in partial changeset: 8:836ac62537ab @@ -244,7 +263,29 @@ ====== Outgoing -R does-not-exist.hg vs partial2 in partial abort: No such file or directory: ../does-not-exist.hg +====== Direct clone from bundle (all-history) +requesting all changes +adding changesets +adding manifests +adding file changes +added 9 changesets with 7 changes to 4 files (+1 heads) +updating working directory +1 files updated, 0 files merged, 0 files removed, 0 files unresolved +changeset: 8:836ac62537ab +tag: tip +parent: 3:ac69c658229d +user: test +date: Mon Jan 12 13:46:40 1970 +0000 +summary: 0.3m + +changeset: 7:80fe151401c2 +user: test +date: Mon Jan 12 13:46:40 1970 +0000 +summary: 1.3m + ====== Unbundle incremental bundles into fresh empty in one go +1 changesets found +1 changesets found adding changesets adding manifests adding file changes @@ -255,8 +296,10 @@ added 1 changesets with 1 changes to 1 files 1 files updated, 0 files merged, 0 files removed, 0 files unresolved ====== test for 540d1059c802 +updating working directory 1 files updated, 0 files merged, 0 files removed, 0 files unresolved searching for changes +1 changesets found comparing with ../bundle.hg searching for changes changeset: 2:ed1b79f46b9a
--- a/tests/test-cat Wed Apr 09 15:27:57 2008 -0700 +++ b/tests/test-cat Wed Apr 09 15:28:30 2008 -0700 @@ -8,7 +8,7 @@ hg ci -A -m m -d "1000000 0" hg rm a hg cat a -sleep 1 # make sure mtime is changed +hg cat --decode a # more tests in test-encode echo 1 > b hg ci -m m -d "1000000 0" echo 2 > b
--- a/tests/test-cat.out Wed Apr 09 15:27:57 2008 -0700 +++ b/tests/test-cat.out Wed Apr 09 15:28:30 2008 -0700 @@ -3,5 +3,6 @@ 0 0 0 +0 a: No such file in rev 03f6b0774996 1
--- a/tests/test-changelog-exec.out Wed Apr 09 15:27:57 2008 -0700 +++ b/tests/test-changelog-exec.out Wed Apr 09 15:28:30 2008 -0700 @@ -2,6 +2,7 @@ bar foo +created new head % manifest of p1: foo 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tests/test-churn Wed Apr 09 15:28:30 2008 -0700 @@ -0,0 +1,33 @@ +#!/bin/sh + +echo "[extensions]" >> $HGRCPATH +echo "churn=" >> $HGRCPATH + +COLUMNS=80; export COLUMNS + +echo % create test repository +hg init repo +cd repo +echo a > a +hg ci -Am adda -u user1 +echo b >> a +echo b > b +hg ci -Am addb -u user2 +echo c >> a +echo c >> b +echo c > c +hg ci -Am addc -u user3 + +echo % churn all +hg churn +echo % churn up to rev 1 +hg churn -r :1 +echo % churn with aliases +cat > ../aliases <<EOF +user1 alias1 +user3 alias3 +EOF +hg churn --aliases ../aliases +echo % churn with column specifier +COLUMNS=40 hg churn +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tests/test-churn.out Wed Apr 09 15:28:30 2008 -0700 @@ -0,0 +1,19 @@ +% create test repository +adding a +adding b +adding c +% churn all +user3 3 *************************************************************** +user2 2 ****************************************** +user1 1 ********************* +% churn up to rev 1 +user2 2 *************************************************************** +user1 1 ******************************* +% churn with aliases +alias3 3 ************************************************************** +user2 2 ***************************************** +alias1 1 ******************** +% churn with column specifier +user3 3 *********************** +user2 2 *************** +user1 1 *******
--- a/tests/test-clone Wed Apr 09 15:27:57 2008 -0700 +++ b/tests/test-clone Wed Apr 09 15:28:30 2008 -0700 @@ -32,4 +32,8 @@ hg clone file://a e grep 'file:' e/.hg/hgrc +# check that path aliases are expanded +hg clone -q -U --config 'paths.foobar=a#0' foobar f +hg -R f showconfig paths.default | sed -e 's,.*/,,' + exit 0
--- a/tests/test-clone-pull-corruption.out Wed Apr 09 15:27:57 2008 -0700 +++ b/tests/test-clone-pull-corruption.out Wed Apr 09 15:28:30 2008 -0700 @@ -1,3 +1,4 @@ +updating working directory 1 files updated, 0 files merged, 0 files removed, 0 files unresolved pulling from ../source transaction abort!
--- a/tests/test-clone-r.out Wed Apr 09 15:27:57 2008 -0700 +++ b/tests/test-clone-r.out Wed Apr 09 15:28:30 2008 -0700 @@ -1,4 +1,5 @@ 1 files updated, 0 files merged, 0 files removed, 0 files unresolved +created new head 1 files updated, 0 files merged, 2 files removed, 0 files unresolved rev offset length base linkrev nodeid p1 p2 0 0 3 0 0 362fef284ce2 000000000000 000000000000 @@ -29,6 +30,7 @@ adding manifests adding file changes added 1 changesets with 1 changes to 1 files +updating working directory 1 files updated, 0 files merged, 0 files removed, 0 files unresolved checking changesets checking manifests @@ -40,6 +42,7 @@ adding manifests adding file changes added 2 changesets with 2 changes to 1 files +updating working directory 1 files updated, 0 files merged, 0 files removed, 0 files unresolved checking changesets checking manifests @@ -51,6 +54,7 @@ adding manifests adding file changes added 3 changesets with 3 changes to 1 files +updating working directory 1 files updated, 0 files merged, 0 files removed, 0 files unresolved checking changesets checking manifests @@ -62,6 +66,7 @@ adding manifests adding file changes added 4 changesets with 4 changes to 1 files +updating working directory 1 files updated, 0 files merged, 0 files removed, 0 files unresolved checking changesets checking manifests @@ -73,6 +78,7 @@ adding manifests adding file changes added 2 changesets with 2 changes to 1 files +updating working directory 1 files updated, 0 files merged, 0 files removed, 0 files unresolved checking changesets checking manifests @@ -84,6 +90,7 @@ adding manifests adding file changes added 3 changesets with 3 changes to 1 files +updating working directory 1 files updated, 0 files merged, 0 files removed, 0 files unresolved checking changesets checking manifests @@ -95,6 +102,7 @@ adding manifests adding file changes added 4 changesets with 5 changes to 2 files +updating working directory 2 files updated, 0 files merged, 0 files removed, 0 files unresolved checking changesets checking manifests @@ -106,6 +114,7 @@ adding manifests adding file changes added 5 changesets with 6 changes to 3 files +updating working directory 2 files updated, 0 files merged, 0 files removed, 0 files unresolved checking changesets checking manifests @@ -117,6 +126,7 @@ adding manifests adding file changes added 5 changesets with 5 changes to 2 files +updating working directory 1 files updated, 0 files merged, 0 files removed, 0 files unresolved checking changesets checking manifests
--- a/tests/test-clone.out Wed Apr 09 15:27:57 2008 -0700 +++ b/tests/test-clone.out Wed Apr 09 15:28:30 2008 -0700 @@ -1,3 +1,4 @@ +updating working directory 1 files updated, 0 files merged, 0 files removed, 0 files unresolved a checking changesets @@ -12,6 +13,9 @@ checking files 1 files, 1 changesets, 1 total revisions destination directory: a +updating working directory 1 files updated, 0 files merged, 0 files removed, 0 files unresolved a +updating working directory 1 files updated, 0 files merged, 0 files removed, 0 files unresolved +a#0
--- a/tests/test-command-template Wed Apr 09 15:27:57 2008 -0700 +++ b/tests/test-command-template Wed Apr 09 15:28:30 2008 -0700 @@ -103,9 +103,11 @@ hg log --template '{date|age}\n' > /dev/null || exit 1 hg log --template '{date|date}\n' hg log --template '{date|isodate}\n' +hg log --template '{date|isodatesec}\n' hg log --template '{date|rfc822date}\n' hg log --template '{desc|firstline}\n' hg log --template '{node|short}\n' +hg log --template '<changeset author="{author|xmlescape}"/>\n' echo '# formatnode filter works' echo '# quiet'
--- a/tests/test-command-template.out Wed Apr 09 15:27:57 2008 -0700 +++ b/tests/test-command-template.out Wed Apr 09 15:28:30 2008 -0700 @@ -1,4 +1,6 @@ +created new head 0 files updated, 0 files merged, 4 files removed, 0 files unresolved +created new head # default style is like normal output # normal # verbose @@ -81,7 +83,7 @@ # error if style not readable abort: Permission denied: ./q # error if no style -abort: No such file or directory: notexist +abort: style not found: notexist # error if style missing key abort: ./t: no key named 'changeset' # error if include fails @@ -517,6 +519,14 @@ 1970-01-14 21:20 +0000 1970-01-13 17:33 +0000 1970-01-12 13:46 +0000 +1970-01-12 13:46:40 +0000 +1970-01-18 08:40:01 +0000 +1970-01-18 08:40:00 +0000 +1970-01-17 04:53:20 +0000 +1970-01-16 01:06:40 +0000 +1970-01-14 21:20:00 +0000 +1970-01-13 17:33:20 +0000 +1970-01-12 13:46:40 +0000 Mon, 12 Jan 1970 13:46:40 +0000 Sun, 18 Jan 1970 08:40:01 +0000 Sun, 18 Jan 1970 08:40:00 +0000 @@ -541,6 +551,14 @@ 97054abb4ab8 b608e9d1a3f0 1e4e1b8f71e0 +<changeset author="User Name <user@hostname>"/> +<changeset author="person"/> +<changeset author="person"/> +<changeset author="person"/> +<changeset author="person"/> +<changeset author="other@place"/> +<changeset author="A. N. Other <other@place>"/> +<changeset author="User Name <user@hostname>"/> # formatnode filter works # quiet 1e4e1b8f71e0
--- a/tests/test-commit Wed Apr 09 15:27:57 2008 -0700 +++ b/tests/test-commit Wed Apr 09 15:28:30 2008 -0700 @@ -1,15 +1,11 @@ #!/bin/sh -cleanpath() -{ - sed -e "s:/.*\(/test/.*\):...\1:" -} - echo % commit date test hg init test cd test echo foo > foo hg add foo +HGEDITOR=true hg commit -m "" hg commit -d '0 0' -m commit-1 echo foo >> foo hg commit -d '1 4444444' -m commit-3 @@ -18,11 +14,12 @@ hg commit -d ' 1 4444' -m commit-6 hg commit -d '111111111111 0' -m commit-7 -echo % partial commit test +echo % commit added file that has been deleted echo bar > bar hg add bar rm bar -hg commit -d "1000000 0" -m commit-8 2>&1 | cleanpath +hg commit -d "1000000 0" -m commit-8 +hg commit -d "1000000 0" -m commit-8-2 bar hg -q revert -a --no-backup @@ -33,22 +30,22 @@ echo > dir.file hg add -hg commit -d '0 0' -m commit-10 dir dir.file 2>&1 | cleanpath +hg commit -d '0 0' -m commit-10 dir dir.file echo >> dir/file mkdir bleh mkdir dir2 cd bleh -hg commit -d '0 0' -m commit-11 . 2>&1 | cleanpath -hg commit -d '0 0' -m commit-12 ../dir ../dir2 2>&1 | cleanpath +hg commit -d '0 0' -m commit-11 . +hg commit -d '0 0' -m commit-12 ../dir ../dir2 hg -v commit -d '0 0' -m commit-13 ../dir cd .. -hg commit -d '0 0' -m commit-14 does-not-exist 2>&1 | cleanpath +hg commit -d '0 0' -m commit-14 does-not-exist ln -s foo baz -hg commit -d '0 0' -m commit-15 baz 2>&1 | cleanpath +hg commit -d '0 0' -m commit-15 baz touch quux -hg commit -d '0 0' -m commit-16 quux 2>&1 | cleanpath +hg commit -d '0 0' -m commit-16 quux echo >> dir/file hg -v commit -d '0 0' -m commit-17 dir/file cd .. @@ -88,4 +85,22 @@ cd .. cd .. +cd .. +hg init issue1049 +cd issue1049 +echo a > a +hg ci -Ama +echo a >> a +hg ci -mb +hg up 0 +echo b >> a +hg ci -mc +HGMERGE=true hg merge +echo % should fail because we are specifying a file name +hg ci -mmerge a +echo % should fail because we are specifying a pattern +hg ci -mmerge -I a +echo % should succeed +hg ci -mmerge + exit 0
--- a/tests/test-commit.out Wed Apr 09 15:27:57 2008 -0700 +++ b/tests/test-commit.out Wed Apr 09 15:28:30 2008 -0700 @@ -1,28 +1,25 @@ % commit date test transaction abort! rollback completed +abort: empty commit message abort: impossible time zone offset: 4444444 -transaction abort! -rollback completed abort: invalid date: '1\t15.1' -transaction abort! -rollback completed abort: invalid date: 'foo bar' +abort: date exceeds 32 bits: 111111111111 +% commit added file that has been deleted nothing changed -% partial commit test -trouble committing bar! -abort: No such file or directory: .../test/bar +abort: file bar not found! adding dir/file dir/file adding dir.file -abort: no match under directory .../test/dir! -abort: no match under directory .../test/bleh! -abort: no match under directory .../test/dir2! +abort: no match under directory dir! +abort: no match under directory .! +abort: no match under directory ../dir2! dir/file does-not-exist: No such file or directory -abort: file .../test/does-not-exist not found! -abort: file .../test/baz not tracked! -abort: file .../test/quux not tracked! +abort: file does-not-exist not found! +abort: file baz not tracked! +abort: file quux not tracked! dir/file % partial subdir commit test adding bar/bar @@ -95,3 +92,14 @@ date: Mon Jan 12 13:46:40 1970 +0000 summary: commit-foo-subdir +adding a +1 files updated, 0 files merged, 0 files removed, 0 files unresolved +created new head +merging a +0 files updated, 1 files merged, 0 files removed, 0 files unresolved +(branch merge, don't forget to commit) +% should fail because we are specifying a file name +abort: cannot partially commit a merge (do not specify files or patterns) +% should fail because we are specifying a pattern +abort: cannot partially commit a merge (do not specify files or patterns) +% should succeed
--- a/tests/test-conflict.out Wed Apr 09 15:27:57 2008 -0700 +++ b/tests/test-conflict.out Wed Apr 09 15:28:30 2008 -0700 @@ -1,15 +1,17 @@ 1 files updated, 0 files merged, 0 files removed, 0 files unresolved +created new head +merging a warning: conflicts during merge. -merging a merging a failed! 0 files updated, 0 files merged, 0 files removed, 1 files unresolved There are unresolved merges, you can redo the full merge using: hg update -C 2 hg merge 1 e7fe8eb3e180+0d24b7662d3e+ tip -<<<<<<< my +<<<<<<< local something else ======= something >>>>>>> other M a +? a.orig
--- a/tests/test-confused-revert.out Wed Apr 09 15:27:57 2008 -0700 +++ b/tests/test-confused-revert.out Wed Apr 09 15:28:30 2008 -0700 @@ -8,6 +8,7 @@ ? b 0 files updated, 0 files merged, 0 files removed, 0 files unresolved 1 files updated, 0 files merged, 0 files removed, 0 files unresolved +created new head merging a 0 files updated, 1 files merged, 0 files removed, 0 files unresolved (branch merge, don't forget to commit) @@ -22,6 +23,7 @@ undeleting a forgetting b %%% should show b unknown and a marked modified (merged) +M a ? b %%% should show foo-b foo-b
--- a/tests/test-context.py Wed Apr 09 15:27:57 2008 -0700 +++ b/tests/test-context.py Wed Apr 09 15:28:30 2008 -0700 @@ -1,5 +1,5 @@ import os -from mercurial import hg, ui, commands +from mercurial import hg, ui u = ui.ui()
--- a/tests/test-convert Wed Apr 09 15:27:57 2008 -0700 +++ b/tests/test-convert Wed Apr 09 15:28:30 2008 -0700 @@ -39,3 +39,8 @@ echo % should succeed chmod 700 bogusdir hg convert a bogusdir + +echo % test pre and post conversion actions +echo 'include b' > filemap +hg convert --debug --filemap filemap a partialb | \ + grep 'run hg'
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tests/test-convert-baz Wed Apr 09 15:28:30 2008 -0700 @@ -0,0 +1,72 @@ +#!/bin/sh + +"$TESTDIR/hghave" baz || exit 80 + +mkdir do_not_use_HOME_baz +cd do_not_use_HOME_baz +HOME=`pwd`; export HOME +cd .. +baz my-id "mercurial <mercurial@selenic.com>" + +echo "[extensions]" >> $HGRCPATH +echo "convert=" >> $HGRCPATH +echo 'hgext.graphlog =' >> $HGRCPATH + +echo % create baz archive +baz make-archive baz@mercurial--convert hg-test-convert-baz + +echo % initialize baz repo +mkdir baz-repo +cd baz-repo/ +baz init-tree baz@mercurial--convert/baz--test--0 +baz import + +echo % create initial files +echo 'this is a file' > a +baz add a +mkdir src +baz add src +cd src +dd count=1 if=/dev/zero of=b > /dev/null 2> /dev/null +baz add b +baz commit -s "added a file, src and src/b (binary)" + +echo % create link file and modify a +ln -s ../a a-link +baz add a-link +echo 'this a modification to a' >> ../a +baz commit -s "added link to a and modify a" + +echo % create second link and modify b +ln -s ../a a-link-2 +baz add a-link-2 +dd count=1 seek=1 if=/dev/zero of=b > /dev/null 2> /dev/null +baz commit -s "added second link and modify b" + +echo % b file to link and a-link-2 to regular file +rm -f a-link-2 +echo 'this is now a regular file' > a-link-2 +ln -sf ../a b +baz commit -s "file to link and link to file test" + +echo % move a-link-2 file and src directory +cd .. +baz mv src/a-link-2 c +baz mv src test +baz commit -s "move and rename a-link-2 file and src directory" + +cd .. + +echo % converting baz repo to Mercurial +hg convert baz-repo baz-repo-hg + +baz register-archive -d baz@mercurial--convert + +glog() +{ + hg glog --template '#rev# "#desc|firstline#" files: #files#\n' "$@" +} + +echo % show graph log +glog -R baz-repo-hg +hg -R baz-repo-hg manifest --debug
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tests/test-convert-baz.out Wed Apr 09 15:28:30 2008 -0700 @@ -0,0 +1,75 @@ +% create baz archive +% initialize baz repo +* creating version baz@mercurial--convert/baz--test--0 +* imported baz@mercurial--convert/baz--test--0 +% create initial files +* build pristine tree for baz@mercurial--convert/baz--test--0--base-0 +* Scanning for full-tree revision: . +* from import revision: baz@mercurial--convert/baz--test--0--base-0 +A/ .arch-ids +A/ src +A/ src/.arch-ids +A .arch-ids/a.id +A a +A src/.arch-ids/=id +A src/.arch-ids/b.id +A src/b +* update pristine tree (baz@mercurial--convert/baz--test--0--base-0 => baz--test--0--patch-1) +* committed baz@mercurial--convert/baz--test--0--patch-1 +% create link file and modify a +A src/.arch-ids/a-link.id +A src/a-link +M a +* update pristine tree (baz@mercurial--convert/baz--test--0--patch-1 => baz--test--0--patch-2) +* committed baz@mercurial--convert/baz--test--0--patch-2 +% create second link and modify b +A src/.arch-ids/a-link-2.id +A src/a-link-2 +Mb src/b +* update pristine tree (baz@mercurial--convert/baz--test--0--patch-2 => baz--test--0--patch-3) +* committed baz@mercurial--convert/baz--test--0--patch-3 +% b file to link and a-link-2 to regular file +fl src/b +lf src/a-link-2 +* update pristine tree (baz@mercurial--convert/baz--test--0--patch-3 => baz--test--0--patch-4) +* committed baz@mercurial--convert/baz--test--0--patch-4 +% move a-link-2 file and src directory +D/ src/.arch-ids +A/ test/.arch-ids +/> src test +=> src/.arch-ids/a-link-2.id .arch-ids/c.id +=> src/a-link-2 c +=> src/.arch-ids/=id test/.arch-ids/=id +=> src/.arch-ids/a-link.id test/.arch-ids/a-link.id +=> src/.arch-ids/b.id test/.arch-ids/b.id +* update pristine tree (baz@mercurial--convert/baz--test--0--patch-4 => baz--test--0--patch-5) +* committed baz@mercurial--convert/baz--test--0--patch-5 +% converting baz repo to Mercurial +initializing destination baz-repo-hg repository +analyzing tree version baz@mercurial--convert/baz--test--0... +scanning source... +sorting... +converting... +5 initial import +4 added a file, src and src/b (binary) +3 added link to a and modify a +2 added second link and modify b +1 file to link and link to file test +0 move and rename a-link-2 file and src directory +% show graph log +o 5 "move and rename a-link-2 file and src directory" files: c src/a-link src/a-link-2 src/b test/a-link test/b +| +o 4 "file to link and link to file test" files: src/a-link-2 src/b +| +o 3 "added second link and modify b" files: src/a-link-2 src/b +| +o 2 "added link to a and modify a" files: a src/a-link +| +o 1 "added a file, src and src/b (binary)" files: a src/b +| +o 0 "initial import" files: + +c4072c4b72e1cabace081888efa148ee80ca3cbb 644 a +e3207be798aaf87a444a62903621edab4ddc1fb6 644 c +1f6b5bb93f1da278ef1fead1e4740a03d8802e9f 644 @ test/a-link +1f6b5bb93f1da278ef1fead1e4740a03d8802e9f 644 @ test/b
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tests/test-convert-clonebranches Wed Apr 09 15:28:30 2008 -0700 @@ -0,0 +1,54 @@ +#!/bin/sh + +echo "[extensions]" >> $HGRCPATH +echo "hgext.convert = " >> $HGRCPATH +echo "[convert]" >> $HGRCPATH +echo "hg.tagsbranch=0" >> $HGRCPATH + +hg init source +cd source +echo a > a +hg ci -qAm adda +# Add a merge with one parent in the same branch +echo a >> a +hg ci -qAm changea +hg up -qC 0 +hg branch branch0 +echo b > b +hg ci -qAm addb +hg up -qC +hg merge +hg ci -qm mergeab +hg tag -ql mergeab +cd .. + +# Miss perl... sometimes +cat > filter.py <<EOF +import sys, re + +r = re.compile(r'^(?:\d+|pulling from)') +sys.stdout.writelines([l for l in sys.stdin if r.search(l)]) +EOF + +echo % convert +hg convert -v --config convert.hg.clonebranches=1 source dest | + python filter.py + +# Add a merge with both parents and child in different branches +cd source +hg branch branch1 +echo a > file1 +hg ci -qAm c1 +hg up -qC mergeab +hg branch branch2 +echo a > file2 +hg ci -qAm c2 +hg merge branch1 +hg branch branch3 +hg ci -qAm c3 +cd .. + +echo % incremental conversion +hg convert -v --config convert.hg.clonebranches=1 source dest | + python filter.py +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tests/test-convert-clonebranches.out Wed Apr 09 15:28:30 2008 -0700 @@ -0,0 +1,29 @@ +marked working directory as branch branch0 +1 files updated, 0 files merged, 0 files removed, 0 files unresolved +(branch merge, don't forget to commit) +% convert +3 adda +2 addb +pulling from default into branch0 +1 changesets found +1 changea +0 mergeab +pulling from default into branch0 +1 changesets found +marked working directory as branch branch1 +marked working directory as branch branch2 +1 files updated, 0 files merged, 0 files removed, 0 files unresolved +(branch merge, don't forget to commit) +marked working directory as branch branch3 +% incremental conversion +2 c2 +pulling from branch0 into branch2 +2 changesets found +1 c1 +pulling from branch0 into branch1 +2 changesets found +0 c3 +pulling from branch2 into branch3 +3 changesets found +pulling from branch1 into branch3 +1 changesets found
--- a/tests/test-convert-cvs Wed Apr 09 15:27:57 2008 -0700 +++ b/tests/test-convert-cvs Wed Apr 09 15:28:30 2008 -0700 @@ -2,6 +2,11 @@ "$TESTDIR/hghave" cvs cvsps || exit 80 +cvscall() +{ + cvs -f $@ +} + echo "[extensions]" >> $HGRCPATH echo "convert = " >> $HGRCPATH @@ -9,9 +14,10 @@ mkdir cvsrepo cd cvsrepo export CVSROOT=`pwd` +export CVS_OPTIONS=-f cd .. -cvs -q -d "$CVSROOT" init +cvscall -q -d "$CVSROOT" init echo % create source directory mkdir src-temp @@ -23,16 +29,17 @@ cd .. echo % import source directory -cvs -q import -m import src INITIAL start +cvscall -q import -m import src INITIAL start cd .. echo % checkout source directory -cvs -q checkout src +cvscall -q checkout src echo % commit a new revision changing b/c cd src +sleep 1 echo c >> b/c -cvs -q commit -mci0 . | grep '<--' |\ +cvscall -q commit -mci0 . | grep '<--' |\ sed -e 's:.*src/\(.*\),v.*:checking in src/\1,v:g' cd .. @@ -51,7 +58,7 @@ cd src echo a >> a echo c >> b/c -cvs -q commit -mci1 . | grep '<--' |\ +cvscall -q commit -mci1 . | grep '<--' |\ sed -e 's:.*src/\(.*\),v.*:checking in src/\1,v:g' cd .. @@ -65,3 +72,25 @@ cat src-hg/b/c hg -R src-filemap log --template '#rev# #desc# files: #files#\n' +echo % commit branch +cd src +cvs -q update -r1.1 b/c +cvs -q tag -b branch +cvs -q update -r branch +echo d >> b/c +cvs -q commit -mci2 . | grep '<--' |\ + sed -e 's:.*src/\(.*\),v.*:checking in src/\1,v:g' +cd .. + +echo % convert again +hg convert src src-hg | sed -e 's/connecting to.*cvsrepo/connecting to cvsrepo/g' +cat src-hg/a +cat src-hg/b/c + +echo % convert again with --filemap +hg convert --filemap filemap src src-filemap | sed -e 's/connecting to.*cvsrepo/connecting to cvsrepo/g' +cat src-hg/b/c +hg -R src-filemap log --template '#rev# #desc# files: #files#\n' + +echo "graphlog = " >> $HGRCPATH +hg -R src-hg glog --template '#rev# (#branches#) #desc# files: #files#\n'
--- a/tests/test-convert-cvs.out Wed Apr 09 15:27:57 2008 -0700 +++ b/tests/test-convert-cvs.out Wed Apr 09 15:28:30 2008 -0700 @@ -67,3 +67,43 @@ 2 update tags files: .hgtags 1 ci0 files: b/c 0 Initial revision files: b/c +% commit branch +U b/c +T a +T b/c +checking in src/b/c,v +% convert again +connecting to cvsrepo +scanning source... +sorting... +converting... +0 ci2 +a +a +c +d +% convert again with --filemap +connecting to cvsrepo +scanning source... +sorting... +converting... +0 ci2 +c +d +4 ci2 files: b/c +3 ci1 files: b/c +2 update tags files: .hgtags +1 ci0 files: b/c +0 Initial revision files: b/c +o 5 (branch) ci2 files: b/c +| +| o 4 () ci1 files: a b/c +| | +| o 3 () update tags files: .hgtags +| | +| o 2 () ci0 files: b/c +|/ +| o 1 (INITIAL) import files: +|/ +o 0 () Initial revision files: a b/c +
--- a/tests/test-convert-darcs Wed Apr 09 15:27:57 2008 -0700 +++ b/tests/test-convert-darcs Wed Apr 09 15:28:30 2008 -0700 @@ -13,7 +13,7 @@ mkdir dummy mkdir dummy/_darcs if hg convert dummy 2>&1 | grep ElementTree > /dev/null; then - echo 'hghave: missing feature: elementtree module' + echo 'skipped: missing feature: elementtree module' exit 80 fi @@ -26,7 +26,7 @@ cd .. echo % branch and update -darcs get darcs-repo darcs-clone +darcs get darcs-repo darcs-clone >/dev/null cd darcs-clone echo c >> a echo c > c @@ -50,7 +50,7 @@ hg glog --template '#rev# "#desc|firstline#" files: #files#\n' "$@" } -hg convert darcs-repo darcs-repo-hg +hg convert darcs-repo darcs-repo-hg 2>&1 | grep -v hGetLine | grep -v '^$' # The converter does not currently handle patch conflicts very well. # When they occur, it reverts *all* changes and moves forward, # letting the conflict resolving patch fix collisions.
--- a/tests/test-convert-darcs.out Wed Apr 09 15:27:57 2008 -0700 +++ b/tests/test-convert-darcs.out Wed Apr 09 15:28:30 2008 -0700 @@ -1,8 +1,6 @@ % initialize darcs repo Finished recording patch 'p0' % branch and update - Copying patch 1 of 1... Copying patch 1 of 1... done. -Finished getting. Finished recording patch 'p1.1' % update source Finished recording patch 'p1.2'
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tests/test-convert-datesort Wed Apr 09 15:28:30 2008 -0700 @@ -0,0 +1,40 @@ +#!/bin/sh + +cat >> $HGRCPATH <<EOF +[extensions] +convert= +graphlog= +EOF + +hg init t +cd t +echo a >> a +hg ci -Am a0 -d '1 0' +hg branch brancha +echo a >> a +hg ci -m a1 -d '2 0' +echo a >> a +hg ci -m a2 -d '3 0' +echo a >> a +hg ci -m a3 -d '4 0' +hg up -C 0 +hg branch branchb +echo b >> b +hg ci -Am b0 -d '5 0' +hg up -C brancha +echo a >> a +hg ci -m a4 -d '6 0' +echo a >> a +hg ci -m a5 -d '7 0' +echo a >> a +hg ci -m a6 -d '8 0' +hg up -C branchb +echo b >> b +hg ci -m b1 -d '9 0' +cd .. + +echo % convert with datesort +hg convert --datesort t t2 +echo % graph converted repo +hg -R t2 glog --template '#rev# "#desc#"\n' +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tests/test-convert-datesort.out Wed Apr 09 15:28:30 2008 -0700 @@ -0,0 +1,41 @@ +adding a +marked working directory as branch brancha +1 files updated, 0 files merged, 0 files removed, 0 files unresolved +marked working directory as branch branchb +adding b +created new head +1 files updated, 0 files merged, 1 files removed, 0 files unresolved +2 files updated, 0 files merged, 0 files removed, 0 files unresolved +% convert with datesort +initializing destination t2 repository +scanning source... +sorting... +converting... +8 a0 +7 a1 +6 a2 +5 a3 +4 b0 +3 a4 +2 a5 +1 a6 +0 b1 +% graph converted repo +o 8 "b1" +| +| 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-filemap.out Wed Apr 09 15:27:57 2008 -0700 +++ b/tests/test-convert-filemap.out Wed Apr 09 15:28:30 2008 -0700 @@ -1,3 +1,5 @@ +created new head +created new head @ 8 "8: change foo" files: foo | o 7 "7: second merge; change bar" files: bar baz
--- a/tests/test-convert-git.out Wed Apr 09 15:27:57 2008 -0700 +++ b/tests/test-convert-git.out Wed Apr 09 15:28:30 2008 -0700 @@ -10,10 +10,10 @@ 2 t4.1 1 t4.2 0 Merge branch other -changeset: 5:c6d72c98aa00 +changeset: 5:4ab1af49a271 tag: tip -parent: 3:a18bdfccf429 -parent: 4:48cb5b72ce56 +parent: 3:0222ab0998d7 +parent: 4:5333c870e3c2 user: test <test@example.org> date: Mon Jan 01 00:00:15 2007 +0000 files: a
--- a/tests/test-convert-hg-sink.out Wed Apr 09 15:27:57 2008 -0700 +++ b/tests/test-convert-hg-sink.out Wed Apr 09 15:28:30 2008 -0700 @@ -34,7 +34,7 @@ % dirstate should be empty: 3 files updated, 0 files merged, 0 files removed, 0 files unresolved % put something in the dirstate: -a 0 -1 unset baz +a 0 -1 unset baz copy: bar -> baz % add a new revision in the original repo scanning source...
--- a/tests/test-convert-hg-source Wed Apr 09 15:27:57 2008 -0700 +++ b/tests/test-convert-hg-source Wed Apr 09 15:28:30 2008 -0700 @@ -29,6 +29,9 @@ hg merge 2 hg ci -m 'merge remote copy' -d '4 0' +chmod +x baz +hg ci -m 'mark baz executable' -d '5 0' + cd .. hg convert --datesort orig new 2>&1 | grep -v 'subversion python bindings could not be loaded' cd new
--- a/tests/test-convert-hg-source.out Wed Apr 09 15:27:57 2008 -0700 +++ b/tests/test-convert-hg-source.out Wed Apr 09 15:28:30 2008 -0700 @@ -1,3 +1,4 @@ +created new head merging baz and foo 1 files updated, 1 files merged, 0 files removed, 0 files unresolved (branch merge, don't forget to commit) @@ -5,15 +6,17 @@ merging foo and baz 1 files updated, 1 files merged, 0 files removed, 0 files unresolved (branch merge, don't forget to commit) +created new head initializing destination new repository scanning source... sorting... converting... -4 add foo bar -3 change foo -2 make bar and baz copies of foo -1 merge local copy -0 merge remote copy +5 add foo bar +4 change foo +3 make bar and baz copies of foo +2 merge local copy +1 merge remote copy +0 mark baz executable comparing with ../orig searching for changes no changes found
--- a/tests/test-convert-hg-svn Wed Apr 09 15:27:57 2008 -0700 +++ b/tests/test-convert-hg-svn Wed Apr 09 15:28:30 2008 -0700 @@ -10,7 +10,7 @@ echo "[extensions]" >> $HGRCPATH echo "convert = " >> $HGRCPATH -svnpath=`pwd`/svn-repo +svnpath=`pwd | fix_path`/svn-repo svnadmin create $svnpath cat > $svnpath/hooks/pre-revprop-change <<'EOF' @@ -31,7 +31,14 @@ EOF chmod +x $svnpath/hooks/pre-revprop-change -svnurl=file://$svnpath +# SVN wants all paths to start with a slash. Unfortunately, +# Windows ones don't. Handle that. +svnurl=$svnpath +expr $svnurl : "\/" > /dev/null +if [ $? -ne 0 ]; then + svnurl='/'$svnurl +fi +svnurl=file://$svnurl svn co $svnurl $svnpath-wc cd $svnpath-wc
--- a/tests/test-convert-hg-svn.out Wed Apr 09 15:27:57 2008 -0700 +++ b/tests/test-convert-hg-svn.out Wed Apr 09 15:28:30 2008 -0700 @@ -19,6 +19,7 @@ sorting... converting... % new hg rev +updating working directory 1 files updated, 0 files merged, 0 files removed, 0 files unresolved % echo hg to svn scanning source...
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tests/test-convert-mtn Wed Apr 09 15:28:30 2008 -0700 @@ -0,0 +1,89 @@ +#!/bin/sh + +"$TESTDIR/hghave" mtn || exit 80 + +# Monotone directory is called .monotone on *nix and monotone +# on Windows. Having a variable here ease test patching. +mtndir=.monotone +echo "[extensions]" >> $HGRCPATH +echo "convert=" >> $HGRCPATH +echo 'hgext.graphlog =' >> $HGRCPATH + +HOME=`pwd`/do_not_use_HOME_mtn; export HOME +# Windows version of monotone home +APPDATA=$HOME; export APPDATA + +echo % tedious monotone keys configuration +# The /dev/null redirection is necessary under Windows, or +# it complains about home directory permissions +mtn --quiet genkey test@selenic.com 1>/dev/null 2>&1 <<EOF +passphrase +passphrase +EOF +cat >> $HOME/$mtndir/monotonerc <<EOF +function get_passphrase(keypair_id) + return "passphrase" +end +EOF + +echo % create monotone repository +mtn db init --db=repo.mtn +mtn --db=repo.mtn --branch=com.selenic.test setup workingdir +cd workingdir +echo a > a +mkdir dir +echo b > dir/b +python -c 'file("bin", "wb").write("a\\x00b")' +echo c > c +mtn add a dir/b c bin +mtn ci -m initialize +echo % update monotone working directory +mtn mv a dir/a +echo a >> dir/a +echo b >> dir/b +mtn drop c +python -c 'file("bin", "wb").write("b\\x00c")' +mtn ci -m update1 +cd .. + +echo % convert once +hg convert -s mtn repo.mtn + +cd workingdir +echo e > e +mtn add e +mtn drop dir/b +mtn mv bin bin2 +mtn ci -m update2 +# Test directory move +mtn mv dir dir2 +mtn ci -m movedir +# Test directory removal with empty directory +mkdir dir2/dir +mkdir dir2/dir/subdir +echo f > dir2/dir/subdir/f +mkdir dir2/dir/emptydir +mtn add -R dir2/dir +mtn ci -m emptydir +mtn drop -R dir2/dir +mtn ci -m dropdirectory +cd .. + +echo % convert incrementally +hg convert -s mtn repo.mtn + +glog() +{ + hg glog --template '#rev# "#desc|firstline#" files: #files#\n' "$@" +} + +cd repo.mtn-hg +hg up -C +glog +echo % manifest +hg manifest +echo % contents +cat dir2/a +test -d dir2/dir && echo 'removed dir2/dir is still there!' +exit 0 +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tests/test-convert-mtn.out Wed Apr 09 15:28:30 2008 -0700 @@ -0,0 +1,72 @@ +% tedious monotone keys configuration +% create monotone repository +mtn: adding a to workspace manifest +mtn: adding bin to workspace manifest +mtn: adding c to workspace manifest +mtn: adding dir to workspace manifest +mtn: adding dir/b to workspace manifest +mtn: beginning commit on branch 'com.selenic.test' +mtn: committed revision 803ef0bf815e35b951dbd4310acd1e45e675016e +% update monotone working directory +mtn: skipping dir, already accounted for in workspace +mtn: renaming a to dir/a in workspace manifest +mtn: dropping c from workspace manifest +mtn: beginning commit on branch 'com.selenic.test' +mtn: committed revision 4daf60753d6fe21a06ce5f716303fe55fd6d3a56 +% convert once +assuming destination repo.mtn-hg +initializing destination repo.mtn-hg repository +scanning source... +sorting... +converting... +1 initialize +0 update1 +mtn: adding e to workspace manifest +mtn: dropping dir/b from workspace manifest +mtn: renaming bin to bin2 in workspace manifest +mtn: beginning commit on branch 'com.selenic.test' +mtn: committed revision 6c6977a6ef609ec80e40779f89dbd2772c96de62 +mtn: renaming dir to dir2 in workspace manifest +mtn: beginning commit on branch 'com.selenic.test' +mtn: committed revision 5de5abe7c15eae70cf3acdda23c9c319ea50c1af +mtn: adding dir2/dir to workspace manifest +mtn: adding dir2/dir/emptydir to workspace manifest +mtn: adding dir2/dir/subdir to workspace manifest +mtn: adding dir2/dir/subdir/f to workspace manifest +mtn: beginning commit on branch 'com.selenic.test' +mtn: committed revision 27a423be1e406595cc57f50f42a8790fa0a93d8e +mtn: dropping dir2/dir/subdir/f from workspace manifest +mtn: dropping dir2/dir/subdir from workspace manifest +mtn: dropping dir2/dir/emptydir from workspace manifest +mtn: dropping dir2/dir from workspace manifest +mtn: beginning commit on branch 'com.selenic.test' +mtn: committed revision ba57ba5ac63178529d37fa8a2a1a012fc0e42047 +% convert incrementally +assuming destination repo.mtn-hg +scanning source... +sorting... +converting... +3 update2 +2 movedir +1 emptydir +0 dropdirectory +3 files updated, 0 files merged, 0 files removed, 0 files unresolved +@ 5 "dropdirectory" files: dir2/dir/subdir/f +| +o 4 "emptydir" files: dir2/dir/subdir/f +| +o 3 "movedir" files: dir/a dir2/a +| +o 2 "update2" files: bin bin2 dir/b e +| +o 1 "update1" files: a bin c dir/a dir/b +| +o 0 "initialize" files: a bin c dir/b + +% manifest +bin2 +dir2/a +e +% contents +a +a
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tests/test-convert-svn-branches Wed Apr 09 15:28:30 2008 -0700 @@ -0,0 +1,91 @@ +#!/bin/sh + +"$TESTDIR/hghave" svn svn-bindings || exit 80 + +fix_path() +{ + tr '\\' / +} + +echo "[extensions]" >> $HGRCPATH +echo "convert = " >> $HGRCPATH +echo "hgext.graphlog =" >> $HGRCPATH + +svnadmin create svn-repo + +svnpath=`pwd | fix_path` +# SVN wants all paths to start with a slash. Unfortunately, +# Windows ones don't. Handle that. +expr $svnpath : "\/" > /dev/null +if [ $? -ne 0 ]; then + svnpath='/'$svnpath +fi + +echo % initial svn import +mkdir projA +cd projA +mkdir trunk +mkdir branches +mkdir tags +cd .. + +svnurl=file://$svnpath/svn-repo/projA +svn import -m "init projA" projA $svnurl | fix_path + +echo % update svn repository +svn co $svnurl A | fix_path +cd A +echo hello > trunk/letter.txt +echo hey > trunk/letter2.txt +echo ho > trunk/letter3.txt +# Add a file within branches, used to confuse branch detection +echo a > branches/readme.txt +svn add trunk/letter.txt trunk/letter2.txt trunk/letter3.txt branches/readme.txt +svn ci -m hello + +echo % branch to old letters +svn copy trunk branches/old +svn rm branches/old/letter3.txt +svn ci -m "branch trunk, remove letter3" +svn up + +echo % update trunk +"$TESTDIR/svn-safe-append.py" "what can I say ?" trunk/letter.txt +svn ci -m "change letter" + +echo % update old branch +"$TESTDIR/svn-safe-append.py" "what's up ?" branches/old/letter2.txt +svn ci -m "change letter2" + +echo % create a cross-branch revision +svn move -m "move letter2" trunk/letter2.txt \ + branches/old/letter3.txt +"$TESTDIR/svn-safe-append.py" "I am fine" branches/old/letter3.txt +svn ci -m "move and update letter3.txt" + +echo % update old branch again +"$TESTDIR/svn-safe-append.py" "bye" branches/old/letter2.txt +svn ci -m "change letter2 again" + +echo % update trunk again +"$TESTDIR/svn-safe-append.py" "how are you ?" trunk/letter.txt +svn ci -m "last change to letter" +cd .. + +echo % convert trunk and branches +hg convert --datesort $svnurl A-hg + +echo % branch again from a converted revision +cd A +svn copy -r 1 $svnurl/trunk branches/old2 +svn ci -m "branch trunk@1 into old2" +cd .. + +echo % convert again +hg convert --datesort $svnurl A-hg + +cd A-hg +hg glog --template '#rev# #desc|firstline# files: #files#\n' +hg branches | sed 's/:.*/:/' +hg tags -q +cd ..
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tests/test-convert-svn-branches.out Wed Apr 09 15:28:30 2008 -0700 @@ -0,0 +1,103 @@ +% initial svn import +Adding projA/trunk +Adding projA/branches +Adding projA/tags + +Committed revision 1. +% update svn repository +A A/trunk +A A/branches +A A/tags +Checked out revision 1. +A trunk/letter.txt +A trunk/letter2.txt +A trunk/letter3.txt +A branches/readme.txt +Adding branches/readme.txt +Adding trunk/letter.txt +Adding trunk/letter2.txt +Adding trunk/letter3.txt +Transmitting file data .... +Committed revision 2. +% branch to old letters +A branches/old +D branches/old/letter3.txt +Adding branches/old +Adding branches/old/letter.txt +Adding branches/old/letter2.txt +Deleting branches/old/letter3.txt + +Committed revision 3. +At revision 3. +% update trunk +Sending trunk/letter.txt +Transmitting file data . +Committed revision 4. +% update old branch +Sending branches/old/letter2.txt +Transmitting file data . +Committed revision 5. +% create a cross-branch revision +A branches/old/letter3.txt +D trunk/letter2.txt +Adding branches/old/letter3.txt +Deleting trunk/letter2.txt +Transmitting file data . +Committed revision 6. +% update old branch again +Sending branches/old/letter2.txt +Transmitting file data . +Committed revision 7. +% update trunk again +Sending trunk/letter.txt +Transmitting file data . +Committed revision 8. +% convert trunk and branches +initializing destination A-hg repository +scanning source... +sorting... +converting... +8 init projA +7 hello +6 branch trunk, remove letter3 +5 change letter +4 change letter2 +3 move and update letter3.txt +2 move and update letter3.txt +1 change letter2 again +0 last change to letter +% branch again from a converted revision +Checked out revision 1. +A branches/old2 +Adding branches/old2 + +Committed revision 9. +% convert again +scanning source... +sorting... +converting... +0 branch trunk@1 into old2 +o 9 branch trunk@1 into old2 files: +| +| o 8 last change to letter files: letter.txt +| | +| | o 7 change letter2 again files: letter2.txt +| | | +| o | 6 move and update letter3.txt files: letter2.txt +| | | +| | o 5 move and update letter3.txt files: letter3.txt +| | | +| | o 4 change letter2 files: letter2.txt +| | | +| o | 3 change letter files: letter.txt +| | | ++---o 2 branch trunk, remove letter3 files: letter.txt letter2.txt +| | +| o 1 hello files: letter.txt letter2.txt letter3.txt +|/ +o 0 init projA files: + +old2 9: +default 8: +old 7: +tip
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tests/test-convert-svn-move Wed Apr 09 15:28:30 2008 -0700 @@ -0,0 +1,65 @@ +#!/bin/sh + +"$TESTDIR/hghave" svn svn-bindings || exit 80 + +fix_path() +{ + tr '\\' / +} + +echo "[extensions]" >> $HGRCPATH +echo "convert = " >> $HGRCPATH +echo "hgext.graphlog =" >> $HGRCPATH + +svnadmin create svn-repo + +svnpath=`pwd | fix_path` +# SVN wants all paths to start with a slash. Unfortunately, +# Windows ones don't. Handle that. +expr $svnpath : "\/" > /dev/null +if [ $? -ne 0 ]; then + svnpath='/'$svnpath +fi + +echo % initial svn import +mkdir projA +cd projA +mkdir trunk +echo a > trunk/a +mkdir trunk/d1 +echo b > trunk/d1/b +echo c > trunk/d1/c +cd .. + +svnurl=file://$svnpath/svn-repo/projA +svn import -m "init projA" projA $svnurl | fix_path + +# Build a module renaming chain which used to confuse the converter. +echo % update svn repository +svn co $svnurl A | fix_path +cd A +"$TESTDIR/svn-safe-append.py" a trunk/a +"$TESTDIR/svn-safe-append.py" c trunk/d1/c +svn ci -m commitbeforemove +svn mv $svnurl/trunk $svnurl/subproject -m movedtrunk +svn up +mkdir subproject/trunk +svn add subproject/trunk +svn ci -m createtrunk +mkdir subproject/branches +svn add subproject/branches +svn ci -m createbranches +svn mv $svnurl/subproject/d1 $svnurl/subproject/trunk/d1 -m moved1 +svn up +"$TESTDIR/svn-safe-append.py" b subproject/trunk/d1/b +svn ci -m changeb +svn mv $svnurl/subproject/trunk/d1 $svnurl/subproject/branches/d1 -m moved1again +cd .. + +echo % convert trunk and branches +hg convert --datesort $svnurl/subproject A-hg + +cd A-hg +hg glog --template '#rev# #desc|firstline# files: #files#\n' +hg branches | sed 's/:.*/:/' +cd ..
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tests/test-convert-svn-move.out Wed Apr 09 15:28:30 2008 -0700 @@ -0,0 +1,76 @@ +% initial svn import +Adding projA/trunk +Adding projA/trunk/a +Adding projA/trunk/d1 +Adding projA/trunk/d1/b +Adding projA/trunk/d1/c + +Committed revision 1. +% update svn repository +A A/trunk +A A/trunk/a +A A/trunk/d1 +A A/trunk/d1/b +A A/trunk/d1/c +Checked out revision 1. +Sending trunk/a +Sending trunk/d1/c +Transmitting file data .. +Committed revision 2. + +Committed revision 3. +D trunk +A subproject +A subproject/a +A subproject/d1 +A subproject/d1/b +A subproject/d1/c +Updated to revision 3. +A subproject/trunk +Adding subproject/trunk + +Committed revision 4. +A subproject/branches +Adding subproject/branches + +Committed revision 5. + +Committed revision 6. +A subproject/trunk/d1 +A subproject/trunk/d1/b +A subproject/trunk/d1/c +D subproject/d1 +Updated to revision 6. +Sending subproject/trunk/d1/b +Transmitting file data . +Committed revision 7. + +Committed revision 8. +% convert trunk and branches +initializing destination A-hg repository +scanning source... +sorting... +converting... +6 createtrunk +5 moved1 +4 moved1 +3 changeb +2 changeb +1 moved1again +0 moved1again +o 6 moved1again files: d1/b d1/c +| +| o 5 moved1again files: +| | +o | 4 changeb files: d1/b +| | +| o 3 changeb files: b +| | +o | 2 moved1 files: d1/b d1/c +| | +| o 1 moved1 files: b c +| +o 0 createtrunk files: + +default 6: +d1 5:
--- a/tests/test-convert-svn-sink Wed Apr 09 15:27:57 2008 -0700 +++ b/tests/test-convert-svn-sink Wed Apr 09 15:28:30 2008 -0700 @@ -2,6 +2,25 @@ "$TESTDIR/hghave" svn svn-bindings || exit 80 +fixpath() +{ + tr '\\' / +} + +svnupanddisplay() +{ + ( + cd $1; + svn up; + svn st -v | fixpath + limit='' + if [ $2 -gt 0 ]; then + limit="--limit=$2" + fi + svn log --xml -v $limit | fixpath | sed 's,<date>.*,<date/>,' + ) +} + echo "[extensions]" >> $HGRCPATH echo "convert = " >> $HGRCPATH @@ -13,13 +32,13 @@ echo % add hg --cwd a ci -d '0 0' -A -m 'add a file' -echo a >> a/a +"$TESTDIR/svn-safe-append.py" a a/a echo % modify hg --cwd a ci -d '1 0' -m 'modify a file' hg --cwd a tip -q hg convert -d svn a -(cd a-hg-wc; svn up; svn st -v; svn log --xml -v --limit=2 | sed 's,<date>.*,<date/>,') +svnupanddisplay a-hg-wc 2 ls a a-hg-wc cmp a/a a-hg-wc/a && echo same || echo different @@ -29,7 +48,7 @@ hg --cwd a tip -q hg convert -d svn a -(cd a-hg-wc; svn up; svn st -v; svn log --xml -v --limit=1 | sed 's,<date>.*,<date/>,') +svnupanddisplay a-hg-wc 1 ls a a-hg-wc hg --cwd a cp b c @@ -38,7 +57,7 @@ hg --cwd a tip -q hg convert -d svn a -(cd a-hg-wc; svn up; svn st -v; svn log --xml -v --limit=1 | sed 's,<date>.*,<date/>,') +svnupanddisplay a-hg-wc 1 ls a a-hg-wc hg --cwd a rm b @@ -47,7 +66,7 @@ hg --cwd a tip -q hg convert -d svn a -(cd a-hg-wc; svn up; svn st -v; svn log --xml -v --limit=1 | sed 's,<date>.*,<date/>,') +svnupanddisplay a-hg-wc 1 ls a a-hg-wc chmod +x a/c @@ -56,30 +75,53 @@ hg --cwd a tip -q hg convert -d svn a -(cd a-hg-wc; svn up; svn st -v; svn log --xml -v --limit=1 | sed 's,<date>.*,<date/>,') +svnupanddisplay a-hg-wc 1 test -x a-hg-wc/c && echo executable || echo not executable +echo % executable in new directory + +rm -rf a a-hg a-hg-wc +hg init a + +mkdir a/d1 +echo a > a/d1/a +chmod +x a/d1/a +hg --cwd a ci -d '0 0' -A -m 'add executable file in new directory' + +hg convert -d svn a +svnupanddisplay a-hg-wc 1 +test -x a-hg-wc/d1/a && echo executable || echo not executable + +echo % copy to new directory + +mkdir a/d2 +hg --cwd a cp d1/a d2/a +hg --cwd a ci -d '1 0' -A -m 'copy file to new directory' + +hg convert -d svn a +svnupanddisplay a-hg-wc 1 + echo % branchy history hg init b echo base > b/b hg --cwd b ci -d '0 0' -Ambase -echo left-1 >> b/b +"$TESTDIR/svn-safe-append.py" left-1 b/b echo left-1 > b/left-1 hg --cwd b ci -d '1 0' -Amleft-1 -echo left-2 >> b/b +"$TESTDIR/svn-safe-append.py" left-2 b/b echo left-2 > b/left-2 hg --cwd b ci -d '2 0' -Amleft-2 hg --cwd b up 0 -echo right-1 >> b/b +"$TESTDIR/svn-safe-append.py" right-1 b/b echo right-1 > b/right-1 hg --cwd b ci -d '3 0' -Amright-1 -echo right-2 >> b/b +"$TESTDIR/svn-safe-append.py" right-2 b/b echo right-2 > b/right-2 hg --cwd b ci -d '4 0' -Amright-2 @@ -90,4 +132,4 @@ hg convert -d svn b echo % expect 4 changes -(cd b-hg-wc; svn up; svn st -v; svn log --xml -v | sed 's,<date>.*,<date/>,') +svnupanddisplay b-hg-wc 0
--- a/tests/test-convert-svn-sink.out Wed Apr 09 15:27:57 2008 -0700 +++ b/tests/test-convert-svn-sink.out Wed Apr 09 15:28:30 2008 -0700 @@ -167,44 +167,102 @@ d1 % executable 5:f205b3636d77 -svn: Path 'b' does not exist assuming destination a-hg initializing svn wc 'a-hg-wc' scanning source... sorting... converting... 0 make a file executable -abort: svn exited with status 1 -At revision 5. - 5 5 test . - M 5 4 test c - 5 1 test d1 - 5 1 test d1/d2 - 5 1 test d1/d2/b +At revision 6. + 6 6 test . + 6 6 test c + 6 1 test d1 + 6 1 test d1/d2 + 6 1 test d1/d2/b <?xml version="1.0"?> <log> <logentry - revision="5"> + revision="6"> <author>test</author> <date/> <paths> <path - action="D">/b</path> + action="M">/c</path> </paths> -<msg>remove a file</msg> +<msg>make a file executable</msg> </logentry> </log> executable +% executable in new directory +adding d1/a +assuming destination a-hg +initializing svn repo 'a-hg' +initializing svn wc 'a-hg-wc' +scanning source... +sorting... +converting... +0 add executable file in new directory +At revision 1. + 1 1 test . + 1 1 test d1 + 1 1 test d1/a +<?xml version="1.0"?> +<log> +<logentry + revision="1"> +<author>test</author> +<date/> +<paths> +<path + action="A">/d1</path> +<path + action="A">/d1/a</path> +</paths> +<msg>add executable file in new directory</msg> +</logentry> +</log> +executable +% copy to new directory +assuming destination a-hg +initializing svn wc 'a-hg-wc' +scanning source... +sorting... +converting... +0 copy file to new directory +At revision 2. + 2 2 test . + 2 1 test d1 + 2 1 test d1/a + 2 2 test d2 + 2 2 test d2/a +<?xml version="1.0"?> +<log> +<logentry + revision="2"> +<author>test</author> +<date/> +<paths> +<path + action="A">/d2</path> +<path + copyfrom-path="/d1/a" + copyfrom-rev="1" + action="A">/d2/a</path> +</paths> +<msg>copy file to new directory</msg> +</logentry> +</log> % branchy history adding b adding left-1 adding left-2 1 files updated, 0 files merged, 2 files removed, 0 files unresolved adding right-1 +created new head adding right-2 3 files updated, 0 files merged, 2 files removed, 0 files unresolved +merging b warning: conflicts during merge. -merging b merging b failed! 2 files updated, 0 files merged, 0 files removed, 1 files unresolved There are unresolved merges, you can redo the full merge using:
--- a/tests/test-convert-svn-source Wed Apr 09 15:27:57 2008 -0700 +++ b/tests/test-convert-svn-source Wed Apr 09 15:28:30 2008 -0700 @@ -9,15 +9,10 @@ echo "[extensions]" >> $HGRCPATH echo "convert = " >> $HGRCPATH +echo 'hgext.graphlog =' >> $HGRCPATH svnadmin create svn-repo -echo % initial svn import -mkdir t -cd t -echo a > a -cd .. - svnpath=`pwd | fix_path` # SVN wants all paths to start with a slash. Unfortunately, # Windows ones don't. Handle that. @@ -26,90 +21,69 @@ svnpath='/'$svnpath fi -svnurl=file://$svnpath/svn-repo/trunk -svn import -m init t $svnurl | fix_path - -echo % update svn repository -svn co $svnurl t2 | fix_path -cd t2 -echo b >> a -echo b > b -svn add b -svn ci -m changea -cd .. - -echo % convert to hg once -hg convert $svnurl - -echo % update svn repository again -cd t2 -echo c >> a -echo c >> b -svn ci -m changeb -cd .. - -echo % test incremental conversion -hg convert $svnurl - -echo % test filemap -echo 'include b' > filemap -hg convert --filemap filemap $svnurl fmap -echo '[extensions]' >> $HGRCPATH -echo 'hgext.graphlog =' >> $HGRCPATH -hg glog -R fmap --template '#rev# #desc|firstline# files: #files#\n' - -######################################## - -echo "# now tests that it works with trunk/branches/tags layout" +echo "# now tests that it works with trunk/tags layout, but no branches yet" echo echo % initial svn import -mkdir projA -cd projA +mkdir projB +cd projB mkdir trunk -mkdir branches mkdir tags cd .. -svnurl=file://$svnpath/svn-repo/projA -svn import -m "init projA" projA $svnurl | fix_path +svnurl=file://$svnpath/svn-repo/projB +svn import -m "init projB" projB $svnurl | fix_path echo % update svn repository -svn co $svnurl/trunk A | fix_path -cd A +svn co $svnurl/trunk B | fix_path +cd B echo hello > letter.txt svn add letter.txt svn ci -m hello -echo world >> letter.txt +"$TESTDIR/svn-safe-append.py" world letter.txt svn ci -m world svn copy -m "tag v0.1" $svnurl/trunk $svnurl/tags/v0.1 -echo 'nice day today!' >> letter.txt +"$TESTDIR/svn-safe-append.py" 'nice day today!' letter.txt svn ci -m "nice day" cd .. echo % convert to hg once -hg convert $svnurl A-hg +hg convert $svnurl B-hg echo % update svn repository again -cd A -echo "see second letter" >> letter.txt +cd B +"$TESTDIR/svn-safe-append.py" "see second letter" letter.txt echo "nice to meet you" > letter2.txt svn add letter2.txt svn ci -m "second letter" svn copy -m "tag v0.2" $svnurl/trunk $svnurl/tags/v0.2 -echo "blah-blah-blah" >> letter2.txt +"$TESTDIR/svn-safe-append.py" "blah-blah-blah" letter2.txt svn ci -m "work in progress" cd .. +######################################## + echo % test incremental conversion -hg convert $svnurl A-hg +hg convert $svnurl B-hg -cd A-hg +cd B-hg hg glog --template '#rev# #desc|firstline# files: #files#\n' hg tags -q cd .. + +echo % test filemap +echo 'include letter2.txt' > filemap +hg convert --filemap filemap $svnurl/trunk fmap +hg glog -R fmap --template '#rev# #desc|firstline# files: #files#\n' + +echo % test stop revision +hg convert --rev 1 $svnurl/trunk stoprev +# Check convert_revision extra-records. +# This is also the only place testing more than one extra field +# in a revision. +hg --cwd stoprev tip --debug | grep extra | sed 's/=.*/=/'
--- a/tests/test-convert-svn-source.out Wed Apr 09 15:27:57 2008 -0700 +++ b/tests/test-convert-svn-source.out Wed Apr 09 15:28:30 2008 -0700 @@ -1,74 +1,30 @@ +# now tests that it works with trunk/tags layout, but no branches yet + % initial svn import -Adding t/a +Adding projB/trunk +Adding projB/tags Committed revision 1. % update svn repository -A t2/a Checked out revision 1. -A b -Sending a -Adding b -Transmitting file data .. +A letter.txt +Adding letter.txt +Transmitting file data . Committed revision 2. +Sending letter.txt +Transmitting file data . +Committed revision 3. + +Committed revision 4. +Sending letter.txt +Transmitting file data . +Committed revision 5. % convert to hg once -assuming destination trunk-hg -initializing destination trunk-hg repository -scanning source... -sorting... -converting... -1 init -0 changea -% update svn repository again -Sending a -Sending b -Transmitting file data .. -Committed revision 3. -% test incremental conversion -assuming destination trunk-hg -scanning source... -sorting... -converting... -0 changeb -% test filemap -initializing destination fmap repository +initializing destination B-hg repository scanning source... sorting... converting... -2 init -1 changea -0 changeb -o 1 changeb files: b -| -o 0 changea files: b - -# now tests that it works with trunk/branches/tags layout - -% initial svn import -Adding projA/trunk -Adding projA/branches -Adding projA/tags - -Committed revision 4. -% update svn repository -Checked out revision 4. -A letter.txt -Adding letter.txt -Transmitting file data . -Committed revision 5. -Sending letter.txt -Transmitting file data . -Committed revision 6. - -Committed revision 7. -Sending letter.txt -Transmitting file data . -Committed revision 8. -% convert to hg once -initializing destination A-hg repository -scanning source... -sorting... -converting... -3 init projA +3 init projB 2 hello 1 world 0 nice day @@ -78,12 +34,12 @@ Sending letter.txt Adding letter2.txt Transmitting file data .. -Committed revision 9. +Committed revision 6. -Committed revision 10. +Committed revision 7. Sending letter2.txt Transmitting file data . -Committed revision 11. +Committed revision 8. % test incremental conversion scanning source... sorting... @@ -105,8 +61,31 @@ | o 1 hello files: letter.txt | -o 0 init projA files: +o 0 init projB files: tip v0.2 v0.1 +% test filemap +initializing destination fmap repository +scanning source... +sorting... +converting... +5 init projB +4 hello +3 world +2 nice day +1 second letter +0 work in progress +o 1 work in progress files: letter2.txt +| +o 0 second letter files: letter2.txt + +% test stop revision +initializing destination stoprev repository +scanning source... +sorting... +converting... +0 init projB +extra: branch= +extra: convert_revision=
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tests/test-convert-svn-startrev Wed Apr 09 15:28:30 2008 -0700 @@ -0,0 +1,81 @@ +#!/bin/sh + +"$TESTDIR/hghave" svn svn-bindings || exit 80 + +fix_path() +{ + tr '\\' / +} + +echo "[extensions]" >> $HGRCPATH +echo "convert = " >> $HGRCPATH +echo "hgext.graphlog =" >> $HGRCPATH + +svnadmin create svn-repo + +svnpath=`pwd | fix_path` +# SVN wants all paths to start with a slash. Unfortunately, +# Windows ones don't. Handle that. +expr $svnpath : "\/" > /dev/null +if [ $? -ne 0 ]; then + svnpath='/'$svnpath +fi + +echo % initial svn import +mkdir projA +cd projA +mkdir trunk +mkdir branches +mkdir tags +cd .. + +svnurl=file://$svnpath/svn-repo/projA +svn import -m "init projA" projA $svnurl | fix_path + +echo % update svn repository +svn co $svnurl A | fix_path +cd A +echo a > trunk/a +echo b > trunk/b +svn add trunk/a trunk/b +svn ci -m createab +svn rm trunk/b +svn ci -m removeb +svn up +"$TESTDIR/svn-safe-append.py" a trunk/a +svn ci -m changeaa + +echo % branch +svn up +svn copy trunk branches/branch1 +"$TESTDIR/svn-safe-append.py" a branches/branch1/a +svn ci -m "branch, changeaaa" + +"$TESTDIR/svn-safe-append.py" a branches/branch1/a +echo c > branches/branch1/c +svn add branches/branch1/c +svn ci -m "addc,changeaaaa" +svn up +cd .. + +convert() +{ + startrev=$1 + repopath=A-r$startrev-hg + hg convert --config convert.svn.startrev=$startrev \ + --config convert.svn.trunk=branches/branch1 \ + --config convert.svn.branches=" " \ + --config convert.svn.tags= \ + --datesort $svnurl $repopath + hg -R $repopath glog --template '#rev# #desc|firstline# files: #files#\n' + echo +} + +echo % convert before branching point +convert 3 +echo % convert before branching point +convert 4 +echo % convert at branching point +convert 5 +echo % convert last revision only +convert 6
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tests/test-convert-svn-startrev.out Wed Apr 09 15:28:30 2008 -0700 @@ -0,0 +1,92 @@ +% initial svn import +Adding projA/trunk +Adding projA/branches +Adding projA/tags + +Committed revision 1. +% update svn repository +A A/trunk +A A/branches +A A/tags +Checked out revision 1. +A trunk/a +A trunk/b +Adding trunk/a +Adding trunk/b +Transmitting file data .. +Committed revision 2. +D trunk/b +Deleting trunk/b + +Committed revision 3. +At revision 3. +Sending trunk/a +Transmitting file data . +Committed revision 4. +% branch +At revision 4. +A branches/branch1 +Adding branches/branch1 +Sending branches/branch1/a +Transmitting file data . +Committed revision 5. +A branches/branch1/c +Sending branches/branch1/a +Adding branches/branch1/c +Transmitting file data .. +Committed revision 6. +At revision 6. +% convert before branching point +initializing destination A-r3-hg repository +scanning source... +sorting... +converting... +3 removeb +2 changeaa +1 branch, changeaaa +0 addc,changeaaaa +o 3 addc,changeaaaa files: a c +| +o 2 branch, changeaaa files: a +| +o 1 changeaa files: a +| +o 0 removeb files: a + + +% convert before branching point +initializing destination A-r4-hg repository +scanning source... +sorting... +converting... +2 changeaa +1 branch, changeaaa +0 addc,changeaaaa +o 2 addc,changeaaaa files: a c +| +o 1 branch, changeaaa files: a +| +o 0 changeaa files: a + + +% convert at branching point +initializing destination A-r5-hg repository +scanning source... +sorting... +converting... +1 branch, changeaaa +0 addc,changeaaaa +o 1 addc,changeaaaa files: a c +| +o 0 branch, changeaaa files: a + + +% convert last revision only +initializing destination A-r6-hg repository +scanning source... +sorting... +converting... +0 addc,changeaaaa +o 0 addc,changeaaaa files: a c + +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tests/test-convert-svn-tags Wed Apr 09 15:28:30 2008 -0700 @@ -0,0 +1,76 @@ +#!/bin/sh + +"$TESTDIR/hghave" svn svn-bindings || exit 80 + +fix_path() +{ + tr '\\' / +} + +echo "[extensions]" >> $HGRCPATH +echo "convert = " >> $HGRCPATH +echo "hgext.graphlog =" >> $HGRCPATH + +svnadmin create svn-repo + +svnpath=`pwd | fix_path` +# SVN wants all paths to start with a slash. Unfortunately, +# Windows ones don't. Handle that. +expr $svnpath : "\/" > /dev/null +if [ $? -ne 0 ]; then + svnpath='/'$svnpath +fi + +echo % initial svn import +mkdir projA +cd projA +mkdir trunk +mkdir branches +mkdir tags +mkdir unrelated +cd .. + +svnurl=file://$svnpath/svn-repo/projA +svn import -m "init projA" projA $svnurl | fix_path + +echo % update svn repository +svn co $svnurl A | fix_path +cd A +echo a > trunk/a +svn add trunk/a +svn ci -m adda +"$TESTDIR/svn-safe-append.py" a trunk/a +svn ci -m changea +"$TESTDIR/svn-safe-append.py" a trunk/a +svn ci -m changea2 +# Add an unrelated commit to test that tags are bound to the +# correct "from" revision and not a dummy one +"$TESTDIR/svn-safe-append.py" a unrelated/dummy +svn add unrelated/dummy +svn ci -m unrelatedchange +echo % tag current revision +svn up +svn copy trunk tags/trunk.v1 +svn copy trunk tags/trunk.badtag +svn ci -m "tagging trunk.v1 trunk.badtag" +"$TESTDIR/svn-safe-append.py" a trunk/a +svn ci -m changea3 +echo % fix the bad tag +# trunk.badtag should not show in converted tags +svn up +svn mv tags/trunk.badtag tags/trunk.goodtag +svn ci -m "fix trunk.badtag" +cd .. + +echo % convert +hg convert --datesort $svnurl A-hg + +cd A-hg +hg glog --template '#rev# #desc|firstline# tags: #tags#\n' +hg tags -q +cd .. + +echo % convert without tags +hg convert --datesort --config convert.svn.tags= $svnurl A-notags-hg +hg -R A-notags-hg tags -q +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tests/test-convert-svn-tags.out Wed Apr 09 15:28:30 2008 -0700 @@ -0,0 +1,84 @@ +% initial svn import +Adding projA/trunk +Adding projA/unrelated +Adding projA/branches +Adding projA/tags + +Committed revision 1. +% update svn repository +A A/trunk +A A/unrelated +A A/branches +A A/tags +Checked out revision 1. +A trunk/a +Adding trunk/a +Transmitting file data . +Committed revision 2. +Sending trunk/a +Transmitting file data . +Committed revision 3. +Sending trunk/a +Transmitting file data . +Committed revision 4. +A unrelated/dummy +Adding unrelated/dummy +Transmitting file data . +Committed revision 5. +% tag current revision +At revision 5. +A tags/trunk.v1 +A tags/trunk.badtag +Adding tags/trunk.badtag +Adding tags/trunk.v1 + +Committed revision 6. +Sending trunk/a +Transmitting file data . +Committed revision 7. +% fix the bad tag +At revision 7. +A tags/trunk.goodtag +D tags/trunk.badtag/a +D tags/trunk.badtag +Deleting tags/trunk.badtag +Adding tags/trunk.goodtag + +Committed revision 8. +% convert +initializing destination A-hg repository +scanning source... +sorting... +converting... +4 init projA +3 adda +2 changea +1 changea2 +0 changea3 +updating tags +o 5 update tags tags: tip +| +o 4 changea3 tags: +| +o 3 changea2 tags: trunk.v1 trunk.goodtag +| +o 2 changea tags: +| +o 1 adda tags: +| +o 0 init projA tags: + +tip +trunk.v1 +trunk.goodtag +% convert without tags +initializing destination A-notags-hg repository +scanning source... +sorting... +converting... +4 init projA +3 adda +2 changea +1 changea2 +0 changea3 +tip
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tests/test-convert-tla Wed Apr 09 15:28:30 2008 -0700 @@ -0,0 +1,72 @@ +#!/bin/sh + +"$TESTDIR/hghave" tla || exit 80 + +mkdir do_not_use_HOME_tla +cd do_not_use_HOME_tla +HOME=`pwd`; export HOME +cd .. +tla my-id "mercurial <mercurial@selenic.com>" + +echo "[extensions]" >> $HGRCPATH +echo "convert=" >> $HGRCPATH +echo 'hgext.graphlog =' >> $HGRCPATH + +echo % create tla archive +tla make-archive tla@mercurial--convert `pwd`/hg-test-convert-tla + +echo % initialize tla repo +mkdir tla-repo +cd tla-repo/ +tla init-tree tla@mercurial--convert/tla--test--0 +tla import + +echo % create initial files +echo 'this is a file' > a +tla add a +mkdir src +tla add src +cd src +dd count=1 if=/dev/zero of=b > /dev/null 2> /dev/null +tla add b +tla commit -s "added a file, src and src/b (binary)" + +echo % create link file and modify a +ln -s ../a a-link +tla add a-link +echo 'this a modification to a' >> ../a +tla commit -s "added link to a and modify a" + +echo % create second link and modify b +ln -s ../a a-link-2 +tla add a-link-2 +dd count=1 seek=1 if=/dev/zero of=b > /dev/null 2> /dev/null +tla commit -s "added second link and modify b" + +echo % b file to link and a-link-2 to regular file +rm -f a-link-2 +echo 'this is now a regular file' > a-link-2 +ln -sf ../a b +tla commit -s "file to link and link to file test" + +echo % move a-link-2 file and src directory +cd .. +tla mv src/a-link-2 c +tla mv src test +tla commit -s "move and rename a-link-2 file and src directory" + +cd .. + +echo % converting tla repo to Mercurial +hg convert tla-repo tla-repo-hg + +tla register-archive -d tla@mercurial--convert + +glog() +{ + hg glog --template '#rev# "#desc|firstline#" files: #files#\n' "$@" +} + +echo % show graph log +glog -R tla-repo-hg +hg -R tla-repo-hg manifest --debug
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tests/test-convert-tla.out Wed Apr 09 15:28:30 2008 -0700 @@ -0,0 +1,72 @@ +% create tla archive +% initialize tla repo +* creating version tla@mercurial--convert/tla--test--0 +* imported tla@mercurial--convert/tla--test--0 +% create initial files +A/ .arch-ids +A/ src +A/ src/.arch-ids +A .arch-ids/a.id +A a +A src/.arch-ids/=id +A src/.arch-ids/b.id +A src/b +* update pristine tree (tla@mercurial--convert/tla--test--0--base-0 => tla--test--0--patch-1) +* committed tla@mercurial--convert/tla--test--0--patch-1 +% create link file and modify a +A src/.arch-ids/a-link.id +A src/a-link +M a +* update pristine tree (tla@mercurial--convert/tla--test--0--patch-1 => tla--test--0--patch-2) +* committed tla@mercurial--convert/tla--test--0--patch-2 +% create second link and modify b +A src/.arch-ids/a-link-2.id +A src/a-link-2 +Mb src/b +* update pristine tree (tla@mercurial--convert/tla--test--0--patch-2 => tla--test--0--patch-3) +* committed tla@mercurial--convert/tla--test--0--patch-3 +% b file to link and a-link-2 to regular file +fl src/b +lf src/a-link-2 +* update pristine tree (tla@mercurial--convert/tla--test--0--patch-3 => tla--test--0--patch-4) +* committed tla@mercurial--convert/tla--test--0--patch-4 +% move a-link-2 file and src directory +D/ src/.arch-ids +A/ test/.arch-ids +/> src test +=> src/.arch-ids/a-link-2.id .arch-ids/c.id +=> src/a-link-2 c +=> src/.arch-ids/=id test/.arch-ids/=id +=> src/.arch-ids/a-link.id test/.arch-ids/a-link.id +=> src/.arch-ids/b.id test/.arch-ids/b.id +* update pristine tree (tla@mercurial--convert/tla--test--0--patch-4 => tla--test--0--patch-5) +* committed tla@mercurial--convert/tla--test--0--patch-5 +% converting tla repo to Mercurial +initializing destination tla-repo-hg repository +analyzing tree version tla@mercurial--convert/tla--test--0... +scanning source... +sorting... +converting... +5 initial import +4 added a file, src and src/b (binary) +3 added link to a and modify a +2 added second link and modify b +1 file to link and link to file test +0 move and rename a-link-2 file and src directory +% show graph log +o 5 "move and rename a-link-2 file and src directory" files: c src/a-link src/a-link-2 src/b test/a-link test/b +| +o 4 "file to link and link to file test" files: src/a-link-2 src/b +| +o 3 "added second link and modify b" files: src/a-link-2 src/b +| +o 2 "added link to a and modify a" files: a src/a-link +| +o 1 "added a file, src and src/b (binary)" files: a src/b +| +o 0 "initial import" files: + +c4072c4b72e1cabace081888efa148ee80ca3cbb 644 a +e3207be798aaf87a444a62903621edab4ddc1fb6 644 c +1f6b5bb93f1da278ef1fead1e4740a03d8802e9f 644 @ test/a-link +1f6b5bb93f1da278ef1fead1e4740a03d8802e9f 644 @ test/b
--- a/tests/test-convert.out Wed Apr 09 15:27:57 2008 -0700 +++ b/tests/test-convert.out Wed Apr 09 15:28:30 2008 -0700 @@ -1,4 +1,4 @@ -hg convert [OPTION]... SOURCE [DEST [MAPFILE]] +hg convert [OPTION]... SOURCE [DEST [REVMAP]] Convert a foreign SCM repository to a Mercurial one. @@ -8,6 +8,8 @@ - Darcs - git - Subversion + - Monotone + - GNU Arch Accepted destination formats: - Mercurial @@ -21,8 +23,8 @@ basename of the source with '-hg' appended. If the destination repository doesn't exist, it will be created. - If <MAPFILE> isn't given, it will be put in a default location - (<dest>/.hg/shamap by default). The <MAPFILE> is a simple text + 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 for that revision, like so: <source ID> <destination ID> @@ -46,7 +48,7 @@ exclude path/to/file rename from/file to/file - + The 'include' directive causes a file, or all files under a directory, to be included in the destination repository, and the exclusion of all other files and dirs not explicitely included. @@ -55,23 +57,60 @@ subdirectory into the root of the repository, use '.' as the path to rename to. - Back end options: + The splicemap is a file that allows insertion of synthetic + history, letting you specify the parents of a revision. This is + useful if you want to e.g. give a Subversion merge two parents, or + graft two disconnected series of history together. Each entry + contains a key, followed by a space, followed by one or two + values, separated by spaces. The key is the revision ID in the + source revision control system whose parents should be modified + (same format as a key in .hg/shamap). The values are the revision + IDs (in either the source or destination revision control system) + that should be used as the new parents for that node. + + Mercurial Source + ----------------- + + --config convert.hg.saverev=True (boolean) + allow target to preserve source revision ID + + Subversion Source + ----------------- + + Subversion source detects classical trunk/branches/tags layouts. + By default, the supplied "svn://repo/path/" source URL is + converted as a single branch. If "svn://repo/path/trunk" exists + it replaces the default branch. If "svn://repo/path/branches" + exists, its subdirectories are listed as possible branches. If + "svn://repo/path/tags" exists, it is looked for tags referencing + converted branches. Default "trunk", "branches" and "tags" values + can be overriden with following options. Set them to paths + relative to the source URL, or leave them blank to disable + autodetection. + + --config convert.svn.branches=branches (directory name) + specify the directory containing branches + --config convert.svn.tags=tags (directory name) + specify the directory containing tags + --config convert.svn.trunk=trunk (directory name) + specify the name of the trunk branch + + Source history can be retrieved starting at a specific revision, + instead of being integrally converted. Only single branch + conversions are supported. + + --config convert.svn.startrev=0 (svn revision number) + specify start Subversion revision. + + Mercurial Destination + --------------------- --config convert.hg.clonebranches=False (boolean) - hg target: XXX not documented - --config convert.hg.saverev=True (boolean) - hg source: allow target to preserve source revision ID + dispatch source branches in separate clones. --config convert.hg.tagsbranch=default (branch name) - hg target: XXX not documented + tag revisions branch name --config convert.hg.usebranchnames=True (boolean) - hg target: preserve branch names - - --config convert.svn.branches=branches (directory name) - svn source: specify the directory containing branches - --config convert.svn.tags=tags (directory name) - svn source: specify the directory containing tags - --config convert.svn.trunk=trunk (directory name) - svn source: specify the name of the trunk branch + preserve branch names options: @@ -80,6 +119,7 @@ --filemap remap file names using contents of file -r --rev import up to target revision REV -s --source-type source repository type + --splicemap splice synthesized history into place --datesort try to sort changesets by date use "hg -v help convert" to show global options @@ -112,3 +152,8 @@ 2 c 1 d 0 e +% test pre and post conversion actions +run hg source pre-conversion action +run hg sink pre-conversion action +run hg sink post-conversion action +run hg source post-conversion action
--- a/tests/test-copy-move-merge.out Wed Apr 09 15:27:57 2008 -0700 +++ b/tests/test-copy-move-merge.out Wed Apr 09 15:28:30 2008 -0700 @@ -14,11 +14,15 @@ a: remote moved to b -> m copying a to b copying a to c +picked tool 'internal:merge' for a (binary False symlink False) merging a and b my a@fb3948d97f07+ other b@40da226db0f0 ancestor a@583c7b748052 + premerge successful removing a +picked tool 'internal:merge' for a (binary False symlink False) merging a and c my a@fb3948d97f07+ other c@40da226db0f0 ancestor a@583c7b748052 + premerge successful 0 files updated, 2 files merged, 0 files removed, 0 files unresolved (branch merge, don't forget to commit) -- b --
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tests/test-custom-filters Wed Apr 09 15:28:30 2008 -0700 @@ -0,0 +1,60 @@ +#!/bin/sh + +hg init + +cat > .hg/hgrc <<EOF +[extensions] +prefixfilter = prefix.py +[encode] +*.txt = stripprefix: Copyright 2046, The Masters +[decode] +*.txt = insertprefix: Copyright 2046, The Masters +EOF + +cat > prefix.py <<EOF +from mercurial import util +def stripprefix(s, cmd, filename, **kwargs): + header = '%s\n' % cmd + if s[:len(header)] != header: + raise util.Abort('missing header "%s" in %s' % (cmd, filename)) + return s[len(header):] +def insertprefix(s, cmd): + return '%s\n%s' % (cmd, s) +def reposetup(ui, repo): + repo.adddatafilter('stripprefix:', stripprefix) + repo.adddatafilter('insertprefix:', insertprefix) +EOF + +cat > .hgignore <<EOF +.hgignore +prefix.py +prefix.pyc +EOF + +cat > stuff.txt <<EOF +Copyright 2046, The Masters +Some stuff to ponder very carefully. +EOF +hg add stuff.txt +hg ci -m stuff + +echo '% Repository data:' +hg cat stuff.txt + +echo '% Fresh checkout:' +rm stuff.txt +hg up +cat stuff.txt +echo >> stuff.txt <<EOF +Very very carefully. +EOF +hg stat + +cat > morestuff.txt <<EOF +Unauthorized material subject to destruction. +EOF + +echo '% Problem encoding:' +hg add morestuff.txt +hg ci -m morestuff +hg stat
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tests/test-custom-filters.out Wed Apr 09 15:28:30 2008 -0700 @@ -0,0 +1,11 @@ +% Repository data: +Some stuff to ponder very carefully. +% Fresh checkout: +1 files updated, 0 files merged, 0 files removed, 0 files unresolved +Copyright 2046, The Masters +Some stuff to ponder very carefully. +M stuff.txt +% Problem encoding: +abort: missing header "Copyright 2046, The Masters" in morestuff.txt +M stuff.txt +A morestuff.txt
--- a/tests/test-debugcomplete.out Wed Apr 09 15:27:57 2008 -0700 +++ b/tests/test-debugcomplete.out Wed Apr 09 15:28:30 2008 -0700 @@ -4,6 +4,7 @@ annotate archive backout +bisect branch branches bundle @@ -62,6 +63,7 @@ debugconfig debugdata debugdate +debugfsinfo debugindex debugindexdot debuginstall @@ -127,6 +129,7 @@ --noninteractive --pid-file --port +--prefix --profile --quiet --repository
--- a/tests/test-debugindexdot.out Wed Apr 09 15:27:57 2008 -0700 +++ b/tests/test-debugindexdot.out Wed Apr 09 15:28:30 2008 -0700 @@ -1,3 +1,4 @@ +created new head digraph G { -1 -> 0 0 -> 1
--- a/tests/test-default-push.out Wed Apr 09 15:27:57 2008 -0700 +++ b/tests/test-default-push.out Wed Apr 09 15:28:30 2008 -0700 @@ -1,5 +1,7 @@ adding a +updating working directory 1 files updated, 0 files merged, 0 files removed, 0 files unresolved +updating working directory 1 files updated, 0 files merged, 0 files removed, 0 files unresolved % push should push to default when default-push not set pushing
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tests/test-diff-copy-depth Wed Apr 09 15:28:30 2008 -0700 @@ -0,0 +1,31 @@ +#!/bin/sh + +for i in aaa zzz; do + hg init t + cd t + + echo "-- With $i" + + touch file + hg add file + hg ci -m "Add" + + hg cp file $i + hg ci -m "a -> $i" + + hg cp $i other-file + echo "different" >> $i + hg ci -m "$i -> other-file" + + hg cp other-file somename + + echo "Status": + hg st -C + echo + echo "Diff:" + hg diff -g + echo + + cd .. + rm -rf t +done
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tests/test-diff-copy-depth.out Wed Apr 09 15:28:30 2008 -0700 @@ -0,0 +1,20 @@ +-- With aaa +Status: +A somename + other-file + +Diff: +diff --git a/other-file b/somename +copy from other-file +copy to somename + +-- With zzz +Status: +A somename + other-file + +Diff: +diff --git a/other-file b/somename +copy from other-file +copy to somename +
--- a/tests/test-diff-hashes Wed Apr 09 15:27:57 2008 -0700 +++ b/tests/test-diff-hashes Wed Apr 09 15:28:30 2008 -0700 @@ -2,6 +2,7 @@ hg init a cd a +hg diff not found echo bar > foo hg add foo hg ci -m 'add foo' -d '1000000 0'
--- a/tests/test-diff-hashes.out Wed Apr 09 15:27:57 2008 -0700 +++ b/tests/test-diff-hashes.out Wed Apr 09 15:28:30 2008 -0700 @@ -1,7 +1,9 @@ +found: No such file or directory +not: No such file or directory quiet: --- a/foo Mon Jan 12 13:46:40 1970 +0000 +++ b/foo Mon Jan 12 13:46:41 1970 +0000 -@@ -1,1 +1,1 @@ bar +@@ -1,1 +1,1 @@ -bar +foobar @@ -9,7 +11,7 @@ diff -r 74de3f1392e2 -r b8b5f023a6ad foo --- a/foo Mon Jan 12 13:46:40 1970 +0000 +++ b/foo Mon Jan 12 13:46:41 1970 +0000 -@@ -1,1 +1,1 @@ bar +@@ -1,1 +1,1 @@ -bar +foobar @@ -17,7 +19,7 @@ diff -r 74de3f1392e2 -r b8b5f023a6ad foo --- a/foo Mon Jan 12 13:46:40 1970 +0000 +++ b/foo Mon Jan 12 13:46:41 1970 +0000 -@@ -1,1 +1,1 @@ bar +@@ -1,1 +1,1 @@ -bar +foobar @@ -25,7 +27,7 @@ diff -r 74de3f1392e2d67856fb155963441f2610494e1a -r b8b5f023a6ad77fc378bd95cf3fa00cd1414d107 foo --- a/foo Mon Jan 12 13:46:40 1970 +0000 +++ b/foo Mon Jan 12 13:46:41 1970 +0000 -@@ -1,1 +1,1 @@ bar +@@ -1,1 +1,1 @@ -bar +foobar
--- a/tests/test-diff-ignore-whitespace.out Wed Apr 09 15:27:57 2008 -0700 +++ b/tests/test-diff-ignore-whitespace.out Wed Apr 09 15:28:30 2008 -0700 @@ -4,7 +4,7 @@ diff -r 540c40a65b78 foo --- a/foo +++ b/foo -@@ -1,2 +1,5 @@ hello world +@@ -1,2 +1,5 @@ + hello world + @@ -14,7 +14,7 @@ diff -r 540c40a65b78 foo --- a/foo +++ b/foo -@@ -1,2 +1,5 @@ hello world +@@ -1,2 +1,5 @@ + hello world + @@ -28,7 +28,7 @@ diff -r 540c40a65b78 foo --- a/foo +++ b/foo -@@ -1,2 +1,2 @@ hello world +@@ -1,2 +1,2 @@ -hello world + hello world goodbye world @@ -36,7 +36,7 @@ diff -r 540c40a65b78 foo --- a/foo +++ b/foo -@@ -1,2 +1,2 @@ hello world +@@ -1,2 +1,2 @@ -hello world + hello world goodbye world @@ -44,7 +44,7 @@ diff -r 540c40a65b78 foo --- a/foo +++ b/foo -@@ -1,2 +1,2 @@ hello world +@@ -1,2 +1,2 @@ -hello world + hello world goodbye world @@ -52,7 +52,7 @@ diff -r 540c40a65b78 foo --- a/foo +++ b/foo -@@ -1,2 +1,2 @@ hello world +@@ -1,2 +1,2 @@ -hello world + hello world goodbye world @@ -61,7 +61,7 @@ diff -r 540c40a65b78 foo --- a/foo +++ b/foo -@@ -1,2 +1,2 @@ hello world +@@ -1,2 +1,2 @@ -hello world +hello world goodbye world @@ -69,7 +69,7 @@ diff -r 540c40a65b78 foo --- a/foo +++ b/foo -@@ -1,2 +1,2 @@ hello world +@@ -1,2 +1,2 @@ -hello world +hello world goodbye world @@ -81,7 +81,7 @@ diff -r 540c40a65b78 foo --- a/foo +++ b/foo -@@ -1,2 +1,2 @@ hello world +@@ -1,2 +1,2 @@ hello world -goodbye world +good bye world @@ -89,7 +89,7 @@ diff -r 540c40a65b78 foo --- a/foo +++ b/foo -@@ -1,2 +1,2 @@ hello world +@@ -1,2 +1,2 @@ hello world -goodbye world +good bye world @@ -97,7 +97,7 @@ diff -r 540c40a65b78 foo --- a/foo +++ b/foo -@@ -1,2 +1,2 @@ hello world +@@ -1,2 +1,2 @@ hello world -goodbye world +good bye world @@ -105,7 +105,7 @@ diff -r 540c40a65b78 foo --- a/foo +++ b/foo -@@ -1,2 +1,2 @@ hello world +@@ -1,2 +1,2 @@ hello world -goodbye world +good bye world @@ -114,7 +114,7 @@ diff -r 540c40a65b78 foo --- a/foo +++ b/foo -@@ -1,2 +1,2 @@ hello world +@@ -1,2 +1,2 @@ hello world -goodbye world +goodbye world @@ -122,7 +122,7 @@ diff -r 540c40a65b78 foo --- a/foo +++ b/foo -@@ -1,2 +1,2 @@ hello world +@@ -1,2 +1,2 @@ hello world -goodbye world +goodbye world @@ -134,7 +134,7 @@ diff -r 540c40a65b78 foo --- a/foo +++ b/foo -@@ -1,2 +1,3 @@ hello world +@@ -1,2 +1,3 @@ hello world + goodbye world @@ -142,7 +142,7 @@ diff -r 540c40a65b78 foo --- a/foo +++ b/foo -@@ -1,2 +1,3 @@ hello world +@@ -1,2 +1,3 @@ hello world + goodbye world @@ -150,7 +150,7 @@ diff -r 540c40a65b78 foo --- a/foo +++ b/foo -@@ -1,2 +1,3 @@ hello world +@@ -1,2 +1,3 @@ hello world + goodbye world @@ -160,7 +160,7 @@ diff -r 540c40a65b78 foo --- a/foo +++ b/foo -@@ -1,2 +1,3 @@ hello world +@@ -1,2 +1,3 @@ -hello world -goodbye world +hello world @@ -170,7 +170,7 @@ diff -r 540c40a65b78 foo --- a/foo +++ b/foo -@@ -1,2 +1,3 @@ hello world +@@ -1,2 +1,3 @@ -hello world -goodbye world +hello world @@ -180,7 +180,7 @@ diff -r 540c40a65b78 foo --- a/foo +++ b/foo -@@ -1,2 +1,3 @@ hello world +@@ -1,2 +1,3 @@ -hello world -goodbye world +hello world @@ -192,7 +192,7 @@ diff -r 540c40a65b78 foo --- a/foo +++ b/foo -@@ -1,2 +1,2 @@ hello world +@@ -1,2 +1,2 @@ -hello world -goodbye world +helloworld @@ -201,7 +201,7 @@ diff -r 540c40a65b78 foo --- a/foo +++ b/foo -@@ -1,2 +1,2 @@ hello world +@@ -1,2 +1,2 @@ -hello world -goodbye world +helloworld @@ -210,7 +210,7 @@ diff -r 540c40a65b78 foo --- a/foo +++ b/foo -@@ -1,2 +1,2 @@ hello world +@@ -1,2 +1,2 @@ -hello world -goodbye world +helloworld @@ -219,7 +219,7 @@ diff -r 540c40a65b78 foo --- a/foo +++ b/foo -@@ -1,2 +1,2 @@ hello world +@@ -1,2 +1,2 @@ -hello world -goodbye world +helloworld @@ -230,7 +230,7 @@ diff -r 540c40a65b78 foo --- a/foo +++ b/foo -@@ -1,2 +1,5 @@ hello world +@@ -1,2 +1,5 @@ -hello world -goodbye world +helloworld @@ -242,7 +242,7 @@ diff -r 540c40a65b78 foo --- a/foo +++ b/foo -@@ -1,2 +1,5 @@ hello world +@@ -1,2 +1,5 @@ -hello world -goodbye world +helloworld @@ -254,7 +254,7 @@ diff -r 540c40a65b78 foo --- a/foo +++ b/foo -@@ -1,2 +1,5 @@ hello world +@@ -1,2 +1,5 @@ -hello world -goodbye world +helloworld @@ -266,7 +266,7 @@ diff -r 540c40a65b78 foo --- a/foo +++ b/foo -@@ -1,2 +1,5 @@ hello world +@@ -1,2 +1,5 @@ -hello world -goodbye world +helloworld @@ -278,7 +278,7 @@ diff -r 540c40a65b78 foo --- a/foo +++ b/foo -@@ -1,2 +1,5 @@ hello world +@@ -1,2 +1,5 @@ -hello world -goodbye world +helloworld
--- a/tests/test-diff-newlines.out Wed Apr 09 15:27:57 2008 -0700 +++ b/tests/test-diff-newlines.out Wed Apr 09 15:28:30 2008 -0700 @@ -2,7 +2,7 @@ diff -r 107ba6f817b5 -r 310ce7989cdc a --- a/a Thu Jan 01 00:00:01 1970 +0000 +++ b/a Thu Jan 01 00:00:02 1970 +0000 -@@ -1,2 +1,3 @@ confuse str.splitlines +@@ -1,2 +1,3 @@ confuse str.splitlines embedded newline +clean diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tests/test-diff-unified Wed Apr 09 15:28:30 2008 -0700 @@ -0,0 +1,49 @@ +#!/bin/sh + +hg init repo +cd repo +cat > a <<EOF +c +c +a +a +b +a +a +c +c +EOF +hg ci -Am adda +cat > a <<EOF +c +c +a +a +dd +a +a +c +c +EOF + +echo '% default context' +hg diff --nodates + +echo '% invalid --unified' +hg diff --nodates -U foo + +echo '% --unified=2' +hg diff --nodates -U 2 + +echo '% diff.unified=2' +hg --config diff.unified=2 diff --nodates + +echo '% diff.unified=2 --unified=1' +hg diff --nodates -U 1 + +echo '% invalid diff.unified' +hg --config diff.unified=foo diff --nodates + +exit 0 + +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tests/test-diff-unified.out Wed Apr 09 15:28:30 2008 -0700 @@ -0,0 +1,49 @@ +adding a +% default context +diff -r cf9f4ba66af2 a +--- a/a ++++ b/a +@@ -2,7 +2,7 @@ + c + a + a +-b ++dd + a + a + c +% invalid --unified +abort: diff context lines count must be an integer, not 'foo' +% --unified=2 +diff -r cf9f4ba66af2 a +--- a/a ++++ b/a +@@ -3,5 +3,5 @@ + a + a +-b ++dd + a + a +% diff.unified=2 +diff -r cf9f4ba66af2 a +--- a/a ++++ b/a +@@ -3,5 +3,5 @@ + a + a +-b ++dd + a + a +% diff.unified=2 --unified=1 +diff -r cf9f4ba66af2 a +--- a/a ++++ b/a +@@ -4,3 +4,3 @@ + a +-b ++dd + a +% invalid diff.unified +abort: diff context lines count must be an integer, not 'foo'
--- a/tests/test-diffdir.out Wed Apr 09 15:27:57 2008 -0700 +++ b/tests/test-diffdir.out Wed Apr 09 15:28:30 2008 -0700 @@ -18,5 +18,5 @@ +++ b/b @@ -0,0 +1,1 @@ +123 -abort: Ambiguous identifier! -abort: Ambiguous identifier! +abort: 00changelog.i@: ambiguous identifier! +abort: 00changelog.i@: ambiguous identifier!
--- a/tests/test-dispatch.out Wed Apr 09 15:27:57 2008 -0700 +++ b/tests/test-dispatch.out Wed Apr 09 15:28:30 2008 -0700 @@ -10,7 +10,7 @@ or tip if no revision is checked out. Output may be to a file, in which case the name of the file is - given using a format string. The formatting rules are the same as + given using a format string. The formatting rules are the same as for the export command, with the following additions: %s basename of file being printed @@ -21,6 +21,7 @@ -o --output print output to file with formatted name -r --rev print the given revision + --decode apply any matching decode filter -I --include include names matching the given patterns -X --exclude exclude names matching the given patterns
--- a/tests/test-dispatch.py.out Wed Apr 09 15:27:57 2008 -0700 +++ b/tests/test-dispatch.py.out Wed Apr 09 15:28:30 2008 -0700 @@ -1,7 +1,7 @@ running: init test1 result: None running: add foo -result: None +result: 0 running: commit -m commit1 -d 2000-01-01 foo result: None running: commit -m commit2 -d 2000-01-02 foo
--- a/tests/test-double-merge.out Wed Apr 09 15:27:57 2008 -0700 +++ b/tests/test-double-merge.out Wed Apr 09 15:28:30 2008 -0700 @@ -1,3 +1,4 @@ +created new head resolving manifests overwrite None partial False ancestor 310fd17130da local 2092631ce82b+ remote 7731dad1c2b9 @@ -10,10 +11,14 @@ foo: versions differ -> m foo: remote copied to bar -> m copying foo to bar +picked tool 'internal:merge' for foo (binary False symlink False) merging foo and bar my foo@2092631ce82b+ other bar@7731dad1c2b9 ancestor foo@310fd17130da + premerge successful +picked tool 'internal:merge' for foo (binary False symlink False) merging foo my foo@2092631ce82b+ other foo@7731dad1c2b9 ancestor foo@310fd17130da + premerge successful 0 files updated, 2 files merged, 0 files removed, 0 files unresolved (branch merge, don't forget to commit) -- foo --
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tests/test-dumprevlog Wed Apr 09 15:28:30 2008 -0700 @@ -0,0 +1,47 @@ +#!/bin/sh + +CONTRIBDIR=$TESTDIR/../contrib + +mkdir repo-a +cd repo-a +hg init + +echo this is file a > a +hg add a +hg commit -m first -d '0 0' + +echo adding to file a >> a +hg commit -m second -d '0 0' + +echo adding more to file a >> a +hg commit -m third -d '0 0' + +hg verify + +echo dumping revlog of file a to stdout: +python $CONTRIBDIR/dumprevlog .hg/store/data/a.i +echo dumprevlog done + +# dump all revlogs to file repo.dump +find .hg/store/ -name "*.i" | xargs python $CONTRIBDIR/dumprevlog > ../repo.dump + +cd .. + +mkdir repo-b +cd repo-b +hg init + +echo undumping: +python $CONTRIBDIR/undumprevlog < ../repo.dump +echo undumping done + +hg verify + +cd .. + +echo comparing repos: +hg -R repo-b incoming repo-a +hg -R repo-a incoming repo-b +echo comparing done + +exit 0
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tests/test-dumprevlog.out Wed Apr 09 15:28:30 2008 -0700 @@ -0,0 +1,53 @@ +checking changesets +checking manifests +crosschecking files in changesets and manifests +checking files +1 files, 3 changesets, 3 total revisions +dumping revlog of file a to stdout: +file: .hg/store/data/a.i +node: 183d2312b35066fb6b3b449b84efc370d50993d0 +linkrev: 0 +parents: 0000000000000000000000000000000000000000 0000000000000000000000000000000000000000 +length: 15 +-start- +this is file a + +-end- +node: b1047953b6e6b633c0d8197eaa5116fbdfd3095b +linkrev: 1 +parents: 183d2312b35066fb6b3b449b84efc370d50993d0 0000000000000000000000000000000000000000 +length: 32 +-start- +this is file a +adding to file a + +-end- +node: 8c4fd1f7129b8cdec6c7f58bf48fb5237a4030c1 +linkrev: 2 +parents: b1047953b6e6b633c0d8197eaa5116fbdfd3095b 0000000000000000000000000000000000000000 +length: 54 +-start- +this is file a +adding to file a +adding more to file a + +-end- +dumprevlog done +undumping: +.hg/store/data/a.i +.hg/store/00manifest.i +.hg/store/00changelog.i +undumping done +checking changesets +checking manifests +crosschecking files in changesets and manifests +checking files +1 files, 3 changesets, 3 total revisions +comparing repos: +comparing with repo-a +searching for changes +no changes found +comparing with repo-b +searching for changes +no changes found +comparing done
--- a/tests/test-empty-file.out Wed Apr 09 15:27:57 2008 -0700 +++ b/tests/test-empty-file.out Wed Apr 09 15:28:30 2008 -0700 @@ -1,4 +1,5 @@ 0 files updated, 0 files merged, 1 files removed, 0 files unresolved +created new head changeset: 2:62ec0e86d1e5 tag: tip parent: 0:567dde5e6e98
--- a/tests/test-empty-group.out Wed Apr 09 15:27:57 2008 -0700 +++ b/tests/test-empty-group.out Wed Apr 09 15:28:30 2008 -0700 @@ -4,22 +4,26 @@ 0 files updated, 0 files merged, 2 files removed, 0 files unresolved adding x adding y +created new head 0 files updated, 0 files merged, 0 files removed, 0 files unresolved (branch merge, don't forget to commit) 0 files updated, 0 files merged, 0 files removed, 0 files unresolved 0 files updated, 0 files merged, 0 files removed, 0 files unresolved (branch merge, don't forget to commit) +created new head requesting all changes adding changesets adding manifests adding file changes added 4 changesets with 3 changes to 3 files +updating working directory 3 files updated, 0 files merged, 0 files removed, 0 files unresolved requesting all changes adding changesets adding manifests adding file changes added 4 changesets with 3 changes to 3 files +updating working directory 3 files updated, 0 files merged, 0 files removed, 0 files unresolved comparing with b searching for changes
--- a/tests/test-encode Wed Apr 09 15:27:57 2008 -0700 +++ b/tests/test-encode Wed Apr 09 15:28:30 2008 -0700 @@ -32,3 +32,11 @@ echo %% uncompress our new working dir copy gunzip < a.gz + +echo %% check hg cat operation +hg cat a.gz +hg cat --decode a.gz | gunzip +mkdir subdir +cd subdir +hg -R .. cat ../a.gz +hg -R .. cat --decode ../a.gz | gunzip
--- a/tests/test-encode.out Wed Apr 09 15:27:57 2008 -0700 +++ b/tests/test-encode.out Wed Apr 09 15:28:30 2008 -0700 @@ -7,3 +7,8 @@ 1 files updated, 0 files merged, 0 files removed, 0 files unresolved %% uncompress our new working dir copy this is a test +%% check hg cat operation +this is a test +this is a test +this is a test +this is a test
--- a/tests/test-excessive-merge.out Wed Apr 09 15:27:57 2008 -0700 +++ b/tests/test-excessive-merge.out Wed Apr 09 15:28:30 2008 -0700 @@ -1,9 +1,11 @@ 1 files updated, 0 files merged, 0 files removed, 0 files unresolved +created new head 1 files updated, 0 files merged, 0 files removed, 0 files unresolved (branch merge, don't forget to commit) 1 files updated, 0 files merged, 0 files removed, 0 files unresolved 1 files updated, 0 files merged, 0 files removed, 0 files unresolved (branch merge, don't forget to commit) +created new head changeset: 4:f6c172c6198c tag: tip parent: 1:448a8c5e42f1
--- a/tests/test-execute-bit Wed Apr 09 15:27:57 2008 -0700 +++ b/tests/test-execute-bit Wed Apr 09 15:28:30 2008 -0700 @@ -4,12 +4,18 @@ hg init echo a > a -hg ci -d'0 0' -Am'not executable' +hg ci -Am'not executable' chmod +x a -hg ci -d'1 0' -m'executable' +hg ci -m'executable' hg id +echo '% make sure we notice the change of mode if the cached size == -1' +hg rm a +hg revert -r 0 a +hg debugstate +hg st + hg up 0 hg id test -x a && echo executable -- eek || echo not executable -- whew
--- a/tests/test-execute-bit.out Wed Apr 09 15:27:57 2008 -0700 +++ b/tests/test-execute-bit.out Wed Apr 09 15:28:30 2008 -0700 @@ -1,5 +1,8 @@ adding a -1549299e88d1 tip +79abf14474dc tip +% make sure we notice the change of mode if the cached size == -1 +n 0 -1 unset a +M a 0 files updated, 0 files merged, 0 files removed, 0 files unresolved d69afc33ff8a not executable -- whew
--- a/tests/test-extdiff.out Wed Apr 09 15:27:57 2008 -0700 +++ b/tests/test-extdiff.out Wed Apr 09 15:28:30 2008 -0700 @@ -26,6 +26,7 @@ use "hg -v help falabala" to show global options diffing a.8a5febb7f867/a a.34eed99112ab/a 1 files updated, 0 files merged, 0 files removed, 0 files unresolved +created new head 1 files updated, 0 files merged, 1 files removed, 0 files unresolved 1 files updated, 0 files merged, 0 files removed, 0 files unresolved (branch merge, don't forget to commit)
--- a/tests/test-extension.out Wed Apr 09 15:27:57 2008 -0700 +++ b/tests/test-extension.out Wed Apr 09 15:28:30 2008 -0700 @@ -9,6 +9,7 @@ ui == repo.ui reposetup called for b ui == repo.ui +updating working directory 1 files updated, 0 files merged, 0 files removed, 0 files unresolved uisetup called ui.parentui is None
--- a/tests/test-fetch Wed Apr 09 15:27:57 2008 -0700 +++ b/tests/test-fetch Wed Apr 09 15:28:30 2008 -0700 @@ -1,5 +1,9 @@ #!/bin/sh +# adjust to non-default HGPORT, e.g. with run-tests.py -j +hideport() { sed "s/localhost:$HGPORT/localhost:20059/"; } +hidehash() { sed "s/changeset 3:............ merges/changeset 3:... merges/"; } + echo "[extensions]" >> $HGRCPATH echo "fetch=" >> $HGRCPATH @@ -20,5 +24,46 @@ echo c > c/c hg --cwd c commit -d '3 0' -Amc + +hg clone c d +hg clone c e + +# We cannot use the default commit message if fetching from a local +# repo, because the path of the repo will be included in the commit +# message, making every commit appear different. + +echo % should merge c into a hg --cwd c fetch -d '4 0' -m 'automated merge' ../a ls c + +hg --cwd a serve -a localhost -p $HGPORT -d --pid-file=hg.pid +cat a/hg.pid >> "$DAEMON_PIDS" + +echo '% fetch over http, no auth' +hg --cwd d fetch -d '5 0' http://localhost:$HGPORT/ | hideport | hidehash +hg --cwd d tip --template '{desc}\n' | hideport + +echo '% fetch over http with auth (should be hidden in desc)' +hg --cwd e fetch -d '5 0' http://user:password@localhost:$HGPORT/ | hideport | hidehash +hg --cwd e tip --template '{desc}\n' | hideport + +hg clone a f +hg clone a g + +echo f > f/f +hg --cwd f ci -d '6 0' -Amf + +echo g > g/g +hg --cwd g ci -d '6 0' -Amg + +hg clone -q f h +hg clone -q g i + +echo % should merge f into g +hg --cwd g fetch -d '7 0' --switch -m 'automated merge' ../f + +rm i/g +echo % should abort, because i is modified +hg --cwd i fetch ../h + +true
--- a/tests/test-fetch.out Wed Apr 09 15:27:57 2008 -0700 +++ b/tests/test-fetch.out Wed Apr 09 15:28:30 2008 -0700 @@ -1,5 +1,7 @@ adding a +updating working directory 1 files updated, 0 files merged, 0 files removed, 0 files unresolved +updating working directory 1 files updated, 0 files merged, 0 files removed, 0 files unresolved adding b 1:97d72e5f12c7 @@ -13,15 +15,67 @@ 1 files updated, 0 files merged, 0 files removed, 0 files unresolved 1:97d72e5f12c7 adding c +updating working directory +2 files updated, 0 files merged, 0 files removed, 0 files unresolved +updating working directory +2 files updated, 0 files merged, 0 files removed, 0 files unresolved +% should merge c into a pulling from ../a searching for changes adding changesets adding manifests adding file changes added 1 changesets with 1 changes to 1 files (+1 heads) -merging with new head 2:97d72e5f12c7 +updating to 2:97d72e5f12c7 +1 files updated, 0 files merged, 1 files removed, 0 files unresolved +merging with 1:5e056962225c 1 files updated, 0 files merged, 0 files removed, 0 files unresolved new changeset 3:cd3a41621cf0 merges remote changes with local a b c +% fetch over http, no auth +pulling from http://localhost:20059/ +searching for changes +adding changesets +adding manifests +adding file changes +added 1 changesets with 1 changes to 1 files (+1 heads) +updating to 2:97d72e5f12c7 +1 files updated, 0 files merged, 1 files removed, 0 files unresolved +merging with 1:5e056962225c +1 files updated, 0 files merged, 0 files removed, 0 files unresolved +new changeset 3:... merges remote changes with local +Automated merge with http://localhost:20059/ +% fetch over http with auth (should be hidden in desc) +pulling from http://user:***@localhost:20059/ +searching for changes +adding changesets +adding manifests +adding file changes +added 1 changesets with 1 changes to 1 files (+1 heads) +updating to 2:97d72e5f12c7 +1 files updated, 0 files merged, 1 files removed, 0 files unresolved +merging with 1:5e056962225c +1 files updated, 0 files merged, 0 files removed, 0 files unresolved +new changeset 3:... merges remote changes with local +Automated merge with http://localhost:20059/ +updating working directory +2 files updated, 0 files merged, 0 files removed, 0 files unresolved +updating working directory +2 files updated, 0 files merged, 0 files removed, 0 files unresolved +adding f +adding g +% should merge f into g +pulling from ../f +searching for changes +adding changesets +adding manifests +adding file changes +added 1 changesets with 1 changes to 1 files (+1 heads) +0 files updated, 0 files merged, 0 files removed, 0 files unresolved +merging with 3:cc6a3744834d +1 files updated, 0 files merged, 0 files removed, 0 files unresolved +new changeset 4:55aa4f32ec59 merges remote changes with local +% should abort, because i is modified +abort: working directory is missing some files
--- a/tests/test-filebranch Wed Apr 09 15:27:57 2008 -0700 +++ b/tests/test-filebranch Wed Apr 09 15:28:30 2008 -0700 @@ -37,7 +37,7 @@ hg commit -m "branch b" -d "1000000 0" echo "we shouldn't have anything but n state here" -hg debugstate | cut -b 1-16,35- +hg debugstate --nodates | grep -v "^n" echo merging hg pull ../a @@ -48,7 +48,7 @@ echo new > quux echo "we shouldn't have anything but foo in merge state here" -hg debugstate | cut -b 1-16,35- | grep "^m" +hg debugstate --nodates | grep "^m" hg ci -m "merge" -d "1000000 0"
--- a/tests/test-filebranch.out Wed Apr 09 15:27:57 2008 -0700 +++ b/tests/test-filebranch.out Wed Apr 09 15:28:30 2008 -0700 @@ -1,12 +1,9 @@ creating base +updating working directory 4 files updated, 0 files merged, 0 files removed, 0 files unresolved creating branch a creating branch b we shouldn't have anything but n state here -n 644 2 bar -n 644 3 baz -n 644 3 foo -n 644 2 quux merging pulling from ../a searching for changes
--- a/tests/test-flags.out Wed Apr 09 15:27:57 2008 -0700 +++ b/tests/test-flags.out Wed Apr 09 15:28:30 2008 -0700 @@ -1,3 +1,4 @@ +updating working directory 2 files updated, 0 files merged, 0 files removed, 0 files unresolved pulling from ../test1 requesting all changes
--- a/tests/test-git-export.out Wed Apr 09 15:27:57 2008 -0700 +++ b/tests/test-git-export.out Wed Apr 09 15:28:30 2008 -0700 @@ -35,7 +35,7 @@ rename to dst --- a/src +++ b/dst -@@ -3,3 +3,4 @@ 3 +@@ -3,3 +3,4 @@ 3 4 5 @@ -86,7 +86,7 @@ rename to bar --- a/foo +++ b/bar -@@ -1,2 +1,3 @@ a +@@ -1,2 +1,3 @@ a b +c @@ -96,7 +96,7 @@ rename to foo --- a/bar +++ b/foo -@@ -1,3 +1,2 @@ a +@@ -1,3 +1,2 @@ a b -c @@ -107,7 +107,7 @@ rename to bar --- a/foo +++ b/bar -@@ -1,1 +1,3 @@ a +@@ -1,1 +1,3 @@ a +b +c @@ -117,7 +117,7 @@ rename to foo --- a/bar +++ b/foo -@@ -1,3 +1,1 @@ a +@@ -1,3 +1,1 @@ a -b -c
--- a/tests/test-globalopts Wed Apr 09 15:27:57 2008 -0700 +++ b/tests/test-globalopts Wed Apr 09 15:28:30 2008 -0700 @@ -25,6 +25,13 @@ hg -R a tip hg --repository b tip +echo %% implicit -R +hg ann a/a +hg ann a/a a/a +hg ann a/a b/b +hg -R b ann a/a +hg log + echo %% abbrev of long option hg --repo c tip
--- a/tests/test-globalopts.out Wed Apr 09 15:27:57 2008 -0700 +++ b/tests/test-globalopts.out Wed Apr 09 15:28:30 2008 -0700 @@ -1,5 +1,6 @@ adding a adding b +updating working directory 1 files updated, 0 files merged, 0 files removed, 0 files unresolved pulling from ../b searching for changes @@ -24,6 +25,12 @@ date: Thu Jan 01 00:00:01 1970 +0000 summary: b +%% implicit -R +0: a +0: a +abort: There is no Mercurial repository here (.hg not found)! +abort: a/a not under root +abort: There is no Mercurial repository here (.hg not found)! %% abbrev of long option changeset: 1:b6c483daf290 tag: tip @@ -147,6 +154,7 @@ annotate show changeset information per file line archive create unversioned archive of a repository revision backout reverse effect of earlier changeset + bisect subdivision search of changesets branch set or show the current branch name branches list repository named branches bundle create a changegroup file @@ -181,7 +189,7 @@ serve export the repository via HTTP showconfig show combined config settings from all hgrc files status show changed files in the working directory - tag add a tag for the current or given revision + tag add one or more tags for the current or given revision tags list repository tags tip show the tip revision unbundle apply one or more changegroup files @@ -199,6 +207,7 @@ annotate show changeset information per file line archive create unversioned archive of a repository revision backout reverse effect of earlier changeset + bisect subdivision search of changesets branch set or show the current branch name branches list repository named branches bundle create a changegroup file @@ -233,7 +242,7 @@ serve export the repository via HTTP showconfig show combined config settings from all hgrc files status show changed files in the working directory - tag add a tag for the current or given revision + tag add one or more tags for the current or given revision tags list repository tags tip show the tip revision unbundle apply one or more changegroup files
--- a/tests/test-glog Wed Apr 09 15:27:57 2008 -0700 +++ b/tests/test-glog Wed Apr 09 15:28:30 2008 -0700 @@ -82,7 +82,7 @@ fi echo $rev > $rev hg add $rev - hg ci -d "$rev 0" -m "($rev) $msg" + hg rawcommit -q -d "$rev 0" -m "($rev) $msg" $rev } echo "[extensions]" >> $HGRCPATH @@ -139,5 +139,8 @@ echo % glog hg glog +echo % file glog +hg glog 5 + echo % unused arguments -hg glog -q foo || echo failed +hg glog -q foo bar || echo failed
--- a/tests/test-glog.out Wed Apr 09 15:27:57 2008 -0700 +++ b/tests/test-glog.out Wed Apr 09 15:28:30 2008 -0700 @@ -1,6 +1,41 @@ % init % empty repo % building tree +(the rawcommit command is deprecated) +(the rawcommit command is deprecated) +(the rawcommit command is deprecated) +(the rawcommit command is deprecated) +(the rawcommit command is deprecated) +(the rawcommit command is deprecated) +(the rawcommit command is deprecated) +(the rawcommit command is deprecated) +(the rawcommit command is deprecated) +(the rawcommit command is deprecated) +(the rawcommit command is deprecated) +(the rawcommit command is deprecated) +(the rawcommit command is deprecated) +(the rawcommit command is deprecated) +(the rawcommit command is deprecated) +(the rawcommit command is deprecated) +(the rawcommit command is deprecated) +(the rawcommit command is deprecated) +(the rawcommit command is deprecated) +(the rawcommit command is deprecated) +(the rawcommit command is deprecated) +(the rawcommit command is deprecated) +(the rawcommit command is deprecated) +(the rawcommit command is deprecated) +(the rawcommit command is deprecated) +(the rawcommit command is deprecated) +(the rawcommit command is deprecated) +(the rawcommit command is deprecated) +(the rawcommit command is deprecated) +(the rawcommit command is deprecated) +(the rawcommit command is deprecated) +(the rawcommit command is deprecated) +(the rawcommit command is deprecated) +(the rawcommit command is deprecated) +(the rawcommit command is deprecated) % glog -q @ 34:0eed7cd895e0 | @@ -307,9 +342,17 @@ date: Thu Jan 01 00:00:00 1970 +0000 summary: (0) root +% file glog +o changeset: 5:3589c3c477ab + parent: 3:02173ffbf857 + parent: 4:e2cad8233c77 + user: test + date: Thu Jan 01 00:00:05 1970 +0000 + summary: (5) expand + % unused arguments hg glog: invalid arguments -hg glog [OPTION]... +hg glog [OPTION]... [FILE] show revision history alongside an ASCII revision graph failed
--- a/tests/test-grep.out Wed Apr 09 15:27:57 2008 -0700 +++ b/tests/test-grep.out Wed Apr 09 15:28:30 2008 -0700 @@ -1,5 +1,5 @@ % pattern error -grep: invalid match pattern: nothing to repeat! +grep: invalid match pattern: nothing to repeat % simple port:4:export port:4:vaportight
--- a/tests/test-help.out Wed Apr 09 15:27:57 2008 -0700 +++ b/tests/test-help.out Wed Apr 09 15:28:30 2008 -0700 @@ -45,6 +45,7 @@ annotate show changeset information per file line archive create unversioned archive of a repository revision backout reverse effect of earlier changeset + bisect subdivision search of changesets branch set or show the current branch name branches list repository named branches bundle create a changegroup file @@ -79,7 +80,7 @@ serve export the repository via HTTP showconfig show combined config settings from all hgrc files status show changed files in the working directory - tag add a tag for the current or given revision + tag add one or more tags for the current or given revision tags list repository tags tip show the tip revision unbundle apply one or more changegroup files @@ -93,6 +94,7 @@ annotate show changeset information per file line archive create unversioned archive of a repository revision backout reverse effect of earlier changeset + bisect subdivision search of changesets branch set or show the current branch name branches list repository named branches bundle create a changegroup file @@ -127,7 +129,7 @@ serve export the repository via HTTP showconfig show combined config settings from all hgrc files status show changed files in the working directory - tag add a tag for the current or given revision + tag add one or more tags for the current or given revision tags list repository tags tip show the tip revision unbundle apply one or more changegroup files @@ -203,17 +205,25 @@ -w --ignore-all-space ignore white space when comparing lines -b --ignore-space-change ignore changes in the amount of white space -B --ignore-blank-lines ignore changes whose lines are all blank + -U --unified number of lines of context to show -I --include include names matching the given patterns -X --exclude exclude names matching the given patterns use "hg -v help diff" to show global options hg status [OPTION]... [FILE]... +aliases: st + show changed files in the working directory - Show status of files in the repository. If names are given, only - files that match are shown. Files that are clean or ignored, are - not listed unless -c (clean), -i (ignored) or -A is given. + Show status of files in the repository. If names are given, only + files that match are shown. Files that are clean or ignored or + source of a copy/move operation, are not listed unless -c (clean), + -i (ignored), -C (copies) or -A is given. Unless options described + with "show only ..." are given, the options -mardu are used. + + Option -q/--quiet hides untracked (unknown and ignored) files + unless explicitly requested with -u/--unknown or -i/-ignored. NOTE: status may appear to disagree with diff if permissions have changed or a merge has occurred. The standard diff format does not @@ -230,11 +240,9 @@ C = clean ! = deleted, but still tracked ? = not tracked - I = ignored (not shown by default) + I = ignored = the previous added file was copied from here -aliases: st - options: -A --all show status of all files
--- a/tests/test-hgignore Wed Apr 09 15:27:57 2008 -0700 +++ b/tests/test-hgignore Wed Apr 09 15:28:30 2008 -0700 @@ -40,6 +40,10 @@ echo ".*\.o" > .hgignore echo "--" ; hg status +# Check it does not ignore the current directory '.' +echo "^\." > .hgignore +echo "--" ; hg status + echo "glob:**.o" > .hgignore echo "--" ; hg status
--- a/tests/test-hgignore.out Wed Apr 09 15:27:57 2008 -0700 +++ b/tests/test-hgignore.out Wed Apr 09 15:28:30 2008 -0700 @@ -15,6 +15,12 @@ ? syntax -- A dir/b.o +? a.c +? a.o +? dir/c.o +? syntax +-- +A dir/b.o ? .hgignore ? a.c ? syntax
--- a/tests/test-hgweb Wed Apr 09 15:27:57 2008 -0700 +++ b/tests/test-hgweb Wed Apr 09 15:28:30 2008 -0700 @@ -7,7 +7,7 @@ echo foo > da/foo echo foo > foo hg ci -Ambase -d '0 0' -hg serve -p $HGPORT -d --pid-file=hg.pid +hg serve -p $HGPORT -d --pid-file=hg.pid -A access.log -E errors.log cat hg.pid >> $DAEMON_PIDS echo % manifest ("$TESTDIR/get-with-headers.py" localhost:$HGPORT '/file/tip/?style=raw') @@ -27,6 +27,18 @@ echo % should give a 404 - file does not exist "$TESTDIR/get-with-headers.py" localhost:$HGPORT '/file/tip/bork?style=raw' +"$TESTDIR/get-with-headers.py" localhost:$HGPORT '/file/tip/bork' +"$TESTDIR/get-with-headers.py" localhost:$HGPORT '/diff/tip/bork?style=raw' + +echo % stop and restart +kill `cat hg.pid` +hg serve -p $HGPORT -d --pid-file=hg.pid -A access.log +cat hg.pid >> $DAEMON_PIDS +# Test the access/error files are opened in append mode +python -c "print len(file('access.log').readlines()), 'log lines written'" echo % static file "$TESTDIR/get-with-headers.py" localhost:$HGPORT '/static/style-gitweb.css' + +echo % errors +cat errors.log
--- a/tests/test-hgweb-commands Wed Apr 09 15:27:57 2008 -0700 +++ b/tests/test-hgweb-commands Wed Apr 09 15:28:30 2008 -0700 @@ -21,17 +21,19 @@ "$TESTDIR/get-with-headers.py" 127.0.0.1:$HGPORT '/log/1/?style=atom' | sed "s/http:\/\/[^/]*\//http:\/\/127.0.0.1\//" "$TESTDIR/get-with-headers.py" 127.0.0.1:$HGPORT '/log/1/foo/?style=atom' | sed "s/http:\/\/[^/]*\//http:\/\/127.0.0.1\//" "$TESTDIR/get-with-headers.py" 127.0.0.1:$HGPORT '/shortlog/' | sed "s/[0-9]* years/many years/" +"$TESTDIR/get-with-headers.py" 127.0.0.1:$HGPORT '/rev/0/' | sed "s/[0-9]* years ago/long ago/g" "$TESTDIR/get-with-headers.py" 127.0.0.1:$HGPORT '/rev/1/?style=raw' echo % File-related "$TESTDIR/get-with-headers.py" 127.0.0.1:$HGPORT '/file/1/foo/?style=raw' "$TESTDIR/get-with-headers.py" 127.0.0.1:$HGPORT '/annotate/1/foo/?style=raw' "$TESTDIR/get-with-headers.py" 127.0.0.1:$HGPORT '/file/1/?style=raw' +"$TESTDIR/get-with-headers.py" 127.0.0.1:$HGPORT '/file/1/foo' | sed "s/[0-9]* years/many years/" "$TESTDIR/get-with-headers.py" 127.0.0.1:$HGPORT '/filediff/1/foo/?style=raw' echo % Overviews "$TESTDIR/get-with-headers.py" 127.0.0.1:$HGPORT '/tags/?style=atom' | sed "s/http:\/\/[^/]*\//http:\/\/127.0.0.1\//" -"$TESTDIR/get-with-headers.py" 127.0.0.1:$HGPORT '/summary/?style=gitweb' | sed "s/[0-9]* years ago/long ago/" +"$TESTDIR/get-with-headers.py" 127.0.0.1:$HGPORT '/summary/?style=gitweb' | sed "s/[0-9]* years ago/long ago/g" echo % capabilities "$TESTDIR/get-with-headers.py" 127.0.0.1:$HGPORT '/capabilities' @@ -45,6 +47,8 @@ "$TESTDIR/get-with-headers.py" 127.0.0.1:$HGPORT '/changegroup' echo % stream_out "$TESTDIR/get-with-headers.py" 127.0.0.1:$HGPORT '/stream_out' +echo % failing unbundle, requires POST request +"$TESTDIR/get-with-headers.py" 127.0.0.1:$HGPORT '/unbundle' echo % Static files "$TESTDIR/get-with-headers.py" 127.0.0.1:$HGPORT '/static/style.css'
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tests/test-hgweb-no-path-info Wed Apr 09 15:28:30 2008 -0700 @@ -0,0 +1,59 @@ +#!/bin/sh +# This tests if hgweb and hgwebdir still work if the REQUEST_URI variable is +# no longer passed with the request. Instead, SCRIPT_NAME and PATH_INFO +# should be used from d74fc8dec2b4 onward to route the request. + +mkdir repo +cd repo +hg init +echo foo > bar +hg add bar +hg commit -m "test" -d "0 0" -u "Testing" +hg tip + +cat > request.py <<EOF +from mercurial.hgweb import hgweb, hgwebdir +from StringIO import StringIO +import os, sys + +errors = StringIO() +input = StringIO() + +def startrsp(headers, data): + print '---- HEADERS' + print headers + print '---- DATA' + print data + return output.write + +env = { + 'wsgi.version': (1, 0), + 'wsgi.url_scheme': 'http', + 'wsgi.errors': errors, + 'wsgi.input': input, + 'wsgi.multithread': False, + 'wsgi.multiprocess': False, + 'wsgi.run_once': False, + 'REQUEST_METHOD': 'GET', + 'SCRIPT_NAME': '', + 'SERVER_NAME': '127.0.0.1', + 'SERVER_PORT': os.environ['HGPORT'], + 'SERVER_PROTOCOL': 'HTTP/1.0' +} + +output = StringIO() +env['QUERY_STRING'] = 'style=atom' +hgweb('.', name = 'repo')(env, startrsp) +print output.getvalue() +print '---- ERRORS' +print errors.getvalue() + +output = StringIO() +env['QUERY_STRING'] = 'style=raw' +hgwebdir({'repo': '.'})(env, startrsp) +print output.getvalue() +print '---- ERRORS' +print errors.getvalue() +EOF + +python request.py | sed "s/http:\/\/127\.0\.0\.1:[0-9]*\//http:\/\/127.0.0.1\//"
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tests/test-hgweb-no-path-info.out Wed Apr 09 15:28:30 2008 -0700 @@ -0,0 +1,50 @@ +changeset: 0:4cbec7e6f8c4 +tag: tip +user: Testing +date: Thu Jan 01 00:00:00 1970 +0000 +summary: test + +---- HEADERS +200 Script output follows +---- DATA +[('Content-Type', 'application/atom+xml; charset=ascii')] +<?xml version="1.0" encoding="ascii"?> +<feed xmlns="http://www.w3.org/2005/Atom"> + <!-- Changelog --> + <id>http://127.0.0.1/</id> + <link rel="self" href="http://127.0.0.1/atom-log"/> + <link rel="alternate" href="http://127.0.0.1/"/> + <title>repo Changelog</title> + <updated>1970-01-01T00:00:00+00:00</updated> + + <entry> + <title>test</title> + <id>http://www.selenic.com/mercurial/#changeset-4cbec7e6f8c42eb52b6b52670e1f7560ae9a101e</id> + <link href="http://127.0.0.1/rev/4cbec7e6f8c42eb52b6b52670e1f7560ae9a101e"/> + <author> + <name>Testing</name> + <email>Testing</email> + </author> + <updated>1970-01-01T00:00:00+00:00</updated> + <published>1970-01-01T00:00:00+00:00</published> + <content type="xhtml"> + <div xmlns="http://www.w3.org/1999/xhtml"> + <pre xml:space="preserve">test</pre> + </div> + </content> + </entry> + +</feed> + +---- ERRORS + +---- HEADERS +200 Script output follows +---- DATA +[('Content-Type', 'text/plain; charset=ascii')] + +repo/ + + +---- ERRORS +
--- a/tests/test-hgweb-no-request-uri.out Wed Apr 09 15:27:57 2008 -0700 +++ b/tests/test-hgweb-no-request-uri.out Wed Apr 09 15:28:30 2008 -0700 @@ -7,7 +7,7 @@ ---- HEADERS 200 Script output follows ---- DATA -[('content-type', 'application/atom+xml; charset=ascii')] +[('Content-Type', 'application/atom+xml; charset=ascii')] <?xml version="1.0" encoding="ascii"?> <feed xmlns="http://www.w3.org/2005/Atom"> <!-- Changelog --> @@ -41,7 +41,7 @@ ---- HEADERS 200 Script output follows ---- DATA -[('content-type', 'text/plain; charset=ascii')] +[('Content-Type', 'text/plain; charset=ascii')] -rw-r--r-- 4 bar @@ -52,7 +52,7 @@ ---- HEADERS 200 Script output follows ---- DATA -[('content-type', 'text/plain; charset=ascii')] +[('Content-Type', 'text/plain; charset=ascii')] /repo/ @@ -62,7 +62,7 @@ ---- HEADERS 200 Script output follows ---- DATA -[('content-type', 'text/plain; charset=ascii')] +[('Content-Type', 'text/plain; charset=ascii')] -rw-r--r-- 4 bar
--- a/tests/test-hgweb.out Wed Apr 09 15:27:57 2008 -0700 +++ b/tests/test-hgweb.out Wed Apr 09 15:28:30 2008 -0700 @@ -35,7 +35,7 @@ <h2>Mercurial Error</h2> <p> -An error occured while processing your request: +An error occurred while processing your request: </p> <p> Not Found @@ -43,8 +43,8 @@ <div class="logo"> -powered by<br/> -<a href="http://www.selenic.com/mercurial/">mercurial</a> +<a href="http://www.selenic.com/mercurial/"> +<img src="/static/hglogo.png" width=75 height=90 border=0 alt="mercurial"></a> </div> </body> @@ -59,12 +59,49 @@ 400 -error: No such method: spam +error: no such method: spam % should give a 404 - file does not exist 404 Not Found -error: Path not found: bork/ +error: bork@2ef0ac749a14: not found in manifest +404 Not Found + +<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"> +<html> +<head> +<link rel="icon" href="/static/hgicon.png" type="image/png"> +<meta name="robots" content="index, nofollow" /> +<link rel="stylesheet" href="/static/style.css" type="text/css" /> + +<title>Mercurial Error</title> +</head> +<body> + +<h2>Mercurial Error</h2> + +<p> +An error occurred while processing your request: +</p> +<p> +bork@2ef0ac749a14: not found in manifest +</p> + + +<div class="logo"> +<a href="http://www.selenic.com/mercurial/"> +<img src="/static/hglogo.png" width=75 height=90 border=0 alt="mercurial"></a> +</div> + +</body> +</html> + +404 Not Found + + +error: bork@2ef0ac749a14: not found in manifest +% stop and restart +9 log lines written % static file 200 Script output follows @@ -104,19 +141,30 @@ tr.dark:hover, .parity1:hover { background-color:#edece6; } td { padding:2px 5px; font-size:12px; vertical-align:top; } td.link { padding:2px 5px; font-family:sans-serif; font-size:10px; } +td.indexlinks { white-space: nowrap; } +td.indexlinks a { + padding: 2px 5px; line-height: 10px; + border: 1px solid; + color: #ffffff; background-color: #7777bb; + border-color: #aaaadd #333366 #333366 #aaaadd; + font-weight: bold; text-align: center; text-decoration: none; + font-size: 10px; +} +td.indexlinks a:hover { background-color: #6666aa; } div.pre { font-family:monospace; font-size:12px; white-space:pre; } div.diff_info { font-family:monospace; color:#000099; background-color:#edece6; font-style:italic; } div.index_include { border:solid #d9d8d1; border-width:0px 0px 1px; padding:12px 8px; } div.search { margin:4px 8px; position:absolute; top:56px; right:12px } .linenr { color:#999999; text-decoration:none } -a.rss_logo { - float:right; padding:3px 0px; width:35px; line-height:10px; +div.rss_logo { float: right; white-space: nowrap; } +div.rss_logo a { + padding:3px 6px; line-height:10px; border:1px solid; border-color:#fcc7a5 #7d3302 #3e1a01 #ff954e; color:#ffffff; background-color:#ff6600; font-weight:bold; font-family:sans-serif; font-size:10px; text-align:center; text-decoration:none; } -a.rss_logo:hover { background-color:#ee5500; } +div.rss_logo a:hover { background-color:#ee5500; } pre { margin: 0; } span.logtags span { padding: 0px 4px; @@ -134,3 +182,8 @@ background-color: #aaffaa; border-color: #ccffcc #00cc33 #00cc33 #ccffcc; } +span.logtags span.inbranchtag { + background-color: #d5dde6; + border-color: #e3ecf4 #9398f4 #9398f4 #e3ecf4; +} +% errors
--- a/tests/test-hgwebdir.out Wed Apr 09 15:27:57 2008 -0700 +++ b/tests/test-hgwebdir.out Wed Apr 09 15:28:30 2008 -0700 @@ -5,7 +5,7 @@ 404 Not Found -error: Path not found: bork/ +error: bork@8580ff50825a: not found in manifest % should succeed 200 Script output follows
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tests/test-hgwebdirsym Wed Apr 09 15:28:30 2008 -0700 @@ -0,0 +1,49 @@ +#!/bin/sh +# Tests whether or not hgwebdir properly handles various symlink topologies. + +"$TESTDIR/hghave" symlink || exit 80 + +hg init a +echo a > a/a +hg --cwd a ci -Ama -d'1 0' + +mkdir webdir +cd webdir + +hg init b +echo b > b/b +hg --cwd b ci -Amb -d'2 0' + +hg init c +echo c > c/c +hg --cwd c ci -Amc -d'3 0' + +ln -s ../a al +ln -s ../webdir circle + +root=`pwd` + +cd .. + +cat > collections.conf <<EOF +[collections] +$root=$root +EOF + +hg serve -p $HGPORT -d --pid-file=hg.pid --webdir-conf collections.conf \ + -A access-collections.log -E error-collections.log +cat hg.pid >> $DAEMON_PIDS + +echo % should succeed +"$TESTDIR/get-with-headers.py" localhost:$HGPORT '/?style=raw' +"$TESTDIR/get-with-headers.py" localhost:$HGPORT '/al/file/tip/a?style=raw' +"$TESTDIR/get-with-headers.py" localhost:$HGPORT '/b/file/tip/b?style=raw' +"$TESTDIR/get-with-headers.py" localhost:$HGPORT '/c/file/tip/c?style=raw' + +echo % should fail +"$TESTDIR/get-with-headers.py" localhost:$HGPORT '/circle/al/file/tip/a?style=raw' +"$TESTDIR/get-with-headers.py" localhost:$HGPORT '/circle/b/file/tip/a?style=raw' +"$TESTDIR/get-with-headers.py" localhost:$HGPORT '/circle/c/file/tip/a?style=raw' + +echo % collections errors +cat error-collections.log
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tests/test-hgwebdirsym.out Wed Apr 09 15:28:30 2008 -0700 @@ -0,0 +1,34 @@ +adding a +adding b +adding c +% should succeed +200 Script output follows + + +/al/ +/b/ +/c/ + +200 Script output follows + +a +200 Script output follows + +b +200 Script output follows + +c +% should fail +404 Not Found + + +error: repository circle not found +404 Not Found + + +error: repository circle not found +404 Not Found + + +error: repository circle not found +% collections errors
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tests/test-highlight Wed Apr 09 15:28:30 2008 -0700 @@ -0,0 +1,53 @@ +#!/bin/sh + +"$TESTDIR/hghave" pygments || exit 80 + +cat <<EOF >> $HGRCPATH +[extensions] +hgext.highlight = +[web] +pygments_style = friendly +EOF + +hg init test +cd test +cp $TESTDIR/get-with-headers.py ./ +hg ci -Ama + +echo % hg serve +hg serve -p $HGPORT -d -n test --pid-file=hg.pid -A access.log -E errors.log +cat hg.pid >> $DAEMON_PIDS + +echo % hgweb filerevision +("$TESTDIR/get-with-headers.py" localhost:$HGPORT '/file/tip/get-with-headers.py') \ + | sed "s/[0-9]* years ago/long ago/g" + +echo % hgweb fileannotate +("$TESTDIR/get-with-headers.py" localhost:$HGPORT '/annotate/tip/get-with-headers.py') \ + | sed "s/[0-9]* years ago/long ago/g" + +echo % hgweb highlightcss friendly +("$TESTDIR/get-with-headers.py" localhost:$HGPORT '/highlightcss') \ + | head -n 4 + +echo % errors encountered +cat errors.log +kill `cat hg.pid` + +# Change the pygments style +cat > .hg/hgrc <<EOF +[web] +pygments_style = fruity +EOF + +echo % hg serve again +hg serve -p $HGPORT -d -n test --pid-file=hg.pid -A access.log -E errors.log +cat hg.pid >> $DAEMON_PIDS + +echo % hgweb highlightcss fruity +("$TESTDIR/get-with-headers.py" localhost:$HGPORT '/highlightcss') \ + | head -n 4 + +echo % errors encountered +cat errors.log +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tests/test-highlight.out Wed Apr 09 15:28:30 2008 -0700 @@ -0,0 +1,141 @@ +adding get-with-headers.py +% hg serve +% hgweb filerevision +200 Script output follows + +<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"> +<html> +<head> +<link rel="icon" href="/static/hgicon.png" type="image/png"> +<meta name="robots" content="index, nofollow" /> +<link rel="stylesheet" href="/static/style.css" type="text/css" /> + +<link rel="stylesheet" href="/highlightcss" type="text/css" /> +<title>test:get-with-headers.py</title> +</head> +<body> + +<div class="buttons"> +<a href="/log/0">changelog</a> +<a href="/shortlog/0">shortlog</a> +<a href="/tags">tags</a> +<a href="/rev/79ee608ca36d">changeset</a> +<a href="/file/79ee608ca36d/">files</a> +<a href="/log/79ee608ca36d/get-with-headers.py">revisions</a> +<a href="/annotate/79ee608ca36d/get-with-headers.py">annotate</a> +<a href="/raw-file/79ee608ca36d/get-with-headers.py">raw</a> +</div> + +<h2>get-with-headers.py</h2> + +<table> +<tr> + <td class="metatag">changeset 0:</td> + <td><a href="/rev/79ee608ca36d">79ee608ca36d</a></td></tr> + + +<tr> + <td class="metatag">author:</td> + <td>test</td></tr> +<tr> + <td class="metatag">date:</td> + <td>Thu Jan 01 00:00:00 1970 +0000 (long ago)</td></tr> +<tr> + <td class="metatag">permissions:</td> + <td>-rwxr-xr-x</td></tr> +<tr> + <td class="metatag">description:</td> + <td>a</td> +</tr> +</table> + +<pre> +<div class="parity0"><a class="lineno" href="#l1" id="l1"> 1</a><span class="c">#!/usr/bin/env python</span></div><div class="parity1"><a class="lineno" href="#l2" id="l2"> 2</a></div><div class="parity0"><a class="lineno" href="#l3" id="l3"> 3</a><span class="n">__doc__</span> <span class="o">=</span> <span class="s">"""This does HTTP get requests given a host:port and path and returns</span></div><div class="parity1"><a class="lineno" href="#l4" id="l4"> 4</a><span class="s">a subset of the headers plus the body of the result."""</span></div><div class="parity0"><a class="lineno" href="#l5" id="l5"> 5</a></div><div class="parity1"><a class="lineno" href="#l6" id="l6"> 6</a><span class="k">import</span> <span class="nn">httplib</span><span class="o">,</span> <span class="nn">sys</span></div><div class="parity0"><a class="lineno" href="#l7" id="l7"> 7</a><span class="n">headers</span> <span class="o">=</span> <span class="p">[</span><span class="n">h</span><span class="o">.</span><span class="n">lower</span><span class="p">()</span> <span class="k">for</span> <span class="n">h</span> <span class="ow">in</span> <span class="n">sys</span><span class="o">.</span><span class="n">argv</span><span class="p">[</span><span class="mf">3</span><span class="p">:]]</span></div><div class="parity1"><a class="lineno" href="#l8" id="l8"> 8</a><span class="n">conn</span> <span class="o">=</span> <span class="n">httplib</span><span class="o">.</span><span class="n">HTTPConnection</span><span class="p">(</span><span class="n">sys</span><span class="o">.</span><span class="n">argv</span><span class="p">[</span><span class="mf">1</span><span class="p">])</span></div><div class="parity0"><a class="lineno" href="#l9" id="l9"> 9</a><span class="n">conn</span><span class="o">.</span><span class="n">request</span><span class="p">(</span><span class="s">"GET"</span><span class="p">,</span> <span class="n">sys</span><span class="o">.</span><span class="n">argv</span><span class="p">[</span><span class="mf">2</span><span class="p">])</span></div><div class="parity1"><a class="lineno" href="#l10" id="l10"> 10</a><span class="n">response</span> <span class="o">=</span> <span class="n">conn</span><span class="o">.</span><span class="n">getresponse</span><span class="p">()</span></div><div class="parity0"><a class="lineno" href="#l11" id="l11"> 11</a><span class="k">print</span> <span class="n">response</span><span class="o">.</span><span class="n">status</span><span class="p">,</span> <span class="n">response</span><span class="o">.</span><span class="n">reason</span></div><div class="parity1"><a class="lineno" href="#l12" id="l12"> 12</a><span class="k">for</span> <span class="n">h</span> <span class="ow">in</span> <span class="n">headers</span><span class="p">:</span></div><div class="parity0"><a class="lineno" href="#l13" id="l13"> 13</a> <span class="k">if</span> <span class="n">response</span><span class="o">.</span><span class="n">getheader</span><span class="p">(</span><span class="n">h</span><span class="p">,</span> <span class="bp">None</span><span class="p">)</span> <span class="ow">is</span> <span class="ow">not</span> <span class="bp">None</span><span class="p">:</span></div><div class="parity1"><a class="lineno" href="#l14" id="l14"> 14</a> <span class="k">print</span> <span class="s">"</span><span class="si">%s</span><span class="s">: </span><span class="si">%s</span><span class="s">"</span> <span class="o">%</span> <span class="p">(</span><span class="n">h</span><span class="p">,</span> <span class="n">response</span><span class="o">.</span><span class="n">getheader</span><span class="p">(</span><span class="n">h</span><span class="p">))</span></div><div class="parity0"><a class="lineno" href="#l15" id="l15"> 15</a><span class="k">print</span></div><div class="parity1"><a class="lineno" href="#l16" id="l16"> 16</a><span class="n">sys</span><span class="o">.</span><span class="n">stdout</span><span class="o">.</span><span class="n">write</span><span class="p">(</span><span class="n">response</span><span class="o">.</span><span class="n">read</span><span class="p">())</span></div><div class="parity0"><a class="lineno" href="#l17" id="l17"> 17</a></div><div class="parity1"><a class="lineno" href="#l18" id="l18"> 18</a><span class="k">if</span> <span class="mf">200</span> <span class="o"><=</span> <span class="n">response</span><span class="o">.</span><span class="n">status</span> <span class="o"><=</span> <span class="mf">299</span><span class="p">:</span></div><div class="parity0"><a class="lineno" href="#l19" id="l19"> 19</a> <span class="n">sys</span><span class="o">.</span><span class="n">exit</span><span class="p">(</span><span class="mf">0</span><span class="p">)</span></div><div class="parity1"><a class="lineno" href="#l20" id="l20"> 20</a><span class="n">sys</span><span class="o">.</span><span class="n">exit</span><span class="p">(</span><span class="mf">1</span><span class="p">)</span></div> +</pre> + + +<div class="logo"> +<a href="http://www.selenic.com/mercurial/"> +<img src="/static/hglogo.png" width=75 height=90 border=0 alt="mercurial"></a> +</div> + +</body> +</html> + +% hgweb fileannotate +200 Script output follows + +<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"> +<html> +<head> +<link rel="icon" href="/static/hgicon.png" type="image/png"> +<meta name="robots" content="index, nofollow" /> +<link rel="stylesheet" href="/static/style.css" type="text/css" /> + +<link rel="stylesheet" href="/highlightcss" type="text/css" /> +<title>test: get-with-headers.py annotate</title> +</head> +<body> + +<div class="buttons"> +<a href="/log/0">changelog</a> +<a href="/shortlog/0">shortlog</a> +<a href="/tags">tags</a> +<a href="/rev/79ee608ca36d">changeset</a> +<a href="/file/79ee608ca36d/">files</a> +<a href="/file/79ee608ca36d/get-with-headers.py">file</a> +<a href="/log/79ee608ca36d/get-with-headers.py">revisions</a> +<a href="/raw-annotate/79ee608ca36d/get-with-headers.py">raw</a> +</div> + +<h2>Annotate get-with-headers.py</h2> + +<table> +<tr> + <td class="metatag">changeset 0:</td> + <td><a href="/rev/79ee608ca36d">79ee608ca36d</a></td></tr> + + +<tr> + <td class="metatag">author:</td> + <td>test</td></tr> +<tr> + <td class="metatag">date:</td> + <td>Thu Jan 01 00:00:00 1970 +0000 (long ago)</td></tr> +<tr> + <td class="metatag">permissions:</td> + <td>-rwxr-xr-x</td></tr> +<tr> + <td class="metatag">description:</td> + <td>a</td> +</tr> +</table> + +<br/> + +<table cellspacing="0" cellpadding="0"> +<tr class="parity0"><td class="annotate"><a href="/annotate/79ee608ca36d/get-with-headers.py#l1">test@0</a></td><td><a class="lineno" href="#l1" id="l1"> 1</a></td><td><pre><span class="c">#!/usr/bin/env python</span></pre></td></tr><tr class="parity1"><td class="annotate"><a href="/annotate/79ee608ca36d/get-with-headers.py#l2">test@0</a></td><td><a class="lineno" href="#l2" id="l2"> 2</a></td><td><pre></pre></td></tr><tr class="parity0"><td class="annotate"><a href="/annotate/79ee608ca36d/get-with-headers.py#l3">test@0</a></td><td><a class="lineno" href="#l3" id="l3"> 3</a></td><td><pre><span class="n">__doc__</span> <span class="o">=</span> <span class="s">"""This does HTTP get requests given a host:port and path and returns</span></pre></td></tr><tr class="parity1"><td class="annotate"><a href="/annotate/79ee608ca36d/get-with-headers.py#l4">test@0</a></td><td><a class="lineno" href="#l4" id="l4"> 4</a></td><td><pre><span class="s">a subset of the headers plus the body of the result."""</span></pre></td></tr><tr class="parity0"><td class="annotate"><a href="/annotate/79ee608ca36d/get-with-headers.py#l5">test@0</a></td><td><a class="lineno" href="#l5" id="l5"> 5</a></td><td><pre></pre></td></tr><tr class="parity1"><td class="annotate"><a href="/annotate/79ee608ca36d/get-with-headers.py#l6">test@0</a></td><td><a class="lineno" href="#l6" id="l6"> 6</a></td><td><pre><span class="k">import</span> <span class="nn">httplib</span><span class="o">,</span> <span class="nn">sys</span></pre></td></tr><tr class="parity0"><td class="annotate"><a href="/annotate/79ee608ca36d/get-with-headers.py#l7">test@0</a></td><td><a class="lineno" href="#l7" id="l7"> 7</a></td><td><pre><span class="n">headers</span> <span class="o">=</span> <span class="p">[</span><span class="n">h</span><span class="o">.</span><span class="n">lower</span><span class="p">()</span> <span class="k">for</span> <span class="n">h</span> <span class="ow">in</span> <span class="n">sys</span><span class="o">.</span><span class="n">argv</span><span class="p">[</span><span class="mf">3</span><span class="p">:]]</span></pre></td></tr><tr class="parity1"><td class="annotate"><a href="/annotate/79ee608ca36d/get-with-headers.py#l8">test@0</a></td><td><a class="lineno" href="#l8" id="l8"> 8</a></td><td><pre><span class="n">conn</span> <span class="o">=</span> <span class="n">httplib</span><span class="o">.</span><span class="n">HTTPConnection</span><span class="p">(</span><span class="n">sys</span><span class="o">.</span><span class="n">argv</span><span class="p">[</span><span class="mf">1</span><span class="p">])</span></pre></td></tr><tr class="parity0"><td class="annotate"><a href="/annotate/79ee608ca36d/get-with-headers.py#l9">test@0</a></td><td><a class="lineno" href="#l9" id="l9"> 9</a></td><td><pre><span class="n">conn</span><span class="o">.</span><span class="n">request</span><span class="p">(</span><span class="s">"GET"</span><span class="p">,</span> <span class="n">sys</span><span class="o">.</span><span class="n">argv</span><span class="p">[</span><span class="mf">2</span><span class="p">])</span></pre></td></tr><tr class="parity1"><td class="annotate"><a href="/annotate/79ee608ca36d/get-with-headers.py#l10">test@0</a></td><td><a class="lineno" href="#l10" id="l10"> 10</a></td><td><pre><span class="n">response</span> <span class="o">=</span> <span class="n">conn</span><span class="o">.</span><span class="n">getresponse</span><span class="p">()</span></pre></td></tr><tr class="parity0"><td class="annotate"><a href="/annotate/79ee608ca36d/get-with-headers.py#l11">test@0</a></td><td><a class="lineno" href="#l11" id="l11"> 11</a></td><td><pre><span class="k">print</span> <span class="n">response</span><span class="o">.</span><span class="n">status</span><span class="p">,</span> <span class="n">response</span><span class="o">.</span><span class="n">reason</span></pre></td></tr><tr class="parity1"><td class="annotate"><a href="/annotate/79ee608ca36d/get-with-headers.py#l12">test@0</a></td><td><a class="lineno" href="#l12" id="l12"> 12</a></td><td><pre><span class="k">for</span> <span class="n">h</span> <span class="ow">in</span> <span class="n">headers</span><span class="p">:</span></pre></td></tr><tr class="parity0"><td class="annotate"><a href="/annotate/79ee608ca36d/get-with-headers.py#l13">test@0</a></td><td><a class="lineno" href="#l13" id="l13"> 13</a></td><td><pre> <span class="k">if</span> <span class="n">response</span><span class="o">.</span><span class="n">getheader</span><span class="p">(</span><span class="n">h</span><span class="p">,</span> <span class="bp">None</span><span class="p">)</span> <span class="ow">is</span> <span class="ow">not</span> <span class="bp">None</span><span class="p">:</span></pre></td></tr><tr class="parity1"><td class="annotate"><a href="/annotate/79ee608ca36d/get-with-headers.py#l14">test@0</a></td><td><a class="lineno" href="#l14" id="l14"> 14</a></td><td><pre> <span class="k">print</span> <span class="s">"</span><span class="si">%s</span><span class="s">: </span><span class="si">%s</span><span class="s">"</span> <span class="o">%</span> <span class="p">(</span><span class="n">h</span><span class="p">,</span> <span class="n">response</span><span class="o">.</span><span class="n">getheader</span><span class="p">(</span><span class="n">h</span><span class="p">))</span></pre></td></tr><tr class="parity0"><td class="annotate"><a href="/annotate/79ee608ca36d/get-with-headers.py#l15">test@0</a></td><td><a class="lineno" href="#l15" id="l15"> 15</a></td><td><pre><span class="k">print</span></pre></td></tr><tr class="parity1"><td class="annotate"><a href="/annotate/79ee608ca36d/get-with-headers.py#l16">test@0</a></td><td><a class="lineno" href="#l16" id="l16"> 16</a></td><td><pre><span class="n">sys</span><span class="o">.</span><span class="n">stdout</span><span class="o">.</span><span class="n">write</span><span class="p">(</span><span class="n">response</span><span class="o">.</span><span class="n">read</span><span class="p">())</span></pre></td></tr><tr class="parity0"><td class="annotate"><a href="/annotate/79ee608ca36d/get-with-headers.py#l17">test@0</a></td><td><a class="lineno" href="#l17" id="l17"> 17</a></td><td><pre></pre></td></tr><tr class="parity1"><td class="annotate"><a href="/annotate/79ee608ca36d/get-with-headers.py#l18">test@0</a></td><td><a class="lineno" href="#l18" id="l18"> 18</a></td><td><pre><span class="k">if</span> <span class="mf">200</span> <span class="o"><=</span> <span class="n">response</span><span class="o">.</span><span class="n">status</span> <span class="o"><=</span> <span class="mf">299</span><span class="p">:</span></pre></td></tr><tr class="parity0"><td class="annotate"><a href="/annotate/79ee608ca36d/get-with-headers.py#l19">test@0</a></td><td><a class="lineno" href="#l19" id="l19"> 19</a></td><td><pre> <span class="n">sys</span><span class="o">.</span><span class="n">exit</span><span class="p">(</span><span class="mf">0</span><span class="p">)</span></pre></td></tr><tr class="parity1"><td class="annotate"><a href="/annotate/79ee608ca36d/get-with-headers.py#l20">test@0</a></td><td><a class="lineno" href="#l20" id="l20"> 20</a></td><td><pre><span class="n">sys</span><span class="o">.</span><span class="n">exit</span><span class="p">(</span><span class="mf">1</span><span class="p">)</span></pre></td></tr> +</table> + + +<div class="logo"> +<a href="http://www.selenic.com/mercurial/"> +<img src="/static/hglogo.png" width=75 height=90 border=0 alt="mercurial"></a> +</div> + +</body> +</html> + +% hgweb highlightcss friendly +200 Script output follows + +/* pygments_style = friendly */ + +% errors encountered +% hg serve again +% hgweb highlightcss fruity +200 Script output follows + +/* pygments_style = fruity */ + +% errors encountered
--- a/tests/test-hook.out Wed Apr 09 15:27:57 2008 -0700 +++ b/tests/test-hook.out Wed Apr 09 15:28:30 2008 -0700 @@ -3,6 +3,7 @@ 0:29b62aeb769f commit hook: HG_NODE=29b62aeb769fdf78d8d9c5f28b017f76d7ef824b HG_PARENT1=0000000000000000000000000000000000000000 commit.b hook: HG_NODE=29b62aeb769fdf78d8d9c5f28b017f76d7ef824b HG_PARENT1=0000000000000000000000000000000000000000 +updating working directory 1 files updated, 0 files merged, 0 files removed, 0 files unresolved precommit hook: HG_PARENT1=29b62aeb769fdf78d8d9c5f28b017f76d7ef824b pretxncommit hook: HG_NODE=b702efe9688826e3a91283852b328b84dbf37bc2 HG_PARENT1=29b62aeb769fdf78d8d9c5f28b017f76d7ef824b @@ -15,6 +16,7 @@ 2:1324a5531bac commit hook: HG_NODE=1324a5531bac09b329c3845d35ae6a7526874edb HG_PARENT1=29b62aeb769fdf78d8d9c5f28b017f76d7ef824b commit.b hook: HG_NODE=1324a5531bac09b329c3845d35ae6a7526874edb HG_PARENT1=29b62aeb769fdf78d8d9c5f28b017f76d7ef824b +created new head 1 files updated, 0 files merged, 0 files removed, 0 files unresolved (branch merge, don't forget to commit) precommit hook: HG_PARENT1=1324a5531bac09b329c3845d35ae6a7526874edb HG_PARENT2=b702efe9688826e3a91283852b328b84dbf37bc2 @@ -40,7 +42,6 @@ added 3 changesets with 2 changes to 2 files (run 'hg update' to get a working copy) pretag hook: HG_LOCAL=0 HG_NODE=4c52fb2e402287dd5dc052090682536c8406c321 HG_TAG=a -tag hook: HG_LOCAL=0 HG_NODE=4c52fb2e402287dd5dc052090682536c8406c321 HG_TAG=a precommit hook: HG_PARENT1=4c52fb2e402287dd5dc052090682536c8406c321 pretxncommit hook: HG_NODE=8ea2ef7ad3e8cac946c72f1e0c79d6aebc301198 HG_PARENT1=4c52fb2e402287dd5dc052090682536c8406c321 4:8ea2ef7ad3e8
--- a/tests/test-http Wed Apr 09 15:27:57 2008 -0700 +++ b/tests/test-http Wed Apr 09 15:28:30 2008 -0700 @@ -9,7 +9,7 @@ hg --config server.uncompressed=True serve -p $HGPORT -d --pid-file=../hg1.pid hg serve -p $HGPORT1 -d --pid-file=../hg2.pid # Test server address cannot be reused -hg serve -p $HGPORT1 2>&1 | sed -e 's/abort: cannot start server:.*/abort: cannot start server:/' +hg serve -p $HGPORT1 2>&1 | sed -e "s/abort: cannot start server at ':$HGPORT1':.*/abort: cannot start server at ':20060':/" cd .. cat hg1.pid hg2.pid >> $DAEMON_PIDS
--- a/tests/test-http-clone-r.out Wed Apr 09 15:27:57 2008 -0700 +++ b/tests/test-http-clone-r.out Wed Apr 09 15:28:30 2008 -0700 @@ -1,5 +1,6 @@ # creating 'remote' 1 files updated, 0 files merged, 0 files removed, 0 files unresolved +created new head 1 files updated, 0 files merged, 2 files removed, 0 files unresolved rev offset length base linkrev nodeid p1 p2 0 0 3 0 0 362fef284ce2 000000000000 000000000000 @@ -32,6 +33,7 @@ adding manifests adding file changes added 1 changesets with 1 changes to 1 files +updating working directory 1 files updated, 0 files merged, 0 files removed, 0 files unresolved checking changesets checking manifests @@ -43,6 +45,7 @@ adding manifests adding file changes added 2 changesets with 2 changes to 1 files +updating working directory 1 files updated, 0 files merged, 0 files removed, 0 files unresolved checking changesets checking manifests @@ -54,6 +57,7 @@ adding manifests adding file changes added 3 changesets with 3 changes to 1 files +updating working directory 1 files updated, 0 files merged, 0 files removed, 0 files unresolved checking changesets checking manifests @@ -65,6 +69,7 @@ adding manifests adding file changes added 4 changesets with 4 changes to 1 files +updating working directory 1 files updated, 0 files merged, 0 files removed, 0 files unresolved checking changesets checking manifests @@ -76,6 +81,7 @@ adding manifests adding file changes added 2 changesets with 2 changes to 1 files +updating working directory 1 files updated, 0 files merged, 0 files removed, 0 files unresolved checking changesets checking manifests @@ -87,6 +93,7 @@ adding manifests adding file changes added 3 changesets with 3 changes to 1 files +updating working directory 1 files updated, 0 files merged, 0 files removed, 0 files unresolved checking changesets checking manifests @@ -98,6 +105,7 @@ adding manifests adding file changes added 4 changesets with 5 changes to 2 files +updating working directory 2 files updated, 0 files merged, 0 files removed, 0 files unresolved checking changesets checking manifests @@ -109,6 +117,7 @@ adding manifests adding file changes added 5 changesets with 6 changes to 3 files +updating working directory 2 files updated, 0 files merged, 0 files removed, 0 files unresolved checking changesets checking manifests @@ -120,6 +129,7 @@ adding manifests adding file changes added 5 changesets with 5 changes to 2 files +updating working directory 1 files updated, 0 files merged, 0 files removed, 0 files unresolved checking changesets checking manifests
--- a/tests/test-http-proxy.out Wed Apr 09 15:27:57 2008 -0700 +++ b/tests/test-http-proxy.out Wed Apr 09 15:28:30 2008 -0700 @@ -3,6 +3,7 @@ streaming all changes XXX files to transfer, XXX bytes of data transferred XXX bytes in XXX seconds (XXX XB/sec) +updating working directory XXX files updated, XXX files merged, XXX files removed, XXX files unresolved checking changesets checking manifests @@ -15,6 +16,7 @@ adding manifests adding file changes added 1 changesets with 1 changes to 1 files +updating working directory 1 files updated, 0 files merged, 0 files removed, 0 files unresolved checking changesets checking manifests @@ -27,6 +29,7 @@ adding manifests adding file changes added 1 changesets with 1 changes to 1 files +updating working directory 1 files updated, 0 files merged, 0 files removed, 0 files unresolved %% proxy url with user name and password requesting all changes @@ -34,6 +37,7 @@ adding manifests adding file changes added 1 changesets with 1 changes to 1 files +updating working directory 1 files updated, 0 files merged, 0 files removed, 0 files unresolved %% url with user name and password requesting all changes @@ -41,6 +45,7 @@ adding manifests adding file changes added 1 changesets with 1 changes to 1 files +updating working directory 1 files updated, 0 files merged, 0 files removed, 0 files unresolved %% bad host:port for proxy abort: error: Connection refused
--- a/tests/test-http.out Wed Apr 09 15:27:57 2008 -0700 +++ b/tests/test-http.out Wed Apr 09 15:28:30 2008 -0700 @@ -1,9 +1,10 @@ adding foo -abort: cannot start server: +abort: cannot start server at ':20060': % clone via stream streaming all changes XXX files to transfer, XXX bytes of data transferred XXX bytes in XXX seconds (XXX XB/sec) +updating working directory XXX files updated, XXX files merged, XXX files removed, XXX files unresolved checking changesets checking manifests @@ -16,6 +17,7 @@ adding manifests adding file changes added 1 changesets with 1 changes to 1 files +updating working directory 1 files updated, 0 files merged, 0 files removed, 0 files unresolved % clone via pull requesting all changes @@ -23,6 +25,7 @@ adding manifests adding file changes added 1 changesets with 1 changes to 1 files +updating working directory 1 files updated, 0 files merged, 0 files removed, 0 files unresolved checking changesets checking manifests
--- a/tests/test-hup.out Wed Apr 09 15:27:57 2008 -0700 +++ b/tests/test-hup.out Wed Apr 09 15:28:30 2008 -0700 @@ -4,4 +4,4 @@ transaction abort! rollback completed killed! -.hg/00changelog.i .hg/journal.dirstate .hg/requires .hg/store .hg/store/00changelog.i .hg/store/00changelog.i.a +.hg/00changelog.i .hg/journal.branch .hg/journal.dirstate .hg/requires .hg/store .hg/store/00changelog.i .hg/store/00changelog.i.a
--- a/tests/test-imerge Wed Apr 09 15:27:57 2008 -0700 +++ b/tests/test-imerge Wed Apr 09 15:28:30 2008 -0700 @@ -33,6 +33,12 @@ echo % next hg imerge next +echo % resolve and unresolve +hg imerge resolve foo +hg -v imerge st +hg imerge unresolve foo +hg -v imerge st + echo % merge next hg --traceback imerge @@ -46,6 +52,11 @@ echo foo > foo2 hg imerge save ../savedmerge +echo % merge auto +hg up -C 1 +hg --traceback imerge --auto +cat foo2 + echo % load hg up -C 0 hg imerge --traceback load ../savedmerge @@ -61,4 +72,10 @@ echo % nothing to merge hg imerge +cd .. +hg -q clone -r 0 base clone +cd clone +echo % load unknown parent +hg imerge load ../savedmerge + exit 0
--- a/tests/test-imerge.out Wed Apr 09 15:27:57 2008 -0700 +++ b/tests/test-imerge.out Wed Apr 09 15:28:30 2008 -0700 @@ -1,6 +1,7 @@ adding bar adding foo 1 files updated, 0 files merged, 1 files removed, 0 files unresolved +created new head % start imerge 1 files updated, 0 files merged, 0 files removed, 0 files unresolved (branch merge, don't forget to commit) @@ -14,6 +15,12 @@ U foo (foo2) % next foo +% resolve and unresolve +merging e6da46716401 and 30d266f502e7 +R foo (foo2) +all conflicts resolved +merging e6da46716401 and 30d266f502e7 +U foo (foo2) % merge next merging foo and foo2 all conflicts resolved @@ -22,6 +29,20 @@ merging foo and foo2 all conflicts resolved % save +% merge auto +2 files updated, 0 files merged, 0 files removed, 0 files unresolved +2 files updated, 0 files merged, 0 files removed, 0 files unresolved +(branch merge, don't forget to commit) +merging foo2 and foo +warning: conflicts during merge. +merging foo2 failed! +U foo2 +foo +<<<<<<< local +foo +======= +bar +>>>>>>> other % load 2 files updated, 0 files merged, 1 files removed, 0 files unresolved 2 files updated, 0 files merged, 0 files removed, 0 files unresolved @@ -46,3 +67,5 @@ 2 files updated, 0 files merged, 1 files removed, 0 files unresolved % nothing to merge abort: there is nothing to merge - use "hg update" instead +% load unknown parent +abort: merge parent e6da46716401 not in repository
--- a/tests/test-import Wed Apr 09 15:27:57 2008 -0700 +++ b/tests/test-import Wed Apr 09 15:28:30 2008 -0700 @@ -32,6 +32,20 @@ hg --cwd b import -mpatch ../tip.patch rm -r b +echo % import of plain diff with specific date and user +hg clone -r0 a b +hg --cwd a diff -r0:1 > tip.patch +hg --cwd b import -mpatch -d '1 0' -u 'user@nowhere.net' ../tip.patch +hg -R b tip -pv +rm -r b + +echo % import of plain diff should be ok with --no-commit +hg clone -r0 a b +hg --cwd a diff -r0:1 > tip.patch +hg --cwd b import --no-commit ../tip.patch +hg --cwd b diff --nodates +rm -r b + echo % hg -R repo import # put the clone in a subdir - having a directory named "a" # used to hide a bug. @@ -111,6 +125,18 @@ hg --cwd b tip --template '{desc}\n' rm -r b +# We weren't backing up the correct dirstate file when importing many patches +# (issue963) +echo '% import patch1 patch2; rollback' +echo line 3 >> a/a +hg --cwd a ci -m'third change' +hg --cwd a export -o '../patch%R' 1 2 +hg clone -qr0 a b +hg --cwd b parents --template 'parent: #rev#\n' +hg --cwd b import ../patch1 ../patch2 +hg --cwd b rollback +hg --cwd b parents --template 'parent: #rev#\n' +rm -r b # bug non regression test # importing a patch in a subdirectory failed at the commit stage @@ -153,3 +179,68 @@ hg ci -m brancha hg import -v tip.patch cd .. + +# Test hunk touching empty files (issue906) +hg init empty +cd empty +touch a +touch b1 +touch c1 +echo d > d +hg ci -Am init +echo a > a +echo b > b1 +hg mv b1 b2 +echo c > c1 +hg copy c1 c2 +rm d +touch d +hg diff --git +hg ci -m empty +hg export --git tip > empty.diff +hg up -C 0 +hg import empty.diff +for name in a b1 b2 c1 c2 d; +do + echo % $name file + test -f $name && cat $name +done +cd .. + +# Test importing a patch ending with a binary file removal +echo % test trailing binary removal +hg init binaryremoval +cd binaryremoval +echo a > a +python -c "file('b', 'wb').write('a\x00b')" +hg ci -Am addall +hg rm a +hg rm b +hg st +hg ci -m remove +hg export --git . > remove.diff +cat remove.diff | grep git +hg up -C 0 +hg import remove.diff +hg manifest +cd .. + +echo % 'test update+rename with common name (issue 927)' +hg init t +cd t +touch a +hg ci -Am t +echo a > a +# Here, bfile.startswith(afile) +hg copy a a2 +hg ci -m copya +hg export --git tip > copy.diff +hg up -C 0 +hg import copy.diff +echo % view a +# a should contain an 'a' +cat a +echo % view a2 +# and a2 should have duplicated it +cat a2 +cd ..
--- a/tests/test-import.out Wed Apr 09 15:27:57 2008 -0700 +++ b/tests/test-import.out Wed Apr 09 15:28:30 2008 -0700 @@ -6,6 +6,7 @@ adding manifests adding file changes added 1 changesets with 2 changes to 2 files +updating working directory 2 files updated, 0 files merged, 0 files removed, 0 files unresolved applying ../tip.patch % message should be same @@ -18,24 +19,68 @@ adding manifests adding file changes added 1 changesets with 2 changes to 2 files +updating working directory 2 files updated, 0 files merged, 0 files removed, 0 files unresolved applying ../tip.patch transaction abort! rollback completed +abort: empty commit message % import of plain diff should be ok with message requesting all changes adding changesets adding manifests adding file changes added 1 changesets with 2 changes to 2 files +updating working directory +2 files updated, 0 files merged, 0 files removed, 0 files unresolved +applying ../tip.patch +% import of plain diff with specific date and user +requesting all changes +adding changesets +adding manifests +adding file changes +added 1 changesets with 2 changes to 2 files +updating working directory 2 files updated, 0 files merged, 0 files removed, 0 files unresolved applying ../tip.patch +changeset: 1:ca68f19f3a40 +tag: tip +user: user@nowhere.net +date: Thu Jan 01 00:00:01 1970 +0000 +files: a +description: +patch + + +diff -r 80971e65b431 -r ca68f19f3a40 a +--- a/a Thu Jan 01 00:00:00 1970 +0000 ++++ b/a Thu Jan 01 00:00:01 1970 +0000 +@@ -1,1 +1,2 @@ + line 1 ++line 2 + +% import of plain diff should be ok with --no-commit +requesting all changes +adding changesets +adding manifests +adding file changes +added 1 changesets with 2 changes to 2 files +updating working directory +2 files updated, 0 files merged, 0 files removed, 0 files unresolved +applying ../tip.patch +diff -r 80971e65b431 a +--- a/a ++++ b/a +@@ -1,1 +1,2 @@ + line 1 ++line 2 % hg -R repo import requesting all changes adding changesets adding manifests adding file changes added 1 changesets with 2 changes to 2 files +updating working directory 2 files updated, 0 files merged, 0 files removed, 0 files unresolved applying tip.patch % import from stdin @@ -44,6 +89,7 @@ adding manifests adding file changes added 1 changesets with 2 changes to 2 files +updating working directory 2 files updated, 0 files merged, 0 files removed, 0 files unresolved applying patch from stdin % override commit message @@ -52,6 +98,7 @@ adding manifests adding file changes added 1 changesets with 2 changes to 2 files +updating working directory 2 files updated, 0 files merged, 0 files removed, 0 files unresolved applying patch from stdin summary: override @@ -61,6 +108,7 @@ adding manifests adding file changes added 1 changesets with 2 changes to 2 files +updating working directory 2 files updated, 0 files merged, 0 files removed, 0 files unresolved applying ../msg.patch user: email patcher @@ -71,6 +119,7 @@ adding manifests adding file changes added 1 changesets with 2 changes to 2 files +updating working directory 2 files updated, 0 files merged, 0 files removed, 0 files unresolved applying patch from stdin % plain diff in email, subject, no message body @@ -79,6 +128,7 @@ adding manifests adding file changes added 1 changesets with 2 changes to 2 files +updating working directory 2 files updated, 0 files merged, 0 files removed, 0 files unresolved applying patch from stdin % plain diff in email, no subject, no message body, should fail @@ -87,16 +137,19 @@ adding manifests adding file changes added 1 changesets with 2 changes to 2 files +updating working directory 2 files updated, 0 files merged, 0 files removed, 0 files unresolved applying patch from stdin transaction abort! rollback completed +abort: empty commit message % hg export in email, should use patch header requesting all changes adding changesets adding manifests adding file changes added 1 changesets with 2 changes to 2 files +updating working directory 2 files updated, 0 files merged, 0 files removed, 0 files unresolved applying patch from stdin summary: second change @@ -106,18 +159,26 @@ adding manifests adding file changes added 1 changesets with 2 changes to 2 files +updating working directory 2 files updated, 0 files merged, 0 files removed, 0 files unresolved applying patch from stdin email patch next line --- +% import patch1 patch2; rollback +parent: 0 +applying ../patch1 +applying ../patch2 +rolling back last transaction +parent: 1 % hg import in a subdirectory requesting all changes adding changesets adding manifests adding file changes added 1 changesets with 2 changes to 2 files +updating working directory 2 files updated, 0 files merged, 0 files removed, 0 files unresolved applying ../../../tip.patch % message should be 'subdir change' @@ -128,7 +189,70 @@ % test fuzziness adding a 1 files updated, 0 files merged, 0 files removed, 0 files unresolved +created new head applying tip.patch patching file a Hunk #1 succeeded at 1 with fuzz 2 (offset -2 lines). a +adding a +adding b1 +adding c1 +adding d +diff --git a/a b/a +--- a/a ++++ b/a +@@ -0,0 +1,1 @@ ++a +diff --git a/b1 b/b2 +rename from b1 +rename to b2 +--- a/b1 ++++ b/b2 +@@ -0,0 +1,1 @@ ++b +diff --git a/c1 b/c1 +--- a/c1 ++++ b/c1 +@@ -0,0 +1,1 @@ ++c +diff --git a/c1 b/c2 +copy from c1 +copy to c2 +--- a/c1 ++++ b/c2 +@@ -0,0 +1,1 @@ ++c +diff --git a/d b/d +--- a/d ++++ b/d +@@ -1,1 +0,0 @@ +-d +4 files updated, 0 files merged, 2 files removed, 0 files unresolved +applying empty.diff +% a file +a +% b1 file +% b2 file +b +% c1 file +c +% c2 file +c +% d file +% test trailing binary removal +adding a +adding b +R a +R b +diff --git a/a b/a +diff --git a/b b/b +2 files updated, 0 files merged, 0 files removed, 0 files unresolved +applying remove.diff +% test update+rename with common name (issue 927) +adding a +1 files updated, 0 files merged, 1 files removed, 0 files unresolved +applying copy.diff +% view a +a +% view a2 +a
--- a/tests/test-incoming-outgoing Wed Apr 09 15:27:57 2008 -0700 +++ b/tests/test-incoming-outgoing Wed Apr 09 15:28:30 2008 -0700 @@ -19,6 +19,8 @@ # local incoming hg -R new incoming test hg -R new incoming -r 4 test +echo "% limit to 2 changesets" +hg -R new incoming -l 2 test # test with --bundle http_proxy= hg -R new incoming --bundle test.hg http://localhost:$HGPORT/ | sed -e 's,:[0-9][0-9]*/,/,' @@ -44,5 +46,7 @@ hg verify cd .. hg -R test-dev outgoing test +echo "% limit to 3 changesets" +hg -R test-dev outgoing -l 3 test http_proxy= hg -R test-dev outgoing http://localhost:$HGPORT/ | sed -e 's,:[0-9][0-9]*/,/,' http_proxy= hg -R test-dev outgoing -r 11 http://localhost:$HGPORT/ | sed -e 's,:[0-9][0-9]*/,/,'
--- a/tests/test-incoming-outgoing.out Wed Apr 09 15:27:57 2008 -0700 +++ b/tests/test-incoming-outgoing.out Wed Apr 09 15:28:30 2008 -0700 @@ -151,6 +151,18 @@ date: Mon Jan 12 13:46:40 1970 +0000 summary: 4 +% limit to 2 changesets +comparing with test +changeset: 0:9cb21d99fe27 +user: test +date: Mon Jan 12 13:46:40 1970 +0000 +summary: 0 + +changeset: 1:d717f5dfad6a +user: test +date: Mon Jan 12 13:46:40 1970 +0000 +summary: 1 + comparing with http://localhost/ changeset: 0:9cb21d99fe27 user: test @@ -267,6 +279,7 @@ date: Mon Jan 12 13:46:40 1970 +0000 summary: 8 +updating working directory 1 files updated, 0 files merged, 0 files removed, 0 files unresolved checking changesets checking manifests @@ -301,6 +314,24 @@ date: Mon Jan 12 13:46:40 1970 +0000 summary: 13 +% limit to 3 changesets +comparing with test +searching for changes +changeset: 9:3741c3ad1096 +user: test +date: Mon Jan 12 13:46:40 1970 +0000 +summary: 9 + +changeset: 10:de4143c8d9a5 +user: test +date: Mon Jan 12 13:46:40 1970 +0000 +summary: 10 + +changeset: 11:0e1c188b9a7a +user: test +date: Mon Jan 12 13:46:40 1970 +0000 +summary: 11 + comparing with http://localhost/ searching for changes changeset: 9:3741c3ad1096
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tests/test-inherit-mode Wed Apr 09 15:28:30 2008 -0700 @@ -0,0 +1,91 @@ +#!/bin/sh + +# test that new files created in .hg inherit the permissions from .hg/store + +"$TESTDIR/hghave" unix-permissions || exit 80 + +mkdir dir +# just in case somebody has a strange $TMPDIR +chmod g-s dir +cd dir + +cat >printmodes.py <<EOF +import os, sys + +allnames = [] +isdir = {} +for root, dirs, files in os.walk(sys.argv[1]): + for d in dirs: + name = os.path.join(root, d) + isdir[name] = 1 + allnames.append(name) + for f in files: + name = os.path.join(root, f) + allnames.append(name) +allnames.sort() +for name in allnames: + suffix = name in isdir and '/' or '' + print '%05o %s%s' % (os.lstat(name).st_mode & 07777, name, suffix) +EOF + +cat >mode.py <<EOF +import sys +import os +print '%05o' % os.lstat(sys.argv[1]).st_mode +EOF + +umask 077 + +hg init repo +cd repo + +chmod 0770 .hg/store + +echo '% before commit' +echo '% store can be written by the group, other files cannot' +echo '% store is setgid' +python ../printmodes.py . + +mkdir dir +touch foo dir/bar +hg ci -qAm 'add files' + +echo +echo '% after commit' +echo '% working dir files can only be written by the owner' +echo '% files created in .hg can be written by the group' +echo '% (in particular, store/**, dirstate, branch cache file, undo files)' +echo '% new directories are setgid' +python ../printmodes.py . + +umask 007 +hg init ../push +echo +echo '% before push' +echo '% group can write everything' +python ../printmodes.py ../push + +umask 077 +hg -q push ../push +echo +echo '% after push' +echo '% group can still write everything' +python ../printmodes.py ../push + +# Test that we don't lose the setgid bit when we call chmod. +# Not all systems support setgid directories (e.g. HFS+), so +# just check that directories have the same mode. +cd .. +hg init setgid +cd setgid +chmod g+rwx .hg/store +chmod g+s .hg/store 2> /dev/null +mkdir dir +touch dir/file +hg ci -qAm 'add dir/file' +storemode=`python ../mode.py .hg/store` +dirmode=`python ../mode.py .hg/store/data/dir` +if [ "$storemode" != "$dirmode" ]; then + echo "$storemode != $dirmode" +fi +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tests/test-inherit-mode.out Wed Apr 09 15:28:30 2008 -0700 @@ -0,0 +1,54 @@ +% before commit +% store can be written by the group, other files cannot +% store is setgid +00700 ./.hg/ +00600 ./.hg/00changelog.i +00600 ./.hg/requires +00770 ./.hg/store/ + +% after commit +% working dir files can only be written by the owner +% files created in .hg can be written by the group +% (in particular, store/**, dirstate, branch cache file, undo files) +% new directories are setgid +00700 ./.hg/ +00600 ./.hg/00changelog.i +00660 ./.hg/dirstate +00600 ./.hg/requires +00770 ./.hg/store/ +00660 ./.hg/store/00changelog.i +00660 ./.hg/store/00manifest.i +00770 ./.hg/store/data/ +00770 ./.hg/store/data/dir/ +00660 ./.hg/store/data/dir/bar.i +00660 ./.hg/store/data/foo.i +00660 ./.hg/store/undo +00660 ./.hg/undo.branch +00660 ./.hg/undo.dirstate +00700 ./dir/ +00600 ./dir/bar +00600 ./foo + +% before push +% group can write everything +00770 ../push/.hg/ +00660 ../push/.hg/00changelog.i +00660 ../push/.hg/requires +00770 ../push/.hg/store/ + +% after push +% group can still write everything +00770 ../push/.hg/ +00660 ../push/.hg/00changelog.i +00660 ../push/.hg/branch.cache +00660 ../push/.hg/requires +00770 ../push/.hg/store/ +00660 ../push/.hg/store/00changelog.i +00660 ../push/.hg/store/00manifest.i +00770 ../push/.hg/store/data/ +00770 ../push/.hg/store/data/dir/ +00660 ../push/.hg/store/data/dir/bar.i +00660 ../push/.hg/store/data/foo.i +00660 ../push/.hg/store/undo +00660 ../push/.hg/undo.branch +00660 ../push/.hg/undo.dirstate
--- a/tests/test-install.out Wed Apr 09 15:27:57 2008 -0700 +++ b/tests/test-install.out Wed Apr 09 15:28:30 2008 -0700 @@ -2,7 +2,6 @@ Checking extensions... Checking templates... Checking patch... -Checking merge helper... Checking commit editor... Checking username... No problems detected
--- a/tests/test-issue352.out Wed Apr 09 15:27:57 2008 -0700 +++ b/tests/test-issue352.out Wed Apr 09 15:28:30 2008 -0700 @@ -1,13 +1,13 @@ adding he llo -abort: '\n' and '\r' disallowed in filenames +abort: '\n' and '\r' disallowed in filenames: 'he\rllo' adding he llo -abort: '\n' and '\r' disallowed in filenames +abort: '\n' and '\r' disallowed in filenames: 'he\rllo' adding hell o -abort: '\n' and '\r' disallowed in filenames +abort: '\n' and '\r' disallowed in filenames: 'hell\no' adding hell o -abort: '\n' and '\r' disallowed in filenames +abort: '\n' and '\r' disallowed in filenames: 'hell\no' f he llo he llo f hell o hell
--- a/tests/test-issue522.out Wed Apr 09 15:27:57 2008 -0700 +++ b/tests/test-issue522.out Wed Apr 09 15:28:30 2008 -0700 @@ -11,7 +11,7 @@ getting foo 1 files updated, 0 files merged, 0 files removed, 0 files unresolved (branch merge, don't forget to commit) -n 0 -2 unset foo +n 0 -2 unset foo M foo c6fc755d7e68f49f880599da29f15add41f42f5a 644 foo rev offset length base linkrev nodeid p1 p2
--- a/tests/test-issue612.out Wed Apr 09 15:27:57 2008 -0700 +++ b/tests/test-issue612.out Wed Apr 09 15:28:30 2008 -0700 @@ -1,6 +1,7 @@ adding src/a.c moving src/a.c to source/a.c 1 files updated, 0 files merged, 1 files removed, 0 files unresolved +created new head ? src/a.o merging src/a.c and source/a.c 1 files updated, 1 files merged, 0 files removed, 0 files unresolved
--- a/tests/test-issue619.out Wed Apr 09 15:27:57 2008 -0700 +++ b/tests/test-issue619.out Wed Apr 09 15:28:30 2008 -0700 @@ -6,5 +6,5 @@ 1 files updated, 0 files merged, 0 files removed, 0 files unresolved (branch merge, don't forget to commit) bogus fast-forward should fail -abort: there is nothing to merge, just use 'hg update' or look at 'hg heads' +abort: can't merge with ancestor done
--- a/tests/test-issue660 Wed Apr 09 15:27:57 2008 -0700 +++ b/tests/test-issue660 Wed Apr 09 15:28:30 2008 -0700 @@ -16,7 +16,7 @@ echo a > a/a echo % should fail - would corrupt dirstate -hg add a/a +hg add a/a echo % removing shadow hg rm --after a
--- a/tests/test-issue672.out Wed Apr 09 15:27:57 2008 -0700 +++ b/tests/test-issue672.out Wed Apr 09 15:28:30 2008 -0700 @@ -1,6 +1,7 @@ adding 1 adding 2 1 files updated, 0 files merged, 1 files removed, 0 files unresolved +created new head resolving manifests overwrite None partial False ancestor 81f4b099af3d local c64f439569a9+ remote 2f8037f47a5c @@ -17,6 +18,7 @@ 1 files updated, 0 files merged, 1 files removed, 0 files unresolved (branch merge, don't forget to commit) 1 files updated, 0 files merged, 1 files removed, 0 files unresolved +created new head 1 files updated, 0 files merged, 1 files removed, 0 files unresolved resolving manifests overwrite None partial False @@ -28,8 +30,10 @@ 1a -> 1 * checking for directory renames 1a: local moved to 1 -> m +picked tool 'internal:merge' for 1a (binary False symlink False) merging 1a and 1 my 1a@ac7575e3c052+ other 1@746e9549ea96 ancestor 1@81f4b099af3d + premerge successful 0 files updated, 1 files merged, 0 files removed, 0 files unresolved (branch merge, don't forget to commit) 1 files updated, 0 files merged, 1 files removed, 0 files unresolved @@ -44,8 +48,10 @@ checking for directory renames 1: remote moved to 1a -> m copying 1 to 1a +picked tool 'internal:merge' for 1 (binary False symlink False) merging 1 and 1a -my 1@746e9549ea96+ other 1a@2f8037f47a5c ancestor 1@81f4b099af3d +my 1@746e9549ea96+ other 1a@ac7575e3c052 ancestor 1@81f4b099af3d + premerge successful removing 1 0 files updated, 1 files merged, 0 files removed, 0 files unresolved (branch merge, don't forget to commit)
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tests/test-issue842 Wed Apr 09 15:28:30 2008 -0700 @@ -0,0 +1,20 @@ +hg init test +cd test +echo foo > a +hg ci -Ama + +hg up -r0000 +echo bar > a +echo % should issue warning +hg ci -Amb + +hg up -r0000 +echo stuffy > a +echo % should not issue warning +hg ci -q -Amc + +hg up -r0000 +echo crap > a +hg branch testing +echo % should not issue warning +hg ci -q -Amd
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tests/test-issue842.out Wed Apr 09 15:28:30 2008 -0700 @@ -0,0 +1,10 @@ +adding a +0 files updated, 0 files merged, 1 files removed, 0 files unresolved +% should issue warning +adding a +created new head +0 files updated, 0 files merged, 1 files removed, 0 files unresolved +% should not issue warning +0 files updated, 0 files merged, 1 files removed, 0 files unresolved +marked working directory as branch testing +% should not issue warning
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tests/test-journal-exists Wed Apr 09 15:28:30 2008 -0700 @@ -0,0 +1,12 @@ +#!/bin/sh + +hg init +echo a > a +hg ci -Am0 + +touch .hg/store/journal + +echo foo > a +hg ci -Am0 + +hg recover
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tests/test-journal-exists.out Wed Apr 09 15:28:30 2008 -0700 @@ -0,0 +1,8 @@ +adding a +abort: journal already exists - run hg recover! +rolling back interrupted transaction +checking changesets +checking manifests +crosschecking files in changesets and manifests +checking files +1 files, 1 changesets, 1 total revisions
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tests/test-keyword Wed Apr 09 15:28:30 2008 -0700 @@ -0,0 +1,308 @@ +#!/bin/sh + +cat <<EOF >> $HGRCPATH +[extensions] +hgext.keyword = +hgext.mq = +hgext.notify = +[keyword] +* = +b = ignore +[hooks] +commit= +commit.test=cp a hooktest +EOF + +echo % help +hg help keyword + +echo % hg kwdemo +hg --quiet kwdemo --default \ +| sed -e 's![^ ][^ ]*demo.txt,v!/TMP/demo.txt,v!' \ + -e 's/,v [a-z0-9][a-z0-9]* /,v xxxxxxxxxxxx /' \ + -e '/[$]Revision/ s/: [a-z0-9][a-z0-9]* /: xxxxxxxxxxxx /' \ + -e 's! 20[0-9][0-9]/[01][0-9]/[0-3][0-9] [0-2][0-9]:[0-6][0-9]:[0-6][0-9]! 2000/00/00 00:00:00!' + +hg --quiet kwdemo "Branch = {branches}" + +hg init Test-bndl +cd Test-bndl + +echo % kwshrink should exit silently in empty/invalid repo +hg kwshrink + +# Symlinks cannot be created on Windows. The bundle was made with: +# +# hg init t +# cd t +# echo a > a +# ln -s a sym +# hg add sym +# hg ci -m addsym -u mercurial +# hg bundle --base null ../test-keyword.hg +# +hg pull -u "$TESTDIR/test-keyword.hg" \ + | sed 's/pulling from.*test-keyword.hg/pulling from test-keyword.hg/' + +echo 'expand $Id$' > a +echo 'do not process $Id:' >> a +echo 'xxx $' >> a +echo 'ignore $Id$' > b +echo % cat +cat a b + +echo % addremove +hg addremove +echo % status +hg status + +echo % default keyword expansion including commit hook +echo % interrupted commit should not change state or run commit hook +hg --debug commit +echo % status +hg status + +echo % commit +hg --debug commit -mabsym -d '0 0' -u 'User Name <user@example.com>' +echo % status +hg status +echo % identify +hg debugrebuildstate +hg --quiet identify +echo % cat +cat a b +echo % hg cat +hg cat sym a b + +echo +echo % diff a hooktest +diff a hooktest + +echo % removing commit hook from config +sed -e '/\[hooks\]/,$ d' $HGRCPATH > $HGRCPATH.nohook +mv $HGRCPATH.nohook $HGRCPATH +rm hooktest + +echo % bundle +hg bundle --base null ../kw.hg + +cd .. +hg init Test +cd Test + +echo % notify on pull to check whether keywords stay as is in email +echo % ie. if patch.diff wrapper acts as it should + +cat <<EOF >> $HGRCPATH +[hooks] +incoming.notify = python:hgext.notify.hook +[notify] +sources = pull +diffstat = False +[reposubs] +* = Test +EOF + +echo % pull from bundle +hg pull -u ../kw.hg 2>&1 | sed -e '/^Date:/,/^diffs (/ d' + +echo % remove notify config +sed -e '/\[hooks\]/,$ d' $HGRCPATH > $HGRCPATH.nonotify +mv $HGRCPATH.nonotify $HGRCPATH + +echo % touch +touch a b +echo % status +hg status + +rm sym a b +echo % update +hg update +echo % cat +cat a b + +echo % check whether expansion is filewise +echo '$Id$' > c +echo 'tests for different changenodes' >> c +echo % commit c +hg commit -A -mcndiff -d '1 0' -u 'User Name <user@example.com>' +echo % force expansion +hg -v kwexpand +echo % compare changenodes in a c +cat a c + +echo % qinit -c +hg qinit -c +echo % qimport +hg qimport -r tip -n mqtest.diff +echo % qcommit +hg qcommit -mqtest +echo % keywords should not be expanded in patch +cat .hg/patches/mqtest.diff +echo % qpop +hg qpop +echo % qgoto - should imply qpush +hg qgoto mqtest.diff +echo % cat +cat c +echo % qpop and move on +hg qpop + +echo % copy +hg cp a c + +echo % kwfiles added +hg kwfiles + +echo % commit +hg --debug commit -ma2c -d '1 0' -u 'User Name <user@example.com>' +echo % cat a c +cat a c +echo % touch copied c +touch c +echo % status +hg status + +echo % kwfiles +hg kwfiles + +echo % diff --rev +hg diff --rev 1 | grep -v 'b/c' + +echo % rollback +hg rollback +echo % status +hg status +echo % update -C +hg update --clean + +echo % custom keyword expansion +echo % try with kwdemo +hg --quiet kwdemo "Xinfo = {author}: {desc}" + +cat <<EOF >>$HGRCPATH +[keywordmaps] +Id = {file} {node|short} {date|rfc822date} {author|user} +Xinfo = {author}: {desc} +EOF + +echo % cat +cat a b +echo % hg cat +hg cat sym a b + +echo +echo '$Xinfo$' >> a +cat <<EOF >> log +firstline +secondline +EOF + +echo % interrupted commit should not change state +hg commit +echo % status +hg status + +echo % commit +hg --debug commit -l log -d '2 0' -u 'User Name <user@example.com>' +rm log +echo % status +hg status + +echo % cat +cat a b +echo % hg cat +hg cat sym a b +echo + +echo % remove +hg debugrebuildstate +hg remove a +hg --debug commit -m rma +echo % status +hg status +echo % rollback +hg rollback +echo % status +hg status +echo % revert a +hg revert --no-backup --rev tip a +echo % cat a +cat a + +echo % clone to test incoming +cd .. +hg clone -r1 Test Test-a +cd Test-a +cat <<EOF >> .hg/hgrc +[paths] +default = ../Test +EOF +echo % incoming +# remove path to temp dir +hg incoming | sed -e 's/^\(comparing with \).*\(test-keyword.*\)/\1\2/' + +sed -e 's/Id.*/& rejecttest/' a > a.new +mv a.new a +echo % commit rejecttest +hg --debug commit -m'rejects?' -d '3 0' -u 'User Name <user@example.com>' +echo % export +hg export -o ../rejecttest.diff tip + +cd ../Test +echo % import +hg import ../rejecttest.diff +echo % cat +cat a b +echo +echo % rollback +hg rollback +echo % clean update +hg update --clean + +echo % kwexpand/kwshrink on selected files +mkdir x +echo % copy a x/a +hg copy a x/a +echo % kwexpand a +hg --verbose kwexpand a +echo % kwexpand x/a should abort +hg --verbose kwexpand x/a +cd x +hg --debug commit -m xa -d '3 0' -u 'User Name <user@example.com>' +echo % cat a +cat a +echo % kwshrink a inside directory x +hg --verbose kwshrink a +echo % cat a +cat a +cd .. + +echo % kwexpand nonexistent +hg kwexpand nonexistent 2>&1 | sed 's/nonexistent:.*/nonexistent:/' + +echo % switch off expansion +echo % kwshrink with unknown file u +cp a u +hg --verbose kwshrink +echo % cat +cat a b +echo % hg cat +hg cat sym a b +echo +rm $HGRCPATH +echo % cat +cat a b +echo % hg cat +hg cat sym a b +echo + +echo % hg serve +hg serve -p $HGPORT -d --pid-file=hg.pid -A access.log -E errors.log +cat hg.pid >> $DAEMON_PIDS +echo % hgweb changeset +("$TESTDIR/get-with-headers.py" localhost:$HGPORT '/rev/tip/?style=raw') +echo % hgweb filediff +("$TESTDIR/get-with-headers.py" localhost:$HGPORT '/diff/bb948857c743/a?style=raw') +echo % errors encountered +cat errors.log
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tests/test-keyword.out Wed Apr 09 15:28:30 2008 -0700 @@ -0,0 +1,431 @@ +% help +keyword extension - keyword expansion in local repositories + +This extension expands RCS/CVS-like or self-customized $Keywords$ +in tracked text files selected by your configuration. + +Keywords are only expanded in local repositories and not stored in +the change history. The mechanism can be regarded as a convenience +for the current user or for archive distribution. + +Configuration is done in the [keyword] and [keywordmaps] sections +of hgrc files. + +Example: + + [keyword] + # expand keywords in every python file except those matching "x*" + **.py = + x* = ignore + +Note: the more specific you are in your filename patterns + the less you lose speed in huge repos. + +For [keywordmaps] template mapping and expansion demonstration and +control run "hg kwdemo". + +An additional date template filter {date|utcdate} is provided. + +The default template mappings (view with "hg kwdemo -d") can be replaced +with customized keywords and templates. +Again, run "hg kwdemo" to control the results of your config changes. + +Before changing/disabling active keywords, run "hg kwshrink" to avoid +the risk of inadvertedly storing expanded keywords in the change history. + +To force expansion after enabling it, or a configuration change, run +"hg kwexpand". + +Also, when committing with the record extension or using mq's qrecord, be aware +that keywords cannot be updated. Again, run "hg kwexpand" on the files in +question to update keyword expansions after all changes have been checked in. + +Expansions spanning more than one line and incremental expansions, +like CVS' $Log$, are not supported. A keyword template map +"Log = {desc}" expands to the first line of the changeset description. + +list of commands: + + kwdemo print [keywordmaps] configuration and an expansion example + kwexpand expand keywords in working directory + kwfiles print files currently configured for keyword expansion + kwshrink revert expanded keywords in working directory + +use "hg -v help keyword" to show aliases and global options +% hg kwdemo +[extensions] +hgext.keyword = +[keyword] +* = +b = ignore +demo.txt = +[keywordmaps] +RCSFile = {file|basename},v +Author = {author|user} +Header = {root}/{file},v {node|short} {date|utcdate} {author|user} +Source = {root}/{file},v +Date = {date|utcdate} +Id = {file|basename},v {node|short} {date|utcdate} {author|user} +Revision = {node|short} +$RCSFile: demo.txt,v $ +$Author: test $ +$Header: /TMP/demo.txt,v xxxxxxxxxxxx 2000/00/00 00:00:00 test $ +$Source: /TMP/demo.txt,v $ +$Date: 2000/00/00 00:00:00 $ +$Id: demo.txt,v xxxxxxxxxxxx 2000/00/00 00:00:00 test $ +$Revision: xxxxxxxxxxxx $ +[extensions] +hgext.keyword = +[keyword] +* = +b = ignore +demo.txt = +[keywordmaps] +Branch = {branches} +$Branch: demobranch $ +% kwshrink should exit silently in empty/invalid repo +pulling from test-keyword.hg +requesting all 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 +% cat +expand $Id$ +do not process $Id: +xxx $ +ignore $Id$ +% addremove +adding a +adding b +% status +A a +A b +% default keyword expansion including commit hook +% interrupted commit should not change state or run commit hook +a +b +transaction abort! +rollback completed +abort: empty commit message +% status +A a +A b +% commit +a +b +overwriting a expanding keywords +running hook commit.test: cp a hooktest +% status +? hooktest +% identify +ef63ca68695b +% cat +expand $Id: a,v ef63ca68695b 1970/01/01 00:00:00 user $ +do not process $Id: +xxx $ +ignore $Id$ +% hg cat +expand $Id: a,v ef63ca68695b 1970/01/01 00:00:00 user $ +do not process $Id: +xxx $ +ignore $Id$ +a +% diff a hooktest +% removing commit hook from config +% bundle +2 changesets found +% notify on pull to check whether keywords stay as is in email +% ie. if patch.diff wrapper acts as it should +% pull from bundle +pulling from ../kw.hg +requesting all changes +adding changesets +adding manifests +adding file changes +added 2 changesets with 3 changes to 3 files + +diff -r 000000000000 -r a2392c293916 sym +--- /dev/null Thu Jan 01 00:00:00 1970 +0000 ++++ b/sym Sat Feb 09 20:25:47 2008 +0100 +@@ -0,0 +1,1 @@ ++a +\ No newline at end of file + +diff -r a2392c293916 -r ef63ca68695b a +--- /dev/null Thu Jan 01 00:00:00 1970 +0000 ++++ b/a Thu Jan 01 00:00:00 1970 +0000 +@@ -0,0 +1,3 @@ ++expand $Id$ ++do not process $Id: ++xxx $ +diff -r a2392c293916 -r ef63ca68695b b +--- /dev/null Thu Jan 01 00:00:00 1970 +0000 ++++ b/b Thu Jan 01 00:00:00 1970 +0000 +@@ -0,0 +1,1 @@ ++ignore $Id$ +3 files updated, 0 files merged, 0 files removed, 0 files unresolved +% remove notify config +% touch +% status +% update +3 files updated, 0 files merged, 0 files removed, 0 files unresolved +% cat +expand $Id: a,v ef63ca68695b 1970/01/01 00:00:00 user $ +do not process $Id: +xxx $ +ignore $Id$ +% check whether expansion is filewise +% commit c +adding c +% force expansion +overwriting a expanding keywords +overwriting c expanding keywords +% compare changenodes in a c +expand $Id: a,v ef63ca68695b 1970/01/01 00:00:00 user $ +do not process $Id: +xxx $ +$Id: c,v 40a904bbbe4c 1970/01/01 00:00:01 user $ +tests for different changenodes +% qinit -c +% qimport +% qcommit +% keywords should not be expanded in patch +# HG changeset patch +# User User Name <user@example.com> +# Date 1 0 +# Node ID 40a904bbbe4cd4ab0a1f28411e35db26341a40ad +# Parent ef63ca68695bc9495032c6fda1350c71e6d256e9 +cndiff + +diff -r ef63ca68695b -r 40a904bbbe4c c +--- /dev/null Thu Jan 01 00:00:00 1970 +0000 ++++ b/c Thu Jan 01 00:00:01 1970 +0000 +@@ -0,0 +1,2 @@ ++$Id$ ++tests for different changenodes +% qpop +Patch queue now empty +% qgoto - should imply qpush +applying mqtest.diff +Now at: mqtest.diff +% cat +$Id: c,v 40a904bbbe4c 1970/01/01 00:00:01 user $ +tests for different changenodes +% qpop and move on +Patch queue now empty +% copy +% kwfiles added +a +c +% commit +c + c: copy a:0045e12f6c5791aac80ca6cbfd97709a88307292 +overwriting c expanding keywords +% cat a c +expand $Id: a,v ef63ca68695b 1970/01/01 00:00:00 user $ +do not process $Id: +xxx $ +expand $Id: c,v e22d299ac0c2 1970/01/01 00:00:01 user $ +do not process $Id: +xxx $ +% touch copied c +% status +% kwfiles +a +c +% diff --rev +diff -r ef63ca68695b c +--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +@@ -0,0 +1,3 @@ ++expand $Id$ ++do not process $Id: ++xxx $ +% rollback +rolling back last transaction +% status +A c +% update -C +0 files updated, 0 files merged, 1 files removed, 0 files unresolved +% custom keyword expansion +% try with kwdemo +[extensions] +hgext.keyword = +[keyword] +* = +b = ignore +demo.txt = +[keywordmaps] +Xinfo = {author}: {desc} +$Xinfo: test: hg keyword config and expansion example $ +% cat +expand $Id: a,v ef63ca68695b 1970/01/01 00:00:00 user $ +do not process $Id: +xxx $ +ignore $Id$ +% hg cat +expand $Id: a ef63ca68695b Thu, 01 Jan 1970 00:00:00 +0000 user $ +do not process $Id: +xxx $ +ignore $Id$ +a +% interrupted commit should not change state +transaction abort! +rollback completed +abort: empty commit message +% status +M a +? log +% commit +a +overwriting a expanding keywords +% status +% cat +expand $Id: a bb948857c743 Thu, 01 Jan 1970 00:00:02 +0000 user $ +do not process $Id: +xxx $ +$Xinfo: User Name <user@example.com>: firstline $ +ignore $Id$ +% hg cat +expand $Id: a bb948857c743 Thu, 01 Jan 1970 00:00:02 +0000 user $ +do not process $Id: +xxx $ +$Xinfo: User Name <user@example.com>: firstline $ +ignore $Id$ +a +% remove +% status +% rollback +rolling back last transaction +% status +R a +% revert a +% cat a +expand $Id: a bb948857c743 Thu, 01 Jan 1970 00:00:02 +0000 user $ +do not process $Id: +xxx $ +$Xinfo: User Name <user@example.com>: firstline $ +% clone to test incoming +requesting all changes +adding changesets +adding manifests +adding file changes +added 2 changesets with 3 changes to 3 files +updating working directory +3 files updated, 0 files merged, 0 files removed, 0 files unresolved +% incoming +comparing with test-keyword/Test +searching for changes +changeset: 2:bb948857c743 +tag: tip +user: User Name <user@example.com> +date: Thu Jan 01 00:00:02 1970 +0000 +summary: firstline + +% commit rejecttest +a +overwriting a expanding keywords +% export +% import +applying ../rejecttest.diff +% cat +expand $Id: a 4e0994474d25 Thu, 01 Jan 1970 00:00:03 +0000 user $ rejecttest +do not process $Id: rejecttest +xxx $ +$Xinfo: User Name <user@example.com>: rejects? $ +ignore $Id$ + +% rollback +rolling back last transaction +% clean update +1 files updated, 0 files merged, 0 files removed, 0 files unresolved +% kwexpand/kwshrink on selected files +% copy a x/a +% kwexpand a +overwriting a expanding keywords +% kwexpand x/a should abort +abort: outstanding uncommitted changes in given files +x/a + x/a: copy a:779c764182ce5d43e2b1eb66ce06d7b47bfe342e +overwriting x/a expanding keywords +% cat a +expand $Id: x/a cfa68229c116 Thu, 01 Jan 1970 00:00:03 +0000 user $ +do not process $Id: +xxx $ +$Xinfo: User Name <user@example.com>: xa $ +% kwshrink a inside directory x +overwriting x/a shrinking keywords +% cat a +expand $Id$ +do not process $Id: +xxx $ +$Xinfo$ +% kwexpand nonexistent +nonexistent: +% switch off expansion +% kwshrink with unknown file u +overwriting a shrinking keywords +overwriting x/a shrinking keywords +% cat +expand $Id$ +do not process $Id: +xxx $ +$Xinfo$ +ignore $Id$ +% hg cat +expand $Id: a bb948857c743 Thu, 01 Jan 1970 00:00:02 +0000 user $ +do not process $Id: +xxx $ +$Xinfo: User Name <user@example.com>: firstline $ +ignore $Id$ +a +% cat +expand $Id$ +do not process $Id: +xxx $ +$Xinfo$ +ignore $Id$ +% hg cat +expand $Id$ +do not process $Id: +xxx $ +$Xinfo$ +ignore $Id$ +a +% hg serve +% hgweb changeset +200 Script output follows + + +# HG changeset patch +# User User Name <user@example.com> +# Date 3 0 +# Node ID cfa68229c1167443337266ebac453c73b1d5d16e +# Parent bb948857c743469b22bbf51f7ec8112279ca5d83 +xa + +--- /dev/null Thu Jan 01 00:00:00 1970 +0000 ++++ b/x/a Thu Jan 01 00:00:03 1970 +0000 +@@ -0,0 +1,4 @@ ++expand $Id$ ++do not process $Id: ++xxx $ ++$Xinfo$ + +% hgweb filediff +200 Script output follows + + +--- a/a Thu Jan 01 00:00:00 1970 +0000 ++++ b/a Thu Jan 01 00:00:02 1970 +0000 +@@ -1,3 +1,4 @@ + expand $Id$ + do not process $Id: + xxx $ ++$Xinfo$ + + + + +% errors encountered
--- a/tests/test-lock-badness.out Wed Apr 09 15:27:57 2008 -0700 +++ b/tests/test-lock-badness.out Wed Apr 09 15:28:30 2008 -0700 @@ -1,4 +1,5 @@ adding a +updating working directory 1 files updated, 0 files merged, 0 files removed, 0 files unresolved adding b pushing to ../a
--- a/tests/test-log Wed Apr 09 15:27:57 2008 -0700 +++ b/tests/test-log Wed Apr 09 15:28:30 2008 -0700 @@ -41,6 +41,11 @@ hg ci -Ame2 -d '6 0' hg log -vC --template '{rev} {file_copies%filecopy}\n' -r 5 +echo % log copies, execute bit set +chmod +x e +hg ci -me3 -d '7 0' +hg log -vC --template '{rev} {file_copies%filecopy}\n' -r 6 + echo '% log -p d' hg log -pv d
--- a/tests/test-log.out Wed Apr 09 15:27:57 2008 -0700 +++ b/tests/test-log.out Wed Apr 09 15:28:30 2008 -0700 @@ -85,7 +85,10 @@ % log copies, non-linear manifest 1 files updated, 0 files merged, 1 files removed, 0 files unresolved adding foo +created new head 5 e (dir/b) +% log copies, execute bit set +6 % log -p d changeset: 3:16b60bf3f99a user: test @@ -104,6 +107,7 @@ adding base 1 files updated, 0 files merged, 0 files removed, 0 files unresolved adding b1 +created new head % log -f changeset: 3:e62f78d544b4 tag: tip @@ -124,6 +128,7 @@ 1 files updated, 0 files merged, 1 files removed, 0 files unresolved adding b2 +created new head % log -f -r 1:tip changeset: 1:3d5bf5654eda user: test @@ -218,4 +223,4 @@ summary: b1 % log -r "" -abort: Ambiguous identifier! +abort: 00changelog.i@: ambiguous identifier!
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tests/test-mactext Wed Apr 09 15:28:30 2008 -0700 @@ -0,0 +1,32 @@ +#!/bin/sh + +cat > unix2mac.py <<EOF +import sys + +for path in sys.argv[1:]: + data = file(path, 'rb').read() + data = data.replace('\n', '\r') + file(path, 'wb').write(data) +EOF + +cat > print.py <<EOF +import sys +print(sys.stdin.read().replace('\n', '<LF>').replace('\r', '<CR>').replace('\0', '<NUL>')) +EOF + +hg init +echo '[hooks]' >> .hg/hgrc +echo 'pretxncommit.cr = python:hgext.win32text.forbidcr' >> .hg/hgrc +echo 'pretxnchangegroup.cr = python:hgext.win32text.forbidcr' >> .hg/hgrc +cat .hg/hgrc +echo + +echo hello > f +hg add f +hg ci -m 1 -d'0 0' +echo + +python unix2mac.py f +hg ci -m 2 -d'0 0' +hg cat f | python print.py +cat f | python print.py
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tests/test-mactext.out Wed Apr 09 15:28:30 2008 -0700 @@ -0,0 +1,12 @@ +[hooks] +pretxncommit.cr = python:hgext.win32text.forbidcr +pretxnchangegroup.cr = python:hgext.win32text.forbidcr + + +Attempt to commit or push text file(s) using CR line endings +in dea860dc51ec: f +transaction abort! +rollback completed +abort: pretxncommit.cr hook failed +hello<LF> +hello<CR>
--- a/tests/test-manifest-merging.out Wed Apr 09 15:27:57 2008 -0700 +++ b/tests/test-manifest-merging.out Wed Apr 09 15:28:30 2008 -0700 @@ -2,6 +2,7 @@ % create alpha in first repo adding alpha % clone foo-base to foo-work +updating working directory 1 files updated, 0 files merged, 0 files removed, 0 files unresolved % create beta in second repo adding beta
--- a/tests/test-merge-commit.out Wed Apr 09 15:27:57 2008 -0700 +++ b/tests/test-merge-commit.out Wed Apr 09 15:28:30 2008 -0700 @@ -1,4 +1,5 @@ 1 files updated, 0 files merged, 0 files removed, 0 files unresolved +created new head merging bar and foo 0 files updated, 1 files merged, 0 files removed, 0 files unresolved (branch merge, don't forget to commit) @@ -14,6 +15,7 @@ 0 0 7 0 0 690b295714ae 000000000000 000000000000 1 7 13 1 1 9e25c27b8757 690b295714ae 000000000000 1 files updated, 0 files merged, 0 files removed, 0 files unresolved +created new head 4:2d2f9a22c82b 2:0a3ab4856510 3:7d3b554bfdf1 2:0a3ab4856510 1:5cd961e4045d 2:0a3ab4856510 0:2665aaee66e9 @@ -25,8 +27,10 @@ ancestor 0a3ab4856510 local 2d2f9a22c82b+ remote 7d3b554bfdf1 searching for copies back to rev 1 bar: versions differ -> m +picked tool 'internal:merge' for bar (binary False symlink False) merging bar my bar@2d2f9a22c82b+ other bar@7d3b554bfdf1 ancestor bar@0a3ab4856510 + premerge successful 0 files updated, 1 files merged, 0 files removed, 0 files unresolved (branch merge, don't forget to commit) % contents of bar should be line1 line2 @@ -60,6 +64,7 @@ 0 0 7 0 0 690b295714ae 000000000000 000000000000 1 7 13 1 1 9e25c27b8757 690b295714ae 000000000000 1 files updated, 0 files merged, 0 files removed, 0 files unresolved +created new head 4:2d2f9a22c82b 2:0a3ab4856510 3:96ab80c60897 1:5cd961e4045d 2:0a3ab4856510 2:0a3ab4856510 0:2665aaee66e9 @@ -71,8 +76,10 @@ ancestor 0a3ab4856510 local 2d2f9a22c82b+ remote 96ab80c60897 searching for copies back to rev 1 bar: versions differ -> m +picked tool 'internal:merge' for bar (binary False symlink False) merging bar my bar@2d2f9a22c82b+ other bar@96ab80c60897 ancestor bar@0a3ab4856510 + premerge successful 0 files updated, 1 files merged, 0 files removed, 0 files unresolved (branch merge, don't forget to commit) % contents of bar should be line1 line2
--- a/tests/test-merge-default.out Wed Apr 09 15:27:57 2008 -0700 +++ b/tests/test-merge-default.out Wed Apr 09 15:28:30 2008 -0700 @@ -1,6 +1,8 @@ adding a 1 files updated, 0 files merged, 0 files removed, 0 files unresolved +created new head 1 files updated, 0 files merged, 0 files removed, 0 files unresolved +created new head 1 files updated, 0 files merged, 0 files removed, 0 files unresolved % should fail because not at a head abort: repo has 3 heads - please merge with an explicit rev
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tests/test-merge-force Wed Apr 09 15:28:30 2008 -0700 @@ -0,0 +1,27 @@ +#!/bin/sh + +hg init repo +cd repo + +echo a > a +hg ci -qAm 'add a' + +echo b > b +hg ci -qAm 'add b' + +hg up -qC 0 +hg rm a +hg ci -m 'rm a' + +hg up -qC 1 +rm a + +echo '% local deleted a file, remote removed' +hg merge # should fail, since there are deleted files +hg -v merge --force +echo % should show a as removed +hg st + +hg ci -m merge +echo % manifest. should not have a: +hg manifest
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tests/test-merge-force.out Wed Apr 09 15:28:30 2008 -0700 @@ -0,0 +1,11 @@ +created new head +% local deleted a file, remote removed +abort: outstanding uncommitted changes +resolving manifests +removing a +0 files updated, 0 files merged, 1 files removed, 0 files unresolved +(branch merge, don't forget to commit) +% should show a as removed +R a +% manifest. should not have a: +b
--- a/tests/test-merge-local.out Wed Apr 09 15:27:57 2008 -0700 +++ b/tests/test-merge-local.out Wed Apr 09 15:28:30 2008 -0700 @@ -18,27 +18,28 @@ M zzz2_merge_bad # local merge with bad merge tool merging zzz1_merge_ok -merging zzz1_merge_ok failed! merging zzz2_merge_bad merging zzz2_merge_bad failed! -3 files updated, 0 files merged, 2 files removed, 2 files unresolved +3 files updated, 1 files merged, 2 files removed, 1 files unresolved There are unresolved merges with locally modified files. -You can redo the full merge using: +You can finish the partial merge using: hg update 0 hg update 1 2 files updated, 0 files merged, 3 files removed, 0 files unresolved --- a/zzz1_merge_ok +++ b/zzz1_merge_ok ++new first line +new last line --- a/zzz2_merge_bad +++ b/zzz2_merge_bad +another last line M zzz1_merge_ok M zzz2_merge_bad +? zzz2_merge_bad.orig # local merge with conflicts -warning: conflicts during merge. merging zzz1_merge_ok merging zzz2_merge_bad +warning: conflicts during merge. merging zzz2_merge_bad failed! 3 files updated, 1 files merged, 2 files removed, 1 files unresolved There are unresolved merges with locally modified files. @@ -57,6 +58,7 @@ +new last line M zzz1_merge_ok M zzz2_merge_bad +? zzz2_merge_bad.orig # local merge without conflicts merging zzz1_merge_ok 4 files updated, 1 files merged, 2 files removed, 0 files unresolved
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tests/test-merge-prompt Wed Apr 09 15:28:30 2008 -0700 @@ -0,0 +1,69 @@ +#!/bin/sh +# +# Test for b5605d88dc27 +# Make ui.prompt repeat on "unrecognized response" again (issue897) +# and for 840e2b315c1f +# Fix misleading error and prompts during update/merge (issue556) + +status() { + [ $? -ne 0 ] && echo "failed." + echo "status:" + hg st -A file1 file2 + for file in file1 file2; do + if [ -f $file ]; then + echo "$file:" + cat $file + else + echo "$file does not exist" + fi + done +} + +hg init repo +cd repo +echo 1 > file1 +echo 2 > file2 +hg ci -Am 'added file1 and file2' # rev 0 + +hg rm file1 +echo changed >> file2 +hg ci -m 'removed file1, changed file2' # rev 1 + +hg co 0 +echo changed >> file1 +hg rm file2 +hg ci -m 'changed file1, removed file2' # rev 2 + +echo +echo "# non-interactive merge" +hg merge -y || echo "failed" +status + +echo +echo "# interactive merge" +hg co -C +hg merge --config ui.interactive=true <<EOF || echo "failed" +c +d +EOF +status + +echo +echo "# interactive merge with bad input" +hg co -C +hg merge --config ui.interactive=true <<EOF || echo "failed" +foo +bar +d +baz +c +EOF +status + +echo +echo "# interactive merge with not enough input" +hg co -C +hg merge --config ui.interactive=true <<EOF || echo "failed" +d +EOF +status
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tests/test-merge-prompt.out Wed Apr 09 15:28:30 2008 -0700 @@ -0,0 +1,65 @@ +adding file1 +adding file2 +2 files updated, 0 files merged, 0 files removed, 0 files unresolved +created new head + +# non-interactive merge +1 files updated, 0 files merged, 0 files removed, 0 files unresolved +(branch merge, don't forget to commit) +status: +M file2 +C file1 +file1: +1 +changed +file2: +2 +changed + +# interactive merge +0 files updated, 0 files merged, 1 files removed, 0 files unresolved + 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) +status: +file2: No such file or directory +C file1 +file1: +1 +changed +file2 does not exist + +# interactive merge with bad input +0 files updated, 0 files merged, 0 files removed, 0 files unresolved + local changed file1 which remote deleted +use (c)hanged version or (d)elete? unrecognized response + local changed file1 which remote deleted +use (c)hanged version or (d)elete? unrecognized response + 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 +use (c)hanged version or leave (d)eleted? 1 files updated, 0 files merged, 1 files removed, 0 files unresolved +(branch merge, don't forget to commit) +status: +M file2 +R file1 +file1 does not exist +file2: +2 +changed + +# interactive merge with not enough input +1 files updated, 0 files merged, 1 files removed, 0 files unresolved + 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 +failed +status: +file2: No such file or directory +C file1 +file1: +1 +changed +file2 does not exist
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tests/test-merge-remove Wed Apr 09 15:28:30 2008 -0700 @@ -0,0 +1,43 @@ +#!/bin/sh + +hg init repo +cd repo + +echo foo > foo +echo bar > bar +hg ci -qAm 'add foo bar' + +echo foo2 >> foo +echo bleh > bar +hg ci -m 'change foo bar' + +hg up -qC 0 +hg mv foo foo1 +echo foo1 > foo1 +hg cat foo >> foo1 +hg ci -m 'mv foo foo1' + +hg merge +hg debugstate --nodates +hg st -q + +echo '% removing foo1 and bar' +cp foo1 F +cp bar B +hg rm -f foo1 bar +hg debugstate --nodates +hg st -qC + +echo '% readding foo1 and bar' +cp F foo1 +cp B bar +hg add -v foo1 bar +hg debugstate --nodates +hg st -qC + +echo '% reverting foo1 and bar' +hg revert -vr . foo1 bar +hg debugstate --nodates +hg st -qC +hg diff +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tests/test-merge-remove.out Wed Apr 09 15:28:30 2008 -0700 @@ -0,0 +1,37 @@ +created new head +merging foo1 and foo +1 files updated, 1 files merged, 0 files removed, 0 files unresolved +(branch merge, don't forget to commit) +n 0 -2 bar +m 644 14 foo1 +copy: foo -> foo1 +M bar +M foo1 +% removing foo1 and bar +r 0 -2 bar +r 0 -1 foo1 +copy: foo -> foo1 +R bar +R foo1 + foo +% readding foo1 and bar +adding bar +adding foo1 +n 0 -2 bar +m 644 14 foo1 +copy: foo -> foo1 +M bar +M foo1 + foo +% reverting foo1 and bar +warning: working directory has two parents, tag '.' uses the first +saving current version of bar as bar.orig +reverting bar +saving current version of foo1 as foo1.orig +reverting foo1 +n 0 -2 bar +m 644 14 foo1 +copy: foo -> foo1 +M bar +M foo1 + foo
--- a/tests/test-merge-revert2.out Wed Apr 09 15:27:57 2008 -0700 +++ b/tests/test-merge-revert2.out Wed Apr 09 15:28:30 2008 -0700 @@ -9,8 +9,8 @@ 1 files updated, 0 files merged, 0 files removed, 0 files unresolved f248da0d4c3e tip 1 files updated, 0 files merged, 0 files removed, 0 files unresolved +merging file1 warning: conflicts during merge. -merging file1 merging file1 failed! 0 files updated, 0 files merged, 0 files removed, 1 files unresolved There are unresolved merges with locally modified files. @@ -20,18 +20,22 @@ diff -r f248da0d4c3e file1 --- a/file1 +++ b/file1 -@@ -1,3 +1,7 @@ added file1 +@@ -1,3 +1,7 @@ added file1 another line of text -+<<<<<<< my ++<<<<<<< local +changed file1 different +======= changed file1 +>>>>>>> other M file1 +? file1.orig f248da0d4c3e+ tip reverting file1 +? file1.orig f248da0d4c3e tip +? file1.orig f248da0d4c3e tip 0 files updated, 0 files merged, 0 files removed, 0 files unresolved +? file1.orig f248da0d4c3e tip
--- a/tests/test-merge-types Wed Apr 09 15:27:57 2008 -0700 +++ b/tests/test-merge-types Wed Apr 09 15:28:30 2008 -0700 @@ -2,35 +2,38 @@ hg init echo a > a -hg ci -Amadd +hg ci -Amadd # 0 chmod +x a -hg ci -mexecutable +hg ci -mexecutable # 1 hg up 0 rm a ln -s symlink a -hg ci -msymlink - -hg merge +hg ci -msymlink # 2 +hg merge --debug -echo % symlink is left parent, executable is right +echo % symlink is local parent, executable is other -if [ -L a ]; then +if [ -h a ]; then echo a is a symlink - readlink a + $TESTDIR/readlink.py a elif [ -x a ]; then echo a is executable +else + echo "a has no flags (default for conflicts)" fi hg update -C 1 -hg merge +hg merge --debug -echo % symlink is right parent, executable is left +echo % symlink is other parent, executable is local -if [ -L a ]; then +if [ -h a ]; then echo a is a symlink - readlink a + $TESTDIR/readlink.py a elif [ -x a ]; then echo a is executable +else + echo "a has no flags (default for conflicts)" fi
--- a/tests/test-merge-types.out Wed Apr 09 15:27:57 2008 -0700 +++ b/tests/test-merge-types.out Wed Apr 09 15:28:30 2008 -0700 @@ -1,1 +1,23 @@ -### This test is for a known, unfixed bug ### +adding a +0 files updated, 0 files merged, 0 files removed, 0 files unresolved +created new head +resolving manifests + overwrite None partial False + ancestor c334dc3be0da local 521a1e40188f+ remote 3574f3e69b1c + searching for copies back to rev 1 + a: update permissions -> e +0 files updated, 0 files merged, 0 files removed, 0 files unresolved +(branch merge, don't forget to commit) +% symlink is local parent, executable is other +a has no flags (default for conflicts) +1 files updated, 0 files merged, 0 files removed, 0 files unresolved +resolving manifests + overwrite None partial False + ancestor c334dc3be0da local 3574f3e69b1c+ remote 521a1e40188f + searching for copies back to rev 1 + a: remote is newer -> g +getting a +1 files updated, 0 files merged, 0 files removed, 0 files unresolved +(branch merge, don't forget to commit) +% symlink is other parent, executable is local +a has no flags (default for conflicts)
--- a/tests/test-merge1.out Wed Apr 09 15:27:57 2008 -0700 +++ b/tests/test-merge1.out Wed Apr 09 15:28:30 2008 -0700 @@ -1,4 +1,5 @@ 0 files updated, 0 files merged, 1 files removed, 0 files unresolved +created new head %% no merges expected 1 files updated, 0 files merged, 0 files removed, 0 files unresolved (branch merge, don't forget to commit) @@ -9,8 +10,9 @@ +This is file b1 M b 0 files updated, 0 files merged, 1 files removed, 0 files unresolved +created new head %% merge should fail -abort: untracked local file 'b' differs from remote version +abort: untracked file in working directory differs from file in requested revision: 'b' %% merge of b expected merging for b merging b @@ -24,6 +26,7 @@ M b %% 1 files updated, 0 files merged, 0 files removed, 0 files unresolved +created new head Contents of b should be "this is file b1" This is file b1 %% merge fails @@ -34,11 +37,12 @@ diff -r c1dd73cbf59f b --- a/b +++ b/b -@@ -1,1 +1,1 @@ This is file b1 +@@ -1,1 +1,1 @@ -This is file b1 +This is file b22 M b 1 files updated, 0 files merged, 0 files removed, 0 files unresolved +created new head %% merge of b should fail abort: outstanding uncommitted changes %% merge of b expected @@ -49,7 +53,7 @@ diff -r c1dd73cbf59f b --- a/b +++ b/b -@@ -1,1 +1,1 @@ This is file b1 +@@ -1,1 +1,1 @@ -This is file b1 +This is file b33 M b
--- a/tests/test-merge10.out Wed Apr 09 15:27:57 2008 -0700 +++ b/tests/test-merge10.out Wed Apr 09 15:28:30 2008 -0700 @@ -1,3 +1,4 @@ +updating working directory 1 files updated, 0 files merged, 0 files removed, 0 files unresolved pulling from ../a searching for changes @@ -14,6 +15,6 @@ diff -r d02b3fc32762 testdir/subdir/a --- a/testdir/subdir/a +++ b/testdir/subdir/a -@@ -1,1 +1,1 @@ a +@@ -1,1 +1,1 @@ -a +alpha
--- a/tests/test-merge2.out Wed Apr 09 15:27:57 2008 -0700 +++ b/tests/test-merge2.out Wed Apr 09 15:28:30 2008 -0700 @@ -1,5 +1,8 @@ 0 files updated, 0 files merged, 0 files removed, 0 files unresolved +created new head 0 files updated, 0 files merged, 0 files removed, 0 files unresolved adding b +created new head 0 files updated, 0 files merged, 0 files removed, 0 files unresolved adding b +created new head
--- a/tests/test-merge4.out Wed Apr 09 15:27:57 2008 -0700 +++ b/tests/test-merge4.out Wed Apr 09 15:28:30 2008 -0700 @@ -1,3 +1,4 @@ 0 files updated, 0 files merged, 1 files removed, 0 files unresolved +created new head 1 files updated, 0 files merged, 0 files removed, 0 files unresolved (branch merge, don't forget to commit)
--- a/tests/test-merge5 Wed Apr 09 15:27:57 2008 -0700 +++ b/tests/test-merge5 Wed Apr 09 15:28:30 2008 -0700 @@ -12,9 +12,14 @@ hg update 0 rm b hg commit -A -m"comment #2" -d "1000000 0" +mv a c # in theory, we shouldn't need the "-y" below, but it prevents # this test from hanging when "hg update" erroneously prompts the # user for "keep or delete" +echo % should abort +hg update -y 1 +mv c a +echo % should succeed hg update -y 1 exit 0
--- a/tests/test-merge5.out Wed Apr 09 15:27:57 2008 -0700 +++ b/tests/test-merge5.out Wed Apr 09 15:28:30 2008 -0700 @@ -1,3 +1,7 @@ 1 files updated, 0 files merged, 0 files removed, 0 files unresolved removing b -abort: update spans branches, use 'hg merge' or 'hg update -C' to lose changes +created new head +% should abort +abort: crosses branches (use 'hg merge' or 'hg update -C' to discard changes) +% should succeed +abort: crosses branches (use 'hg merge' or 'hg update -C')
--- a/tests/test-merge6.out Wed Apr 09 15:27:57 2008 -0700 +++ b/tests/test-merge6.out Wed Apr 09 15:28:30 2008 -0700 @@ -1,5 +1,8 @@ +updating working directory 2 files updated, 0 files merged, 0 files removed, 0 files unresolved +updating working directory 1 files updated, 0 files merged, 0 files removed, 0 files unresolved +updating working directory 2 files updated, 0 files merged, 0 files removed, 0 files unresolved pulling from ../B1 searching for changes
--- a/tests/test-merge7.out Wed Apr 09 15:27:57 2008 -0700 +++ b/tests/test-merge7.out Wed Apr 09 15:28:30 2008 -0700 @@ -1,3 +1,4 @@ +updating working directory 1 files updated, 0 files merged, 0 files removed, 0 files unresolved pulling from ../test-a searching for changes @@ -6,8 +7,8 @@ adding file changes added 1 changesets with 1 changes to 1 files (+1 heads) (run 'hg heads' to see heads, 'hg merge' to merge) +merging test.txt warning: conflicts during merge. -merging test.txt merging test.txt failed! 0 files updated, 0 files merged, 0 files removed, 1 files unresolved There are unresolved merges, you can redo the full merge using: @@ -20,21 +21,22 @@ adding file changes added 1 changesets with 1 changes to 1 files (+1 heads) (run 'hg heads' to see heads, 'hg merge' to merge) -warning: conflicts during merge. resolving manifests overwrite None partial False ancestor faaea63e63a9 local 451c744aabcc+ remote a070d41e8360 searching for copies back to rev 1 test.txt: versions differ -> m +picked tool 'internal:merge' for test.txt (binary False symlink False) merging test.txt my test.txt@451c744aabcc+ other test.txt@a070d41e8360 ancestor test.txt@faaea63e63a9 +warning: conflicts during merge. merging test.txt failed! 0 files updated, 0 files merged, 0 files removed, 1 files unresolved There are unresolved merges, you can redo the full merge using: hg update -C 3 hg merge 4 one -<<<<<<< my +<<<<<<< local two-point-five ======= two-point-one
--- a/tests/test-merge8.out Wed Apr 09 15:27:57 2008 -0700 +++ b/tests/test-merge8.out Wed Apr 09 15:28:30 2008 -0700 @@ -1,3 +1,4 @@ +updating working directory 1 files updated, 0 files merged, 0 files removed, 0 files unresolved pulling from ../a searching for changes
--- a/tests/test-merge9.out Wed Apr 09 15:27:57 2008 -0700 +++ b/tests/test-merge9.out Wed Apr 09 15:28:30 2008 -0700 @@ -2,11 +2,11 @@ adding foo adding quux1 adding quux2 +created new head merging bar merging bar failed! merging foo and baz -merging baz failed! -1 files updated, 0 files merged, 0 files removed, 2 files unresolved +1 files updated, 1 files merged, 0 files removed, 1 files unresolved There are unresolved merges, you can redo the full merge using: hg update -C 2 hg merge 1 @@ -14,8 +14,7 @@ merging bar merging bar failed! merging baz and foo -merging baz failed! -1 files updated, 0 files merged, 0 files removed, 2 files unresolved +1 files updated, 1 files merged, 0 files removed, 1 files unresolved There are unresolved merges, you can redo the full merge using: hg update -C 1 hg merge 2
--- a/tests/test-mq Wed Apr 09 15:27:57 2008 -0700 +++ b/tests/test-mq Wed Apr 09 15:28:30 2008 -0700 @@ -42,6 +42,12 @@ hg --cwd c qinit -c hg -R c/.hg/patches st +echo % qnew should refuse bad patch names +hg -R c qnew series +hg -R c qnew status +hg -R c qnew guards +hg -R c qnew .hgignore + echo % qnew implies add hg -R c qnew test.patch @@ -260,6 +266,14 @@ hg strip tip 2>&1 | sed 's/\(saving bundle to \).*/\1/' hg unbundle .hg/strip-backup/* +echo % strip with local changes, should complain +hg up +echo y>y +hg add y +hg strip tip | sed 's/\(saving bundle to \).*/\1/' +echo % --force strip with local changes +hg strip -f tip 2>&1 | sed 's/\(saving bundle to \).*/\1/' + echo '% cd b; hg qrefresh' hg init refresh cd refresh @@ -297,6 +311,13 @@ echo % mq tags hg log --template '{rev} {tags}\n' -r qparent:qtip +echo % bad node in status +hg qpop +hg strip -qn tip +hg tip 2>&1 | sed -e 's/unknown node .*/unknown node/' +hg branches 2>&1 | sed -e 's/unknown node .*/unknown node/' +hg qpop + cat >>$HGRCPATH <<EOF [diff] git = True @@ -474,3 +495,13 @@ qlog cd .. +echo % 'test applying on an empty file (issue 1033)' +hg init empty +cd empty +touch a +hg ci -Am addempty +echo a > a +hg qnew -f -e changea +hg qpop +hg qpush +cd ..
--- a/tests/test-mq-caches Wed Apr 09 15:27:57 2008 -0700 +++ b/tests/test-mq-caches Wed Apr 09 15:28:30 2008 -0700 @@ -1,18 +1,18 @@ #!/bin/sh +branches=.hg/branch.cache echo '[extensions]' >> $HGRCPATH echo 'hgext.mq=' >> $HGRCPATH show_branch_cache() { - branches=.hg/branch.cache # force cache (re)generation hg log -r does-not-exist 2> /dev/null hg log -r tip --template 'tip: #rev#\n' if [ -f $branches ]; then sort $branches else - echo No $branches + echo No branch cache fi if [ "$1" = 1 ]; then for b in foo bar; do @@ -61,7 +61,7 @@ echo echo '# removing the cache' -rm .hg/branch.cache +rm $branches show_branch_cache 1 echo
--- a/tests/test-mq-caches.out Wed Apr 09 15:27:57 2008 -0700 +++ b/tests/test-mq-caches.out Wed Apr 09 15:28:30 2008 -0700 @@ -1,8 +1,8 @@ # mq patch on an empty repo tip: 0 -No .hg/branch.cache +No branch cache tip: 0 -No .hg/branch.cache +No branch cache # some regular revisions Patch queue now empty
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tests/test-mq-header-date Wed Apr 09 15:28:30 2008 -0700 @@ -0,0 +1,177 @@ +#!/bin/sh + +echo "[extensions]" >> $HGRCPATH +echo "mq=" >> $HGRCPATH +echo "[diff]" >> $HGRCPATH +echo "nodates=true" >> $HGRCPATH + + +catpatch() { + cat .hg/patches/$1.patch | sed -e "s/^diff \-r [0-9a-f]* /diff -r ... /" +} + +catlog() { + catpatch $1 + hg log --template "{rev}: {desc} - {author}\n" +} + +catlogd() { + catpatch $1 + hg log --template "{rev}: {desc} - {author} - {date}\n" +} + +drop() { + hg qpop + hg qdel $1.patch +} + + +echo ==== init +hg init a +cd a +hg qinit + + +echo ==== qnew -d +hg qnew -d '3 0' 1.patch +catlogd 1 + +echo ==== qref +echo "1" >1 +hg add +hg qref +catlogd 1 + +echo ==== qref -d +hg qref -d '4 0' +catlogd 1 + + +echo ==== qnew +hg qnew 2.patch +echo "2" >2 +hg add +hg qref +catlog 2 + +echo ==== qref -d +hg qref -d '5 0' +catlog 2 + +drop 2 + + +echo ==== qnew -d -m +hg qnew -d '6 0' -m "Three" 3.patch +catlogd 3 + +echo ==== qref +echo "3" >3 +hg add +hg qref +catlogd 3 + +echo ==== qref -m +hg qref -m "Drei" +catlogd 3 + +echo ==== qref -d +hg qref -d '7 0' +catlogd 3 + +echo ==== qref -d -m +hg qref -d '8 0' -m "Three (again)" +catlogd 3 + + +echo ==== qnew -m +hg qnew -m "Four" 4.patch +echo "4" >4 +hg add +hg qref +catlog 4 + +echo ==== qref -d +hg qref -d '9 0' +catlog 4 + +drop 4 + + +echo ==== qnew with HG header +hg qnew 5.patch +hg qpop +echo "# HG changeset patch" >>.hg/patches/5.patch +echo "# Date 10 0" >>.hg/patches/5.patch +hg qpush 2>&1 | grep 'Now at' +catlogd 5 + +echo ==== hg qref +echo "5" >5 +hg add +hg qref +catlogd 5 + +echo ==== hg qref -d +hg qref -d '11 0' +catlogd 5 + + +echo ==== qnew -u +hg qnew -u jane 6.patch +echo "6" >6 +hg add +hg qref +catlog 6 + +echo ==== qref -d +hg qref -d '12 0' +catlog 6 + +drop 6 + + +echo ==== qnew -d +hg qnew -d '13 0' 7.patch +echo "7" >7 +hg add +hg qref +catlog 7 + +echo ==== qref -u +hg qref -u john +catlogd 7 + + +echo ==== qnew +hg qnew 8.patch +echo "8" >8 +hg add +hg qref +catlog 8 + +echo ==== qref -u -d +hg qref -u john -d '14 0' +catlog 8 + +drop 8 + + +echo ==== qnew -m +hg qnew -m "Nine" 9.patch +echo "9" >9 +hg add +hg qref +catlog 9 + +echo ==== qref -u -d +hg qref -u john -d '15 0' +catlog 9 + +drop 9 + + +echo ==== "qpop -a / qpush -a" +hg qpop -a +hg qpush -a +hg log --template "{rev}: {desc} - {author} - {date}\n"
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tests/test-mq-header-date.out Wed Apr 09 15:28:30 2008 -0700 @@ -0,0 +1,284 @@ +==== init +==== qnew -d +# HG changeset patch +# Date 3 0 + +0: [mq]: 1.patch - test - 3.00 +==== qref +adding 1 +# HG changeset patch +# Date 3 0 + +diff -r ... 1 +--- /dev/null ++++ b/1 +@@ -0,0 +1,1 @@ ++1 +0: [mq]: 1.patch - test - 3.00 +==== qref -d +# HG changeset patch +# Date 4 0 + +diff -r ... 1 +--- /dev/null ++++ b/1 +@@ -0,0 +1,1 @@ ++1 +0: [mq]: 1.patch - test - 4.00 +==== qnew +adding 2 +diff -r ... 2 +--- /dev/null ++++ b/2 +@@ -0,0 +1,1 @@ ++2 +1: [mq]: 2.patch - test +0: [mq]: 1.patch - test +==== qref -d +diff -r ... 2 +--- /dev/null ++++ b/2 +@@ -0,0 +1,1 @@ ++2 +1: [mq]: 2.patch - test +0: [mq]: 1.patch - test +Now at: 1.patch +==== qnew -d -m +# HG changeset patch +# Date 6 0 + +Three +1: Three - test - 6.00 +0: [mq]: 1.patch - test - 4.00 +==== qref +adding 3 +# HG changeset patch +# Date 6 0 + +Three + +diff -r ... 3 +--- /dev/null ++++ b/3 +@@ -0,0 +1,1 @@ ++3 +1: Three - test - 6.00 +0: [mq]: 1.patch - test - 4.00 +==== qref -m +# HG changeset patch +# Date 6 0 + +Drei + +diff -r ... 3 +--- /dev/null ++++ b/3 +@@ -0,0 +1,1 @@ ++3 +1: Drei - test - 6.00 +0: [mq]: 1.patch - test - 4.00 +==== qref -d +# HG changeset patch +# Date 7 0 + +Drei + +diff -r ... 3 +--- /dev/null ++++ b/3 +@@ -0,0 +1,1 @@ ++3 +1: Drei - test - 7.00 +0: [mq]: 1.patch - test - 4.00 +==== qref -d -m +# HG changeset patch +# Date 8 0 + +Three (again) + +diff -r ... 3 +--- /dev/null ++++ b/3 +@@ -0,0 +1,1 @@ ++3 +1: Three (again) - test - 8.00 +0: [mq]: 1.patch - test - 4.00 +==== qnew -m +adding 4 +Four + +diff -r ... 4 +--- /dev/null ++++ b/4 +@@ -0,0 +1,1 @@ ++4 +2: Four - test +1: Three (again) - test +0: [mq]: 1.patch - test +==== qref -d +Four + +diff -r ... 4 +--- /dev/null ++++ b/4 +@@ -0,0 +1,1 @@ ++4 +2: Four - test +1: Three (again) - test +0: [mq]: 1.patch - test +Now at: 3.patch +==== qnew with HG header +Now at: 3.patch +Now at: 5.patch +# HG changeset patch +# Date 10 0 +2: imported patch 5.patch - test - 10.00 +1: Three (again) - test - 8.00 +0: [mq]: 1.patch - test - 4.00 +==== hg qref +adding 5 +# HG changeset patch +# Date 10 0 + +diff -r ... 5 +--- /dev/null ++++ b/5 +@@ -0,0 +1,1 @@ ++5 +2: [mq]: 5.patch - test - 10.00 +1: Three (again) - test - 8.00 +0: [mq]: 1.patch - test - 4.00 +==== hg qref -d +# HG changeset patch +# Date 11 0 + +diff -r ... 5 +--- /dev/null ++++ b/5 +@@ -0,0 +1,1 @@ ++5 +2: [mq]: 5.patch - test - 11.00 +1: Three (again) - test - 8.00 +0: [mq]: 1.patch - test - 4.00 +==== qnew -u +adding 6 +From: jane + +diff -r ... 6 +--- /dev/null ++++ b/6 +@@ -0,0 +1,1 @@ ++6 +3: [mq]: 6.patch - jane +2: [mq]: 5.patch - test +1: Three (again) - test +0: [mq]: 1.patch - test +==== qref -d +From: jane + +diff -r ... 6 +--- /dev/null ++++ b/6 +@@ -0,0 +1,1 @@ ++6 +3: [mq]: 6.patch - jane +2: [mq]: 5.patch - test +1: Three (again) - test +0: [mq]: 1.patch - test +Now at: 5.patch +==== qnew -d +adding 7 +# HG changeset patch +# Date 13 0 + +diff -r ... 7 +--- /dev/null ++++ b/7 +@@ -0,0 +1,1 @@ ++7 +3: [mq]: 7.patch - test +2: [mq]: 5.patch - test +1: Three (again) - test +0: [mq]: 1.patch - test +==== qref -u +# HG changeset patch +# User john +# Date 13 0 + +diff -r ... 7 +--- /dev/null ++++ b/7 +@@ -0,0 +1,1 @@ ++7 +3: [mq]: 7.patch - john - 13.00 +2: [mq]: 5.patch - test - 11.00 +1: Three (again) - test - 8.00 +0: [mq]: 1.patch - test - 4.00 +==== qnew +adding 8 +diff -r ... 8 +--- /dev/null ++++ b/8 +@@ -0,0 +1,1 @@ ++8 +4: [mq]: 8.patch - test +3: [mq]: 7.patch - john +2: [mq]: 5.patch - test +1: Three (again) - test +0: [mq]: 1.patch - test +==== qref -u -d +From: john + + +diff -r ... 8 +--- /dev/null ++++ b/8 +@@ -0,0 +1,1 @@ ++8 +4: [mq]: 8.patch - john +3: [mq]: 7.patch - john +2: [mq]: 5.patch - test +1: Three (again) - test +0: [mq]: 1.patch - test +Now at: 7.patch +==== qnew -m +adding 9 +Nine + +diff -r ... 9 +--- /dev/null ++++ b/9 +@@ -0,0 +1,1 @@ ++9 +4: Nine - test +3: [mq]: 7.patch - john +2: [mq]: 5.patch - test +1: Three (again) - test +0: [mq]: 1.patch - test +==== qref -u -d +From: john + +Nine + +diff -r ... 9 +--- /dev/null ++++ b/9 +@@ -0,0 +1,1 @@ ++9 +4: Nine - john +3: [mq]: 7.patch - john +2: [mq]: 5.patch - test +1: Three (again) - test +0: [mq]: 1.patch - test +Now at: 7.patch +==== qpop -a / qpush -a +Patch queue now empty +applying 1.patch +applying 3.patch +applying 5.patch +applying 7.patch +Now at: 7.patch +3: imported patch 7.patch - john - 13.00 +2: imported patch 5.patch - test - 11.00 +1: Three (again) - test - 8.00 +0: imported patch 1.patch - test - 4.00
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tests/test-mq-header-from Wed Apr 09 15:28:30 2008 -0700 @@ -0,0 +1,107 @@ +#!/bin/sh + +echo "[extensions]" >> $HGRCPATH +echo "mq=" >> $HGRCPATH +echo "[diff]" >> $HGRCPATH +echo "nodates=true" >> $HGRCPATH + + +catlog() { + cat .hg/patches/$1.patch | sed -e "s/^diff \-r [0-9a-f]* /diff -r ... /" + hg log --template "{rev}: {desc} - {author}\n" +} + + +echo ==== init +hg init a +cd a +hg qinit + + +echo ==== qnew -U +hg qnew -U 1.patch +catlog 1 + +echo ==== qref +echo "1" >1 +hg add +hg qref +catlog 1 + +echo ==== qref -u +hg qref -u mary +catlog 1 + +echo ==== qnew +hg qnew 2.patch +echo "2" >2 +hg add +hg qref +catlog 2 + +echo ==== qref -u +hg qref -u jane +catlog 2 + + +echo ==== qnew -U -m +hg qnew -U -m "Three" 3.patch +catlog 3 + +echo ==== qref +echo "3" >3 +hg add +hg qref +catlog 3 + +echo ==== qref -m +hg qref -m "Drei" +catlog 3 + +echo ==== qref -u +hg qref -u mary +catlog 3 + +echo ==== qref -u -m +hg qref -u maria -m "Three (again)" +catlog 3 + +echo ==== qnew -m +hg qnew -m "Four" 4.patch +echo "4" >4 +hg add +hg qref +catlog 4 + +echo ==== qref -u +hg qref -u jane +catlog 4 + + +echo ==== qnew with HG header +hg qnew 5.patch +hg qpop +echo "# HG changeset patch" >>.hg/patches/5.patch +echo "# User johndoe" >>.hg/patches/5.patch +hg qpush 2>&1 | grep 'Now at' +catlog 5 + +echo ==== hg qref +echo "5" >5 +hg add +hg qref +catlog 5 + +echo ==== hg qref -U +hg qref -U +catlog 5 + +echo ==== hg qref -u +hg qref -u johndeere +catlog 5 + + +echo ==== "qpop -a / qpush -a" +hg qpop -a +hg qpush -a +hg log --template "{rev}: {desc} - {author}\n"
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tests/test-mq-header-from.out Wed Apr 09 15:28:30 2008 -0700 @@ -0,0 +1,197 @@ +==== init +==== qnew -U +From: test + +0: [mq]: 1.patch - test +==== qref +adding 1 +From: test + +diff -r ... 1 +--- /dev/null ++++ b/1 +@@ -0,0 +1,1 @@ ++1 +0: [mq]: 1.patch - test +==== qref -u +From: mary + +diff -r ... 1 +--- /dev/null ++++ b/1 +@@ -0,0 +1,1 @@ ++1 +0: [mq]: 1.patch - mary +==== qnew +adding 2 +diff -r ... 2 +--- /dev/null ++++ b/2 +@@ -0,0 +1,1 @@ ++2 +1: [mq]: 2.patch - test +0: [mq]: 1.patch - mary +==== qref -u +From: jane + + +diff -r ... 2 +--- /dev/null ++++ b/2 +@@ -0,0 +1,1 @@ ++2 +1: [mq]: 2.patch - jane +0: [mq]: 1.patch - mary +==== qnew -U -m +From: test + +Three +2: Three - test +1: [mq]: 2.patch - jane +0: [mq]: 1.patch - mary +==== qref +adding 3 +From: test + +Three + +diff -r ... 3 +--- /dev/null ++++ b/3 +@@ -0,0 +1,1 @@ ++3 +2: Three - test +1: [mq]: 2.patch - jane +0: [mq]: 1.patch - mary +==== qref -m +From: test + +Drei + +diff -r ... 3 +--- /dev/null ++++ b/3 +@@ -0,0 +1,1 @@ ++3 +2: Drei - test +1: [mq]: 2.patch - jane +0: [mq]: 1.patch - mary +==== qref -u +From: mary + +Drei + +diff -r ... 3 +--- /dev/null ++++ b/3 +@@ -0,0 +1,1 @@ ++3 +2: Drei - mary +1: [mq]: 2.patch - jane +0: [mq]: 1.patch - mary +==== qref -u -m +From: maria + +Three (again) + +diff -r ... 3 +--- /dev/null ++++ b/3 +@@ -0,0 +1,1 @@ ++3 +2: Three (again) - maria +1: [mq]: 2.patch - jane +0: [mq]: 1.patch - mary +==== qnew -m +adding 4 +Four + +diff -r ... 4 +--- /dev/null ++++ b/4 +@@ -0,0 +1,1 @@ ++4 +3: Four - test +2: Three (again) - maria +1: [mq]: 2.patch - jane +0: [mq]: 1.patch - mary +==== qref -u +From: jane + +Four + +diff -r ... 4 +--- /dev/null ++++ b/4 +@@ -0,0 +1,1 @@ ++4 +3: Four - jane +2: Three (again) - maria +1: [mq]: 2.patch - jane +0: [mq]: 1.patch - mary +==== qnew with HG header +Now at: 4.patch +Now at: 5.patch +# HG changeset patch +# User johndoe +4: imported patch 5.patch - johndoe +3: Four - jane +2: Three (again) - maria +1: [mq]: 2.patch - jane +0: [mq]: 1.patch - mary +==== hg qref +adding 5 +# HG changeset patch +# User johndoe + +diff -r ... 5 +--- /dev/null ++++ b/5 +@@ -0,0 +1,1 @@ ++5 +4: [mq]: 5.patch - johndoe +3: Four - jane +2: Three (again) - maria +1: [mq]: 2.patch - jane +0: [mq]: 1.patch - mary +==== hg qref -U +# HG changeset patch +# User test + +diff -r ... 5 +--- /dev/null ++++ b/5 +@@ -0,0 +1,1 @@ ++5 +4: [mq]: 5.patch - test +3: Four - jane +2: Three (again) - maria +1: [mq]: 2.patch - jane +0: [mq]: 1.patch - mary +==== hg qref -u +# HG changeset patch +# User johndeere + +diff -r ... 5 +--- /dev/null ++++ b/5 +@@ -0,0 +1,1 @@ ++5 +4: [mq]: 5.patch - johndeere +3: Four - jane +2: Three (again) - maria +1: [mq]: 2.patch - jane +0: [mq]: 1.patch - mary +==== qpop -a / qpush -a +Patch queue now empty +applying 1.patch +applying 2.patch +applying 3.patch +applying 4.patch +applying 5.patch +Now at: 5.patch +4: imported patch 5.patch - johndeere +3: Four - jane +2: Three (again) - maria +1: imported patch 2.patch - jane +0: imported patch 1.patch - mary
--- a/tests/test-mq-merge.out Wed Apr 09 15:27:57 2008 -0700 +++ b/tests/test-mq-merge.out Wed Apr 09 15:28:30 2008 -0700 @@ -3,6 +3,7 @@ copy .hg/patches to .hg/patches.1 1 files updated, 0 files merged, 0 files removed, 0 files unresolved M b +created new head a b merging with queue at: .hg/patches.1
--- a/tests/test-mq-missingfiles.out Wed Apr 09 15:27:57 2008 -0700 +++ b/tests/test-mq-missingfiles.out Wed Apr 09 15:28:30 2008 -0700 @@ -13,13 +13,13 @@ % display rejections --- b +++ b -@@ -1,3 +1,5 @@ a +@@ -1,3 +1,5 @@ +b +b a a a -@@ -8,3 +10,5 @@ a +@@ -8,3 +10,5 @@ a a a
--- a/tests/test-mq-pull-from-bundle Wed Apr 09 15:27:57 2008 -0700 +++ b/tests/test-mq-pull-from-bundle Wed Apr 09 15:28:30 2008 -0700 @@ -1,14 +1,13 @@ #!/bin/sh -echo "[extensions]" >> $HGRCPATH -echo "mq=" >> $HGRCPATH -echo "[defaults]" >> $HGRCPATH -echo "log = --template \"{rev}: {desc}\\n\"" >> $HGRCPATH -echo "heads = --template \"{rev}: {desc}\\n\"" >> $HGRCPATH -echo "incoming = --template \"{rev}: {desc}\\n\"" >> $HGRCPATH - -echo "====== .hgrc" -cat $HGRCPATH +cat <<EOF >> $HGRCPATH +[extensions] +mq= +[defaults] +log = --template "{rev}: {desc}\\n" +heads = --template "{rev}: {desc}\\n" +incoming = --template "{rev}: {desc}\\n" +EOF echo "====== Setup main" hg init base
--- a/tests/test-mq-pull-from-bundle.out Wed Apr 09 15:27:57 2008 -0700 +++ b/tests/test-mq-pull-from-bundle.out Wed Apr 09 15:28:30 2008 -0700 @@ -1,20 +1,7 @@ -====== .hgrc -[ui] -slash = True -[defaults] -backout = -d "0 0" -commit = -d "0 0" -debugrawcommit = -d "0 0" -tag = -d "0 0" -[extensions] -mq= -[defaults] -log = --template "{rev}: {desc}\n" -heads = --template "{rev}: {desc}\n" -incoming = --template "{rev}: {desc}\n" ====== Setup main adding one ====== Bundle main +2 changesets found ====== Incoming to fresh repo >> hg -R fresh incoming main.hg comparing with main.hg @@ -28,7 +15,9 @@ adding two Patch queue now empty ====== Bundle queue +1 changesets found ====== Clone base +updating working directory 1 files updated, 0 files merged, 0 files removed, 0 files unresolved ====== Incoming queue bundle >> hg -R .hg/patches incoming ../queue.hgq @@ -51,6 +40,7 @@ >> hg qseries two.patch ====== Clone base again +updating working directory 1 files updated, 0 files merged, 0 files removed, 0 files unresolved ====== Unbundle queue bundle >> hg -R .hg/patches unbundle --update ../queue.hgq
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tests/test-mq-qclone-http Wed Apr 09 15:28:30 2008 -0700 @@ -0,0 +1,39 @@ +#! /bin/sh + +echo "[extensions]" >> $HGRCPATH +echo "mq=" >> $HGRCPATH + +mkdir webdir +cd webdir + +hg init a +hg --cwd a qinit -c +echo a > a/a +hg --cwd a ci -A -m a +echo b > a/b +hg --cwd a addremove +hg --cwd a qnew -f b.patch +hg --cwd a qcommit -m b.patch + +hg --cwd a log --template "{desc}\n" +hg --cwd a/.hg/patches log --template "{desc}\n" + +root=`pwd` + +cd .. + +cat > collections.conf <<EOF +[collections] +$root=$root +EOF + +hg serve -p $HGPORT -d --pid-file=hg.pid --webdir-conf collections.conf \ + -A access-paths.log -E error-paths-1.log +cat hg.pid >> $DAEMON_PIDS + +"$TESTDIR/get-with-headers.py" localhost:$HGPORT '/?style=raw' + +hg qclone http://localhost:$HGPORT/a b +hg --cwd b log --template "{desc}\n" +hg --cwd b qpush -a +hg --cwd b log --template "{desc}\n"
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tests/test-mq-qclone-http.out Wed Apr 09 15:28:30 2008 -0700 @@ -0,0 +1,29 @@ +adding a +adding b +[mq]: b.patch +a +b.patch +200 Script output follows + + +/a/ +/a/.hg/patches/ + +requesting all changes +adding changesets +adding manifests +adding file changes +added 2 changesets with 2 changes to 2 files +requesting all changes +adding changesets +adding manifests +adding file changes +added 1 changesets with 3 changes to 3 files +updating working directory +3 files updated, 0 files merged, 0 files removed, 0 files unresolved +1 files updated, 0 files merged, 0 files removed, 0 files unresolved +a +applying b.patch +Now at: b.patch +imported patch b.patch +a
--- a/tests/test-mq-qdiff.out Wed Apr 09 15:27:57 2008 -0700 +++ b/tests/test-mq-qdiff.out Wed Apr 09 15:28:30 2008 -0700 @@ -7,13 +7,13 @@ diff -r 67e992f2c4f3 base --- a/base +++ b/base -@@ -1,1 +1,1 @@ base +@@ -1,1 +1,1 @@ -base +patched % qdiff dirname diff -r 67e992f2c4f3 base --- a/base +++ b/base -@@ -1,1 +1,1 @@ base +@@ -1,1 +1,1 @@ -base +patched
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tests/test-mq-qimport Wed Apr 09 15:28:30 2008 -0700 @@ -0,0 +1,36 @@ +#!/bin/sh + +cat > writelines.py <<EOF +import sys +path = sys.argv[1] +args = sys.argv[2:] +assert (len(args) % 2) == 0 + +f = file(path, 'wb') +for i in xrange(len(args)/2): + count, s = args[2*i:2*i+2] + count = int(count) + s = s.decode('string_escape') + f.write(s*count) +f.close() + +EOF + +echo "[extensions]" >> $HGRCPATH +echo "mq=" >> $HGRCPATH +echo "[diff]" >> $HGRCPATH +echo "git=1" >> $HGRCPATH + +echo % build diff with CRLF +hg init repo +cd repo +python ../writelines.py b 5 'a\n' 5 'a\r\n' +hg ci -Am addb +python ../writelines.py b 2 'a\n' 10 'b\n' 2 'a\r\n' +hg diff > b.diff +hg up -C +echo % qimport CRLF diff +hg qimport b.diff +hg qpush + +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tests/test-mq-qimport.out Wed Apr 09 15:28:30 2008 -0700 @@ -0,0 +1,7 @@ +% build diff with CRLF +adding b +1 files updated, 0 files merged, 0 files removed, 0 files unresolved +% qimport CRLF diff +adding b.diff to series file +applying b.diff +Now at: b.diff
--- a/tests/test-mq-qrefresh.out Wed Apr 09 15:27:57 2008 -0700 +++ b/tests/test-mq-qrefresh.out Wed Apr 09 15:28:30 2008 -0700 @@ -8,26 +8,26 @@ diff -r b55ecdccb5cf 1/base --- a/1/base +++ b/1/base -@@ -1,1 +1,1 @@ base +@@ -1,1 +1,1 @@ -base +patched diff -r b55ecdccb5cf 2/base --- a/2/base +++ b/2/base -@@ -1,1 +1,1 @@ base +@@ -1,1 +1,1 @@ -base +patched % qdiff dirname diff -r b55ecdccb5cf 1/base --- a/1/base +++ b/1/base -@@ -1,1 +1,1 @@ base +@@ -1,1 +1,1 @@ -base +patched diff -r b55ecdccb5cf 2/base --- a/2/base +++ b/2/base -@@ -1,1 +1,1 @@ base +@@ -1,1 +1,1 @@ -base +patched % patch file contents @@ -36,13 +36,13 @@ diff -r b55ecdccb5cf 1/base --- a/1/base +++ b/1/base -@@ -1,1 +1,1 @@ base +@@ -1,1 +1,1 @@ -base +patched diff -r b55ecdccb5cf 2/base --- a/2/base +++ b/2/base -@@ -1,1 +1,1 @@ base +@@ -1,1 +1,1 @@ -base +patched % qrefresh 1 @@ -50,26 +50,26 @@ diff -r b55ecdccb5cf 1/base --- a/1/base +++ b/1/base -@@ -1,1 +1,1 @@ base +@@ -1,1 +1,1 @@ -base +patched diff -r b55ecdccb5cf 2/base --- a/2/base +++ b/2/base -@@ -1,1 +1,1 @@ base +@@ -1,1 +1,1 @@ -base +patched % qdiff dirname diff -r b55ecdccb5cf 1/base --- a/1/base +++ b/1/base -@@ -1,1 +1,1 @@ base +@@ -1,1 +1,1 @@ -base +patched diff -r b55ecdccb5cf 2/base --- a/2/base +++ b/2/base -@@ -1,1 +1,1 @@ base +@@ -1,1 +1,1 @@ -base +patched % patch file contents @@ -78,7 +78,7 @@ diff -r b55ecdccb5cf 1/base --- a/1/base +++ b/1/base -@@ -1,1 +1,1 @@ base +@@ -1,1 +1,1 @@ -base +patched % qrefresh . in subdir @@ -86,26 +86,26 @@ diff -r b55ecdccb5cf 1/base --- a/1/base +++ b/1/base -@@ -1,1 +1,1 @@ base +@@ -1,1 +1,1 @@ -base +patched diff -r b55ecdccb5cf 2/base --- a/2/base +++ b/2/base -@@ -1,1 +1,1 @@ base +@@ -1,1 +1,1 @@ -base +patched % qdiff dirname diff -r b55ecdccb5cf 1/base --- a/1/base +++ b/1/base -@@ -1,1 +1,1 @@ base +@@ -1,1 +1,1 @@ -base +patched diff -r b55ecdccb5cf 2/base --- a/2/base +++ b/2/base -@@ -1,1 +1,1 @@ base +@@ -1,1 +1,1 @@ -base +patched % patch file contents @@ -114,7 +114,7 @@ diff -r b55ecdccb5cf 1/base --- a/1/base +++ b/1/base -@@ -1,1 +1,1 @@ base +@@ -1,1 +1,1 @@ -base +patched % qrefresh in hg-root again @@ -122,26 +122,26 @@ diff -r b55ecdccb5cf 1/base --- a/1/base +++ b/1/base -@@ -1,1 +1,1 @@ base +@@ -1,1 +1,1 @@ -base +patched diff -r b55ecdccb5cf 2/base --- a/2/base +++ b/2/base -@@ -1,1 +1,1 @@ base +@@ -1,1 +1,1 @@ -base +patched % qdiff dirname diff -r b55ecdccb5cf 1/base --- a/1/base +++ b/1/base -@@ -1,1 +1,1 @@ base +@@ -1,1 +1,1 @@ -base +patched diff -r b55ecdccb5cf 2/base --- a/2/base +++ b/2/base -@@ -1,1 +1,1 @@ base +@@ -1,1 +1,1 @@ -base +patched % patch file contents @@ -150,12 +150,12 @@ diff -r b55ecdccb5cf 1/base --- a/1/base +++ b/1/base -@@ -1,1 +1,1 @@ base +@@ -1,1 +1,1 @@ -base +patched diff -r b55ecdccb5cf 2/base --- a/2/base +++ b/2/base -@@ -1,1 +1,1 @@ base +@@ -1,1 +1,1 @@ -base +patched
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tests/test-mq-safety Wed Apr 09 15:28:30 2008 -0700 @@ -0,0 +1,44 @@ +#!/bin/sh + +echo '[extensions]' >> $HGRCPATH +echo 'hgext.mq =' >> $HGRCPATH + +hg init repo +cd repo + +echo foo > foo +hg ci -qAm 'add a file' + +hg qinit + +hg qnew foo +echo foo >> foo +hg qrefresh -m 'append foo' + +hg qnew bar +echo bar >> foo +hg qrefresh -m 'append bar' + +echo '% try to commit on top of a patch' +echo quux >> foo +hg ci -m 'append quux' + +# cheat a bit... +mv .hg/patches .hg/patches2 +hg ci -m 'append quux' +mv .hg/patches2 .hg/patches + +echo '% qpop/qrefresh on the wrong revision' +hg qpop +hg qpop -n patches 2>&1 | sed -e 's/\(using patch queue:\).*/\1/' +hg qrefresh + +hg up -C qtip +echo '% qpop' +hg qpop + +echo '% qrefresh' +hg qrefresh + +echo '% tip:' +hg tip --template '#rev# #desc#\n'
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tests/test-mq-safety.out Wed Apr 09 15:28:30 2008 -0700 @@ -0,0 +1,14 @@ +% try to commit on top of a patch +abort: cannot commit over an applied mq patch +% qpop/qrefresh on the wrong revision +abort: working directory revision is not qtip +using patch queue: +abort: popping would remove a revision not managed by this patch queue +abort: working directory revision is not qtip +1 files updated, 0 files merged, 0 files removed, 0 files unresolved +% qpop +abort: popping would remove a revision not managed by this patch queue +% qrefresh +abort: cannot refresh a revision with children +% tip: +3 append quux
--- a/tests/test-mq-symlinks Wed Apr 09 15:27:57 2008 -0700 +++ b/tests/test-mq-symlinks Wed Apr 09 15:28:30 2008 -0700 @@ -1,19 +1,10 @@ #!/bin/sh +"$TESTDIR/hghave" symlink || exit 80 + echo "[extensions]" >> $HGRCPATH echo "mq=" >> $HGRCPATH -cat >> readlink.py <<EOF -import errno, os, sys - -for f in sys.argv[1:]: - try: - print f, '->', os.readlink(f) - except OSError, err: - if err.errno != errno.EINVAL: raise - print f, 'not a symlink' -EOF - hg init hg qinit hg qnew base.patch @@ -21,14 +12,14 @@ echo b > b hg add a b hg qrefresh -python readlink.py a +$TESTDIR/readlink.py a hg qnew symlink.patch rm a ln -s b a hg qrefresh --git -python readlink.py a +$TESTDIR/readlink.py a hg qpop hg qpush -python readlink.py a +$TESTDIR/readlink.py a
--- a/tests/test-mq.out Wed Apr 09 15:27:57 2008 -0700 +++ b/tests/test-mq.out Wed Apr 09 15:28:30 2008 -0700 @@ -52,6 +52,7 @@ use "hg -v help mq" to show aliases and global options adding a +updating working directory 1 files updated, 0 files merged, 0 files removed, 0 files unresolved adding b/z % qinit @@ -59,12 +60,19 @@ % qinit -c A .hgignore A series +% qnew should refuse bad patch names +abort: "series" cannot be used as the name of a patch +abort: "status" cannot be used as the name of a patch +abort: "guards" cannot be used as the name of a patch +abort: ".hgignore" cannot be used as the name of a patch % qnew implies add A .hgignore A series A test.patch % qinit; qinit -c .hgignore: +^\.hg +^\.mq syntax: glob status guards @@ -99,7 +107,7 @@ diff -r xa --- a/a +++ b/a -@@ -1,1 +1,2 @@ a +@@ -1,1 +1,2 @@ a +a % empty qrefresh @@ -110,7 +118,7 @@ working dir diff: --- a/a +++ b/a -@@ -1,1 +1,2 @@ a +@@ -1,1 +1,2 @@ a +a % qpop @@ -242,6 +250,12 @@ adding file changes added 1 changesets with 1 changes to 1 files (run 'hg update' to get a working copy) +% strip with local changes, should complain +1 files updated, 0 files merged, 0 files removed, 0 files unresolved +abort: local changes found +% --force strip with local changes +0 files updated, 0 files merged, 2 files removed, 0 files unresolved +saving bundle to % cd b; hg qrefresh adding a foo @@ -249,7 +263,7 @@ diff -r cb9a9f314b8b a --- a/a +++ b/a -@@ -1,1 +1,2 @@ a +@@ -1,1 +1,2 @@ a +a diff -r cb9a9f314b8b b/f @@ -281,6 +295,18 @@ 0 qparent 1 qbase foo 2 qtip bar tip +% bad node in status +Now at: foo +changeset: 0:cb9a9f314b8b +mq status file refers to unknown node +tag: tip +user: test +date: Thu Jan 01 00:00:00 1970 +0000 +summary: a + +mq status file refers to unknown node +default 0:cb9a9f314b8b +abort: working directory revision is not qtip new file diff --git a/new b/new @@ -304,14 +330,15 @@ copy from new copy to copy 1 files updated, 0 files merged, 2 files removed, 0 files unresolved +created new head 2 files updated, 0 files merged, 1 files removed, 0 files unresolved adding branch adding changesets adding manifests adding file changes added 1 changesets with 1 changes to 1 files -(run 'hg update' to get a working copy) Patch queue now empty +(working directory not at tip) applying bar Now at: bar diff --git a/bar b/bar @@ -344,8 +371,8 @@ adding manifests adding file changes added 1 changesets with 1 changes to 1 files -(run 'hg update' to get a working copy) Patch queue now empty +(working directory not at tip) applying bar Now at: bar diff --git a/foo b/bleh @@ -384,6 +411,7 @@ 8ba2a2f3e77b55d03051ff9c24ad65e7 bucephalus % strip again 1 files updated, 0 files merged, 0 files removed, 0 files unresolved +created new head merging foo 0 files updated, 1 files merged, 0 files removed, 0 files unresolved (branch merge, don't forget to commit) @@ -419,7 +447,6 @@ adding manifests adding file changes added 1 changesets with 1 changes to 1 files -(run 'hg update' to get a working copy) changeset: 1:20cbbe65cff7 tag: tip user: test @@ -439,6 +466,7 @@ rev 0: add foo patch repo: rev 0: checkpoint +updating working directory 3 files updated, 0 files merged, 0 files removed, 0 files unresolved 1 files updated, 0 files merged, 0 files removed, 0 files unresolved main repo: @@ -450,9 +478,15 @@ rev 0: add foo patch repo: rev 0: checkpoint +updating working directory 3 files updated, 0 files merged, 0 files removed, 0 files unresolved 1 files updated, 0 files merged, 0 files removed, 0 files unresolved main repo: rev 0: add foo patch repo: rev 0: checkpoint +% test applying on an empty file (issue 1033) +adding a +Patch queue now empty +applying changea +Now at: changea
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tests/test-mv-cp-st-diff Wed Apr 09 15:28:30 2008 -0700 @@ -0,0 +1,76 @@ +#!/bin/sh + +add() +{ + echo $2 >> $1 +} + +hg init t +cd t + +# set up a boring main branch +add a a +hg add a +mkdir x +add x/x x +hg add x/x +hg ci -m0 + +add a m1 +hg ci -m1 + +add a m2 +add x/y y1 +hg add x/y +hg ci -m2 + +show() +{ + echo "- $2: $1" + hg st -C $1 + echo + hg diff --git $1 + echo +} + +count=0 +# make a new branch and get diff/status output +# $1 - first commit +# $2 - second commit +# $3 - working dir action +# $4 - test description +tb() +{ + hg co -q -C 0 + + add a $count + count=`expr $count + 1` + hg ci -m "t0" + $1 + hg ci -m "t1" + $2 + hg ci -m "t2" + $3 + + echo "** $4 **" + echo "** $1 / $2 / $3" + show "" "working to parent" + show "--rev 0" "working to root" + show "--rev 2" "working to branch" + show "--rev 0 --rev ." "root to parent" + show "--rev . --rev 0" "parent to root" + show "--rev 2 --rev ." "branch to parent" + show "--rev . --rev 2" "parent to branch" + echo +} + + +tb "add a a1" "add a a2" "hg mv a b" "rename in working dir" +tb "add a a1" "add a a2" "hg cp a b" "copy in working dir" +tb "hg mv a b" "add b b1" "add b w" "single rename" +tb "hg cp a b" "add b b1" "add a w" "single copy" +tb "hg mv a b" "hg mv b c" "hg mv c d" "rename chain" +tb "hg cp a b" "hg cp b c" "hg cp c d" "copy chain" +tb "add a a1" "hg mv a b" "hg mv b a" "circular rename" + +tb "hg mv x y" "add y/x x1" "add y/x x2" "directory move"
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tests/test-mv-cp-st-diff.out Wed Apr 09 15:28:30 2008 -0700 @@ -0,0 +1,1220 @@ +created new head +** rename in working dir ** +** add a a1 / add a a2 / hg mv a b +- working to parent: +A b + a +R a + +diff --git a/a b/b +rename from a +rename to b + +- working to root: --rev 0 +A b + a +R a + +diff --git a/a b/b +rename from a +rename to b +--- a/a ++++ b/b +@@ -1,1 +1,4 @@ + a ++0 ++a1 ++a2 + +- working to branch: --rev 2 +A b + a +R a +R x/y + +diff --git a/a b/b +rename from a +rename to b +--- a/a ++++ b/b +@@ -1,3 +1,4 @@ + a +-m1 +-m2 ++0 ++a1 ++a2 +diff --git a/x/y b/x/y +deleted file mode 100644 +--- a/x/y ++++ /dev/null +@@ -1,1 +0,0 @@ +-y1 + +- root to parent: --rev 0 --rev . +M a + +diff --git a/a b/a +--- a/a ++++ b/a +@@ -1,1 +1,4 @@ + a ++0 ++a1 ++a2 + +- parent to root: --rev . --rev 0 +M a + +diff --git a/a b/a +--- a/a ++++ b/a +@@ -1,4 +1,1 @@ + a +-0 +-a1 +-a2 + +- branch to parent: --rev 2 --rev . +M a +R x/y + +diff --git a/a b/a +--- a/a ++++ b/a +@@ -1,3 +1,4 @@ + a +-m1 +-m2 ++0 ++a1 ++a2 +diff --git a/x/y b/x/y +deleted file mode 100644 +--- a/x/y ++++ /dev/null +@@ -1,1 +0,0 @@ +-y1 + +- parent to branch: --rev . --rev 2 +M a +A x/y + +diff --git a/a b/a +--- a/a ++++ b/a +@@ -1,4 +1,3 @@ + a +-0 +-a1 +-a2 ++m1 ++m2 +diff --git a/x/y b/x/y +new file mode 100644 +--- /dev/null ++++ b/x/y +@@ -0,0 +1,1 @@ ++y1 + + +created new head +** copy in working dir ** +** add a a1 / add a a2 / hg cp a b +- working to parent: +A b + a + +diff --git a/a b/b +copy from a +copy to b + +- working to root: --rev 0 +M a +A b + a + +diff --git a/a b/a +--- a/a ++++ b/a +@@ -1,1 +1,4 @@ + a ++1 ++a1 ++a2 +diff --git a/a b/b +copy from a +copy to b +--- a/a ++++ b/b +@@ -1,1 +1,4 @@ + a ++1 ++a1 ++a2 + +- working to branch: --rev 2 +M a +A b + a +R x/y + +diff --git a/a b/a +--- a/a ++++ b/a +@@ -1,3 +1,4 @@ + a +-m1 +-m2 ++1 ++a1 ++a2 +diff --git a/a b/b +copy from a +copy to b +--- a/a ++++ b/b +@@ -1,3 +1,4 @@ + a +-m1 +-m2 ++1 ++a1 ++a2 +diff --git a/x/y b/x/y +deleted file mode 100644 +--- a/x/y ++++ /dev/null +@@ -1,1 +0,0 @@ +-y1 + +- root to parent: --rev 0 --rev . +M a + +diff --git a/a b/a +--- a/a ++++ b/a +@@ -1,1 +1,4 @@ + a ++1 ++a1 ++a2 + +- parent to root: --rev . --rev 0 +M a + +diff --git a/a b/a +--- a/a ++++ b/a +@@ -1,4 +1,1 @@ + a +-1 +-a1 +-a2 + +- branch to parent: --rev 2 --rev . +M a +R x/y + +diff --git a/a b/a +--- a/a ++++ b/a +@@ -1,3 +1,4 @@ + a +-m1 +-m2 ++1 ++a1 ++a2 +diff --git a/x/y b/x/y +deleted file mode 100644 +--- a/x/y ++++ /dev/null +@@ -1,1 +0,0 @@ +-y1 + +- parent to branch: --rev . --rev 2 +M a +A x/y + +diff --git a/a b/a +--- a/a ++++ b/a +@@ -1,4 +1,3 @@ + a +-1 +-a1 +-a2 ++m1 ++m2 +diff --git a/x/y b/x/y +new file mode 100644 +--- /dev/null ++++ b/x/y +@@ -0,0 +1,1 @@ ++y1 + + +created new head +** single rename ** +** hg mv a b / add b b1 / add b w +- working to parent: +M b + +diff --git a/b b/b +--- a/b ++++ b/b +@@ -1,3 +1,4 @@ + a + 2 + b1 ++w + +- working to root: --rev 0 +A b + a +R a + +diff --git a/a b/b +rename from a +rename to b +--- a/a ++++ b/b +@@ -1,1 +1,4 @@ + a ++2 ++b1 ++w + +- working to branch: --rev 2 +A b + a +R a +R x/y + +diff --git a/a b/b +rename from a +rename to b +--- a/a ++++ b/b +@@ -1,3 +1,4 @@ + a +-m1 +-m2 ++2 ++b1 ++w +diff --git a/x/y b/x/y +deleted file mode 100644 +--- a/x/y ++++ /dev/null +@@ -1,1 +0,0 @@ +-y1 + +- root to parent: --rev 0 --rev . +A b + a +R a + +diff --git a/a b/b +rename from a +rename to b +--- a/a ++++ b/b +@@ -1,1 +1,3 @@ + a ++2 ++b1 + +- parent to root: --rev . --rev 0 +A a + b +R b + +diff --git a/b b/a +rename from b +rename to a +--- a/b ++++ b/a +@@ -1,3 +1,1 @@ + a +-2 +-b1 + +- branch to parent: --rev 2 --rev . +A b + a +R a +R x/y + +diff --git a/a b/b +rename from a +rename to b +--- a/a ++++ b/b +@@ -1,3 +1,3 @@ + a +-m1 +-m2 ++2 ++b1 +diff --git a/x/y b/x/y +deleted file mode 100644 +--- a/x/y ++++ /dev/null +@@ -1,1 +0,0 @@ +-y1 + +- parent to branch: --rev . --rev 2 +A a + b +A x/y +R b + +diff --git a/b b/a +rename from b +rename to a +--- a/b ++++ b/a +@@ -1,3 +1,3 @@ + a +-2 +-b1 ++m1 ++m2 +diff --git a/x/y b/x/y +new file mode 100644 +--- /dev/null ++++ b/x/y +@@ -0,0 +1,1 @@ ++y1 + + +created new head +** single copy ** +** hg cp a b / add b b1 / add a w +- working to parent: +M a + +diff --git a/a b/a +--- a/a ++++ b/a +@@ -1,2 +1,3 @@ + a + 3 ++w + +- working to root: --rev 0 +M a +A b + a + +diff --git a/a b/a +--- a/a ++++ b/a +@@ -1,1 +1,3 @@ + a ++3 ++w +diff --git a/a b/b +copy from a +copy to b +--- a/a ++++ b/b +@@ -1,1 +1,3 @@ + a ++3 ++b1 + +- working to branch: --rev 2 +M a +A b + a +R x/y + +diff --git a/a b/a +--- a/a ++++ b/a +@@ -1,3 +1,3 @@ + a +-m1 +-m2 ++3 ++w +diff --git a/a b/b +copy from a +copy to b +--- a/a ++++ b/b +@@ -1,3 +1,3 @@ + a +-m1 +-m2 ++3 ++b1 +diff --git a/x/y b/x/y +deleted file mode 100644 +--- a/x/y ++++ /dev/null +@@ -1,1 +0,0 @@ +-y1 + +- root to parent: --rev 0 --rev . +M a +A b + a + +diff --git a/a b/a +--- a/a ++++ b/a +@@ -1,1 +1,2 @@ + a ++3 +diff --git a/a b/b +copy from a +copy to b +--- a/a ++++ b/b +@@ -1,1 +1,3 @@ + a ++3 ++b1 + +- parent to root: --rev . --rev 0 +M a +R b + +diff --git a/a b/a +--- a/a ++++ b/a +@@ -1,2 +1,1 @@ + a +-3 +diff --git a/b b/b +deleted file mode 100644 +--- a/b ++++ /dev/null +@@ -1,3 +0,0 @@ +-a +-3 +-b1 + +- branch to parent: --rev 2 --rev . +M a +A b + a +R x/y + +diff --git a/a b/a +--- a/a ++++ b/a +@@ -1,3 +1,2 @@ + a +-m1 +-m2 ++3 +diff --git a/a b/b +copy from a +copy to b +--- a/a ++++ b/b +@@ -1,3 +1,3 @@ + a +-m1 +-m2 ++3 ++b1 +diff --git a/x/y b/x/y +deleted file mode 100644 +--- a/x/y ++++ /dev/null +@@ -1,1 +0,0 @@ +-y1 + +- parent to branch: --rev . --rev 2 +M a +A x/y +R b + +diff --git a/a b/a +--- a/a ++++ b/a +@@ -1,2 +1,3 @@ + a +-3 ++m1 ++m2 +diff --git a/b b/b +deleted file mode 100644 +--- a/b ++++ /dev/null +@@ -1,3 +0,0 @@ +-a +-3 +-b1 +diff --git a/x/y b/x/y +new file mode 100644 +--- /dev/null ++++ b/x/y +@@ -0,0 +1,1 @@ ++y1 + + +created new head +** rename chain ** +** hg mv a b / hg mv b c / hg mv c d +- working to parent: +A d + c +R c + +diff --git a/c b/d +rename from c +rename to d + +- working to root: --rev 0 +A d + a +R a + +diff --git a/a b/d +rename from a +rename to d +--- a/a ++++ b/d +@@ -1,1 +1,2 @@ + a ++4 + +- working to branch: --rev 2 +A d + a +R a +R x/y + +diff --git a/a b/d +rename from a +rename to d +--- a/a ++++ b/d +@@ -1,3 +1,2 @@ + a +-m1 +-m2 ++4 +diff --git a/x/y b/x/y +deleted file mode 100644 +--- a/x/y ++++ /dev/null +@@ -1,1 +0,0 @@ +-y1 + +- root to parent: --rev 0 --rev . +A c + a +R a + +diff --git a/a b/c +rename from a +rename to c +--- a/a ++++ b/c +@@ -1,1 +1,2 @@ + a ++4 + +- parent to root: --rev . --rev 0 +A a + c +R c + +diff --git a/c b/a +rename from c +rename to a +--- a/c ++++ b/a +@@ -1,2 +1,1 @@ + a +-4 + +- branch to parent: --rev 2 --rev . +A c + a +R a +R x/y + +diff --git a/a b/c +rename from a +rename to c +--- a/a ++++ b/c +@@ -1,3 +1,2 @@ + a +-m1 +-m2 ++4 +diff --git a/x/y b/x/y +deleted file mode 100644 +--- a/x/y ++++ /dev/null +@@ -1,1 +0,0 @@ +-y1 + +- parent to branch: --rev . --rev 2 +A a + c +A x/y +R c + +diff --git a/c b/a +rename from c +rename to a +--- a/c ++++ b/a +@@ -1,2 +1,3 @@ + a +-4 ++m1 ++m2 +diff --git a/x/y b/x/y +new file mode 100644 +--- /dev/null ++++ b/x/y +@@ -0,0 +1,1 @@ ++y1 + + +created new head +** copy chain ** +** hg cp a b / hg cp b c / hg cp c d +- working to parent: +A d + c + +diff --git a/c b/d +copy from c +copy to d + +- working to root: --rev 0 +M a +A b + a +A c + a +A d + a + +diff --git a/a b/a +--- a/a ++++ b/a +@@ -1,1 +1,2 @@ + a ++5 +diff --git a/a b/b +copy from a +copy to b +--- a/a ++++ b/b +@@ -1,1 +1,2 @@ + a ++5 +diff --git a/a b/c +copy from a +copy to c +--- a/a ++++ b/c +@@ -1,1 +1,2 @@ + a ++5 +diff --git a/a b/d +copy from a +copy to d +--- a/a ++++ b/d +@@ -1,1 +1,2 @@ + a ++5 + +- working to branch: --rev 2 +M a +A b + a +A c + a +A d + a +R x/y + +diff --git a/a b/a +--- a/a ++++ b/a +@@ -1,3 +1,2 @@ + a +-m1 +-m2 ++5 +diff --git a/a b/b +copy from a +copy to b +--- a/a ++++ b/b +@@ -1,3 +1,2 @@ + a +-m1 +-m2 ++5 +diff --git a/a b/c +copy from a +copy to c +--- a/a ++++ b/c +@@ -1,3 +1,2 @@ + a +-m1 +-m2 ++5 +diff --git a/a b/d +copy from a +copy to d +--- a/a ++++ b/d +@@ -1,3 +1,2 @@ + a +-m1 +-m2 ++5 +diff --git a/x/y b/x/y +deleted file mode 100644 +--- a/x/y ++++ /dev/null +@@ -1,1 +0,0 @@ +-y1 + +- root to parent: --rev 0 --rev . +M a +A b + a +A c + a + +diff --git a/a b/a +--- a/a ++++ b/a +@@ -1,1 +1,2 @@ + a ++5 +diff --git a/a b/b +copy from a +copy to b +--- a/a ++++ b/b +@@ -1,1 +1,2 @@ + a ++5 +diff --git a/a b/c +copy from a +copy to c +--- a/a ++++ b/c +@@ -1,1 +1,2 @@ + a ++5 + +- parent to root: --rev . --rev 0 +M a +R b +R c + +diff --git a/a b/a +--- a/a ++++ b/a +@@ -1,2 +1,1 @@ + a +-5 +diff --git a/b b/b +deleted file mode 100644 +--- a/b ++++ /dev/null +@@ -1,2 +0,0 @@ +-a +-5 +diff --git a/c b/c +deleted file mode 100644 +--- a/c ++++ /dev/null +@@ -1,2 +0,0 @@ +-a +-5 + +- branch to parent: --rev 2 --rev . +M a +A b + a +A c + a +R x/y + +diff --git a/a b/a +--- a/a ++++ b/a +@@ -1,3 +1,2 @@ + a +-m1 +-m2 ++5 +diff --git a/a b/b +copy from a +copy to b +--- a/a ++++ b/b +@@ -1,3 +1,2 @@ + a +-m1 +-m2 ++5 +diff --git a/a b/c +copy from a +copy to c +--- a/a ++++ b/c +@@ -1,3 +1,2 @@ + a +-m1 +-m2 ++5 +diff --git a/x/y b/x/y +deleted file mode 100644 +--- a/x/y ++++ /dev/null +@@ -1,1 +0,0 @@ +-y1 + +- parent to branch: --rev . --rev 2 +M a +A x/y +R b +R c + +diff --git a/a b/a +--- a/a ++++ b/a +@@ -1,2 +1,3 @@ + a +-5 ++m1 ++m2 +diff --git a/b b/b +deleted file mode 100644 +--- a/b ++++ /dev/null +@@ -1,2 +0,0 @@ +-a +-5 +diff --git a/c b/c +deleted file mode 100644 +--- a/c ++++ /dev/null +@@ -1,2 +0,0 @@ +-a +-5 +diff --git a/x/y b/x/y +new file mode 100644 +--- /dev/null ++++ b/x/y +@@ -0,0 +1,1 @@ ++y1 + + +created new head +** circular rename ** +** add a a1 / hg mv a b / hg mv b a +- working to parent: +A a + b +R b + +diff --git a/b b/a +rename from b +rename to a + +- working to root: --rev 0 +M a + +diff --git a/a b/a +--- a/a ++++ b/a +@@ -1,1 +1,3 @@ + a ++6 ++a1 + +- working to branch: --rev 2 +M a +R x/y + +diff --git a/a b/a +--- a/a ++++ b/a +@@ -1,3 +1,3 @@ + a +-m1 +-m2 ++6 ++a1 +diff --git a/x/y b/x/y +deleted file mode 100644 +--- a/x/y ++++ /dev/null +@@ -1,1 +0,0 @@ +-y1 + +- root to parent: --rev 0 --rev . +A b + a +R a + +diff --git a/a b/b +rename from a +rename to b +--- a/a ++++ b/b +@@ -1,1 +1,3 @@ + a ++6 ++a1 + +- parent to root: --rev . --rev 0 +A a + b +R b + +diff --git a/b b/a +rename from b +rename to a +--- a/b ++++ b/a +@@ -1,3 +1,1 @@ + a +-6 +-a1 + +- branch to parent: --rev 2 --rev . +A b + a +R a +R x/y + +diff --git a/a b/b +rename from a +rename to b +--- a/a ++++ b/b +@@ -1,3 +1,3 @@ + a +-m1 +-m2 ++6 ++a1 +diff --git a/x/y b/x/y +deleted file mode 100644 +--- a/x/y ++++ /dev/null +@@ -1,1 +0,0 @@ +-y1 + +- parent to branch: --rev . --rev 2 +A a + b +A x/y +R b + +diff --git a/b b/a +rename from b +rename to a +--- a/b ++++ b/a +@@ -1,3 +1,3 @@ + a +-6 +-a1 ++m1 ++m2 +diff --git a/x/y b/x/y +new file mode 100644 +--- /dev/null ++++ b/x/y +@@ -0,0 +1,1 @@ ++y1 + + +created new head +moving x/x to y/x +** directory move ** +** hg mv x y / add y/x x1 / add y/x x2 +- working to parent: +M y/x + +diff --git a/y/x b/y/x +--- a/y/x ++++ b/y/x +@@ -1,2 +1,3 @@ + x + x1 ++x2 + +- working to root: --rev 0 +M a +A y/x + x/x +R x/x + +diff --git a/a b/a +--- a/a ++++ b/a +@@ -1,1 +1,2 @@ + a ++7 +diff --git a/x/x b/y/x +rename from x/x +rename to y/x +--- a/x/x ++++ b/y/x +@@ -1,1 +1,3 @@ + x ++x1 ++x2 + +- working to branch: --rev 2 +M a +A y/x + x/x +R x/x +R x/y + +diff --git a/a b/a +--- a/a ++++ b/a +@@ -1,3 +1,2 @@ + a +-m1 +-m2 ++7 +diff --git a/x/y b/x/y +deleted file mode 100644 +--- a/x/y ++++ /dev/null +@@ -1,1 +0,0 @@ +-y1 +diff --git a/x/x b/y/x +rename from x/x +rename to y/x +--- a/x/x ++++ b/y/x +@@ -1,1 +1,3 @@ + x ++x1 ++x2 + +- root to parent: --rev 0 --rev . +M a +A y/x + x/x +R x/x + +diff --git a/a b/a +--- a/a ++++ b/a +@@ -1,1 +1,2 @@ + a ++7 +diff --git a/x/x b/y/x +rename from x/x +rename to y/x +--- a/x/x ++++ b/y/x +@@ -1,1 +1,2 @@ + x ++x1 + +- parent to root: --rev . --rev 0 +M a +A x/x + y/x +R y/x + +diff --git a/a b/a +--- a/a ++++ b/a +@@ -1,2 +1,1 @@ + a +-7 +diff --git a/y/x b/x/x +rename from y/x +rename to x/x +--- a/y/x ++++ b/x/x +@@ -1,2 +1,1 @@ + x +-x1 + +- branch to parent: --rev 2 --rev . +M a +A y/x + x/x +R x/x +R x/y + +diff --git a/a b/a +--- a/a ++++ b/a +@@ -1,3 +1,2 @@ + a +-m1 +-m2 ++7 +diff --git a/x/y b/x/y +deleted file mode 100644 +--- a/x/y ++++ /dev/null +@@ -1,1 +0,0 @@ +-y1 +diff --git a/x/x b/y/x +rename from x/x +rename to y/x +--- a/x/x ++++ b/y/x +@@ -1,1 +1,2 @@ + x ++x1 + +- parent to branch: --rev . --rev 2 +M a +A x/x + y/x +A x/y +R y/x + +diff --git a/a b/a +--- a/a ++++ b/a +@@ -1,2 +1,3 @@ + a +-7 ++m1 ++m2 +diff --git a/y/x b/x/x +rename from y/x +rename to x/x +--- a/y/x ++++ b/x/x +@@ -1,2 +1,1 @@ + x +-x1 +diff --git a/x/y b/x/y +new file mode 100644 +--- /dev/null ++++ b/x/y +@@ -0,0 +1,1 @@ ++y1 + +
--- a/tests/test-newbranch Wed Apr 09 15:27:57 2008 -0700 +++ b/tests/test-newbranch Wed Apr 09 15:28:30 2008 -0700 @@ -1,5 +1,7 @@ #!/bin/sh +branchcache=.hg/branch.cache + hg init t cd t hg branches @@ -32,14 +34,23 @@ echo % test for invalid branch cache hg rollback -cp .hg/branch.cache .hg/bc-invalid +cp $branchcache .hg/bc-invalid hg log -r foo -cp .hg/bc-invalid .hg/branch.cache +cp .hg/bc-invalid $branchcache hg --debug log -r foo -rm .hg/branch.cache -echo corrupted > .hg/branch.cache +rm $branchcache +echo corrupted > $branchcache hg log -qr foo -cat .hg/branch.cache +cat $branchcache + +echo % push should update the branch cache +hg init ../target +echo % pushing just rev 0 +hg push -qr 0 ../target +cat ../target/$branchcache +echo % pushing everything +hg push -qf ../target +cat ../target/$branchcache echo % update with no arguments: tipmost revision of the current branch hg up -q -C 0
--- a/tests/test-newbranch.out Wed Apr 09 15:27:57 2008 -0700 +++ b/tests/test-newbranch.out Wed Apr 09 15:28:30 2008 -0700 @@ -6,6 +6,7 @@ marked working directory as branch default 0 files updated, 0 files merged, 0 files removed, 0 files unresolved foo +created new head 0 files updated, 0 files merged, 0 files removed, 0 files unresolved (branch merge, don't forget to commit) foo @@ -63,7 +64,7 @@ date: Mon Jan 12 13:46:40 1970 +0000 summary: modify a branch -Invalid branch cache: unknown tip +invalidating branch cache (tip differs) changeset: 4:4909a3732169c0c20011c4f4b8fdff4e3d89b23f branch: foo tag: tip @@ -83,6 +84,15 @@ bf1bc2f45e834c75404d0ddab57d53beab56e2f8 default 4909a3732169c0c20011c4f4b8fdff4e3d89b23f foo 67ec16bde7f1575d523313b9bca000f6a6f12dca bar +% push should update the branch cache +% pushing just rev 0 +be8523e69bf892e25817fc97187516b3c0804ae4 0 +be8523e69bf892e25817fc97187516b3c0804ae4 default +% pushing everything +4909a3732169c0c20011c4f4b8fdff4e3d89b23f 4 +bf1bc2f45e834c75404d0ddab57d53beab56e2f8 default +4909a3732169c0c20011c4f4b8fdff4e3d89b23f foo +67ec16bde7f1575d523313b9bca000f6a6f12dca bar % update with no arguments: tipmost revision of the current branch bf1bc2f45e83 4909a3732169 (foo) tip
--- a/tests/test-no-symlinks.out Wed Apr 09 15:27:57 2008 -0700 +++ b/tests/test-no-symlinks.out Wed Apr 09 15:28:30 2008 -0700 @@ -6,6 +6,7 @@ a d/b % bundle +2 changesets found pulling from ../symlinks.hg requesting all changes adding changesets
--- a/tests/test-non-interactive-wsgi.out Wed Apr 09 15:27:57 2008 -0700 +++ b/tests/test-non-interactive-wsgi.out Wed Apr 09 15:28:30 2008 -0700 @@ -7,6 +7,6 @@ ---- HEADERS 200 Script output follows ---- DATA -[('content-type', 'text/html; charset=ascii')] +[('Content-Type', 'text/html; charset=ascii')] ---- ERRORS
--- a/tests/test-notfound Wed Apr 09 15:27:57 2008 -0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,9 +0,0 @@ -#!/bin/sh - -hg init - -echo "Is there an error message when trying to diff non-existing files?" -hg diff not found - -echo "Is there an error message when trying to add non-existing files?" -hg add not found
--- a/tests/test-notfound.out Wed Apr 09 15:27:57 2008 -0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,6 +0,0 @@ -Is there an error message when trying to diff non-existing files? -found: No such file or directory -not: No such file or directory -Is there an error message when trying to add non-existing files? -found: No such file or directory -not: No such file or directory
--- a/tests/test-notify Wed Apr 09 15:27:57 2008 -0700 +++ b/tests/test-notify Wed Apr 09 15:28:30 2008 -0700 @@ -31,8 +31,18 @@ echo % commit hg --traceback --cwd a commit -Amb -d '1 0' +# on Mac OS X 10.5 the tmp path is very long so would get stripped in the subject line +cat <<EOF >> $HGRCPATH +[notify] +maxsubject = 200 +EOF + +# the python call below wraps continuation lines, which appear on Mac OS X 10.5 because +# of the very long subject line echo '% pull (minimal config)' -hg --traceback --cwd b pull ../a 2>&1 | sed -e 's/\(Message-Id:\).*/\1/' \ +hg --traceback --cwd b pull ../a 2>&1 | + python -c 'import sys,re; print re.sub("\n\t", " ", sys.stdin.read()),' | + sed -e 's/\(Message-Id:\).*/\1/' \ -e 's/changeset \([0-9a-f]* *\)in .*test-notif/changeset \1in test-notif/' \ -e 's/^details: .*test-notify/details: test-notify/' \ -e 's/^Date:.*/Date:/'
--- a/tests/test-notify.out Wed Apr 09 15:27:57 2008 -0700 +++ b/tests/test-notify.out Wed Apr 09 15:28:30 2008 -0700 @@ -4,6 +4,7 @@ % commit adding a % clone +updating working directory 1 files updated, 0 files merged, 0 files removed, 0 files unresolved % commit % pull (minimal config) @@ -22,15 +23,14 @@ changeset 0647d048b600 in test-notify/b details: test-notify/b?cmd=changeset;node=0647d048b600 -description: - b +description: b diffs (6 lines): diff -r cb9a9f314b8b -r 0647d048b600 a --- a/a Thu Jan 01 00:00:00 1970 +0000 +++ b/a Thu Jan 01 00:00:01 1970 +0000 -@@ -1,1 +1,2 @@ a +@@ -1,1 +1,2 @@ a +a (run 'hg update' to get a working copy) @@ -60,7 +60,7 @@ diff -r cb9a9f314b8b -r 0647d048b600 a --- a/a Thu Jan 01 00:00:00 1970 +0000 +++ b/a Thu Jan 01 00:00:01 1970 +0000 -@@ -1,1 +1,2 @@ a +@@ -1,1 +1,2 @@ a +a (run 'hg update' to get a working copy)
--- a/tests/test-parentrevspec Wed Apr 09 15:27:57 2008 -0700 +++ b/tests/test-parentrevspec Wed Apr 09 15:28:30 2008 -0700 @@ -16,7 +16,7 @@ echo >> foo - hg commit -d '0 0' -qAm "$msg" foo + hg commit -d '0 0' -qAm "$msg" } hg init repo
--- a/tests/test-parents.out Wed Apr 09 15:27:57 2008 -0700 +++ b/tests/test-parents.out Wed Apr 09 15:28:30 2008 -0700 @@ -4,6 +4,7 @@ adding c 1 files updated, 0 files merged, 1 files removed, 0 files unresolved adding c +created new head 2 files updated, 0 files merged, 0 files removed, 0 files unresolved % hg parents changeset: 3:02d851b7e549
--- a/tests/test-parse-date Wed Apr 09 15:27:57 2008 -0700 +++ b/tests/test-parse-date Wed Apr 09 15:28:30 2008 -0700 @@ -32,3 +32,14 @@ echo "d" > a hg ci -d "2006-01-15 13:30 +0500" -m "winter@UTC+5" hg log --template '{date|date}\n' + +# Test issue1014 (fractional timezones) +hg debugdate "1000000000 -16200" # 0430 +hg debugdate "1000000000 -15300" # 0415 +hg debugdate "1000000000 -14400" # 0400 +hg debugdate "1000000000 0" # GMT +hg debugdate "1000000000 14400" # -0400 +hg debugdate "1000000000 15300" # -0415 +hg debugdate "1000000000 16200" # -0430 +hg debugdate "Sat Sep 08 21:16:40 2001 +0430" +hg debugdate "Sat Sep 08 21:16:40 2001 -0430"
--- a/tests/test-parse-date.out Wed Apr 09 15:27:57 2008 -0700 +++ b/tests/test-parse-date.out Wed Apr 09 15:28:30 2008 -0700 @@ -1,16 +1,11 @@ reverting a +created new head changeset 3:107ce1ee2b43 backs out changeset 1:25a1420a55f8 -merging with changeset 2:e6c3abc120e7 +merging with changeset 3:107ce1ee2b43 1 files updated, 0 files merged, 0 files removed, 0 files unresolved (branch merge, don't forget to commit) -transaction abort! -rollback completed abort: invalid date: 'should fail' -transaction abort! -rollback completed abort: date exceeds 32 bits: 100000000000000000 -transaction abort! -rollback completed abort: impossible time zone offset: 1400000 Sun Jan 15 13:30:00 2006 +0500 Sun Jan 15 13:30:00 2006 -0800 @@ -21,3 +16,21 @@ Sat Apr 15 13:30:00 2006 +0000 Wed Feb 01 13:00:30 2006 -0500 Wed Feb 01 13:00:30 2006 +0000 +internal: 1000000000 -16200 +standard: Sun Sep 09 06:16:40 2001 +0430 +internal: 1000000000 -15300 +standard: Sun Sep 09 06:01:40 2001 +0415 +internal: 1000000000 -14400 +standard: Sun Sep 09 05:46:40 2001 +0400 +internal: 1000000000 0 +standard: Sun Sep 09 01:46:40 2001 +0000 +internal: 1000000000 14400 +standard: Sat Sep 08 21:46:40 2001 -0400 +internal: 1000000000 15300 +standard: Sat Sep 08 21:31:40 2001 -0415 +internal: 1000000000 16200 +standard: Sat Sep 08 21:16:40 2001 -0430 +internal: 999967600 -16200 +standard: Sat Sep 08 21:16:40 2001 +0430 +internal: 1000000000 16200 +standard: Sat Sep 08 21:16:40 2001 -0430
--- a/tests/test-patch.out Wed Apr 09 15:27:57 2008 -0700 +++ b/tests/test-patch.out Wed Apr 09 15:28:30 2008 -0700 @@ -5,6 +5,7 @@ adding manifests adding file changes added 1 changesets with 1 changes to 1 files +updating working directory 1 files updated, 0 files merged, 0 files removed, 0 files unresolved applying ../a.diff Using custom patch
--- a/tests/test-patchbomb Wed Apr 09 15:27:57 2008 -0700 +++ b/tests/test-patchbomb Wed Apr 09 15:28:30 2008 -0700 @@ -1,20 +1,46 @@ #!/bin/sh +fixheaders() +{ + sed -e 's/\(Message-Id:.*@\).*/\1/' \ + -e 's/\(In-Reply-To:.*@\).*/\1/' \ + -e 's/===.*/===/' +} + echo "[extensions]" >> $HGRCPATH echo "patchbomb=" >> $HGRCPATH -hg init +hg init t +cd t echo a > a hg commit -Ama -d '1 0' hg email --date '1970-1-1 0:1' -n -f quux -t foo -c bar tip | \ - sed -e 's/\(Message-Id:.*@\).*/\1/' + fixheaders echo b > b hg commit -Amb -d '2 0' hg email --date '1970-1-1 0:2' -n -f quux -t foo -c bar -s test 0:tip | \ - sed -e 's/\(Message-Id:.*@\).*/\1/' | \ - sed -e 's/\(In-Reply-To:.*@\).*/\1/' + fixheaders hg email -m test.mbox -f quux -t foo -c bar -s test 0:tip + +cd .. + +hg clone -q t t2 +cd t2 +echo c > c +hg commit -Amc -d '3 0' + +cat > description <<EOF +a multiline + +description +EOF + +echo % test bundle and description +hg email --date '1970-1-1 0:3' -n -f quux -t foo \ + -c bar -s test -r tip -b --desc description | \ + fixheaders +
--- a/tests/test-patchbomb.out Wed Apr 09 15:27:57 2008 -0700 +++ b/tests/test-patchbomb.out Wed Apr 09 15:28:30 2008 -0700 @@ -107,3 +107,41 @@ Writing [PATCH 0 of 2] test ... Writing [PATCH 1 of 2] a ... Writing [PATCH 2 of 2] b ... +adding c +% test bundle and description +searching for changes +1 changesets found + +Displaying test ... +Content-Type: multipart/mixed; boundary="=== +MIME-Version: 1.0 +Subject: test +Message-Id: <patchbomb.180@ +Date: Thu, 01 Jan 1970 00:03:00 +0000 +From: quux +To: foo +Cc: bar + +--=== +Content-Type: text/plain; charset="us-ascii" +MIME-Version: 1.0 +Content-Transfer-Encoding: 7bit + +a multiline + +description + +--=== +Content-Type: application/x-mercurial-bundle +MIME-Version: 1.0 +Content-Disposition: attachment; filename="bundle.hg" +Content-Transfer-Encoding: base64 + +SEcxMEJaaDkxQVkmU1nvR7I3AAAN////lFYQWj1/4HwRkdC/AywIAk0E4pfoSIIIgQCgGEQOcLAA +2tA1VPyp4mkeoG0EaaPU0GTT1GjRiNPIg9CZGBqZ6UbU9J+KFU09DNUaGgAAAAAANAGgAAAAA1U8 +oGgAADQGgAANNANAAAAAAZipFLz3XoakCEQB3PVPyHJVi1iYkAAKQAZQGpQGZESInRnCFMqLDla2 +Bx3qfRQeA2N4lnzKkAmP8kR2asievLLXXebVU8Vg4iEBqcJNJAxIapSU6SM4888ZAciRG6MYAIEE +SlIBpFisgGkyRjX//TMtfcUAEsGu56+YnE1OlTZmzKm8BSu2rvo4rHAYYaadIFFuTy0LYgIkgLVD +sgVa2F19D1tx9+hgbAygLgQwaIqcDdgA4BjQgIiz/AEP72++llgDKhKducqodGE4B0ETqF3JFOFC +Q70eyNw= +--===
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tests/test-paths Wed Apr 09 15:28:30 2008 -0700 @@ -0,0 +1,10 @@ +#!/bin/sh +hg init a +hg clone a b +cd a +echo '[paths]' >> .hg/hgrc +echo 'dupe = ../b' >> .hg/hgrc +hg in dupe | fgrep '../' +cd .. +hg -R a in dupe | fgrep '../' +true
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tests/test-paths.out Wed Apr 09 15:28:30 2008 -0700 @@ -0,0 +1,2 @@ +updating working directory +0 files updated, 0 files merged, 0 files removed, 0 files unresolved
--- a/tests/test-permissions.out Wed Apr 09 15:27:57 2008 -0700 +++ b/tests/test-permissions.out Wed Apr 09 15:28:30 2008 -0700 @@ -17,6 +17,6 @@ diff -r c1fab96507ef a --- a/a +++ b/a -@@ -1,1 +1,1 @@ foo +@@ -1,1 +1,1 @@ -foo +barber
--- a/tests/test-pull-permission.out Wed Apr 09 15:27:57 2008 -0700 +++ b/tests/test-pull-permission.out Wed Apr 09 15:28:30 2008 -0700 @@ -3,6 +3,7 @@ adding manifests adding file changes added 1 changesets with 1 changes to 1 files +updating working directory 1 files updated, 0 files merged, 0 files removed, 0 files unresolved checking changesets checking manifests
--- a/tests/test-pull-pull-corruption.out Wed Apr 09 15:27:57 2008 -0700 +++ b/tests/test-pull-pull-corruption.out Wed Apr 09 15:28:30 2008 -0700 @@ -3,6 +3,7 @@ adding manifests adding file changes added 1 changesets with 1 changes to 1 files +updating working directory 1 files updated, 0 files merged, 0 files removed, 0 files unresolved pulling from ../source2 pulling from ../source1
--- a/tests/test-pull-r Wed Apr 09 15:27:57 2008 -0700 +++ b/tests/test-pull-r Wed Apr 09 15:28:30 2008 -0700 @@ -14,6 +14,9 @@ hg init copy cd copy +echo '% pull a missing revision' +hg pull -qr missing ../repo + echo '% pull -r 0' hg pull -qr 0 ../repo hg log
--- a/tests/test-pull-r.out Wed Apr 09 15:27:57 2008 -0700 +++ b/tests/test-pull-r.out Wed Apr 09 15:28:30 2008 -0700 @@ -15,6 +15,8 @@ date: Thu Jan 01 00:00:00 1970 +0000 summary: add foo +% pull a missing revision +abort: unknown revision 'missing'! % pull -r 0 changeset: 0:bbd179dfa0a7 tag: tip
--- a/tests/test-pull-update.out Wed Apr 09 15:27:57 2008 -0700 +++ b/tests/test-pull-update.out Wed Apr 09 15:28:30 2008 -0700 @@ -1,4 +1,5 @@ adding foo +updating working directory 1 files updated, 0 files merged, 0 files removed, 0 files unresolved % should fail pulling from ../tt
--- a/tests/test-pull.out Wed Apr 09 15:27:57 2008 -0700 +++ b/tests/test-pull.out Wed Apr 09 15:28:30 2008 -0700 @@ -9,6 +9,7 @@ adding manifests adding file changes added 1 changesets with 1 changes to 1 files +updating working directory 1 files updated, 0 files merged, 0 files removed, 0 files unresolved checking changesets checking manifests
--- a/tests/test-push-hook-lock.out Wed Apr 09 15:27:57 2008 -0700 +++ b/tests/test-push-hook-lock.out Wed Apr 09 15:28:30 2008 -0700 @@ -1,5 +1,7 @@ adding foo +updating working directory 1 files updated, 0 files merged, 0 files removed, 0 files unresolved +updating working directory 1 files updated, 0 files merged, 0 files removed, 0 files unresolved pushing to ../2 searching for changes
--- a/tests/test-push-http Wed Apr 09 15:27:57 2008 -0700 +++ b/tests/test-push-http Wed Apr 09 15:28:30 2008 -0700 @@ -13,53 +13,44 @@ echo a >> a hg ci -mb -d '0 0' +req() { + hg serve -p $HGPORT -d --pid-file=hg.pid -E errors.log + cat hg.pid >> $DAEMON_PIDS + hg --cwd ../test2 push http://localhost:$HGPORT/ | sed -e 's,:[0-9][0-9]*/,/,' + kill `cat hg.pid` + echo % serve errors + cat errors.log +} + cd ../test echo % expect ssl error -hg serve -p $HGPORT -d --pid-file=hg.pid -cat hg.pid >> $DAEMON_PIDS -hg --cwd ../test2 push http://localhost:$HGPORT/ | sed -e 's,:[0-9][0-9]*/,/,' -kill `cat hg.pid` +req echo % expect authorization error echo '[web]' > .hg/hgrc echo 'push_ssl = false' >> .hg/hgrc -hg serve -p $HGPORT -d --pid-file=hg.pid -cat hg.pid >> $DAEMON_PIDS -hg --cwd ../test2 push http://localhost:$HGPORT/ | sed -e 's,:[0-9][0-9]*/,/,' -kill `cat hg.pid` +req echo % expect authorization error: must have authorized user echo 'allow_push = unperson' >> .hg/hgrc -hg serve -p $HGPORT -d --pid-file=hg.pid -cat hg.pid >> $DAEMON_PIDS -hg --cwd ../test2 push http://localhost:$HGPORT/ | sed -e 's,:[0-9][0-9]*/,/,' -kill `cat hg.pid` +req echo % expect success echo 'allow_push = *' >> .hg/hgrc echo '[hooks]' >> .hg/hgrc echo 'changegroup = python ../printenv.py changegroup 0 ../urls' >> .hg/hgrc -hg serve -p $HGPORT -d --pid-file=hg.pid -cat hg.pid >> $DAEMON_PIDS -hg --cwd ../test2 push http://localhost:$HGPORT/ | sed -e 's,:[0-9][0-9]*/,/,' -kill `cat hg.pid` -hg rollback +req cat ../urls +hg rollback echo % expect authorization error: all users denied echo '[web]' > .hg/hgrc echo 'push_ssl = false' >> .hg/hgrc echo 'deny_push = *' >> .hg/hgrc -hg serve -p $HGPORT -d --pid-file=hg.pid -cat hg.pid >> $DAEMON_PIDS -hg --cwd ../test2 push http://localhost:$HGPORT/ | sed -e 's,:[0-9][0-9]*/,/,' -kill `cat hg.pid` +req echo % expect authorization error: some users denied, users must be authenticated echo 'deny_push = unperson' >> .hg/hgrc -hg serve -p $HGPORT -d --pid-file=hg.pid -cat hg.pid >> $DAEMON_PIDS -hg --cwd ../test2 push http://localhost:$HGPORT/ | sed -e 's,:[0-9][0-9]*/,/,' -kill `cat hg.pid` +req
--- a/tests/test-push-http.out Wed Apr 09 15:27:57 2008 -0700 +++ b/tests/test-push-http.out Wed Apr 09 15:28:30 2008 -0700 @@ -1,17 +1,21 @@ adding a +updating working directory 1 files updated, 0 files merged, 0 files removed, 0 files unresolved % expect ssl error pushing to http://localhost/ searching for changes ssl required +% serve errors % expect authorization error +abort: authorization failed pushing to http://localhost/ searching for changes -push not authorized +% serve errors % expect authorization error: must have authorized user +abort: authorization failed pushing to http://localhost/ searching for changes -push not authorized +% serve errors % expect success pushing to http://localhost/ searching for changes @@ -19,13 +23,16 @@ adding manifests adding file changes added 1 changesets with 1 changes to 1 files -rolling back last transaction +% serve errors changegroup hook: HG_NODE=ba677d0156c1196c1a699fa53f390dcfc3ce3872 HG_SOURCE=serve HG_URL=remote:http +rolling back last transaction % expect authorization error: all users denied +abort: authorization failed pushing to http://localhost/ searching for changes -push not authorized +% serve errors % expect authorization error: some users denied, users must be authenticated +abort: authorization failed pushing to http://localhost/ searching for changes -push not authorized +% serve errors
--- a/tests/test-push-r.out Wed Apr 09 15:27:57 2008 -0700 +++ b/tests/test-push-r.out Wed Apr 09 15:28:30 2008 -0700 @@ -1,4 +1,5 @@ 1 files updated, 0 files merged, 0 files removed, 0 files unresolved +created new head 1 files updated, 0 files merged, 2 files removed, 0 files unresolved rev offset length base linkrev nodeid p1 p2 0 0 3 0 0 362fef284ce2 000000000000 000000000000
--- a/tests/test-push-warn.out Wed Apr 09 15:27:57 2008 -0700 +++ b/tests/test-push-warn.out Wed Apr 09 15:28:30 2008 -0700 @@ -1,7 +1,8 @@ +updating working directory 1 files updated, 0 files merged, 0 files removed, 0 files unresolved pushing to ../a searching for changes -abort: push creates new remote branches! +abort: push creates new remote heads! (did you forget to merge? use push -f to force) pulling from ../a searching for changes @@ -12,7 +13,7 @@ (run 'hg heads' to see heads, 'hg merge' to merge) pushing to ../a searching for changes -abort: push creates new remote branches! +abort: push creates new remote heads! (did you forget to merge? use push -f to force) 1 files updated, 0 files merged, 0 files removed, 0 files unresolved (branch merge, don't forget to commit) @@ -23,31 +24,34 @@ adding file changes added 2 changesets with 1 changes to 1 files adding foo +updating working directory 1 files updated, 0 files merged, 0 files removed, 0 files unresolved 1 files updated, 0 files merged, 0 files removed, 0 files unresolved +created new head 1 files updated, 0 files merged, 0 files removed, 0 files unresolved +created new head merging foo 0 files updated, 1 files merged, 0 files removed, 0 files unresolved (branch merge, don't forget to commit) pushing to ../c searching for changes -abort: push creates new remote branches! +abort: push creates new remote heads! (did you forget to merge? use push -f to force) -0 +1 pushing to ../c searching for changes no changes found 0 pushing to ../c searching for changes -abort: push creates new remote branches! +abort: push creates new remote heads! (did you forget to merge? use push -f to force) -0 +1 pushing to ../c searching for changes -abort: push creates new remote branches! +abort: push creates new remote heads! (did you forget to merge? use push -f to force) -0 +1 pushing to ../c searching for changes adding changesets
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tests/test-qrecord Wed Apr 09 15:28:30 2008 -0700 @@ -0,0 +1,100 @@ +#!/bin/sh + +echo "[ui]" >> $HGRCPATH +echo "interactive=true" >> $HGRCPATH +echo "[extensions]" >> $HGRCPATH +echo "record=" >> $HGRCPATH + +echo "% help (no mq, so no qrecord)" + +hg help qrecord + +echo "mq=" >> $HGRCPATH + +echo "% help (mq present)" + +hg help qrecord + +hg init a +cd a + +echo % base commit + +cat > 1.txt <<EOF +1 +2 +3 +4 +5 +EOF +cat > 2.txt <<EOF +a +b +c +d +e +f +EOF +mkdir dir +cat > dir/a.txt <<EOF +hello world + +someone +up +there +loves +me +EOF + +hg add 1.txt 2.txt dir/a.txt +hg commit -d '0 0' -m 'initial checkin' + +echo % changing files + +sed -e 's/2/2 2/;s/4/4 4/' 1.txt > 1.txt.new +sed -e 's/b/b b/' 2.txt > 2.txt.new +sed -e 's/hello world/hello world!/' dir/a.txt > dir/a.txt.new + +mv -f 1.txt.new 1.txt +mv -f 2.txt.new 2.txt +mv -f dir/a.txt.new dir/a.txt + +echo % whole diff + +hg diff --nodates + +echo % qrecord a.patch + +hg qrecord -d '0 0' -m aaa a.patch <<EOF +y +y +n +y +y +n +EOF + +echo +echo % "after qrecord a.patch 'tip'" +hg tip -p +echo +echo % "after qrecord a.patch 'diff'" +hg diff --nodates + +echo % qrecord b.patch +hg qrecord -d '0 0' -m bbb b.patch <<EOF +y +y +y +y +EOF + +echo +echo % "after qrecord b.patch 'tip'" +hg tip -p +echo +echo % "after qrecord b.patch 'diff'" +hg diff --nodates + +echo +echo % --- end ---
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tests/test-qrecord.out Wed Apr 09 15:28:30 2008 -0700 @@ -0,0 +1,207 @@ +% help (no mq, so no qrecord) +hg: unknown command 'qrecord' +Mercurial Distributed SCM + +basic commands: + + add add the specified files on the next commit + annotate show changeset information per file line + clone make a copy of an existing repository + commit commit the specified files or all outstanding changes + diff diff repository (or selected files) + export dump the header and diffs for one or more changesets + init create a new repository in the given directory + log show revision history of entire repository or files + merge merge working directory with another revision + parents show the parents of the working dir or revision + pull pull changes from the specified source + push push changes to the specified destination + remove remove the specified files on the next commit + serve export the repository via HTTP + status show changed files in the working directory + update update working directory + +use "hg help" for the full list of commands or "hg -v" for details +% help (mq present) +hg qrecord [OPTION]... PATCH [FILE]... + +interactively record a new patch + + see 'hg help qnew' & 'hg help record' for more information and usage + +options: + + -e --edit edit commit message + -g --git use git extended diff format + -I --include include names matching the given patterns + -X --exclude exclude names matching the given patterns + -m --message use <text> as commit message + -l --logfile read commit message from <file> + -U --currentuser add "From: <current user>" to patch + -u --user add "From: <given user>" to patch + -D --currentdate add "Date: <current date>" to patch + -d --date add "Date: <given date>" to patch + +use "hg -v help qrecord" to show global options +% base commit +% changing files +% whole diff +diff -r 1057167b20ef 1.txt +--- a/1.txt ++++ b/1.txt +@@ -1,5 +1,5 @@ + 1 +-2 ++2 2 + 3 +-4 ++4 4 + 5 +diff -r 1057167b20ef 2.txt +--- a/2.txt ++++ b/2.txt +@@ -1,5 +1,5 @@ + a +-b ++b b + c + d + e +diff -r 1057167b20ef dir/a.txt +--- a/dir/a.txt ++++ b/dir/a.txt +@@ -1,4 +1,4 @@ +-hello world ++hello world! + + someone + up +% qrecord a.patch +diff --git a/1.txt b/1.txt +2 hunks, 4 lines changed +examine changes to '1.txt'? [Ynsfdaq?] @@ -1,3 +1,3 @@ + 1 +-2 ++2 2 + 3 +record this change to '1.txt'? [Ynsfdaq?] @@ -3,3 +3,3 @@ + 3 +-4 ++4 4 + 5 +record this change to '1.txt'? [Ynsfdaq?] diff --git a/2.txt b/2.txt +1 hunks, 2 lines changed +examine changes to '2.txt'? [Ynsfdaq?] @@ -1,5 +1,5 @@ + a +-b ++b b + c + d + e +record this change to '2.txt'? [Ynsfdaq?] diff --git a/dir/a.txt b/dir/a.txt +1 hunks, 2 lines changed +examine changes to 'dir/a.txt'? [Ynsfdaq?] +% after qrecord a.patch 'tip' +changeset: 1:5d1ca63427ee +tag: qtip +tag: tip +tag: a.patch +tag: qbase +user: test +date: Thu Jan 01 00:00:00 1970 +0000 +summary: aaa + +diff -r 1057167b20ef -r 5d1ca63427ee 1.txt +--- a/1.txt Thu Jan 01 00:00:00 1970 +0000 ++++ b/1.txt Thu Jan 01 00:00:00 1970 +0000 +@@ -1,5 +1,5 @@ + 1 +-2 ++2 2 + 3 + 4 + 5 +diff -r 1057167b20ef -r 5d1ca63427ee 2.txt +--- a/2.txt Thu Jan 01 00:00:00 1970 +0000 ++++ b/2.txt Thu Jan 01 00:00:00 1970 +0000 +@@ -1,5 +1,5 @@ + a +-b ++b b + c + d + e + + +% after qrecord a.patch 'diff' +diff -r 5d1ca63427ee 1.txt +--- a/1.txt ++++ b/1.txt +@@ -1,5 +1,5 @@ + 1 + 2 2 + 3 +-4 ++4 4 + 5 +diff -r 5d1ca63427ee dir/a.txt +--- a/dir/a.txt ++++ b/dir/a.txt +@@ -1,4 +1,4 @@ +-hello world ++hello world! + + someone + up +% qrecord b.patch +diff --git a/1.txt b/1.txt +1 hunks, 2 lines changed +examine changes to '1.txt'? [Ynsfdaq?] @@ -1,5 +1,5 @@ + 1 + 2 2 + 3 +-4 ++4 4 + 5 +record this change to '1.txt'? [Ynsfdaq?] diff --git a/dir/a.txt b/dir/a.txt +1 hunks, 2 lines changed +examine changes to 'dir/a.txt'? [Ynsfdaq?] @@ -1,4 +1,4 @@ +-hello world ++hello world! + + someone + up +record this change to 'dir/a.txt'? [Ynsfdaq?] +% after qrecord b.patch 'tip' +changeset: 2:b056198bf878 +tag: qtip +tag: tip +tag: b.patch +user: test +date: Thu Jan 01 00:00:00 1970 +0000 +summary: bbb + +diff -r 5d1ca63427ee -r b056198bf878 1.txt +--- a/1.txt Thu Jan 01 00:00:00 1970 +0000 ++++ b/1.txt Thu Jan 01 00:00:00 1970 +0000 +@@ -1,5 +1,5 @@ + 1 + 2 2 + 3 +-4 ++4 4 + 5 +diff -r 5d1ca63427ee -r b056198bf878 dir/a.txt +--- a/dir/a.txt Thu Jan 01 00:00:00 1970 +0000 ++++ b/dir/a.txt Thu Jan 01 00:00:00 1970 +0000 +@@ -1,4 +1,4 @@ +-hello world ++hello world! + + someone + up + + +% after qrecord b.patch 'diff' + +% --- end ---
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tests/test-race Wed Apr 09 15:28:30 2008 -0700 @@ -0,0 +1,15 @@ +#!/bin/sh + +mkdir t +cd t +hg init +echo a > a +hg add a +hg commit -m test + +# do we ever miss a sub-second change? +for i in 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20; do + hg co -qC 0 + echo b > a + hg st +done
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tests/test-race.out Wed Apr 09 15:28:30 2008 -0700 @@ -0,0 +1,20 @@ +M a +M a +M a +M a +M a +M a +M a +M a +M a +M a +M a +M a +M a +M a +M a +M a +M a +M a +M a +M a
--- a/tests/test-rebuildstate Wed Apr 09 15:27:57 2008 -0700 +++ b/tests/test-rebuildstate Wed Apr 09 15:28:30 2008 -0700 @@ -11,14 +11,9 @@ hg add baz hg rm bar -echo '% state dump' -hg debugstate | cut -b 1-16,35- | sort +hg debugrebuildstate +echo '% state dump after' +hg debugstate --nodates | sort echo '% status' hg st -A -hg debugrebuildstate -echo '% state dump' -hg debugstate | cut -b 1-16,35- | sort -echo '% status' -hg st -A -
--- a/tests/test-rebuildstate.out Wed Apr 09 15:27:57 2008 -0700 +++ b/tests/test-rebuildstate.out Wed Apr 09 15:28:30 2008 -0700 @@ -1,14 +1,6 @@ adding bar adding foo -% state dump -a 0 -1 baz -n 644 0 foo -r 0 0 bar -% status -A baz -R bar -C foo -% state dump +% state dump after n 666 -1 bar n 666 -1 foo % status
--- a/tests/test-record.out Wed Apr 09 15:27:57 2008 -0700 +++ b/tests/test-record.out Wed Apr 09 15:28:30 2008 -0700 @@ -6,6 +6,8 @@ If a list of files is omitted, all changes reported by "hg status" will be candidates for recording. + See 'hg help dates' for a list of formats valid for -d/--date. + You will be prompted for whether to record changes to each modified file, and for files with multiple changes, for each change to use. For each query, the following responses are @@ -50,6 +52,7 @@ new file mode 100644 examine changes to 'empty-rw'? [Ynsfdaq?] transaction abort! rollback completed +abort: empty commit message changeset: -1:000000000000 tag: tip @@ -104,6 +107,7 @@ % add binary file +1 changesets found diff --git a/tip.bundle b/tip.bundle new file mode 100644 this is a binary file @@ -118,6 +122,7 @@ Binary file tip.bundle has changed % change binary file +1 changesets found diff --git a/tip.bundle b/tip.bundle this modifies a binary file (all or nothing) examine changes to 'tip.bundle'? [Ynsfdaq?] @@ -131,6 +136,7 @@ Binary file tip.bundle has changed % rename and change binary file +1 changesets found diff --git a/tip.bundle b/top.bundle rename from tip.bundle rename to top.bundle @@ -175,7 +181,7 @@ % modify end of plain file diff --git a/plain b/plain 1 hunks, 1 lines changed -examine changes to 'plain'? [Ynsfdaq?] @@ -8,3 +8,4 @@ 8 +examine changes to 'plain'? [Ynsfdaq?] @@ -8,3 +8,4 @@ 8 9 10 @@ -183,7 +189,7 @@ record this change to 'plain'? [Ynsfdaq?] % modify end of plain file, no EOL diff --git a/plain b/plain 1 hunks, 1 lines changed -examine changes to 'plain'? [Ynsfdaq?] @@ -9,3 +9,4 @@ 9 +examine changes to 'plain'? [Ynsfdaq?] @@ -9,3 +9,4 @@ 9 10 11 @@ -192,7 +198,7 @@ record this change to 'plain'? [Ynsfdaq?] % modify end of plain file, add EOL diff --git a/plain b/plain 1 hunks, 2 lines changed -examine changes to 'plain'? [Ynsfdaq?] @@ -9,4 +9,4 @@ 9 +examine changes to 'plain'? [Ynsfdaq?] @@ -9,4 +9,4 @@ 9 10 11 @@ -202,13 +208,13 @@ record this change to 'plain'? [Ynsfdaq?] % modify beginning, trim end, record both diff --git a/plain b/plain 2 hunks, 4 lines changed -examine changes to 'plain'? [Ynsfdaq?] @@ -1,4 +1,4 @@ 1 +examine changes to 'plain'? [Ynsfdaq?] @@ -1,4 +1,4 @@ -1 +2 2 3 4 -record this change to 'plain'? [Ynsfdaq?] @@ -8,5 +8,3 @@ 8 +record this change to 'plain'? [Ynsfdaq?] @@ -8,5 +8,3 @@ 8 9 10 @@ -224,13 +230,13 @@ diff -r e2ecd9b0b78d -r d09ab1967dab plain --- a/plain Thu Jan 01 00:00:10 1970 +0000 +++ b/plain Thu Jan 01 00:00:10 1970 +0000 -@@ -1,4 +1,4 @@ 1 +@@ -1,4 +1,4 @@ -1 +2 2 3 4 -@@ -8,5 +8,3 @@ 8 +@@ -8,5 +8,3 @@ 8 9 10 @@ -241,7 +247,7 @@ % record end diff --git a/plain b/plain 2 hunks, 5 lines changed -examine changes to 'plain'? [Ynsfdaq?] @@ -1,9 +1,6 @@ 2 +examine changes to 'plain'? [Ynsfdaq?] @@ -1,9 +1,6 @@ -2 -2 -3 @@ -270,7 +276,7 @@ diff -r d09ab1967dab -r 44516c9708ae plain --- a/plain Thu Jan 01 00:00:10 1970 +0000 +++ b/plain Thu Jan 01 00:00:11 1970 +0000 -@@ -7,4 +7,4 @@ 7 +@@ -7,4 +7,4 @@ 7 8 9 @@ -280,7 +286,7 @@ % record beginning diff --git a/plain b/plain 1 hunks, 3 lines changed -examine changes to 'plain'? [Ynsfdaq?] @@ -1,6 +1,3 @@ 2 +examine changes to 'plain'? [Ynsfdaq?] @@ -1,6 +1,3 @@ -2 -2 -3 @@ -297,7 +303,7 @@ diff -r 44516c9708ae -r 3ebbace64a8d plain --- a/plain Thu Jan 01 00:00:11 1970 +0000 +++ b/plain Thu Jan 01 00:00:12 1970 +0000 -@@ -1,6 +1,3 @@ 2 +@@ -1,6 +1,3 @@ -2 -2 -3 @@ -309,7 +315,7 @@ % record end diff --git a/plain b/plain 2 hunks, 4 lines changed -examine changes to 'plain'? [Ynsfdaq?] @@ -1,6 +1,9 @@ 4 +examine changes to 'plain'? [Ynsfdaq?] @@ -1,6 +1,9 @@ +1 +2 +3 @@ -331,7 +337,7 @@ % record beginning, middle diff --git a/plain b/plain 3 hunks, 7 lines changed -examine changes to 'plain'? [Ynsfdaq?] @@ -1,2 +1,5 @@ 4 +examine changes to 'plain'? [Ynsfdaq?] @@ -1,2 +1,5 @@ +1 +2 +3 @@ -363,7 +369,7 @@ diff -r efc0dad7bd9f -r c1c639d8b268 plain --- a/plain Thu Jan 01 00:00:13 1970 +0000 +++ b/plain Thu Jan 01 00:00:14 1970 +0000 -@@ -1,5 +1,10 @@ 4 +@@ -1,5 +1,10 @@ +1 +2 +3 @@ -378,7 +384,7 @@ % record end diff --git a/plain b/plain 1 hunks, 2 lines changed -examine changes to 'plain'? [Ynsfdaq?] @@ -9,3 +9,5 @@ 7 +examine changes to 'plain'? [Ynsfdaq?] @@ -9,3 +9,5 @@ 7 8 9 @@ -394,7 +400,7 @@ diff -r c1c639d8b268 -r 80b74bbc7808 plain --- a/plain Thu Jan 01 00:00:14 1970 +0000 +++ b/plain Thu Jan 01 00:00:15 1970 +0000 -@@ -9,3 +9,5 @@ 7 +@@ -9,3 +9,5 @@ 7 8 9 @@ -404,7 +410,7 @@ adding subdir/a diff --git a/subdir/a b/subdir/a 1 hunks, 1 lines changed -examine changes to 'subdir/a'? [Ynsfdaq?] @@ -1,1 +1,2 @@ a +examine changes to 'subdir/a'? [Ynsfdaq?] @@ -1,1 +1,2 @@ a +a record this change to 'subdir/a'? [Ynsfdaq?] @@ -417,7 +423,7 @@ diff -r aecf2b2ea83c -r 33ff5c4fb017 subdir/a --- a/subdir/a Thu Jan 01 00:00:16 1970 +0000 +++ b/subdir/a Thu Jan 01 00:00:16 1970 +0000 -@@ -1,1 +1,2 @@ a +@@ -1,1 +1,2 @@ a +a @@ -466,7 +472,7 @@ diff -r f9e855cd9374 -r 094183e04b7c subdir/f2 --- a/subdir/f2 Thu Jan 01 00:00:17 1970 +0000 +++ b/subdir/f2 Thu Jan 01 00:00:18 1970 +0000 -@@ -1,1 +1,2 @@ b +@@ -1,1 +1,2 @@ b +b @@ -483,7 +489,7 @@ diff -r 094183e04b7c -r 38164785b0ef subdir/f1 --- a/subdir/f1 Thu Jan 01 00:00:18 1970 +0000 +++ b/subdir/f1 Thu Jan 01 00:00:19 1970 +0000 -@@ -1,1 +1,2 @@ a +@@ -1,1 +1,2 @@ a +a
--- a/tests/test-remove Wed Apr 09 15:27:57 2008 -0700 +++ b/tests/test-remove Wed Apr 09 15:28:30 2008 -0700 @@ -1,45 +1,110 @@ #!/bin/sh +remove() { + hg rm $@ + hg st + # do not use ls -R, which recurses in .hg subdirs on Mac OS X 10.5 + find . -name .hg -prune -o -type f -print | sort + hg up -C +} + hg init a cd a echo a > foo -hg rm foo + +echo % file not managed +remove foo + hg add foo -hg commit -m 1 -d "1000000 0" -hg remove +hg commit -m1 + +# the table cases + +echo % 00 state added, options none +echo b > bar +hg add bar +remove bar + +echo % 01 state clean, options none +remove foo + +echo % 02 state modified, options none +echo b >> foo +remove foo + +echo % 03 state missing, options none rm foo -hg remove foo -hg revert --all +remove foo + +echo % 10 state added, options -f +echo b > bar +hg add bar +remove -f bar +rm bar + +echo % 11 state clean, options -f +remove -f foo + +echo % 12 state modified, options -f +echo b >> foo +remove -f foo + +echo % 13 state missing, options -f rm foo -hg remove --after -hg commit -m 2 -d "1000000 0" -hg export --nodates 0 -hg export --nodates 1 -hg log -p -r 0 -hg log -p -r 1 +remove -f foo + +echo % 20 state added, options -A +echo b > bar +hg add bar +remove -A bar -echo a > a -hg add a -hg rm a -hg rm -f a -echo b > b -mkdir c -echo d > c/d -hg ci -A -m 3 -d "1000001 0" -echo c >> b -hg rm b -hg rm -f b -hg rm -A c/d -hg st -cat c/d -hg revert c -hg rm -A -hg st -hg rm -A c -hg st -rm c/d -hg rm -A -hg st +echo % 21 state clean, options -A +remove -A foo + +echo % 22 state modified, options -A +echo b >> foo +remove -A foo + +echo % 23 state missing, options -A +rm foo +remove -A foo + +echo % 30 state added, options -Af +echo b > bar +hg add bar +remove -Af bar +rm bar + +echo % 31 state clean, options -Af +remove -Af foo + +echo % 32 state modified, options -Af +echo b >> foo +remove -Af foo -cd .. -hg clone a b +echo % 33 state missing, options -Af +rm foo +remove -Af foo + +# test some directory stuff + +mkdir test +echo a > test/foo +echo b > test/bar +hg ci -Am2 + +echo % dir, options none +rm test/bar +remove test + +echo % dir, options -f +rm test/bar +remove -f test + +echo % dir, options -A +rm test/bar +remove -A test + +echo % dir, options -Af +rm test/bar +remove -Af test
--- a/tests/test-remove.out Wed Apr 09 15:27:57 2008 -0700 +++ b/tests/test-remove.out Wed Apr 09 15:28:30 2008 -0700 @@ -1,66 +1,100 @@ -not removing foo: file is not managed -abort: no files specified -undeleting foo -removing foo -# HG changeset patch -# User test -# Date 1000000 0 -# Node ID 8ba83d44753d6259db5ce6524974dd1174e90f47 -# Parent 0000000000000000000000000000000000000000 -1 - -diff -r 000000000000 -r 8ba83d44753d foo ---- /dev/null -+++ b/foo -@@ -0,0 +1,1 @@ -+a -# HG changeset patch -# User test -# Date 1000000 0 -# Node ID a1fce69c50d97881c5c014ab23f580f720c78678 -# Parent 8ba83d44753d6259db5ce6524974dd1174e90f47 -2 - -diff -r 8ba83d44753d -r a1fce69c50d9 foo ---- a/foo -+++ /dev/null -@@ -1,1 +0,0 @@ --a -changeset: 0:8ba83d44753d -user: test -date: Mon Jan 12 13:46:40 1970 +0000 -summary: 1 - -diff -r 000000000000 -r 8ba83d44753d foo ---- /dev/null Thu Jan 01 00:00:00 1970 +0000 -+++ b/foo Mon Jan 12 13:46:40 1970 +0000 -@@ -0,0 +1,1 @@ -+a - -changeset: 1:a1fce69c50d9 -tag: tip -user: test -date: Mon Jan 12 13:46:40 1970 +0000 -summary: 2 - -diff -r 8ba83d44753d -r a1fce69c50d9 foo ---- a/foo Mon Jan 12 13:46:40 1970 +0000 -+++ /dev/null Thu Jan 01 00:00:00 1970 +0000 -@@ -1,1 +0,0 @@ --a - -not removing a: file has been marked for add (use -f to force removal) -adding a -adding b -adding c/d -not removing b: file is modified (use -f to force removal) -R b -R c/d -d -undeleting c/d -R b -R b -removing c/d -R b -R c/d -3 files updated, 0 files merged, 0 files removed, 0 files unresolved +% file not managed +? foo +./foo +0 files updated, 0 files merged, 0 files removed, 0 files unresolved +% 00 state added, options none +not removing bar: file has been marked for add (use -f to force removal) +A bar +./bar +./foo +0 files updated, 0 files merged, 1 files removed, 0 files unresolved +% 01 state clean, options none +R foo +1 files updated, 0 files merged, 0 files removed, 0 files unresolved +% 02 state modified, options none +not removing foo: file is modified (use -f to force removal) +M foo +./foo +1 files updated, 0 files merged, 0 files removed, 0 files unresolved +% 03 state missing, options none +R foo +1 files updated, 0 files merged, 0 files removed, 0 files unresolved +% 10 state added, options -f +? bar +./bar +./foo +0 files updated, 0 files merged, 0 files removed, 0 files unresolved +% 11 state clean, options -f +R foo +1 files updated, 0 files merged, 0 files removed, 0 files unresolved +% 12 state modified, options -f +R foo +1 files updated, 0 files merged, 0 files removed, 0 files unresolved +% 13 state missing, options -f +R foo +1 files updated, 0 files merged, 0 files removed, 0 files unresolved +% 20 state added, options -A +not removing bar: file still exists (use -f to force removal) +A bar +./bar +./foo +0 files updated, 0 files merged, 1 files removed, 0 files unresolved +% 21 state clean, options -A +not removing foo: file still exists (use -f to force removal) +./foo +0 files updated, 0 files merged, 0 files removed, 0 files unresolved +% 22 state modified, options -A +not removing foo: file still exists (use -f to force removal) +M foo +./foo +1 files updated, 0 files merged, 0 files removed, 0 files unresolved +% 23 state missing, options -A +R foo +1 files updated, 0 files merged, 0 files removed, 0 files unresolved +% 30 state added, options -Af +? bar +./bar +./foo +0 files updated, 0 files merged, 0 files removed, 0 files unresolved +% 31 state clean, options -Af +R foo +./foo +1 files updated, 0 files merged, 0 files removed, 0 files unresolved +% 32 state modified, options -Af +R foo +./foo +1 files updated, 0 files merged, 0 files removed, 0 files unresolved +% 33 state missing, options -Af +R foo +1 files updated, 0 files merged, 0 files removed, 0 files unresolved +adding test/bar +adding test/foo +% dir, options none +removing test/foo +removing test/bar +R test/bar +R test/foo +./foo +2 files updated, 0 files merged, 0 files removed, 0 files unresolved +% dir, options -f +removing test/foo +removing test/bar +R test/bar +R test/foo +./foo +2 files updated, 0 files merged, 0 files removed, 0 files unresolved +% dir, options -A +not removing test/foo: file still exists (use -f to force removal) +removing test/bar +R test/bar +./foo +./test/foo +1 files updated, 0 files merged, 0 files removed, 0 files unresolved +% dir, options -Af +removing test/foo +removing test/bar +R test/bar +R test/foo +./foo +./test/foo +2 files updated, 0 files merged, 0 files removed, 0 files unresolved
--- a/tests/test-rename-after-merge.out Wed Apr 09 15:27:57 2008 -0700 +++ b/tests/test-rename-after-merge.out Wed Apr 09 15:28:30 2008 -0700 @@ -1,6 +1,7 @@ % create source repository adding a % fork source repository +updating working directory 1 files updated, 0 files merged, 0 files removed, 0 files unresolved adding b % update source repository
--- a/tests/test-rename-dir-merge.out Wed Apr 09 15:27:57 2008 -0700 +++ b/tests/test-rename-dir-merge.out Wed Apr 09 15:28:30 2008 -0700 @@ -4,6 +4,7 @@ moving a/a to b/a moving a/b to b/b 2 files updated, 0 files merged, 2 files removed, 0 files unresolved +created new head resolving manifests overwrite None partial False ancestor f9b20c0d4c51 local ce36d17b18fb+ remote 55119e611c80 @@ -70,4 +71,5 @@ A b/c a/c ? b/d +created new head b/c renamed from a/c:354ae8da6e890359ef49ade27b68bbc361f3ca88
--- a/tests/test-rename-dir-merge2.out Wed Apr 09 15:27:57 2008 -0700 +++ b/tests/test-rename-dir-merge2.out Wed Apr 09 15:28:30 2008 -0700 @@ -1,4 +1,5 @@ adding a/f +updating working directory 1 files updated, 0 files merged, 0 files removed, 0 files unresolved moving a/f to b/f adding a/aa/g
--- a/tests/test-rename-merge1.out Wed Apr 09 15:27:57 2008 -0700 +++ b/tests/test-rename-merge1.out Wed Apr 09 15:28:30 2008 -0700 @@ -1,5 +1,6 @@ checkout 2 files updated, 0 files merged, 2 files removed, 0 files unresolved +created new head merge resolving manifests overwrite None partial False @@ -11,16 +12,18 @@ b b2 all copies found (* = to merge, ! = divergent): - c2 -> a2 + c2 -> a2 ! b -> a * - b2 -> a2 + b2 -> a2 ! checking for directory renames a2: divergent renames -> dr a: remote moved to b -> m b2: remote created -> g copying a to b +picked tool 'internal:merge' for a (binary False symlink False) merging a and b my a@f26ec4fc3fa3+ other b@8e765a822af2 ancestor a@af1939970a1c + premerge successful removing a warning: detected divergent renames of a2 to: c2
--- a/tests/test-rename-merge2.out Wed Apr 09 15:27:57 2008 -0700 +++ b/tests/test-rename-merge2.out Wed Apr 09 15:28:30 2008 -0700 @@ -1,3 +1,4 @@ +created new head -------------- test L:up a R:nc a b W: - 1 get local a to b -------------- @@ -13,8 +14,11 @@ rev: versions differ -> m a: remote copied to b -> m copying a to b +picked tool 'python ../merge' for a (binary False symlink False) merging a and b my a@e300d1c794ec+ other b@735846fee2d7 ancestor a@924404dff337 + premerge successful +picked tool 'python ../merge' for rev (binary False symlink False) merging rev my rev@e300d1c794ec+ other rev@735846fee2d7 ancestor rev@924404dff337 0 files updated, 2 files merged, 0 files removed, 0 files unresolved @@ -25,6 +29,7 @@ C a -------------- +created new head -------------- test L:nc a b R:up a W: - 2 get rem change to a and b -------------- @@ -41,8 +46,11 @@ b: local copied to a -> m rev: versions differ -> m getting a +picked tool 'python ../merge' for b (binary False symlink False) merging b and a my b@ac809aeed39a+ other a@f4db7e329e71 ancestor a@924404dff337 + premerge successful +picked tool 'python ../merge' for rev (binary False symlink False) merging rev my rev@ac809aeed39a+ other rev@f4db7e329e71 ancestor rev@924404dff337 1 files updated, 2 files merged, 0 files removed, 0 files unresolved @@ -53,6 +61,7 @@ a -------------- +created new head -------------- test L:up a R:nm a b W: - 3 get local a change to b, remove a -------------- @@ -68,9 +77,12 @@ rev: versions differ -> m a: remote moved to b -> m copying a to b +picked tool 'python ../merge' for a (binary False symlink False) merging a and b my a@e300d1c794ec+ other b@e03727d2d66b ancestor a@924404dff337 + premerge successful removing a +picked tool 'python ../merge' for rev (binary False symlink False) merging rev my rev@e300d1c794ec+ other rev@e03727d2d66b ancestor rev@924404dff337 0 files updated, 2 files merged, 0 files removed, 0 files unresolved @@ -80,6 +92,7 @@ a -------------- +created new head -------------- test L:nm a b R:up a W: - 4 get remote change to b -------------- @@ -94,8 +107,11 @@ checking for directory renames b: local moved to a -> m rev: versions differ -> m +picked tool 'python ../merge' for b (binary False symlink False) merging b and a my b@ecf3cb2a4219+ other a@f4db7e329e71 ancestor a@924404dff337 + premerge successful +picked tool 'python ../merge' for rev (binary False symlink False) merging rev my rev@ecf3cb2a4219+ other rev@f4db7e329e71 ancestor rev@924404dff337 0 files updated, 2 files merged, 0 files removed, 0 files unresolved @@ -105,6 +121,7 @@ a -------------- +created new head -------------- test L: R:nc a b W: - 5 get b -------------- @@ -120,6 +137,7 @@ rev: versions differ -> m b: remote created -> g getting b +picked tool 'python ../merge' for rev (binary False symlink False) merging rev my rev@94b33a1b7f2d+ other rev@735846fee2d7 ancestor rev@924404dff337 1 files updated, 1 files merged, 0 files removed, 0 files unresolved @@ -129,6 +147,7 @@ C a -------------- +created new head -------------- test L:nc a b R: W: - 6 nothing -------------- @@ -142,6 +161,7 @@ b -> a checking for directory renames rev: versions differ -> m +picked tool 'python ../merge' for rev (binary False symlink False) merging rev my rev@ac809aeed39a+ other rev@97c705ade336 ancestor rev@924404dff337 0 files updated, 1 files merged, 0 files removed, 0 files unresolved @@ -151,6 +171,7 @@ C b -------------- +created new head -------------- test L: R:nm a b W: - 7 get b -------------- @@ -168,6 +189,7 @@ b: remote created -> g removing a getting b +picked tool 'python ../merge' for rev (binary False symlink False) merging rev my rev@94b33a1b7f2d+ other rev@e03727d2d66b ancestor rev@924404dff337 1 files updated, 1 files merged, 1 files removed, 0 files unresolved @@ -176,6 +198,7 @@ M b -------------- +created new head -------------- test L:nm a b R: W: - 8 nothing -------------- @@ -189,6 +212,7 @@ b -> a checking for directory renames rev: versions differ -> m +picked tool 'python ../merge' for rev (binary False symlink False) merging rev my rev@ecf3cb2a4219+ other rev@97c705ade336 ancestor rev@924404dff337 0 files updated, 1 files merged, 0 files removed, 0 files unresolved @@ -197,6 +221,7 @@ C b -------------- +created new head -------------- test L:um a b R:um a b W: - 9 do merge with ancestor in a -------------- @@ -206,8 +231,10 @@ searching for copies back to rev 1 b: versions differ -> m rev: versions differ -> m +picked tool 'python ../merge' for b (binary False symlink False) merging b my b@ec03c2ca8642+ other b@79cc6877a3b7 ancestor a@924404dff337 +picked tool 'python ../merge' for rev (binary False symlink False) merging rev my rev@ec03c2ca8642+ other rev@79cc6877a3b7 ancestor rev@924404dff337 0 files updated, 2 files merged, 0 files removed, 0 files unresolved @@ -216,6 +243,7 @@ M b -------------- +created new head -------------- test L:nm a b R:nm a c W: - 11 get c, keep b -------------- @@ -228,8 +256,8 @@ unmatched files in other: c all copies found (* = to merge, ! = divergent): - c -> a - b -> a + c -> a ! + b -> a ! checking for directory renames a: divergent renames -> dr rev: versions differ -> m @@ -238,6 +266,7 @@ b c getting c +picked tool 'python ../merge' for rev (binary False symlink False) merging rev my rev@ecf3cb2a4219+ other rev@e6abcc1a30c2 ancestor rev@924404dff337 1 files updated, 1 files merged, 0 files removed, 0 files unresolved @@ -247,6 +276,7 @@ C b -------------- +created new head -------------- test L:nc a b R:up b W: - 12 merge b no ancestor -------------- @@ -256,8 +286,10 @@ searching for copies back to rev 1 b: versions differ -> m rev: versions differ -> m +picked tool 'python ../merge' for b (binary False symlink False) merging b my b@ac809aeed39a+ other b@af30c7647fc7 ancestor b@000000000000 +picked tool 'python ../merge' for rev (binary False symlink False) merging rev my rev@ac809aeed39a+ other rev@af30c7647fc7 ancestor rev@924404dff337 0 files updated, 2 files merged, 0 files removed, 0 files unresolved @@ -267,6 +299,7 @@ C a -------------- +created new head -------------- test L:up b R:nm a b W: - 13 merge b no ancestor -------------- @@ -278,8 +311,10 @@ b: versions differ -> m rev: versions differ -> m removing a +picked tool 'python ../merge' for b (binary False symlink False) merging b my b@59318016310c+ other b@e03727d2d66b ancestor b@000000000000 +picked tool 'python ../merge' for rev (binary False symlink False) merging rev my rev@59318016310c+ other rev@e03727d2d66b ancestor rev@924404dff337 0 files updated, 2 files merged, 1 files removed, 0 files unresolved @@ -288,6 +323,7 @@ M b -------------- +created new head -------------- test L:nc a b R:up a b W: - 14 merge b no ancestor -------------- @@ -299,8 +335,10 @@ b: versions differ -> m rev: versions differ -> m getting a +picked tool 'python ../merge' for b (binary False symlink False) merging b my b@ac809aeed39a+ other b@8dbce441892a ancestor b@000000000000 +picked tool 'python ../merge' for rev (binary False symlink False) merging rev my rev@ac809aeed39a+ other rev@8dbce441892a ancestor rev@924404dff337 1 files updated, 2 files merged, 0 files removed, 0 files unresolved @@ -310,6 +348,7 @@ M b -------------- +created new head -------------- test L:up b R:nm a b W: - 15 merge b no ancestor, remove a -------------- @@ -321,8 +360,10 @@ b: versions differ -> m rev: versions differ -> m removing a +picked tool 'python ../merge' for b (binary False symlink False) merging b my b@59318016310c+ other b@e03727d2d66b ancestor b@000000000000 +picked tool 'python ../merge' for rev (binary False symlink False) merging rev my rev@59318016310c+ other rev@e03727d2d66b ancestor rev@924404dff337 0 files updated, 2 files merged, 1 files removed, 0 files unresolved @@ -331,6 +372,7 @@ M b -------------- +created new head -------------- test L:nc a b R:up a b W: - 16 get a, merge b no ancestor -------------- @@ -342,8 +384,10 @@ b: versions differ -> m rev: versions differ -> m getting a +picked tool 'python ../merge' for b (binary False symlink False) merging b my b@ac809aeed39a+ other b@8dbce441892a ancestor b@000000000000 +picked tool 'python ../merge' for rev (binary False symlink False) merging rev my rev@ac809aeed39a+ other rev@8dbce441892a ancestor rev@924404dff337 1 files updated, 2 files merged, 0 files removed, 0 files unresolved @@ -353,6 +397,7 @@ M b -------------- +created new head -------------- test L:up a b R:nc a b W: - 17 keep a, merge b no ancestor -------------- @@ -362,8 +407,10 @@ searching for copies back to rev 1 b: versions differ -> m rev: versions differ -> m +picked tool 'python ../merge' for b (binary False symlink False) merging b my b@0b76e65c8289+ other b@735846fee2d7 ancestor b@000000000000 +picked tool 'python ../merge' for rev (binary False symlink False) merging rev my rev@0b76e65c8289+ other rev@735846fee2d7 ancestor rev@924404dff337 0 files updated, 2 files merged, 0 files removed, 0 files unresolved @@ -373,6 +420,7 @@ C a -------------- +created new head -------------- test L:nm a b R:up a b W: - 18 merge b no ancestor -------------- @@ -384,8 +432,10 @@ rev: versions differ -> m a: prompt recreating -> g getting a +picked tool 'python ../merge' for b (binary False symlink False) merging b my b@ecf3cb2a4219+ other b@8dbce441892a ancestor b@000000000000 +picked tool 'python ../merge' for rev (binary False symlink False) merging rev my rev@ecf3cb2a4219+ other rev@8dbce441892a ancestor rev@924404dff337 1 files updated, 2 files merged, 0 files removed, 0 files unresolved @@ -395,6 +445,7 @@ M b -------------- +created new head -------------- test L:up a b R:nm a b W: - 19 merge b no ancestor, prompt remove a -------------- @@ -404,8 +455,10 @@ searching for copies back to rev 1 b: versions differ -> m rev: versions differ -> m +picked tool 'python ../merge' for b (binary False symlink False) merging b my b@0b76e65c8289+ other b@e03727d2d66b ancestor b@000000000000 +picked tool 'python ../merge' for rev (binary False symlink False) merging rev my rev@0b76e65c8289+ other rev@e03727d2d66b ancestor rev@924404dff337 0 files updated, 2 files merged, 0 files removed, 0 files unresolved @@ -415,6 +468,7 @@ C a -------------- +created new head -------------- test L:up a R:um a b W: - 20 merge a and b to b, remove a -------------- @@ -430,9 +484,11 @@ rev: versions differ -> m a: remote moved to b -> m copying a to b +picked tool 'python ../merge' for a (binary False symlink False) merging a and b my a@e300d1c794ec+ other b@79cc6877a3b7 ancestor a@924404dff337 removing a +picked tool 'python ../merge' for rev (binary False symlink False) merging rev my rev@e300d1c794ec+ other rev@79cc6877a3b7 ancestor rev@924404dff337 0 files updated, 2 files merged, 0 files removed, 0 files unresolved @@ -442,6 +498,7 @@ a -------------- +created new head -------------- test L:um a b R:up a W: - 21 merge a and b to b -------------- @@ -456,8 +513,10 @@ checking for directory renames b: local moved to a -> m rev: versions differ -> m +picked tool 'python ../merge' for b (binary False symlink False) merging b and a my b@ec03c2ca8642+ other a@f4db7e329e71 ancestor a@924404dff337 +picked tool 'python ../merge' for rev (binary False symlink False) merging rev my rev@ec03c2ca8642+ other rev@f4db7e329e71 ancestor rev@924404dff337 0 files updated, 2 files merged, 0 files removed, 0 files unresolved @@ -467,6 +526,7 @@ a -------------- +created new head -------------- test L:nm a b R:up a c W: - 23 get c, keep b -------------- @@ -484,9 +544,12 @@ b: local moved to a -> m rev: versions differ -> m c: remote created -> g +picked tool 'python ../merge' for b (binary False symlink False) merging b and a my b@ecf3cb2a4219+ other a@2b958612230f ancestor a@924404dff337 + premerge successful getting c +picked tool 'python ../merge' for rev (binary False symlink False) merging rev my rev@ecf3cb2a4219+ other rev@2b958612230f ancestor rev@924404dff337 1 files updated, 2 files merged, 0 files removed, 0 files unresolved
--- a/tests/test-revert Wed Apr 09 15:27:57 2008 -0700 +++ b/tests/test-revert Wed Apr 09 15:28:30 2008 -0700 @@ -1,6 +1,7 @@ #!/bin/sh -hg init +hg init repo +cd repo echo 123 > a echo 123 > c echo 123 > e @@ -27,7 +28,16 @@ hg revert --no-backup c echo %% should show unknown: b hg status -echo %% should show a b c e +hg add b +echo %% should show b added +hg status b +rm b +echo %% should show b deleted +hg status b +hg revert -v b +echo %% should not find b +hg status b +echo %% should show a c e ls echo %% should verbosely save backup to e.orig echo z > e @@ -40,19 +50,26 @@ rm q echo %% should say file not found hg revert notfound +touch d +hg add d hg rm a hg commit -m "second" -d "1000000 0" echo z > z hg add z hg st -echo %% should add a, forget z +echo %% should add a, remove d, forget z hg revert --all -r0 -echo %% should forget a +echo %% should forget a, undelete d hg revert --all -rtip rm a *.orig echo %% should silently add a hg revert -r0 a hg st a +hg rm d +hg st d +echo %% should silently keep d removed +hg revert -r0 d +hg st d hg update -C chmod +x c @@ -68,6 +85,8 @@ echo %% should print executable test -x c && echo executable +cd .. + echo %% issue 241 hg init a cd a @@ -100,3 +119,33 @@ hg revert newa hg st a newa +cd .. + +hg init ignored +cd ignored +echo '^ignored$' > .hgignore +echo '^ignoreddir$' >> .hgignore +echo '^removed$' >> .hgignore + +mkdir ignoreddir +touch ignoreddir/file +touch ignoreddir/removed +touch ignored +touch removed +echo '%% 4 ignored files (we will add/commit everything)' +hg st -A -X .hgignore +hg ci -qAm 'add files' ignored ignoreddir/file ignoreddir/removed removed + +echo >> ignored +echo >> ignoreddir/file +hg rm removed ignoreddir/removed +echo '%% should revert ignored* and undelete *removed' +hg revert -a --no-backup +hg st -mardi + +hg up -qC +echo >> ignored +hg rm removed +echo %% should silently revert the named files +hg revert --no-backup ignored removed +hg st -mardi
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tests/test-revert-flags Wed Apr 09 15:28:30 2008 -0700 @@ -0,0 +1,17 @@ +#!/bin/sh + +"$TESTDIR/hghave" execbit || exit 80 + +hg init repo +cd repo +echo foo > foo +chmod 644 foo +hg ci -qAm '644' + +chmod 755 foo +hg ci -qAm '755' + +echo '% reverting to rev 0' +hg revert -a -r 0 +hg st +hg diff --git
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tests/test-revert-flags.out Wed Apr 09 15:28:30 2008 -0700 @@ -0,0 +1,6 @@ +% reverting to rev 0 +reverting foo +M foo +diff --git a/foo b/foo +old mode 100755 +new mode 100644
--- a/tests/test-revert.out Wed Apr 09 15:27:57 2008 -0700 +++ b/tests/test-revert.out Wed Apr 09 15:28:30 2008 -0700 @@ -18,16 +18,20 @@ ? b %% should show unknown: b ? b -%% should show a b c e +%% should show b added +A b +%% should show b deleted +! b +forgetting b +%% should not find b +b: No such file or directory +%% should show a c e a -b c e %% should verbosely save backup to e.orig saving current version of e as e.orig reverting e -resolving manifests -getting e %% should say no changes needed no changes needed to a %% should say file not managed @@ -35,16 +39,20 @@ %% should say file not found notfound: No such file in rev 095eacd0c0d7 A z -? b ? e.orig -%% should add a, forget z +%% should add a, remove d, forget z adding a +removing d forgetting z -%% should forget a +%% should forget a, undelete d forgetting a +undeleting d %% should silently add a A a -0 files updated, 0 files merged, 1 files removed, 0 files unresolved +R d +%% should silently keep d removed +R d +1 files updated, 0 files merged, 1 files removed, 0 files unresolved reverting c %% should print non-executable non-executable @@ -60,8 +68,20 @@ reverting a %% issue332 adding b/b +created new head reverting b/b forgetting newdir/newfile reverting b/b % reverting a rename target should revert the source ? newa +%% 4 ignored files (we will add/commit everything) +I ignored +I ignoreddir/file +I ignoreddir/removed +I removed +%% should revert ignored* and undelete *removed +reverting ignored +reverting ignoreddir/file +undeleting ignoreddir/removed +undeleting removed +%% should silently revert the named files
--- a/tests/test-revlog-packentry.out Wed Apr 09 15:27:57 2008 -0700 +++ b/tests/test-revlog-packentry.out Wed Apr 09 15:28:30 2008 -0700 @@ -1,6 +1,7 @@ adding foo 0 files updated, 0 files merged, 1 files removed, 0 files unresolved adding foo +created new head rev offset length base linkrev nodeid p1 p2 0 0 0 0 0 b80de5d13875 000000000000 000000000000 1 0 24 0 1 0376abec49b8 000000000000 000000000000
--- a/tests/test-ro-message Wed Apr 09 15:27:57 2008 -0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,17 +0,0 @@ -#!/bin/sh -HG=hg -"$HG" init -mkdir b -echo 'Bouncy' >b/bouncy -echo 'tricycle' >b/vehicle -"$HG" add b/bouncy -"$HG" add b/vehicle -"$HG" commit -m 'Adding bouncy' -echo 'bouncy' >>b/bouncy -"$HG" commit -m 'Making it bouncier' -"$HG" update -C 0 -echo 'stationary' >>b/vehicle -"$HG" commit -m 'Clarifying the vehicle.' -"$HG" update -C 1 -chmod a-w b/vehicle -"$HG" merge 2 2>&1 | sed 's|^\(.*[ ]\).*/\([^/]*/[^/]*/[^/]*\)$|\1\2|g'
--- a/tests/test-ro-message.out Wed Apr 09 15:27:57 2008 -0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,3 +0,0 @@ -1 files updated, 0 files merged, 0 files removed, 0 files unresolved -2 files updated, 0 files merged, 0 files removed, 0 files unresolved -abort: Permission denied: test-ro-message/b/vehicle
--- a/tests/test-rollback Wed Apr 09 15:27:57 2008 -0700 +++ b/tests/test-rollback Wed Apr 09 15:28:30 2008 -0700 @@ -13,3 +13,16 @@ hg verify hg parents hg status + +echo % Test issue 902 +hg commit -m "test" +hg branch test +hg rollback +hg branch + +echo % Test rollback of hg before issue 902 was fixed +hg commit -m "test" +hg branch test +rm .hg/undo.branch +hg rollback +hg branch
--- a/tests/test-rollback.out Wed Apr 09 15:27:57 2008 -0700 +++ b/tests/test-rollback.out Wed Apr 09 15:28:30 2008 -0700 @@ -16,3 +16,12 @@ checking files 0 files, 0 changesets, 0 total revisions A a +% Test issue 902 +marked working directory as branch test +rolling back last transaction +default +% Test rollback of hg before issue 902 was fixed +marked working directory as branch test +rolling back last transaction +Named branch could not be reset, current branch still is: test +test
--- a/tests/test-serve Wed Apr 09 15:27:57 2008 -0700 +++ b/tests/test-serve Wed Apr 09 15:28:30 2008 -0700 @@ -1,5 +1,17 @@ #!/bin/sh +hgserve() +{ + hg serve -a localhost -p $HGPORT1 -d --pid-file=hg.pid -E errors.log -v $@ \ + | sed -e 's/:[0-9][0-9]*//g' -e 's/http:\/\/[^/]*\//http:\/\/localhost\//' + cat hg.pid >> "$DAEMON_PIDS" + echo % errors + cat errors.log + sleep 1 + kill `cat hg.pid` + sleep 1 +} + hg init test cd test @@ -7,12 +19,25 @@ echo 'accesslog = access.log' >> .hg/hgrc echo % Without -v -hg serve -a localhost -p $HGPORT -d --pid-file=hg.pid +hg serve -a localhost -p $HGPORT -d --pid-file=hg.pid -E errors.log cat hg.pid >> "$DAEMON_PIDS" if [ -f access.log ]; then echo 'access log created - .hg/hgrc respected' fi +echo % errors +cat errors.log echo % With -v -hg serve -a localhost -p $HGPORT1 -d --pid-file=hg.pid -v | sed -e 's,:[0-9][0-9]*/,/,' -cat hg.pid >> "$DAEMON_PIDS" +hgserve + +echo % With --prefix foo +hgserve --prefix foo + +echo % With --prefix /foo +hgserve --prefix /foo + +echo % With --prefix foo/ +hgserve --prefix foo/ + +echo % With --prefix /foo/ +hgserve --prefix /foo/
--- a/tests/test-serve.out Wed Apr 09 15:27:57 2008 -0700 +++ b/tests/test-serve.out Wed Apr 09 15:28:30 2008 -0700 @@ -1,4 +1,18 @@ % Without -v access log created - .hg/hgrc respected +% errors % With -v -listening at http://localhost/ +listening at http://localhost/ (bound to 127.0.0.1) +% errors +% With --prefix foo +listening at http://localhost/foo/ (bound to 127.0.0.1) +% errors +% With --prefix /foo +listening at http://localhost/foo/ (bound to 127.0.0.1) +% errors +% With --prefix foo/ +listening at http://localhost/foo/ (bound to 127.0.0.1) +% errors +% With --prefix /foo/ +listening at http://localhost/foo/ (bound to 127.0.0.1) +% errors
--- a/tests/test-simple-update.out Wed Apr 09 15:27:57 2008 -0700 +++ b/tests/test-simple-update.out Wed Apr 09 15:28:30 2008 -0700 @@ -4,6 +4,7 @@ crosschecking files in changesets and manifests checking files 1 files, 1 changesets, 1 total revisions +updating working directory 1 files updated, 0 files merged, 0 files removed, 0 files unresolved 0 files updated, 0 files merged, 0 files removed, 0 files unresolved pulling from ../branch
--- a/tests/test-simplemerge.py Wed Apr 09 15:27:57 2008 -0700 +++ b/tests/test-simplemerge.py Wed Apr 09 15:28:30 2008 -0700 @@ -14,19 +14,26 @@ # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA -import os import unittest from unittest import TestCase -import imp -import shutil -from mercurial import util +from mercurial import util, simplemerge + +# bzr compatible interface, for the tests +class Merge3(simplemerge.Merge3Text): + """3-way merge of texts. -# copy simplemerge to the cwd to avoid creating a .pyc file in the source tree -shutil.copyfile(os.path.join(os.environ['TESTDIR'], os.path.pardir, - 'contrib', 'simplemerge'), - 'simplemerge.py') -simplemerge = imp.load_source('simplemerge', 'simplemerge.py') -Merge3 = simplemerge.Merge3 + Given BASE, OTHER, THIS, tries to produce a combined text + incorporating the changes from both BASE->OTHER and BASE->THIS. + All three will typically be sequences of lines.""" + def __init__(self, base, a, b): + basetext = '\n'.join([i.strip('\n') for i in base] + ['']) + atext = '\n'.join([i.strip('\n') for i in a] + ['']) + btext = '\n'.join([i.strip('\n') for i in b] + ['']) + if util.binary(basetext) or util.binary(atext) or util.binary(btext): + raise util.Abort("don't know how to merge binary files") + simplemerge.Merge3Text.__init__(self, basetext, atext, btext, + base, a, b) + CantReprocessAndShowBase = simplemerge.CantReprocessAndShowBase def split_lines(t):
--- a/tests/test-ssh Wed Apr 09 15:27:57 2008 -0700 +++ b/tests/test-ssh Wed Apr 09 15:28:30 2008 -0700 @@ -27,6 +27,11 @@ sys.exit(bool(r)) EOF +cat <<EOF > badhook +import sys +sys.stdout.write("KABOOM\n") +EOF + echo "# creating 'remote'" hg init remote cd remote @@ -91,13 +96,16 @@ echo z > z hg ci -A -m z -d '1000001 0' z +# a bad, evil hook that prints to stdout +echo 'changegroup.stdout = python ../badhook' >> .hg/hgrc cd ../local echo r > r hg ci -A -m z -d '1000002 0' r -echo "# push should succeed" +echo "# push should succeed even though it has an unexpected response" hg push +hg -R ../remote heads cd .. cat dummylog
--- a/tests/test-ssh-clone-r.out Wed Apr 09 15:27:57 2008 -0700 +++ b/tests/test-ssh-clone-r.out Wed Apr 09 15:28:30 2008 -0700 @@ -1,5 +1,6 @@ # creating 'remote' 1 files updated, 0 files merged, 0 files removed, 0 files unresolved +created new head 1 files updated, 0 files merged, 2 files removed, 0 files unresolved rev offset length base linkrev nodeid p1 p2 0 0 3 0 0 362fef284ce2 000000000000 000000000000 @@ -31,6 +32,7 @@ adding manifests adding file changes added 1 changesets with 1 changes to 1 files +updating working directory 1 files updated, 0 files merged, 0 files removed, 0 files unresolved checking changesets checking manifests @@ -42,6 +44,7 @@ adding manifests adding file changes added 2 changesets with 2 changes to 1 files +updating working directory 1 files updated, 0 files merged, 0 files removed, 0 files unresolved checking changesets checking manifests @@ -53,6 +56,7 @@ adding manifests adding file changes added 3 changesets with 3 changes to 1 files +updating working directory 1 files updated, 0 files merged, 0 files removed, 0 files unresolved checking changesets checking manifests @@ -64,6 +68,7 @@ adding manifests adding file changes added 4 changesets with 4 changes to 1 files +updating working directory 1 files updated, 0 files merged, 0 files removed, 0 files unresolved checking changesets checking manifests @@ -75,6 +80,7 @@ adding manifests adding file changes added 2 changesets with 2 changes to 1 files +updating working directory 1 files updated, 0 files merged, 0 files removed, 0 files unresolved checking changesets checking manifests @@ -86,6 +92,7 @@ adding manifests adding file changes added 3 changesets with 3 changes to 1 files +updating working directory 1 files updated, 0 files merged, 0 files removed, 0 files unresolved checking changesets checking manifests @@ -97,6 +104,7 @@ adding manifests adding file changes added 4 changesets with 5 changes to 2 files +updating working directory 2 files updated, 0 files merged, 0 files removed, 0 files unresolved checking changesets checking manifests @@ -108,6 +116,7 @@ adding manifests adding file changes added 5 changesets with 6 changes to 3 files +updating working directory 2 files updated, 0 files merged, 0 files removed, 0 files unresolved checking changesets checking manifests @@ -119,6 +128,7 @@ adding manifests adding file changes added 5 changesets with 5 changes to 2 files +updating working directory 1 files updated, 0 files merged, 0 files removed, 0 files unresolved checking changesets checking manifests
--- a/tests/test-ssh.out Wed Apr 09 15:27:57 2008 -0700 +++ b/tests/test-ssh.out Wed Apr 09 15:28:30 2008 -0700 @@ -6,6 +6,7 @@ streaming all changes XXX files to transfer, XXX bytes of data transferred XXX bytes in XXX seconds (XXX XB/sec) +updating working directory XXX files updated, XXX files merged, XXX files removed, XXX files unresolved checking changesets checking manifests @@ -18,6 +19,7 @@ adding manifests adding file changes added 1 changesets with 2 changes to 2 files +updating working directory 2 files updated, 0 files merged, 0 files removed, 0 files unresolved # verify checking changesets @@ -70,7 +72,8 @@ checking files 2 files, 2 changesets, 3 total revisions bleah -# push should succeed +created new head +# push should succeed even though it has an unexpected response pushing to ssh://user@dummy/remote searching for changes note: unsynced remote changes! @@ -78,6 +81,20 @@ remote: adding manifests remote: adding file changes remote: added 1 changesets with 1 changes to 1 files +remote: KABOOM +changeset: 3:ac7448082955 +tag: tip +parent: 1:572896fe480d +user: test +date: Mon Jan 12 13:46:42 1970 +0000 +summary: z + +changeset: 2:187c6caa0d1e +parent: 0:e34318c26897 +user: test +date: Mon Jan 12 13:46:41 1970 +0000 +summary: z + Got arguments 1:user@dummy 2:hg -R nonexistent serve --stdio Got arguments 1:user@dummy 2:hg -R remote serve --stdio Got arguments 1:user@dummy 2:hg -R remote serve --stdio
--- a/tests/test-static-http Wed Apr 09 15:27:57 2008 -0700 +++ b/tests/test-static-http Wed Apr 09 15:28:30 2008 -0700 @@ -63,4 +63,19 @@ cat a hg paths | sed -e 's,:[0-9][0-9]*/,/,' +echo '% test with empty repo (issue965)' +cd .. +hg init remotempty + +http_proxy= hg clone static-http://localhost:$HGPORT/remotempty local3 | sed -e 's,:[0-9][0-9]*/,/,' + +cd local3 +hg verify +hg paths | sed -e 's,:[0-9][0-9]*/,/,' + +echo '% test with non-repo' +cd .. +mkdir notarepo +http_proxy= hg clone static-http://localhost:$HGPORT/notarepo local3 2>&1 | sed -e 's,:[0-9][0-9]*/,/,' + kill $!
--- a/tests/test-static-http.out Wed Apr 09 15:27:57 2008 -0700 +++ b/tests/test-static-http.out Wed Apr 09 15:28:30 2008 -0700 @@ -12,6 +12,7 @@ adding manifests adding file changes added 1 changesets with 1 changes to 1 files +updating working directory 1 files updated, 0 files merged, 0 files removed, 0 files unresolved checking changesets checking manifests @@ -34,6 +35,7 @@ adding manifests adding file changes added 1 changesets with 1 changes to 1 files +updating working directory 1 files updated, 0 files merged, 0 files removed, 0 files unresolved checking changesets checking manifests @@ -42,3 +44,15 @@ 1 files, 1 changesets, 1 total revisions a default = static-http://localhost/ +% test with empty repo (issue965) +no changes found +updating working directory +0 files updated, 0 files merged, 0 files removed, 0 files unresolved +checking changesets +checking manifests +crosschecking files in changesets and manifests +checking files +0 files, 0 changesets, 0 total revisions +default = static-http://localhost/remotempty +% test with non-repo +abort: 'http://localhost/notarepo' does not appear to be an hg repository!
--- a/tests/test-status Wed Apr 09 15:27:57 2008 -0700 +++ b/tests/test-status Wed Apr 09 15:28:30 2008 -0700 @@ -21,9 +21,8 @@ hg init repo2 cd repo2 touch modified removed deleted ignored -echo "ignored" > .hgignore +echo "^ignored$" > .hgignore hg ci -A -m 'initial checkin' -d "1000000 0" -sleep 1 # make sure mtime is changed touch modified added unknown ignored hg add added hg remove removed @@ -37,3 +36,59 @@ hg status -C echo "hg status -A:" hg status -A +echo "^ignoreddir$" > .hgignore +mkdir ignoreddir +touch ignoreddir/file +echo "hg status ignoreddir/file:" +hg status ignoreddir/file +echo "hg status -i ignoreddir/file:" +hg status -i ignoreddir/file +cd .. + +# check 'status -q' and some combinations +hg init repo3 +cd repo3 +touch modified removed deleted ignored +echo "^ignored$" > .hgignore +hg commit -A -m 'initial checkin' +touch added unknown ignored +hg add added +echo "test" >> modified +hg remove removed +rm deleted +hg copy modified copied + +# Run status with 2 different flags. +# Check if result is the same or different. +# If result is not as expected, raise error +assert() { + hg status $1 > ../a + hg status $2 > ../b + out=`diff ../a ../b` + if [ $? -ne 0 ]; then + out=1 + else + out=0 + fi + if [ $3 -eq 0 ]; then + df="same" + else + df="different" + fi + if [ $out -ne $3 ]; then + echo "Error on $1 and $2, should be $df." + fi +} + +# assert flag1 flag2 [0-same | 1-different] +assert "-q" "-mard" 0 +assert "-A" "-marduicC" 0 +assert "-qA" "-mardcC" 0 +assert "-qAui" "-A" 0 +assert "-qAu" "-marducC" 0 +assert "-qAi" "-mardicC" 0 +assert "-qu" "-u" 0 +assert "-q" "-u" 1 +assert "-m" "-a" 1 +assert "-r" "-d" 1 +
--- a/tests/test-status.out Wed Apr 09 15:27:57 2008 -0700 +++ b/tests/test-status.out Wed Apr 09 15:28:30 2008 -0700 @@ -99,7 +99,6 @@ A added R removed ! deleted -? ignored ? unknown hg status -C: A added @@ -118,3 +117,10 @@ I ignored C .hgignore C modified +hg status ignoreddir/file: +hg status -i ignoreddir/file: +I ignoreddir/file +adding .hgignore +adding deleted +adding modified +adding removed
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tests/test-strip-cross Wed Apr 09 15:28:30 2008 -0700 @@ -0,0 +1,68 @@ +#!/bin/sh + +# test stripping of filelogs where the linkrev doesn't always increase + +echo '[extensions]' >> $HGRCPATH +echo 'hgext.mq =' >> $HGRCPATH + +hg init orig +cd orig + +hidefilename() +{ + sed -e 's/saving bundle to .*strip-backup/saving bundle to strip-backup/' +} + +commit() +{ + hg up -qC null + count=1 + for i in "$@"; do + for f in $i; do + echo $count > $f + done + count=`expr $count + 1` + done + hg commit -qAm "$*" +} + +# 2 1 0 2 0 1 2 +commit '201 210' + +commit '102 120' '210' + +commit '021' + +commit '201' '021 120' + +commit '012 021' '102 201' '120 210' + +commit 'manifest-file' + +commit '102 120' '012 210' '021 201' + +commit '201 210' '021 120' '012 102' + +HGUSER=another-user; export HGUSER +commit 'manifest-file' + +commit '012' 'manifest-file' + +cd .. +hg clone -q -U -r -1 -r -2 -r -3 -r -4 -r -6 orig crossed + +for i in crossed/.hg/store/00manifest.i crossed/.hg/store/data/*.i; do + echo $i + hg debugindex $i + echo +done + +for i in 0 1 2 3 4; do + hg clone -q -U --pull crossed $i + echo "% Trying to strip revision $i" + hg --cwd $i strip $i 2>&1 | hidefilename + echo "% Verifying" + hg --cwd $i verify + echo +done +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tests/test-strip-cross.out Wed Apr 09 15:28:30 2008 -0700 @@ -0,0 +1,118 @@ +crossed/.hg/store/00manifest.i + rev offset length base linkrev nodeid p1 p2 + 0 0 112 0 0 6f105cbb914d 000000000000 000000000000 + 1 112 56 1 3 1b55917b3699 000000000000 000000000000 + 2 168 123 1 1 8f3d04e263e5 000000000000 000000000000 + 3 291 122 1 2 f0ef8726ac4f 000000000000 000000000000 + 4 413 87 4 4 0b76e38b4070 000000000000 000000000000 + +crossed/.hg/store/data/012.i + rev offset length base linkrev nodeid p1 p2 + 0 0 3 0 0 b8e02f643373 000000000000 000000000000 + 1 3 3 1 1 5d9299349fc0 000000000000 000000000000 + 2 6 3 2 2 2661d26c6496 000000000000 000000000000 + +crossed/.hg/store/data/021.i + rev offset length base linkrev nodeid p1 p2 + 0 0 3 0 0 b8e02f643373 000000000000 000000000000 + 1 3 3 1 2 5d9299349fc0 000000000000 000000000000 + 2 6 3 2 1 2661d26c6496 000000000000 000000000000 + +crossed/.hg/store/data/102.i + rev offset length base linkrev nodeid p1 p2 + 0 0 3 0 1 b8e02f643373 000000000000 000000000000 + 1 3 3 1 0 5d9299349fc0 000000000000 000000000000 + 2 6 3 2 2 2661d26c6496 000000000000 000000000000 + +crossed/.hg/store/data/120.i + rev offset length base linkrev nodeid p1 p2 + 0 0 3 0 1 b8e02f643373 000000000000 000000000000 + 1 3 3 1 2 5d9299349fc0 000000000000 000000000000 + 2 6 3 2 0 2661d26c6496 000000000000 000000000000 + +crossed/.hg/store/data/201.i + rev offset length base linkrev nodeid p1 p2 + 0 0 3 0 2 b8e02f643373 000000000000 000000000000 + 1 3 3 1 0 5d9299349fc0 000000000000 000000000000 + 2 6 3 2 1 2661d26c6496 000000000000 000000000000 + +crossed/.hg/store/data/210.i + rev offset length base linkrev nodeid p1 p2 + 0 0 3 0 2 b8e02f643373 000000000000 000000000000 + 1 3 3 1 1 5d9299349fc0 000000000000 000000000000 + 2 6 3 2 0 2661d26c6496 000000000000 000000000000 + +crossed/.hg/store/data/manifest-file.i + rev offset length base linkrev nodeid p1 p2 + 0 0 3 0 3 b8e02f643373 000000000000 000000000000 + 1 3 3 1 4 5d9299349fc0 000000000000 000000000000 + +% Trying to strip revision 0 +saving bundle to strip-backup/cbb8c2f0a2e3-backup +saving bundle to strip-backup/cbb8c2f0a2e3-temp +adding branch +adding changesets +adding manifests +adding file changes +added 4 changesets with 15 changes to 7 files (+3 heads) +% Verifying +checking changesets +checking manifests +crosschecking files in changesets and manifests +checking files +7 files, 4 changesets, 15 total revisions + +% Trying to strip revision 1 +saving bundle to strip-backup/124ecc0cbec9-backup +saving bundle to strip-backup/124ecc0cbec9-temp +adding branch +adding changesets +adding manifests +adding file changes +added 3 changesets with 12 changes to 7 files (+3 heads) +% Verifying +checking changesets +checking manifests +crosschecking files in changesets and manifests +checking files +7 files, 4 changesets, 14 total revisions + +% Trying to strip revision 2 +saving bundle to strip-backup/f6439b304a1a-backup +saving bundle to strip-backup/f6439b304a1a-temp +adding branch +adding changesets +adding manifests +adding file changes +added 2 changesets with 8 changes to 6 files (+2 heads) +% Verifying +checking changesets +checking manifests +crosschecking files in changesets and manifests +checking files +7 files, 4 changesets, 14 total revisions + +% Trying to strip revision 3 +saving bundle to strip-backup/6e54ec5db740-backup +saving bundle to strip-backup/6e54ec5db740-temp +adding branch +adding changesets +adding manifests +adding file changes +added 1 changesets with 1 changes to 2 files (+1 heads) +% Verifying +checking changesets +checking manifests +crosschecking files in changesets and manifests +checking files +7 files, 4 changesets, 19 total revisions + +% Trying to strip revision 4 +saving bundle to strip-backup/9147ea23c156-backup +% Verifying +checking changesets +checking manifests +crosschecking files in changesets and manifests +checking files +7 files, 4 changesets, 19 total revisions +
--- a/tests/test-symlink-basic Wed Apr 09 15:27:57 2008 -0700 +++ b/tests/test-symlink-basic Wed Apr 09 15:28:30 2008 -0700 @@ -2,52 +2,49 @@ "$TESTDIR/hghave" symlink || exit 80 -cleanpath() -{ - sed -e "s:/.*\(/test-symlink-basic/.*\):...\1:" -} - -cat >> readlink.py <<EOF -import os -import sys - -for f in sys.argv[1:]: - print f, '->', os.readlink(f) -EOF - hg init a cd a ln -s nothing dangling -hg commit -m 'commit symlink without adding' -d '0 0' dangling 2>&1 | cleanpath +hg commit -m 'commit symlink without adding' dangling hg add dangling -hg commit -m 'add symlink' -d '0 0' +hg commit -m 'add symlink' hg tip -v hg manifest --debug echo '% rev 0:' -python ../readlink.py dangling +$TESTDIR/readlink.py dangling rm dangling ln -s void dangling hg commit -m 'change symlink' echo '% rev 1:' -python ../readlink.py dangling +$TESTDIR/readlink.py dangling echo '% modifying link' rm dangling ln -s empty dangling -python ../readlink.py dangling +$TESTDIR/readlink.py dangling echo '% reverting to rev 0:' hg revert -r 0 -a -python ../readlink.py dangling +$TESTDIR/readlink.py dangling echo '% backups:' -python ../readlink.py *.orig +$TESTDIR/readlink.py *.orig rm *.orig hg up -C echo '% copies' hg cp -v dangling dangling2 hg st -Cmard -python ../readlink.py dangling dangling2 +$TESTDIR/readlink.py dangling dangling2 + +echo '% issue995' +hg up -C +mkdir dir +ln -s dir dirlink +hg ci -qAm 'add dirlink' +mkdir newdir +mv dir newdir/dir +mv dirlink newdir/dirlink +hg mv -A dirlink newdir/dirlink
--- a/tests/test-symlink-basic.out Wed Apr 09 15:27:57 2008 -0700 +++ b/tests/test-symlink-basic.out Wed Apr 09 15:28:30 2008 -0700 @@ -1,4 +1,4 @@ -abort: file .../test-symlink-basic/a/dangling not tracked! +abort: file dangling not tracked! changeset: 0:cabd88b706fc tag: tip user: test @@ -27,3 +27,5 @@ dangling dangling -> void dangling2 -> void +% issue995 +0 files updated, 0 files merged, 1 files removed, 0 files unresolved
--- a/tests/test-symlinks.out Wed Apr 09 15:27:57 2008 -0700 +++ b/tests/test-symlinks.out Wed Apr 09 15:28:30 2008 -0700 @@ -19,6 +19,7 @@ ? a/b/c/demo adding a/b/c/demo 2. clone it +updating working directory 1 files updated, 0 files merged, 0 files removed, 0 files unresolved # git symlink diff diff --git a/a/b/c/demo b/a/b/c/demo
--- a/tests/test-tag Wed Apr 09 15:27:57 2008 -0700 +++ b/tests/test-tag Wed Apr 09 15:28:30 2008 -0700 @@ -10,11 +10,21 @@ echo foo >> .hgtags hg tag -d "1000000 0" "bleah2" || echo "failed" -hg tag -d "1000000 0" -r 0 "bleah2" 1 || echo "failed" hg revert .hgtags +hg tag -d "1000000 0" -r 0 x y z y y z || echo "failed" +hg tag -d "1000000 0" tap nada dot tip null . || echo "failed" +hg tag -d "1000000 0" "bleah" || echo "failed" +hg tag -d "1000000 0" "blecch" "bleah" || echo "failed" + +hg tag -d "1000000 0" --remove "blecch" || echo "failed" +hg tag -d "1000000 0" --remove "bleah" "blecch" "blough" || echo "failed" + hg tag -d "1000000 0" -r 0 "bleah0" -hg tag -l -d "1000000 0" "bleah1" 1 +hg tag -l -d "1000000 0" -r 1 "bleah1" +hg tag -d "1000000 0" gack gawk gorp +hg tag -d "1000000 0" -f gack +hg tag -d "1000000 0" --remove gack gorp cat .hgtags cat .hg/localtags
--- a/tests/test-tag.out Wed Apr 09 15:27:57 2008 -0700 +++ b/tests/test-tag.out Wed Apr 09 15:28:30 2008 -0700 @@ -18,12 +18,26 @@ abort: working copy of .hgtags is changed (please commit .hgtags manually) failed -use of 'hg tag NAME [REV]' is deprecated, please use 'hg tag [-r REV] NAME' instead -abort: use only one form to specify the revision +abort: tag names must be unique +failed +abort: the name 'tip' is reserved +failed +abort: tag 'bleah' already exists (use -f to force) failed -use of 'hg tag NAME [REV]' is deprecated, please use 'hg tag [-r REV] NAME' instead +abort: tag 'bleah' already exists (use -f to force) +failed +abort: tag 'blecch' does not exist +failed +abort: tag 'blecch' does not exist +failed 0acdaf8983679e0aac16e811534eb49d7ee1f2b4 bleah 0acdaf8983679e0aac16e811534eb49d7ee1f2b4 bleah0 +868cc8fbb43b754ad09fa109885d243fc49adae7 gack +868cc8fbb43b754ad09fa109885d243fc49adae7 gawk +868cc8fbb43b754ad09fa109885d243fc49adae7 gorp +3807bcf62c5614cb6c16436b514d7764ca5f1631 gack +0000000000000000000000000000000000000000 gack +0000000000000000000000000000000000000000 gorp 3ecf002a1c572a2f3bb4e665417e60fca65bbd42 bleah1 0 files updated, 0 files merged, 1 files removed, 0 files unresolved 0acdaf8983679e0aac16e811534eb49d7ee1f2b4 foobar
--- a/tests/test-tags.out Wed Apr 09 15:27:57 2008 -0700 +++ b/tests/test-tags.out Wed Apr 09 15:28:30 2008 -0700 @@ -13,6 +13,7 @@ 0acdaf898367+ first 0acdaf898367+ first M a +created new head 8216907a933d tip 1 files updated, 0 files merged, 0 files removed, 0 files unresolved (branch merge, don't forget to commit) @@ -21,6 +22,7 @@ tip 6:e2174d339386 first 0:0acdaf898367 1 files updated, 0 files merged, 0 files removed, 0 files unresolved +created new head .hgtags@c071f74ab5eb, line 2: cannot parse entry .hgtags@c071f74ab5eb, line 4: node 'foo' is not well formed .hgtags@4ca6f1b1a68c, line 2: node 'x' is not well formed @@ -40,6 +42,7 @@ 1 files updated, 0 files merged, 0 files removed, 0 files unresolved 1 files updated, 0 files merged, 0 files removed, 0 files unresolved +created new head tip 4:36195b728445 bar 1:b204a97e6e8d changeset: 5:57e1983b4a60 @@ -50,7 +53,7 @@ tip 5:57e1983b4a60 % remove nonexistent tag -abort: tag foobar does not exist +abort: tag 'foobar' does not exist changeset: 5:57e1983b4a60 tag: tip user: test @@ -60,9 +63,10 @@ tip 5:d8bb4d1eff25 bar 0:b409d9da318e 1 files updated, 0 files merged, 0 files removed, 0 files unresolved +created new head tip 6:b5ff9d142648 bar 0:b409d9da318e -abort: a tag named bar already exists (use -f to force) +abort: tag 'bar' already exists (use -f to force) tip 6:b5ff9d142648 bar 0:b409d9da318e adding foo @@ -72,8 +76,8 @@ tip 4:40af5d225513 bar 2:72b852876a42 adding foo -abort: localtag tag is local -abort: globaltag tag is global +abort: tag 'localtag' is not a global tag +abort: tag 'globaltag' is not a local tag tip 1:a0b6fe111088 localtag 0:bbd179dfa0a7 local globaltag 0:bbd179dfa0a7
--- a/tests/test-transplant.out Wed Apr 09 15:27:57 2008 -0700 +++ b/tests/test-transplant.out Wed Apr 09 15:28:30 2008 -0700 @@ -2,6 +2,7 @@ adding r2 0 files updated, 0 files merged, 1 files removed, 0 files unresolved adding b1 +created new head adding b2 adding b3 4 b3 @@ -9,6 +10,7 @@ 2 0:17ab29e464c6 b1 1 r2 0 r1 +updating working directory 4 files updated, 0 files merged, 0 files removed, 0 files unresolved 1 files updated, 0 files merged, 3 files removed, 0 files unresolved % rebase b onto r1 @@ -26,6 +28,7 @@ 2 0:17ab29e464c6 b1 1 r2 0 r1 +updating working directory 4 files updated, 0 files merged, 0 files removed, 0 files unresolved 1 files updated, 0 files merged, 3 files removed, 0 files unresolved % rebase b onto r1, skipping b2 @@ -46,6 +49,7 @@ adding manifests adding file changes added 2 changesets with 2 changes to 2 files +updating working directory 2 files updated, 0 files merged, 0 files removed, 0 files unresolved searching for changes applying 37a1297eb21b @@ -71,6 +75,7 @@ 0 r1 % skip local changes transplanted to the source adding b4 +updating working directory 4 files updated, 0 files merged, 0 files removed, 0 files unresolved searching for changes applying 4333daefcb15 @@ -81,6 +86,7 @@ adding manifests adding file changes added 1 changesets with 1 changes to 1 files +updating working directory 1 files updated, 0 files merged, 0 files removed, 0 files unresolved searching for changes searching for changes @@ -100,6 +106,7 @@ removing toremove adding bar 2 files updated, 0 files merged, 2 files removed, 0 files unresolved +created new head applying a1e30dd1b8e7 patching file foo Hunk #1 FAILED at 0
--- a/tests/test-up-local-change.out Wed Apr 09 15:27:57 2008 -0700 +++ b/tests/test-up-local-change.out Wed Apr 09 15:28:30 2008 -0700 @@ -1,10 +1,11 @@ adding a +updating working directory 1 files updated, 0 files merged, 0 files removed, 0 files unresolved 0 files updated, 0 files merged, 0 files removed, 0 files unresolved diff -r 33aaa84a386b a --- a/a +++ b/a -@@ -1,1 +1,1 @@ a +@@ -1,1 +1,1 @@ -a +abc adding b @@ -22,6 +23,7 @@ b a: versions differ -> m b: remote created -> g +picked tool 'true' for a (binary False symlink False) merging a my a@33aaa84a386b+ other a@802f095af299 ancestor a@33aaa84a386b getting b @@ -58,6 +60,7 @@ b a: versions differ -> m b: remote created -> g +picked tool 'true' for a (binary False symlink False) merging a my a@33aaa84a386b+ other a@802f095af299 ancestor a@33aaa84a386b getting b @@ -88,18 +91,19 @@ diff -r 802f095af299 a --- a/a +++ b/a -@@ -1,1 +1,1 @@ a2 +@@ -1,1 +1,1 @@ -a2 +abc 1 files updated, 0 files merged, 1 files removed, 0 files unresolved adding b +created new head M a changeset: 1:802f095af299 user: test date: Mon Jan 12 13:46:40 1970 +0000 summary: 2 -abort: update spans branches, use 'hg merge' or 'hg update -C' to lose changes +abort: crosses branches (use 'hg merge' or 'hg update -C' to discard changes) failed abort: outstanding uncommitted changes failed @@ -109,8 +113,10 @@ searching for copies back to rev 1 a: versions differ -> m b: versions differ -> m +picked tool 'true' for a (binary False symlink False) merging a my a@802f095af299+ other a@030602aee63d ancestor a@33aaa84a386b +picked tool 'true' for b (binary False symlink False) merging b my b@802f095af299+ other b@030602aee63d ancestor b@000000000000 0 files updated, 2 files merged, 0 files removed, 0 files unresolved @@ -130,7 +136,7 @@ diff -r 802f095af299 a --- a/a +++ b/a -@@ -1,1 +1,1 @@ a2 +@@ -1,1 +1,1 @@ -a2 +abc adding a
--- a/tests/test-update-reverse.out Wed Apr 09 15:27:57 2008 -0700 +++ b/tests/test-update-reverse.out Wed Apr 09 15:28:30 2008 -0700 @@ -1,6 +1,7 @@ 0 files updated, 0 files merged, 1 files removed, 0 files unresolved Main should be gone a +created new head changeset: 3:ded32b0db104 tag: tip user: test
--- a/tests/test-url-rev.out Wed Apr 09 15:27:57 2008 -0700 +++ b/tests/test-url-rev.out Wed Apr 09 15:28:30 2008 -0700 @@ -5,6 +5,7 @@ adding manifests adding file changes added 2 changesets with 2 changes to 1 files +updating working directory 1 files updated, 0 files merged, 0 files removed, 0 files unresolved % heads changeset: 1:cd2a86ecc814
--- a/tests/test-walk Wed Apr 09 15:27:57 2008 -0700 +++ b/tests/test-walk Wed Apr 09 15:28:30 2008 -0700 @@ -58,6 +58,7 @@ debugwalk ../.hg chdir .. debugwalk -Ibeans +debugwalk -I '{*,{b,m}*/*}k' debugwalk 'glob:mammals/../beans/b*' debugwalk '-X*/Procyonidae' mammals debugwalk path:mammals @@ -92,6 +93,13 @@ debugwalk fenugreek touch new debugwalk new + +mkdir ignored +touch ignored/file +echo '^ignored$' > .hgignore +debugwalk ignored +debugwalk ignored/file + chdir .. debugwalk -R t t/mammals/skunk mkdir t2
--- a/tests/test-walk.out Wed Apr 09 15:27:57 2008 -0700 +++ b/tests/test-walk.out Wed Apr 09 15:28:30 2008 -0700 @@ -174,6 +174,11 @@ f beans/pinto beans/pinto f beans/turtle beans/turtle +hg debugwalk -I {*,{b,m}*/*}k +f beans/black beans/black +f fenugreek fenugreek +f mammals/skunk mammals/skunk + hg debugwalk glob:mammals/../beans/b* f beans/black beans/black f beans/borlotti beans/borlotti @@ -278,6 +283,11 @@ hg debugwalk new f new new exact +hg debugwalk ignored + +hg debugwalk ignored/file +f ignored/file ignored/file exact + cd .. hg debugwalk -R t t/mammals/skunk
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tests/test-walkrepo.py Wed Apr 09 15:28:30 2008 -0700 @@ -0,0 +1,53 @@ +import os +import os.path +from mercurial import hg, ui +from mercurial.util import walkrepos, set, frozenset +from os import mkdir, chdir +from os.path import join as pjoin + +u = ui.ui() +sym = hasattr(os, 'symlink') and hasattr(os.path, 'samestat') + +hg.repository(u, 'top1', create=1) +mkdir('subdir') +chdir('subdir') +hg.repository(u, 'sub1', create=1) +mkdir('subsubdir') +chdir('subsubdir') +hg.repository(u, 'subsub1', create=1) +chdir(os.path.pardir) +if sym: + os.symlink(os.path.pardir, 'circle') + os.symlink(pjoin('subsubdir', 'subsub1'), 'subsub1') + +def runtest(): + reposet = frozenset(walkrepos('.', followsym=True)) + if sym and (len(reposet) != 3): + print "reposet = %r" % (reposet,) + raise SystemExit(1, "Found %d repositories when I should have found 3" % (len(reposet),)) + if (not sym) and (len(reposet) != 2): + print "reposet = %r" % (reposet,) + raise SystemExit(1, "Found %d repositories when I should have found 2" % (len(reposet),)) + sub1set = frozenset((pjoin('.', 'sub1'), + pjoin('.', 'circle', 'subdir', 'sub1'))) + if len(sub1set & reposet) != 1: + print "sub1set = %r" % (sub1set,) + print "reposet = %r" % (reposet,) + raise SystemExit(1, "sub1set and reposet should have exactly one path in common.") + sub2set = frozenset((pjoin('.', 'subsub1'), + pjoin('.', 'subsubdir', 'subsub1'))) + if len(sub2set & reposet) != 1: + print "sub2set = %r" % (sub2set,) + print "reposet = %r" % (reposet,) + raise SystemExit(1, "sub1set and reposet should have exactly one path in common.") + sub3 = pjoin('.', 'circle', 'top1') + if sym and not (sub3 in reposet): + print "reposet = %r" % (reposet,) + raise SystemExit(1, "Symbolic links are supported and %s is not in reposet" % (sub3,)) + +runtest() +if sym: + # Simulate not having symlinks. + del os.path.samestat + sym = False + runtest()
--- a/tests/test-webraw Wed Apr 09 15:27:57 2008 -0700 +++ b/tests/test-webraw Wed Apr 09 15:28:30 2008 -0700 @@ -2,17 +2,18 @@ hg init test cd test -cat >sometext.txt <<ENDSOME +mkdir sub +cat >'sub/some "text".txt' <<ENDSOME This is just some random text that will go inside the file and take a few lines. It is very boring to read, but computers don't care about things like that. ENDSOME -hg add sometext.txt +hg add 'sub/some "text".txt' hg commit -d "1 0" -m "Just some text" hg serve -p $HGPORT -A access.log -E error.log -d --pid-file=hg.pid cat hg.pid >> $DAEMON_PIDS -("$TESTDIR/get-with-headers.py" localhost:$HGPORT '/?f=f165dc289438;file=sometext.txt;style=raw' content-type content-length content-disposition) >getoutput.txt & +("$TESTDIR/get-with-headers.py" localhost:$HGPORT '/?f=a23bf1310f6e;file=sub/some%20%22text%22.txt;style=raw' content-type content-length content-disposition) >getoutput.txt & sleep 5 kill `cat hg.pid`
--- a/tests/test-webraw.out Wed Apr 09 15:27:57 2008 -0700 +++ b/tests/test-webraw.out Wed Apr 09 15:28:30 2008 -0700 @@ -1,10 +1,10 @@ 200 Script output follows content-type: text/plain content-length: 157 -content-disposition: filename=sometext.txt +content-disposition: inline; filename="some \"text\".txt" This is just some random text that will go inside the file and take a few lines. It is very boring to read, but computers don't care about things like that. -host - - [date] "GET /?f=f165dc289438;file=sometext.txt;style=raw HTTP/1.1" 200 - +host - - [date] "GET /?f=a23bf1310f6e;file=sub/some%20%22text%22.txt;style=raw HTTP/1.1" 200 -
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tests/test-win32text Wed Apr 09 15:28:30 2008 -0700 @@ -0,0 +1,115 @@ +#!/bin/sh + +cat > unix2dos.py <<EOF +import sys + +for path in sys.argv[1:]: + data = file(path, 'rb').read() + data = data.replace('\n', '\r\n') + file(path, 'wb').write(data) +EOF + +cat > print.py <<EOF +import sys +print(sys.stdin.read().replace('\n', '<LF>').replace('\r', '<CR>').replace('\0', '<NUL>')) +EOF + +hg init +echo '[hooks]' >> .hg/hgrc +echo 'pretxncommit.crlf = python:hgext.win32text.forbidcrlf' >> .hg/hgrc +echo 'pretxnchangegroup.crlf = python:hgext.win32text.forbidcrlf' >> .hg/hgrc +cat .hg/hgrc +echo + +echo hello > f +hg add f +hg ci -m 1 -d'0 0' +echo + +python unix2dos.py f +hg ci -m 2 -d'0 0' +hg revert -a +echo + +mkdir d +echo hello > d/f2 +python unix2dos.py d/f2 +hg add d/f2 +hg ci -m 3 -d'0 0' +hg revert -a +rm d/f2 +echo + +hg rem f +hg ci -m 4 -d'0 0' +echo + +python -c 'file("bin", "wb").write("hello\x00\x0D\x0A")' +hg add bin +hg ci -m 5 -d'0 0' +hg log -v +echo + +hg clone . dupe +echo +for x in a b c d; do echo content > dupe/$x; done +hg -R dupe add +python unix2dos.py dupe/b dupe/c dupe/d +hg -R dupe ci -m a -d'0 0' dupe/a +hg -R dupe ci -m b/c -d'0 0' dupe/[bc] +hg -R dupe ci -m d -d'0 0' dupe/d +hg -R dupe log -v +echo + +hg pull dupe +echo + +hg log -v +echo + +rm .hg/hgrc +(echo some; echo text) > f3 +python -c 'file("f4.bat", "wb").write("rem empty\x0D\x0A")' +hg add f3 f4.bat +hg ci -m 6 -d'0 0' + +python print.py < bin +python print.py < f3 +python print.py < f4.bat +echo + +echo '[extensions]' >> .hg/hgrc +echo 'win32text = ' >> .hg/hgrc +echo '[decode]' >> .hg/hgrc +echo '** = cleverdecode:' >> .hg/hgrc +echo '[encode]' >> .hg/hgrc +echo '** = cleverencode:' >> .hg/hgrc +cat .hg/hgrc +echo + +rm f3 f4.bat bin +hg co 2>&1 | python -c 'import sys, os; sys.stdout.write(sys.stdin.read().replace(os.getcwd(), "...."))' +python print.py < bin +python print.py < f3 +python print.py < f4.bat +echo + +python -c 'file("f5.sh", "wb").write("# empty\x0D\x0A")' +hg add f5.sh +hg ci -m 7 -d'0 0' +python print.py < f5.sh +hg cat f5.sh | python print.py + +echo '% just linefeed' > linefeed +hg ci -qAm 8 linefeed +python print.py < linefeed +hg cat linefeed | python print.py +hg st -q +hg revert -a linefeed +python print.py < linefeed +hg st -q +echo modified >> linefeed +hg st -q +hg revert -a +hg st -q +python print.py < linefeed
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tests/test-win32text.out Wed Apr 09 15:28:30 2008 -0700 @@ -0,0 +1,187 @@ +[hooks] +pretxncommit.crlf = python:hgext.win32text.forbidcrlf +pretxnchangegroup.crlf = python:hgext.win32text.forbidcrlf + + +Attempt to commit or push text file(s) using CRLF line endings +in b1aa5cde7ff4: f +transaction abort! +rollback completed +abort: pretxncommit.crlf hook failed +reverting f + +Attempt to commit or push text file(s) using CRLF line endings +in 88b17af74937: d/f2 +transaction abort! +rollback completed +abort: pretxncommit.crlf hook failed +forgetting d/f2 + + +changeset: 2:b67b2dae057a +tag: tip +user: test +date: Thu Jan 01 00:00:00 1970 +0000 +files: bin +description: +5 + + +changeset: 1:c72a7d1d0907 +user: test +date: Thu Jan 01 00:00:00 1970 +0000 +files: f +description: +4 + + +changeset: 0:fcf06d5c4e1d +user: test +date: Thu Jan 01 00:00:00 1970 +0000 +files: f +description: +1 + + + +updating working directory +1 files updated, 0 files merged, 0 files removed, 0 files unresolved + +adding dupe/a +adding dupe/b +adding dupe/c +adding dupe/d +changeset: 5:6e8a7629ff5b +tag: tip +user: test +date: Thu Jan 01 00:00:00 1970 +0000 +files: d +description: +d + + +changeset: 4:ac30a42ce8bc +user: test +date: Thu Jan 01 00:00:00 1970 +0000 +files: b c +description: +b/c + + +changeset: 3:a73b85ef1fb7 +user: test +date: Thu Jan 01 00:00:00 1970 +0000 +files: a +description: +a + + +changeset: 2:b67b2dae057a +user: test +date: Thu Jan 01 00:00:00 1970 +0000 +files: bin +description: +5 + + +changeset: 1:c72a7d1d0907 +user: test +date: Thu Jan 01 00:00:00 1970 +0000 +files: f +description: +4 + + +changeset: 0:fcf06d5c4e1d +user: test +date: Thu Jan 01 00:00:00 1970 +0000 +files: f +description: +1 + + + +pulling from dupe +searching for changes +adding changesets +adding manifests +adding file changes +added 3 changesets with 4 changes to 4 files +Attempt to commit or push text file(s) using CRLF line endings +in ac30a42ce8bc: b +in ac30a42ce8bc: c +in 6e8a7629ff5b: d + +To prevent this mistake in your local repository, +add to Mercurial.ini or .hg/hgrc: + +[hooks] +pretxncommit.crlf = python:hgext.win32text.forbidcrlf + +and also consider adding: + +[extensions] +hgext.win32text = +[encode] +** = cleverencode: +[decode] +** = cleverdecode: +transaction abort! +rollback completed +abort: pretxnchangegroup.crlf hook failed + +changeset: 2:b67b2dae057a +tag: tip +user: test +date: Thu Jan 01 00:00:00 1970 +0000 +files: bin +description: +5 + + +changeset: 1:c72a7d1d0907 +user: test +date: Thu Jan 01 00:00:00 1970 +0000 +files: f +description: +4 + + +changeset: 0:fcf06d5c4e1d +user: test +date: Thu Jan 01 00:00:00 1970 +0000 +files: f +description: +1 + + + +hello<NUL><CR><LF> +some<LF>text<LF> +rem empty<CR><LF> + +[extensions] +win32text = +[decode] +** = cleverdecode: +[encode] +** = cleverencode: + +WARNING: f4.bat already has CRLF line endings +and does not need EOL conversion by the win32text plugin. +Before your next commit, please reconsider your encode/decode settings in +Mercurial.ini or ..../.hg/hgrc. +3 files updated, 0 files merged, 0 files removed, 0 files unresolved +hello<NUL><CR><LF> +some<CR><LF>text<CR><LF> +rem empty<CR><LF> + +# empty<CR><LF> +# empty<LF> +% just linefeed<LF> +% just linefeed<LF> +no changes needed to linefeed +% just linefeed<LF> +M linefeed +reverting linefeed +% just linefeed<CR><LF>