# HG changeset patch # User Martin Geisler # Date 1275334983 -7200 # Node ID ac6fec2af8c882670144d9cb8e3637d1cee1a171 # Parent 102d8eb5c89094be2cd713cbe63a23842c44afae# Parent 0bb67503ad4b05a91531e6e6a066e2b4682a9de6 Merge with stable diff -r 0bb67503ad4b -r ac6fec2af8c8 Makefile --- a/Makefile Mon May 31 21:37:01 2010 +0200 +++ b/Makefile Mon May 31 21:43:03 2010 +0200 @@ -51,7 +51,7 @@ install: install-bin install-doc install-bin: build - $(PYTHON) setup.py $(PURE) install --prefix="$(PREFIX)" --force + $(PYTHON) setup.py $(PURE) install --root="$(DESTDIR)/" --prefix="$(PREFIX)" --force install-doc: doc cd doc && $(MAKE) $(MFLAGS) install diff -r 0bb67503ad4b -r ac6fec2af8c8 contrib/check-code.py --- a/contrib/check-code.py Mon May 31 21:37:01 2010 +0200 +++ b/contrib/check-code.py Mon May 31 21:43:03 2010 +0200 @@ -7,12 +7,19 @@ # This software may be used and distributed according to the terms of the # GNU General Public License version 2 or any later version. -import sys, re, glob +import re, glob +import optparse def repquote(m): - t = re.sub(r"\w", "x", m.group(2)) + t = re.sub(r"\w", "x", m.group('text')) t = re.sub(r"[^\sx]", "o", t) - return m.group(1) + t + m.group(1) + return m.group('quote') + t + m.group('quote') + +def reppython(m): + comment = m.group('comment') + if comment: + return "#" * len(comment) + return repquote(m) def repcomment(m): return m.group(1) + "#" * len(m.group(2)) @@ -54,6 +61,7 @@ (r'[^\n]\Z', "no trailing newline"), (r'export.*=', "don't export and assign at once"), ('^([^"\']|("[^"]*")|(\'[^\']*\'))*\\^', "^ must be quoted"), + (r'^source\b', "don't use 'source', use '.'"), ] testfilters = [ @@ -84,6 +92,7 @@ (r'[\x80-\xff]', "non-ASCII character literal"), (r'("\')\.format\(', "str.format() not available in Python 2.4"), (r'^\s*with\s+', "with not available in Python 2.4"), + (r'^\s*(any|all)\(', "any/all not available in Python 2.4"), (r'if\s.*\selse', "if ... else form not available in Python 2.4"), (r'([\(\[]\s\S)|(\S\s[\)\]])', "gratuitous whitespace in () or []"), # (r'\s\s=', "gratuitous whitespace before ="), @@ -92,15 +101,15 @@ (r'\s(\+=|-=|!=|<>|<=|>=|<<=|>>=)\S', "missing whitespace around operator"), (r'[^+=*!<>&| -](\s=|=\s)[^= ]', "wrong whitespace around ="), (r'raise Exception', "don't raise generic exceptions"), - (r'ui\.(status|progress|write|note)\([\'\"]x', "unwrapped ui message"), + (r'ui\.(status|progress|write|note)\([\'\"]x', + "warning: unwrapped ui message"), ] pyfilters = [ - (r"""(''')(([^']|\\'|'{1,2}(?!'))*)'''""", repquote), - (r'''(""")(([^"]|\\"|"{1,2}(?!"))*)"""''', repquote), - (r'''(?\#.*?$)| + ((?P('''|\"\"\"|(?(([^\\]|\\.)*?)) + (?P=quote))""", reppython), ] cpats = [ @@ -123,7 +132,7 @@ cfilters = [ (r'(/\*)(((\*(?!/))|[^*])*)\*/', repccomment), - (r'''(?(?([^"]|\\")+)"(?!")''', repquote), (r'''(#\s*include\s+<)([^>]+)>''', repinclude), (r'(\()([^)]+\))', repcallspaces), ] @@ -134,12 +143,42 @@ ('c', r'.*\.c$', cfilters, cpats), ] -if len(sys.argv) == 1: - check = glob.glob("*") -else: - check = sys.argv[1:] +class norepeatlogger(object): + def __init__(self): + self._lastseen = None + + def log(self, fname, lineno, line, msg): + """print error related a to given line of a given file. + + The faulty line will also be printed but only once in the case + of multiple errors. -for f in check: + :fname: filename + :lineno: line number + :line: actual content of the line + :msg: error message + """ + msgid = fname, lineno, line + if msgid != self._lastseen: + print "%s:%d:" % (fname, lineno) + print " > %s" % line + self._lastseen = msgid + print " " + msg + +_defaultlogger = norepeatlogger() + +def checkfile(f, logfunc=_defaultlogger.log, maxerr=None, warnings=False): + """checks style and portability of a given file + + :f: filepath + :logfunc: function used to report error + logfunc(filename, linenumber, linecontent, errormessage) + :maxerr: number of error to display before arborting. + Set to None (default) to report all errors + + return True if no error is found, False otherwise. + """ + result = True for name, match, filters, pats in checks: fc = 0 if not re.match(match, f): @@ -154,16 +193,34 @@ for n, l in z: if "check-code" + "-ignore" in l[0]: continue - lc = 0 for p, msg in pats: + if not warnings and msg.startswith("warning"): + continue if re.search(p, l[1]): - if not lc: - print "%s:%d:" % (f, n + 1) - print " > %s" % l[0] - print " %s" % msg - lc += 1 + logfunc(f, n + 1, l[0], msg) fc += 1 - if fc == 15: + result = False + if maxerr is not None and fc >= maxerr: print " (too many errors, giving up)" break break + return result + + +if __name__ == "__main__": + parser = optparse.OptionParser("%prog [options] [files]") + parser.add_option("-w", "--warnings", action="store_true", + help="include warning-level checks") + parser.add_option("-p", "--per-file", type="int", + help="max warnings per file") + + parser.set_defaults(per_file=15, warnings=False) + (options, args) = parser.parse_args() + + if len(args) == 0: + check = glob.glob("*") + else: + check = args + + for f in check: + checkfile(f, maxerr=options.per_file, warnings=options.warnings) diff -r 0bb67503ad4b -r ac6fec2af8c8 contrib/git-viz/git-cat-file --- a/contrib/git-viz/git-cat-file Mon May 31 21:37:01 2010 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,5 +0,0 @@ -#!/bin/sh - -op=`basename $0 | sed -e 's/^git-//'` -exec hgit $op "$@" - diff -r 0bb67503ad4b -r ac6fec2af8c8 contrib/git-viz/git-diff-tree --- a/contrib/git-viz/git-diff-tree Mon May 31 21:37:01 2010 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,5 +0,0 @@ -#!/bin/sh - -op=`basename $0 | sed -e 's/^git-//'` -exec hgit $op "$@" - diff -r 0bb67503ad4b -r ac6fec2af8c8 contrib/git-viz/git-rev-list --- a/contrib/git-viz/git-rev-list Mon May 31 21:37:01 2010 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,5 +0,0 @@ -#!/bin/sh - -op=`basename $0 | sed -e 's/^git-//'` -exec hgit $op "$@" - diff -r 0bb67503ad4b -r ac6fec2af8c8 contrib/git-viz/git-rev-tree --- a/contrib/git-viz/git-rev-tree Mon May 31 21:37:01 2010 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,5 +0,0 @@ -#!/bin/sh - -op=`basename $0 | sed -e 's/^git-//'` -exec hgit $op "$@" - diff -r 0bb67503ad4b -r ac6fec2af8c8 contrib/git-viz/hg-viz --- a/contrib/git-viz/hg-viz Mon May 31 21:37:01 2010 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,26 +0,0 @@ -#!/bin/sh - -set -e - -if test x"$1" != x ; then - cd $1 -fi - -if [ ! -d ".hg" ]; then - echo "${1:-.} is not a mercurial repository" 1>&2 - echo "Aborting" 1>&2 - exit 1 -fi -if [ ! -d ".git" ]; then - mkdir -v ".git" -fi -if [ -e ".git/HEAD" ]; then - if [ ! -e ".git/HEAD.hg-viz-save" ]; then - mv -v ".git/HEAD" ".git/HEAD.hg-viz-save" - else - rm -vf ".git/HEAD" - fi -fi -hg history | head -1 | awk -F: '{print $3}' > .git/HEAD -git-viz - diff -r 0bb67503ad4b -r ac6fec2af8c8 contrib/hgdiff --- a/contrib/hgdiff Mon May 31 21:37:01 2010 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,105 +0,0 @@ -#!/usr/bin/env python - -import os, sys, struct, stat -import difflib -import re -from optparse import OptionParser -from mercurial.bdiff import bdiff, blocks -from mercurial.mdiff import bunidiff, diffopts - -VERSION="0.3" -usage = "usage: %prog [options] file1 file2" -parser = OptionParser(usage=usage) - -parser.add_option("-d", "--difflib", action="store_true", default=False) -parser.add_option('-x', '--count', default=1) -parser.add_option('-c', '--context', type="int", default=3) -parser.add_option('-p', '--show-c-function', action="store_true", default=False) -parser.add_option('-w', '--ignore-all-space', action="store_true", - default=False) - -(options, args) = parser.parse_args() - -if not args: - parser.print_help() - sys.exit(1) - -# simple utility function to put all the -# files from a directory tree into a dict -def buildlist(names, top): - tlen = len(top) - for root, dirs, files in os.walk(top): - l = root[tlen + 1:] - for x in files: - p = os.path.join(root, x) - st = os.lstat(p) - if stat.S_ISREG(st.st_mode): - names[os.path.join(l, x)] = (st.st_dev, st.st_ino) - -def diff_files(file1, file2): - if file1 is None: - b = file(file2).read().splitlines(True) - l1 = "--- %s\n" % (file2) - l2 = "+++ %s\n" % (file2) - l3 = "@@ -0,0 +1,%d @@\n" % len(b) - l = [l1, l2, l3] + ["+" + e for e in b] - elif file2 is None: - a = file(file1).read().splitlines(True) - l1 = "--- %s\n" % (file1) - l2 = "+++ %s\n" % (file1) - l3 = "@@ -1,%d +0,0 @@\n" % len(a) - l = [l1, l2, l3] + ["-" + e for e in a] - else: - t1 = file(file1).read() - t2 = file(file2).read() - l1 = t1.splitlines(True) - l2 = t2.splitlines(True) - if options.difflib: - l = difflib.unified_diff(l1, l2, file1, file2) - else: - l = bunidiff(t1, t2, l1, l2, file1, file2, - diffopts(context=options.context, - showfunc=options.show_c_function, - ignorews=options.ignore_all_space)) - for x in l: - if x[-1] != '\n': - x += "\n\ No newline at end of file\n" - print x, - -file1 = args[0] -file2 = args[1] - -if os.path.isfile(file1) and os.path.isfile(file2): - diff_files(file1, file2) -elif os.path.isdir(file1): - if not os.path.isdir(file2): - sys.stderr.write("file types don't match\n") - sys.exit(1) - - d1 = {} - d2 = {} - - buildlist(d1, file1) - buildlist(d2, file2) - keys = d1.keys() - keys.sort() - for x in keys: - if x not in d2: - f2 = None - else: - f2 = os.path.join(file2, x) - st1 = d1[x] - st2 = d2[x] - del d2[x] - if st1[0] == st2[0] and st1[1] == st2[1]: - sys.stderr.write("%s is a hard link\n" % x) - continue - x = os.path.join(file1, x) - diff_files(x, f2) - keys = d2.keys() - keys.sort() - for x in keys: - f1 = None - x = os.path.join(file2, x) - diff_files(f1, x) - diff -r 0bb67503ad4b -r ac6fec2af8c8 contrib/hgweb.fcgi --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/contrib/hgweb.fcgi Mon May 31 21:43:03 2010 +0200 @@ -0,0 +1,18 @@ +#!/usr/bin/env python +# +# An example FastCGI script for use with flup, edit as necessary + +# Path to repo or hgweb config to serve (see 'hg help hgweb') +config = "/path/to/repo/or/config" + +# Uncomment and adjust if Mercurial is not installed system-wide: +#import sys; sys.path.insert(0, "/path/to/python/lib") + +# Uncomment to send python tracebacks to the browser if an error occurs: +#import cgitb; cgitb.enable() + +from mercurial import demandimport; demandimport.enable() +from mercurial.hgweb import hgweb +from flup.server.fcgi import WSGIServer +application = hgweb(config) +WSGIServer(application).run() diff -r 0bb67503ad4b -r ac6fec2af8c8 contrib/hgweb.wsgi --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/contrib/hgweb.wsgi Mon May 31 21:43:03 2010 +0200 @@ -0,0 +1,16 @@ +# An example WSGI for use with mod_wsgi, edit as necessary + +# Path to repo or hgweb config to serve (see 'hg help hgweb') +config = "/path/to/repo/or/config" + +# Uncomment and adjust if Mercurial is not installed system-wide: +#import sys; sys.path.insert(0, "/path/to/python/lib") + +# Uncomment to send python tracebacks to the browser if an error occurs: +#import cgitb; cgitb.enable() + +# enable demandloading to reduce startup time +from mercurial import demandimport; demandimport.enable() + +from mercurial.hgweb import hgweb +application = hgweb(config) diff -r 0bb67503ad4b -r ac6fec2af8c8 contrib/hgwebdir.fcgi --- a/contrib/hgwebdir.fcgi Mon May 31 21:37:01 2010 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,62 +0,0 @@ -#!/usr/bin/env python -# -# An example CGI script to export multiple hgweb repos, edit as necessary - -# adjust python path if not a system-wide install: -#import sys -#sys.path.insert(0, "/path/to/python/lib") - -# enable demandloading to reduce startup time -from mercurial import demandimport; demandimport.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. -# Note that this will cause your .hgrc files to be interpreted in -# UTF-8 and all your repo files to be displayed using UTF-8. -# -#import os -#os.environ["HGENCODING"] = "UTF-8" - -from mercurial.hgweb.hgwebdir_mod import hgwebdir -from flup.server.fcgi import WSGIServer - -# The config file looks like this. You can have paths to individual -# repos, collections of repos in a directory tree, or both. -# -# [paths] -# virtual/path1 = /real/path1 -# virtual/path2 = /real/path2 -# virtual/root = /real/root/* -# / = /real/root2/* -# -# [collections] -# /prefix/to/strip/off = /root/of/tree/full/of/repos -# -# paths example: -# -# * First two lines mount one repository into one virtual path, like -# '/real/path1' into 'virtual/path1'. -# -# * The third entry tells every mercurial repository found in -# '/real/root', recursively, should be mounted in 'virtual/root'. This -# format is preferred over the [collections] one, using absolute paths -# as configuration keys is not supported on every platform (including -# Windows). -# -# * The last entry is a special case mounting all repositories in -# '/real/root2' in the root of the virtual directory. -# -# collections example: say directory tree /foo contains repos /foo/bar, -# /foo/quux/baz. Give this config section: -# [collections] -# /foo = /foo -# Then repos will list as bar and quux/baz. -# -# Alternatively you can pass a list of ('virtual/path', '/real/path') tuples -# or use a dictionary with entries like 'virtual/path': '/real/path' - -WSGIServer(hgwebdir('hgweb.config')).run() diff -r 0bb67503ad4b -r ac6fec2af8c8 contrib/hgwebdir.wsgi --- a/contrib/hgwebdir.wsgi Mon May 31 21:37:01 2010 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,51 +0,0 @@ -# An example WSGI (use with mod_wsgi) script to export multiple hgweb repos - -# adjust python path if not a system-wide install: -#import sys -#sys.path.insert(0, "/path/to/python/lib") - -# enable demandloading to reduce startup time -from mercurial import demandimport; demandimport.enable() -from mercurial.hgweb.hgwebdir_mod import hgwebdir - -# 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. -# Note that this will cause your .hgrc files to be interpreted in -# UTF-8 and all your repo files to be displayed using UTF-8. -# -#import os -#os.environ["HGENCODING"] = "UTF-8" - -# The config file looks like this. You can have paths to individual -# repos, collections of repos in a directory tree, or both. -# -# [paths] -# virtual/path1 = /real/path1 -# virtual/path2 = /real/path2 -# virtual/root = /real/root/* -# / = /real/root2/* -# -# paths example: -# -# * First two lines mount one repository into one virtual path, like -# '/real/path1' into 'virtual/path1'. -# -# * The third entry tells every mercurial repository found in -# '/real/root', recursively, should be mounted in 'virtual/root'. This -# format is preferred over the [collections] one, using absolute paths -# as configuration keys is not supported on every platform (including -# Windows). -# -# * The last entry is a special case mounting all repositories in -# '/real/root2' in the root of the virtual directory. -# -# collections example: say directory tree /foo contains repos /foo/bar, -# /foo/quux/baz. Give this config section: -# [collections] -# /foo = /foo -# Then repos will list as bar and quux/baz. -# -# Alternatively you can pass a list of ('virtual/path', '/real/path') tuples -# or use a dictionary with entries like 'virtual/path': '/real/path' - -application = hgwebdir('hgweb.config') diff -r 0bb67503ad4b -r ac6fec2af8c8 contrib/logo-droplets.svg --- a/contrib/logo-droplets.svg Mon May 31 21:37:01 2010 +0200 +++ b/contrib/logo-droplets.svg Mon May 31 21:43:03 2010 +0200 @@ -1,624 +1,5 @@ -image/svg+xmlMercurial "droplets" logoCali Mastny and Matt MackallFeb 12 2008 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file +image/svg+xmlMercurial "droplets" logoCali Mastny and Matt MackallFeb 12 2008 + + diff -r 0bb67503ad4b -r ac6fec2af8c8 contrib/mercurial.spec --- a/contrib/mercurial.spec Mon May 31 21:37:01 2010 +0200 +++ b/contrib/mercurial.spec Mon May 31 21:43:03 2010 +0200 @@ -38,14 +38,11 @@ %install rm -rf $RPM_BUILD_ROOT -python setup.py install --root $RPM_BUILD_ROOT --prefix %{_prefix} -make install-doc DESTDIR=$RPM_BUILD_ROOT MANDIR=%{_mandir} +make install DESTDIR=$RPM_BUILD_ROOT PREFIX=%{_prefix} MANDIR=%{_mandir} install contrib/hgk $RPM_BUILD_ROOT%{_bindir} install contrib/convert-repo $RPM_BUILD_ROOT%{_bindir}/mercurial-convert-repo install contrib/hg-ssh $RPM_BUILD_ROOT%{_bindir} -install contrib/git-viz/hg-viz $RPM_BUILD_ROOT%{_bindir} -install contrib/git-viz/git-rev-tree $RPM_BUILD_ROOT%{_bindir} bash_completion_dir=$RPM_BUILD_ROOT%{_sysconfdir}/bash_completion.d mkdir -p $bash_completion_dir @@ -77,8 +74,6 @@ %{_bindir}/hg %{_bindir}/hgk %{_bindir}/hg-ssh -%{_bindir}/hg-viz -%{_bindir}/git-rev-tree %{_bindir}/mercurial-convert-repo %dir %{_sysconfdir}/bash_completion.d/ %dir %{_datadir}/zsh/site-functions/ diff -r 0bb67503ad4b -r ac6fec2af8c8 contrib/mergetools.hgrc --- a/contrib/mergetools.hgrc Mon May 31 21:37:01 2010 +0200 +++ b/contrib/mergetools.hgrc Mon May 31 21:43:03 2010 +0200 @@ -6,6 +6,7 @@ kdiff3.regappend=\kdiff3.exe kdiff3.fixeol=True kdiff3.gui=True +kdiff3.diffargs=--L1 '$plabel1' --L2 '$clabel' $parent $child gvimdiff.args=--nofork -d -g -O $local $other $base gvimdiff.regkey=Software\Vim\GVim @@ -13,20 +14,23 @@ gvimdiff.priority=-9 merge.checkconflicts=True -merge.priority=-10 +merge.priority=-100 gpyfm.gui=True meld.gui=True meld.args=--label='local' $local --label='base' $base --label='other' $other +meld.diffargs=-a --label='$plabel1' $parent --label='$clabel' $child tkdiff.args=$local $other -a $base -o $output tkdiff.gui=True tkdiff.priority=-8 +tkdiff.diffargs=-L '$plabel1' $parent -L '$clabel' $child 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 +xxdiff.diffargs=--title1 '$plabel1' $parent --title2 '$clabel' $child diffmerge.regkey=Software\SourceGear\SourceGear DiffMerge\ diffmerge.regname=Location @@ -34,6 +38,7 @@ diffmerge.args=-nosplash -merge -title1=local -title2=merged -title3=other $local $base $other -result=$output diffmerge.checkchanged=True diffmerge.gui=True +diffmerge.diffargs=--nosplash --title1='$plabel1' --title2='$clabel' $parent $child p4merge.args=$base $local $other $output p4merge.regkey=Software\Perforce\Environment @@ -41,16 +46,19 @@ p4merge.regappend=\p4merge.exe p4merge.gui=True p4merge.priority=-8 +p4merge.diffargs=$parent $child tortoisemerge.args=/base:$base /mine:$local /theirs:$other /merged:$output tortoisemerge.regkey=Software\TortoiseSVN tortoisemerge.checkchanged=True tortoisemerge.gui=True tortoisemerge.priority=-8 +tortoisemerge.diffargs=/base:$parent /mine:$child /basename:'$plabel1' /minename:'$clabel' 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 +ecmerge.diffargs=$parent $child --mode=diff2 --title1='$plabel1' --title2='$clabel' filemerge.executable=/Developer/Applications/Utilities/FileMerge.app/Contents/MacOS/FileMerge filemerge.args=-left $other -right $local -ancestor $base -merge $output @@ -62,12 +70,14 @@ beyondcompare3.regname=ExePath beyondcompare3.gui=True beyondcompare3.priority=-2 +beyondcompare3.diffargs=/lro /lefttitle='$plabel1' /righttitle='$clabel' /solo /expandall $parent $child ; Linux version of Beyond Compare bcompare.args=$local $other $base -mergeoutput=$output -ro -lefttitle=parent1 -centertitle=base -righttitle=parent2 -outputtitle=merged -automerge -reviewconflicts -solo bcompare.premerge=False bcompare.gui=True bcompare.priority=-1 +bcompare.diffargs=-lro -lefttitle='$plabel1' -righttitle='$clabel' -solo -expandall $parent $child winmerge.args=/e /x /wl /ub /dl other /dr local $other $local $output winmerge.regkey=Software\Thingamahoochie\WinMerge @@ -75,6 +85,7 @@ winmerge.checkchanged=True winmerge.gui=True winmerge.priority=-10 +winmerge.diffargs=/r /e /x /ub /wl /dl '$plabel1' /dr '$clabel' $parent $child araxis.regkey=SOFTWARE\Classes\TypeLib\{46799e0a-7bd1-4330-911c-9660bb964ea2}\7.0\HELPDIR araxis.regappend=\ConsoleCompare.exe @@ -84,3 +95,9 @@ araxis.checkconflict=True araxis.binary=True araxis.gui=True +araxis.diffargs=/2 /wait /title1:"$plabel1" /title2:"$clabel" $parent $child + +diffuse.priority=-3 +diffuse.args=$local $base $other +diffuse.gui=True +diffuse.diffargs=$parent $child diff -r 0bb67503ad4b -r ac6fec2af8c8 contrib/rewrite-log --- a/contrib/rewrite-log Mon May 31 21:37:01 2010 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,23 +0,0 @@ -#!/usr/bin/env python -import sys, os -from mercurial import revlog, transaction, node, util - -f = sys.argv[1] - -r1 = revlog.revlog(util.opener(os.getcwd(), audit=False), f + ".i", f + ".d") -r2 = revlog.revlog(util.opener(os.getcwd(), audit=False), f + ".i2", f + ".d2") - -tr = transaction.transaction(sys.stderr.write, open, "journal") - -for i in xrange(r1.count()): - n = r1.node(i) - p1, p2 = r1.parents(n) - l = r1.linkrev(n) - t = r1.revision(n) - n2 = r2.addrevision(t, tr, l, p1, p2) -tr.close() - -os.rename(f + ".i", f + ".i.old") -os.rename(f + ".d", f + ".d.old") -os.rename(f + ".i2", f + ".i") -os.rename(f + ".d2", f + ".d") diff -r 0bb67503ad4b -r ac6fec2af8c8 contrib/shrink-revlog.py --- a/contrib/shrink-revlog.py Mon May 31 21:37:01 2010 +0200 +++ b/contrib/shrink-revlog.py Mon May 31 21:43:03 2010 +0200 @@ -24,50 +24,81 @@ from mercurial import changegroup from mercurial.i18n import _ -def toposort(ui, rl): + +def postorder(start, edges): + result = [] + visit = list(start) + finished = set() - children = {} - root = [] - # build children and roots + while visit: + cur = visit[-1] + for p in edges[cur]: + if p not in finished: + visit.append(p) + break + else: + result.append(cur) + finished.add(cur) + visit.pop() + + return result + +def toposort_reversepostorder(ui, rl): + # postorder of the reverse directed graph + + # map rev to list of parent revs (p2 first) + parents = {} + heads = set() ui.status(_('reading revs\n')) try: - for i in rl: - ui.progress(_('reading'), i, total=len(rl)) - children[i] = [] - parents = [p for p in rl.parentrevs(i) if p != node.nullrev] - # in case of duplicate parents - if len(parents) == 2 and parents[0] == parents[1]: - del parents[1] - for p in parents: - assert p in children - children[p].append(i) + for rev in rl: + ui.progress(_('reading'), rev, total=len(rl)) + (p1, p2) = rl.parentrevs(rev) + if p1 == p2 == node.nullrev: + parents[rev] = () # root node + elif p1 == p2 or p2 == node.nullrev: + parents[rev] = (p1,) # normal node + else: + parents[rev] = (p2, p1) # merge node + heads.add(rev) + for p in parents[rev]: + heads.discard(p) + finally: + ui.progress(_('reading'), None) - if len(parents) == 0: - root.append(i) - finally: - ui.progress(_('reading'), None, total=len(rl)) + heads = list(heads) + heads.sort(reverse=True) + + ui.status(_('sorting revs\n')) + return postorder(heads, parents) + +def toposort_postorderreverse(ui, rl): + # reverse-postorder of the reverse directed graph - # XXX this is a reimplementation of the 'branchsort' topo sort - # algorithm in hgext.convert.convcmd... would be nice not to duplicate - # the algorithm + children = {} + roots = set() + ui.status(_('reading revs\n')) + try: + for rev in rl: + ui.progress(_('reading'), rev, total=len(rl)) + (p1, p2) = rl.parentrevs(rev) + if p1 == p2 == node.nullrev: + roots.add(rev) + children[rev] = [] + if p1 != node.nullrev: + children[p1].append(rev) + if p2 != node.nullrev: + children[p2].append(rev) + finally: + ui.progress(_('reading'), None) + + root = list(roots) + roots.sort() + ui.status(_('sorting revs\n')) - visit = root - ret = [] - while visit: - i = visit.pop(0) - ret.append(i) - if i not in children: - # This only happens if some node's p1 == p2, which can - # happen in the manifest in certain circumstances. - continue - next = [] - for c in children.pop(i): - parents_unseen = [p for p in rl.parentrevs(c) - if p != node.nullrev and p in children] - if len(parents_unseen) == 0: - next.append(c) - visit = next + visit - return ret + result = postorder(roots, children) + result.reverse() + return result def writerevs(ui, r1, r2, order, tr): @@ -89,7 +120,7 @@ chunkiter = changegroup.chunkiter(group) r2.addgroup(chunkiter, unlookup, tr) finally: - ui.progress(_('writing'), None, len(order)) + ui.progress(_('writing'), None) def report(ui, r1, r2): def getsize(r): @@ -118,9 +149,15 @@ % (shrink_percent, shrink_factor)) def shrink(ui, repo, **opts): + """shrink a revlog by reordering revisions + + Rewrites all the entries in some revlog of the current repository + (by default, the manifest log) to save space. + + Different sort algorithms have different performance + characteristics. Use ``--sort`` to select a sort algorithm so you + can determine which works best for your data. """ - Shrink revlog by re-ordering revisions. Will operate on manifest for - the given repository if no other revlog is specified.""" if not repo.local(): raise util.Abort(_('not a local repository: %s') % repo.root) @@ -139,6 +176,12 @@ raise util.Abort(_('--revlog option must specify a revlog in %s, ' 'not %s') % (store, indexfn)) + sortname = opts['sort'] + try: + toposort = globals()['toposort_' + sortname] + except KeyError: + raise util.Abort(_('no such toposort algorithm: %s') % sortname) + if not os.path.exists(indexfn): raise util.Abort(_('no such file: %s') % indexfn) if '00changelog' in indexfn: @@ -187,6 +230,15 @@ try: try: order = toposort(ui, r1) + + suboptimal = 0 + for i in xrange(1, len(order)): + parents = [p for p in r1.parentrevs(order[i]) + if p != node.nullrev] + if parents and order[i - 1] not in parents: + suboptimal += 1 + ui.note(_('%d suboptimal nodes\n') % suboptimal) + writerevs(ui, r1, r2, order, tr) report(ui, r1, r2) tr.close() @@ -229,6 +281,7 @@ 'shrink': (shrink, [('', 'revlog', '', _('index (.i) file of the revlog to shrink')), ('n', 'dry-run', None, _('do not shrink, simulate only')), + ('', 'sort', 'reversepostorder', 'name of sort algorithm to use'), ], _('hg shrink [--revlog PATH]')) } diff -r 0bb67503ad4b -r ac6fec2af8c8 contrib/win32/win32-build.txt --- a/contrib/win32/win32-build.txt Mon May 31 21:37:01 2010 +0200 +++ b/contrib/win32/win32-build.txt Mon May 31 21:43:03 2010 +0200 @@ -82,16 +82,10 @@ Microsoft.VC90.MFC.manifest) Before building the installer, you have to build Mercurial HTML documentation -(or fix mercurial.iss to not reference the doc directory). Docutils does not -come with a ready-made script for rst2html.py, so you will have to write your -own and put it in %PATH% like: - - @python c:\pythonXX\scripts\rst2html.py %* - -Then build the documentation with: +(or fix mercurial.iss to not reference the doc directory): cd doc - mingw32-make RST2HTML=rst2html.bat html + mingw32-make html cd .. If you use ISTool, you open the C:\hg\hg-release\contrib\win32\mercurial.iss @@ -113,7 +107,7 @@ echo compiler=mingw32 >> setup.cfg python setup.py py2exe -b 1 cd doc - mingw32-make RST2HTML=rst2html.bat html + mingw32-make html cd .. iscc contrib\win32\mercurial.iss /DVERSION=snapshot diff -r 0bb67503ad4b -r ac6fec2af8c8 contrib/wix/contrib.wxs --- a/contrib/wix/contrib.wxs Mon May 31 21:37:01 2010 +0200 +++ b/contrib/wix/contrib.wxs Mon May 31 21:43:03 2010 +0200 @@ -1,218 +1,42 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff -r 0bb67503ad4b -r ac6fec2af8c8 contrib/wix/doc.wxs --- a/contrib/wix/doc.wxs Mon May 31 21:37:01 2010 +0200 +++ b/contrib/wix/doc.wxs Mon May 31 21:43:03 2010 +0200 @@ -1,33 +1,49 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff -r 0bb67503ad4b -r ac6fec2af8c8 contrib/wix/guids.wxi --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/contrib/wix/guids.wxi Mon May 31 21:43:03 2010 +0200 @@ -0,0 +1,46 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff -r 0bb67503ad4b -r ac6fec2af8c8 contrib/wix/help.wxs --- a/contrib/wix/help.wxs Mon May 31 21:37:01 2010 +0200 +++ b/contrib/wix/help.wxs Mon May 31 21:43:03 2010 +0200 @@ -1,72 +1,25 @@ - + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + diff -r 0bb67503ad4b -r ac6fec2af8c8 contrib/wix/i18n.wxs --- a/contrib/wix/i18n.wxs Mon May 31 21:37:01 2010 +0200 +++ b/contrib/wix/i18n.wxs Mon May 31 21:43:03 2010 +0200 @@ -1,78 +1,25 @@ - + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + diff -r 0bb67503ad4b -r ac6fec2af8c8 contrib/wix/locale.wxs --- a/contrib/wix/locale.wxs Mon May 31 21:37:01 2010 +0200 +++ b/contrib/wix/locale.wxs Mon May 31 21:43:03 2010 +0200 @@ -1,172 +1,32 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + diff -r 0bb67503ad4b -r ac6fec2af8c8 contrib/wix/mercurial.wxs --- a/contrib/wix/mercurial.wxs Mon May 31 21:37:01 2010 +0200 +++ b/contrib/wix/mercurial.wxs Mon May 31 21:43:03 2010 +0200 @@ -6,24 +6,31 @@ This software may be used and distributed according to the terms of the GNU General Public License version 2 or any later version. --> - - + - + Language='1033' Codepage='1252' Version='$(var.Version)' + Manufacturer='Matt Mackall and others'> - - + - VersionNT >= 501 + VersionNT >= 501 - + @@ -37,30 +44,35 @@ amus + + 1 + - - - + + + - - + + - - + + - - + + - - + + @@ -68,17 +80,16 @@ - + - + - - + - + diff -r 0bb67503ad4b -r ac6fec2af8c8 contrib/wix/templates.wxs --- a/contrib/wix/templates.wxs Mon May 31 21:37:01 2010 +0200 +++ b/contrib/wix/templates.wxs Mon May 31 21:43:03 2010 +0200 @@ -1,801 +1,206 @@ - + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff -r 0bb67503ad4b -r ac6fec2af8c8 doc/Makefile --- a/doc/Makefile Mon May 31 21:37:01 2010 +0200 +++ b/doc/Makefile Mon May 31 21:43:03 2010 +0200 @@ -6,7 +6,6 @@ MANDIR=$(PREFIX)/share/man INSTALL=install -c -m 644 PYTHON=python -RST2HTML=$(shell which rst2html 2> /dev/null || echo rst2html.py) export LANGUAGE=C export LC_ALL=C @@ -25,11 +24,11 @@ mv $@.tmp $@ %: %.txt common.txt - $(PYTHON) rst2man.py --halt warning \ + $(PYTHON) runrst manpage --halt warning \ --strip-elements-with-class htmlonly $*.txt $* %.html: %.txt common.txt - $(RST2HTML) --halt warning \ + $(PYTHON) runrst html --halt warning \ --link-stylesheet --stylesheet-path style.css $*.txt $*.html MANIFEST: man html diff -r 0bb67503ad4b -r ac6fec2af8c8 doc/hgrc.5.txt --- a/doc/hgrc.5.txt Mon May 31 21:37:01 2010 +0200 +++ b/doc/hgrc.5.txt Mon May 31 21:43:03 2010 +0200 @@ -146,15 +146,29 @@ current configuration file. The inclusion is recursive, which means that included files can include other files. Filenames are relative to the configuration file in which the ``%include`` directive is found. +Environment variables and ``~user`` constructs are expanded in +``file``. This lets you do something like:: + + %include ~/.hgrc.d/$HOST.rc + +to include a different configuration file on each computer you use. A line with ``%unset name`` will remove ``name`` from the current section, if it has been set previously. The values are either free-form text strings, lists of text strings, -or Boolean values. Lists are split on whitespace and commas. Boolean -values can be set to true using any of "1", "yes", "true", or "on" and -to false using "0", "no", "false", or "off" (all case insensitive). +or Boolean values. Boolean values can be set to true using any of "1", +"yes", "true", or "on" and to false using "0", "no", "false", or "off" +(all case insensitive). +List values are separated by whitespace or comma, except when values are +placed in double quotation marks:: + + allow_read = "John Doe, PhD", brian, betty + +Quotation marks can be escaped by prefixing them with a backslash. Only +quotation marks at the beginning of a word is counted as a quotation +(e.g., ``foo"bar baz`` is the list of ``foo"bar`` and ``baz``). Sections -------- @@ -308,8 +322,8 @@ Use the ``[defaults]`` section to define command defaults, i.e. the default options/arguments to pass to the specified commands. -The following example makes ``hg log`` run in verbose mode, and ``hg -status`` show only the modified files, by default:: +The following example makes :hg:`log` run in verbose mode, and :hg:`hg +status` show only the modified files, by default:: [defaults] log = -v @@ -486,7 +500,8 @@ Default: ``$local $base $other`` ``premerge`` Attempt to run internal non-interactive 3-way merge tool before - launching external tool. + launching external tool. Options are ``true``, ``false``, or ``keep`` + to leave markers in the file if the premerge fails. Default: True ``binary`` This tool can merge binary files. Defaults to False, unless tool @@ -494,13 +509,21 @@ ``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. +``check`` + A list of merge success-checking options: + + ``changed`` + Ask whether merge was successful when the merged file shows no changes. + ``conflicts`` + Check whether there are conflicts even though the tool reported success. + ``prompt`` + Always prompt for merge success, regardless of success reported by tool. + +``checkchanged`` + True is equivalent to ``check = changed``. Default: False -``checkchanged`` - Check whether outputs were written even though the tool reported - success. +``checkconflicts`` + True is equivalent to ``check = conflicts``. Default: False ``fixeol`` Attempt to fix up EOL changes caused by the merge tool. @@ -797,7 +820,7 @@ ``archivemeta`` Whether to include the .hg_archival.txt file containing meta data (hashes for the repository base and for tip) in archives created - by the hg archive command or downloaded via hgweb. + by the :hg:`archive` command or downloaded via hgweb. Default is True. ``askusername`` Whether to prompt for a username when committing. If True, and @@ -876,7 +899,8 @@ ``. 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). + ``username =`` in the system hgrc). Environment variables in the + username are expanded. ``verbose`` Increase the amount of output printed. True or False. Default is False. @@ -907,20 +931,18 @@ 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 deny_push - list. + be present in this list. The contents of the allow_push list are + examined after the deny_push list. ``allow_read`` If the user has not already been denied repository access due to the contents of deny_read, this list determines whether to grant repository access to the user. If this list is not empty, and the - user is unauthenticated or not present in the list (separated by - whitespace or ``,``), then access is denied for the user. If the - list is empty or not set, then access is permitted to all users by - default. Setting allow_read to the special value ``*`` is equivalent - to it not being set (i.e. access is permitted to all users). The - contents of the allow_read list are examined after the deny_read - list. + user is unauthenticated or not present in the list, then access is + denied for the user. If the list is empty or not set, then access + is permitted to all users by default. Setting allow_read to the + special value ``*`` is equivalent to it not being set (i.e. access + is permitted to all users). The contents of the allow_read list are + examined after the deny_read list. ``allowzip`` (DEPRECATED) Whether to allow .zip downloading of repository revisions. Default is False. This feature creates temporary files. @@ -955,17 +977,15 @@ 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 - list are examined before the allow_push list. + any authenticated user name present in this list is also denied. The + contents of the deny_push list are examined before the allow_push list. ``deny_read`` Whether to deny reading/viewing of the repository. If this list is not empty, unauthenticated users are all denied, and any - authenticated user name present in this list (separated by - whitespace or ``,``) is also denied access to the repository. If set - to the special value ``*``, all remote users are denied access - (rarely needed ;). If deny_read is empty or not set, the - determination of repository access depends on the presence and + authenticated user name present in this list is also denied access to + the repository. If set to the special value ``*``, all remote users + are denied access (rarely needed ;). If deny_read is empty or not set, + the determination of repository access depends on the presence and content of the allow_read list (see description). If both deny_read and allow_read are empty or not set, then access is permitted to all users by default. If the repository is being @@ -981,7 +1001,7 @@ Textual description of the repository's purpose or contents. Default is "unknown". ``encoding`` - Character encoding name. + Character encoding name. Default is the current locale charset. Example: "UTF-8" ``errorlog`` Where to output the error log. Default is stderr. diff -r 0bb67503ad4b -r ac6fec2af8c8 doc/manpage.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/doc/manpage.py Mon May 31 21:43:03 2010 +0200 @@ -0,0 +1,1102 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +# $Id: manpage.py 6110 2009-08-31 14:40:33Z grubert $ +# Author: Engelbert Gruber +# Copyright: This module is put into the public domain. + +""" +Simple man page writer for reStructuredText. + +Man pages (short for "manual pages") contain system documentation on unix-like +systems. The pages are grouped in numbered sections: + + 1 executable programs and shell commands + 2 system calls + 3 library functions + 4 special files + 5 file formats + 6 games + 7 miscellaneous + 8 system administration + +Man pages are written *troff*, a text file formatting system. + +See http://www.tldp.org/HOWTO/Man-Page for a start. + +Man pages have no subsection only parts. +Standard parts + + NAME , + SYNOPSIS , + DESCRIPTION , + OPTIONS , + FILES , + SEE ALSO , + BUGS , + +and + + AUTHOR . + +A unix-like system keeps an index of the DESCRIPTIONs, which is accesable +by the command whatis or apropos. + +""" + +__docformat__ = 'reStructuredText' + +import re + +from docutils import nodes, writers, languages +import roman + +FIELD_LIST_INDENT = 7 +DEFINITION_LIST_INDENT = 7 +OPTION_LIST_INDENT = 7 +BLOCKQOUTE_INDENT = 3.5 + +# Define two macros so man/roff can calculate the +# indent/unindent margins by itself +MACRO_DEF = (r""". +.nr rst2man-indent-level 0 +. +.de1 rstReportMargin +\\$1 \\n[an-margin] +level \\n[rst2man-indent-level] +level margin: \\n[rst2man-indent\\n[rst2man-indent-level]] +- +\\n[rst2man-indent0] +\\n[rst2man-indent1] +\\n[rst2man-indent2] +.. +.de1 INDENT +.\" .rstReportMargin pre: +. RS \\$1 +. nr rst2man-indent\\n[rst2man-indent-level] \\n[an-margin] +. nr rst2man-indent-level +1 +.\" .rstReportMargin post: +.. +.de UNINDENT +. RE +.\" indent \\n[an-margin] +.\" old: \\n[rst2man-indent\\n[rst2man-indent-level]] +.nr rst2man-indent-level -1 +.\" new: \\n[rst2man-indent\\n[rst2man-indent-level]] +.in \\n[rst2man-indent\\n[rst2man-indent-level]]u +.. +""") + +class Writer(writers.Writer): + + supported = ('manpage') + """Formats this writer supports.""" + + output = None + """Final translated form of `document`.""" + + def __init__(self): + writers.Writer.__init__(self) + self.translator_class = Translator + + def translate(self): + visitor = self.translator_class(self.document) + self.document.walkabout(visitor) + self.output = visitor.astext() + + +class Table: + def __init__(self): + self._rows = [] + self._options = ['center'] + self._tab_char = '\t' + self._coldefs = [] + def new_row(self): + self._rows.append([]) + def append_separator(self, separator): + """Append the separator for table head.""" + self._rows.append([separator]) + def append_cell(self, cell_lines): + """cell_lines is an array of lines""" + start = 0 + if len(cell_lines) > 0 and cell_lines[0] == '.sp\n': + start = 1 + self._rows[-1].append(cell_lines[start:]) + if len(self._coldefs) < len(self._rows[-1]): + self._coldefs.append('l') + def _minimize_cell(self, cell_lines): + """Remove leading and trailing blank and ``.sp`` lines""" + while (cell_lines and cell_lines[0] in ('\n', '.sp\n')): + del cell_lines[0] + while (cell_lines and cell_lines[-1] in ('\n', '.sp\n')): + del cell_lines[-1] + def as_list(self): + text = ['.TS\n'] + text.append(' '.join(self._options) + ';\n') + text.append('|%s|.\n' % ('|'.join(self._coldefs))) + for row in self._rows: + # row = array of cells. cell = array of lines. + text.append('_\n') # line above + text.append('T{\n') + for i in range(len(row)): + cell = row[i] + self._minimize_cell(cell) + text.extend(cell) + if not text[-1].endswith('\n'): + text[-1] += '\n' + if i < len(row)-1: + text.append('T}'+self._tab_char+'T{\n') + else: + text.append('T}\n') + text.append('_\n') + text.append('.TE\n') + return text + +class Translator(nodes.NodeVisitor): + """""" + + words_and_spaces = re.compile(r'\S+| +|\n') + document_start = """Man page generated from reStructeredText.""" + + def __init__(self, document): + nodes.NodeVisitor.__init__(self, document) + self.settings = settings = document.settings + lcode = settings.language_code + self.language = languages.get_language(lcode) + self.head = [] + self.body = [] + self.foot = [] + self.section_level = 0 + self.context = [] + self.topic_class = '' + self.colspecs = [] + self.compact_p = 1 + self.compact_simple = None + # the list style "*" bullet or "#" numbered + self._list_char = [] + # writing the header .TH and .SH NAME is postboned after + # docinfo. + self._docinfo = { + "title" : "", "title_upper": "", + "subtitle" : "", + "manual_section" : "", "manual_group" : "", + "author" : [], + "date" : "", + "copyright" : "", + "version" : "", + } + self._docinfo_keys = [] # a list to keep the sequence as in source. + self._docinfo_names = {} # to get name from text not normalized. + self._in_docinfo = None + self._active_table = None + self._in_literal = False + self.header_written = 0 + self._line_block = 0 + self.authors = [] + self.section_level = 0 + self._indent = [0] + # central definition of simple processing rules + # what to output on : visit, depart + # Do not use paragraph requests ``.PP`` because these set indentation. + # use ``.sp``. Remove superfluous ``.sp`` in ``astext``. + # + # Fonts are put on a stack, the top one is used. + # ``.ft P`` or ``\\fP`` pop from stack. + # ``B`` bold, ``I`` italic, ``R`` roman should be available. + # Hopefully ``C`` courier too. + self.defs = { + 'indent' : ('.INDENT %.1f\n', '.UNINDENT\n'), + 'definition_list_item' : ('.TP', ''), + 'field_name' : ('.TP\n.B ', '\n'), + 'literal' : ('\\fB', '\\fP'), + 'literal_block' : ('.sp\n.nf\n.ft C\n', '\n.ft P\n.fi\n'), + + 'option_list_item' : ('.TP\n', ''), + + 'reference' : (r'\%', r'\:'), + 'emphasis': ('\\fI', '\\fP'), + 'strong' : ('\\fB', '\\fP'), + 'term' : ('\n.B ', '\n'), + 'title_reference' : ('\\fI', '\\fP'), + + 'topic-title' : ('.SS ',), + 'sidebar-title' : ('.SS ',), + + 'problematic' : ('\n.nf\n', '\n.fi\n'), + } + # NOTE don't specify the newline before a dot-command, but ensure + # it is there. + + def comment_begin(self, text): + """Return commented version of the passed text WITHOUT end of + line/comment.""" + prefix = '.\\" ' + out_text = ''.join( + [(prefix + in_line + '\n') + for in_line in text.split('\n')]) + return out_text + + def comment(self, text): + """Return commented version of the passed text.""" + return self.comment_begin(text)+'.\n' + + def ensure_eol(self): + """Ensure the last line in body is terminated by new line.""" + if self.body[-1][-1] != '\n': + self.body.append('\n') + + def astext(self): + """Return the final formatted document as a string.""" + if not self.header_written: + # ensure we get a ".TH" as viewers require it. + self.head.append(self.header()) + # filter body + for i in xrange(len(self.body)-1, 0, -1): + # remove superfluous vertical gaps. + if self.body[i] == '.sp\n': + if self.body[i - 1][:4] in ('.BI ','.IP '): + self.body[i] = '.\n' + elif (self.body[i - 1][:3] == '.B ' and + self.body[i - 2][:4] == '.TP\n'): + self.body[i] = '.\n' + elif (self.body[i - 1] == '\n' and + self.body[i - 2][0] != '.' and + (self.body[i - 3][:7] == '.TP\n.B ' + or self.body[i - 3][:4] == '\n.B ') + ): + self.body[i] = '.\n' + return ''.join(self.head + self.body + self.foot) + + def deunicode(self, text): + text = text.replace(u'\xa0', '\\ ') + text = text.replace(u'\u2020', '\\(dg') + return text + + def visit_Text(self, node): + text = node.astext() + text = text.replace('\\','\\e') + replace_pairs = [ + (u'-', ur'\-'), + (u'\'', ur'\(aq'), + (u'´', ur'\''), + (u'`', ur'\(ga'), + ] + for (in_char, out_markup) in replace_pairs: + text = text.replace(in_char, out_markup) + # unicode + text = self.deunicode(text) + if self._in_literal: + # prevent interpretation of "." at line start + if text[0] == '.': + text = '\\&' + text + text = text.replace('\n.', '\n\\&.') + self.body.append(text) + + def depart_Text(self, node): + pass + + def list_start(self, node): + class enum_char: + enum_style = { + 'bullet' : '\\(bu', + 'emdash' : '\\(em', + } + + def __init__(self, style): + self._style = style + if node.has_key('start'): + self._cnt = node['start'] - 1 + else: + self._cnt = 0 + self._indent = 2 + if style == 'arabic': + # indentation depends on number of childrens + # and start value. + self._indent = len(str(len(node.children))) + self._indent += len(str(self._cnt)) + 1 + elif style == 'loweralpha': + self._cnt += ord('a') - 1 + self._indent = 3 + elif style == 'upperalpha': + self._cnt += ord('A') - 1 + self._indent = 3 + elif style.endswith('roman'): + self._indent = 5 + + def next(self): + if self._style == 'bullet': + return self.enum_style[self._style] + elif self._style == 'emdash': + return self.enum_style[self._style] + self._cnt += 1 + # TODO add prefix postfix + if self._style == 'arabic': + return "%d." % self._cnt + elif self._style in ('loweralpha', 'upperalpha'): + return "%c." % self._cnt + elif self._style.endswith('roman'): + res = roman.toRoman(self._cnt) + '.' + if self._style.startswith('upper'): + return res.upper() + return res.lower() + else: + return "%d." % self._cnt + def get_width(self): + return self._indent + def __repr__(self): + return 'enum_style-%s' % list(self._style) + + if node.has_key('enumtype'): + self._list_char.append(enum_char(node['enumtype'])) + else: + self._list_char.append(enum_char('bullet')) + if len(self._list_char) > 1: + # indent nested lists + self.indent(self._list_char[-2].get_width()) + else: + self.indent(self._list_char[-1].get_width()) + + def list_end(self): + self.dedent() + self._list_char.pop() + + def header(self): + tmpl = (".TH %(title_upper)s %(manual_section)s" + " \"%(date)s\" \"%(version)s\" \"%(manual_group)s\"\n" + ".SH NAME\n" + "%(title)s \- %(subtitle)s\n") + return tmpl % self._docinfo + + def append_header(self): + """append header with .TH and .SH NAME""" + # NOTE before everything + # .TH title_upper section date source manual + if self.header_written: + return + self.body.append(self.header()) + self.body.append(MACRO_DEF) + self.header_written = 1 + + def visit_address(self, node): + self.visit_docinfo_item(node, 'address') + + def depart_address(self, node): + pass + + def visit_admonition(self, node, name=None): + if name: + self.body.append('.IP %s\n' % + self.language.labels.get(name, name)) + + def depart_admonition(self, node): + self.body.append('.RE\n') + + def visit_attention(self, node): + self.visit_admonition(node, 'attention') + + depart_attention = depart_admonition + + def visit_docinfo_item(self, node, name): + if name == 'author': + self._docinfo[name].append(node.astext()) + else: + self._docinfo[name] = node.astext() + self._docinfo_keys.append(name) + raise nodes.SkipNode + + def depart_docinfo_item(self, node): + pass + + def visit_author(self, node): + self.visit_docinfo_item(node, 'author') + + depart_author = depart_docinfo_item + + def visit_authors(self, node): + # _author is called anyway. + pass + + def depart_authors(self, node): + pass + + def visit_block_quote(self, node): + # BUG/HACK: indent alway uses the _last_ indention, + # thus we need two of them. + self.indent(BLOCKQOUTE_INDENT) + self.indent(0) + + def depart_block_quote(self, node): + self.dedent() + self.dedent() + + def visit_bullet_list(self, node): + self.list_start(node) + + def depart_bullet_list(self, node): + self.list_end() + + def visit_caption(self, node): + pass + + def depart_caption(self, node): + pass + + def visit_caution(self, node): + self.visit_admonition(node, 'caution') + + depart_caution = depart_admonition + + def visit_citation(self, node): + num, text = node.astext().split(None, 1) + num = num.strip() + self.body.append('.IP [%s] 5\n' % num) + + def depart_citation(self, node): + pass + + def visit_citation_reference(self, node): + self.body.append('['+node.astext()+']') + raise nodes.SkipNode + + def visit_classifier(self, node): + pass + + def depart_classifier(self, node): + pass + + def visit_colspec(self, node): + self.colspecs.append(node) + + def depart_colspec(self, node): + pass + + def write_colspecs(self): + self.body.append("%s.\n" % ('L '*len(self.colspecs))) + + def visit_comment(self, node, + sub=re.compile('-(?=-)').sub): + self.body.append(self.comment(node.astext())) + raise nodes.SkipNode + + def visit_contact(self, node): + self.visit_docinfo_item(node, 'contact') + + depart_contact = depart_docinfo_item + + def visit_container(self, node): + pass + + def depart_container(self, node): + pass + + def visit_compound(self, node): + pass + + def depart_compound(self, node): + pass + + def visit_copyright(self, node): + self.visit_docinfo_item(node, 'copyright') + + def visit_danger(self, node): + self.visit_admonition(node, 'danger') + + depart_danger = depart_admonition + + def visit_date(self, node): + self.visit_docinfo_item(node, 'date') + + def visit_decoration(self, node): + pass + + def depart_decoration(self, node): + pass + + def visit_definition(self, node): + pass + + def depart_definition(self, node): + pass + + def visit_definition_list(self, node): + self.indent(DEFINITION_LIST_INDENT) + + def depart_definition_list(self, node): + self.dedent() + + def visit_definition_list_item(self, node): + self.body.append(self.defs['definition_list_item'][0]) + + def depart_definition_list_item(self, node): + self.body.append(self.defs['definition_list_item'][1]) + + def visit_description(self, node): + pass + + def depart_description(self, node): + pass + + def visit_docinfo(self, node): + self._in_docinfo = 1 + + def depart_docinfo(self, node): + self._in_docinfo = None + # NOTE nothing should be written before this + self.append_header() + + def visit_doctest_block(self, node): + self.body.append(self.defs['literal_block'][0]) + self._in_literal = True + + def depart_doctest_block(self, node): + self._in_literal = False + self.body.append(self.defs['literal_block'][1]) + + def visit_document(self, node): + # no blank line between comment and header. + self.body.append(self.comment(self.document_start).rstrip()+'\n') + # writing header is postboned + self.header_written = 0 + + def depart_document(self, node): + if self._docinfo['author']: + self.body.append('.SH AUTHOR\n%s\n' + % ', '.join(self._docinfo['author'])) + skip = ('author', 'copyright', 'date', + 'manual_group', 'manual_section', + 'subtitle', + 'title', 'title_upper', 'version') + for name in self._docinfo_keys: + if name == 'address': + self.body.append("\n%s:\n%s%s.nf\n%s\n.fi\n%s%s" % ( + self.language.labels.get(name, name), + self.defs['indent'][0] % 0, + self.defs['indent'][0] % BLOCKQOUTE_INDENT, + self._docinfo[name], + self.defs['indent'][1], + self.defs['indent'][1])) + elif not name in skip: + if name in self._docinfo_names: + label = self._docinfo_names[name] + else: + label = self.language.labels.get(name, name) + self.body.append("\n%s: %s\n" % (label, self._docinfo[name])) + if self._docinfo['copyright']: + self.body.append('.SH COPYRIGHT\n%s\n' + % self._docinfo['copyright']) + self.body.append(self.comment( + 'Generated by docutils manpage writer.\n')) + + def visit_emphasis(self, node): + self.body.append(self.defs['emphasis'][0]) + + def depart_emphasis(self, node): + self.body.append(self.defs['emphasis'][1]) + + def visit_entry(self, node): + # a cell in a table row + if 'morerows' in node: + self.document.reporter.warning('"table row spanning" not supported', + base_node=node) + if 'morecols' in node: + self.document.reporter.warning( + '"table cell spanning" not supported', base_node=node) + self.context.append(len(self.body)) + + def depart_entry(self, node): + start = self.context.pop() + self._active_table.append_cell(self.body[start:]) + del self.body[start:] + + def visit_enumerated_list(self, node): + self.list_start(node) + + def depart_enumerated_list(self, node): + self.list_end() + + def visit_error(self, node): + self.visit_admonition(node, 'error') + + depart_error = depart_admonition + + def visit_field(self, node): + pass + + def depart_field(self, node): + pass + + def visit_field_body(self, node): + if self._in_docinfo: + name_normalized = self._field_name.lower().replace(" ","_") + self._docinfo_names[name_normalized] = self._field_name + self.visit_docinfo_item(node, name_normalized) + raise nodes.SkipNode + + def depart_field_body(self, node): + pass + + def visit_field_list(self, node): + self.indent(FIELD_LIST_INDENT) + + def depart_field_list(self, node): + self.dedent() + + def visit_field_name(self, node): + if self._in_docinfo: + self._field_name = node.astext() + raise nodes.SkipNode + else: + self.body.append(self.defs['field_name'][0]) + + def depart_field_name(self, node): + self.body.append(self.defs['field_name'][1]) + + def visit_figure(self, node): + self.indent(2.5) + self.indent(0) + + def depart_figure(self, node): + self.dedent() + self.dedent() + + def visit_footer(self, node): + self.document.reporter.warning('"footer" not supported', + base_node=node) + + def depart_footer(self, node): + pass + + def visit_footnote(self, node): + num, text = node.astext().split(None, 1) + num = num.strip() + self.body.append('.IP [%s] 5\n' % self.deunicode(num)) + + def depart_footnote(self, node): + pass + + def footnote_backrefs(self, node): + self.document.reporter.warning('"footnote_backrefs" not supported', + base_node=node) + + def visit_footnote_reference(self, node): + self.body.append('['+self.deunicode(node.astext())+']') + raise nodes.SkipNode + + def depart_footnote_reference(self, node): + pass + + def visit_generated(self, node): + pass + + def depart_generated(self, node): + pass + + def visit_header(self, node): + raise NotImplementedError, node.astext() + + def depart_header(self, node): + pass + + def visit_hint(self, node): + self.visit_admonition(node, 'hint') + + depart_hint = depart_admonition + + def visit_subscript(self, node): + self.body.append('\\s-2\\d') + + def depart_subscript(self, node): + self.body.append('\\u\\s0') + + def visit_superscript(self, node): + self.body.append('\\s-2\\u') + + def depart_superscript(self, node): + self.body.append('\\d\\s0') + + def visit_attribution(self, node): + self.body.append('\\(em ') + + def depart_attribution(self, node): + self.body.append('\n') + + def visit_image(self, node): + self.document.reporter.warning('"image" not supported', + base_node=node) + text = [] + if 'alt' in node.attributes: + text.append(node.attributes['alt']) + if 'uri' in node.attributes: + text.append(node.attributes['uri']) + self.body.append('[image: %s]\n' % ('/'.join(text))) + raise nodes.SkipNode + + def visit_important(self, node): + self.visit_admonition(node, 'important') + + depart_important = depart_admonition + + def visit_label(self, node): + # footnote and citation + if (isinstance(node.parent, nodes.footnote) + or isinstance(node.parent, nodes.citation)): + raise nodes.SkipNode + self.document.reporter.warning('"unsupported "label"', + base_node=node) + self.body.append('[') + + def depart_label(self, node): + self.body.append(']\n') + + def visit_legend(self, node): + pass + + def depart_legend(self, node): + pass + + # WHAT should we use .INDENT, .UNINDENT ? + def visit_line_block(self, node): + self._line_block += 1 + if self._line_block == 1: + self.body.append('.sp\n') + self.body.append('.nf\n') + else: + self.body.append('.in +2\n') + + def depart_line_block(self, node): + self._line_block -= 1 + if self._line_block == 0: + self.body.append('.fi\n') + self.body.append('.sp\n') + else: + self.body.append('.in -2\n') + + def visit_line(self, node): + pass + + def depart_line(self, node): + self.body.append('\n') + + def visit_list_item(self, node): + # man 7 man argues to use ".IP" instead of ".TP" + self.body.append('.IP %s %d\n' % ( + self._list_char[-1].next(), + self._list_char[-1].get_width(),)) + + def depart_list_item(self, node): + pass + + def visit_literal(self, node): + self.body.append(self.defs['literal'][0]) + + def depart_literal(self, node): + self.body.append(self.defs['literal'][1]) + + def visit_literal_block(self, node): + self.body.append(self.defs['literal_block'][0]) + self._in_literal = True + + def depart_literal_block(self, node): + self._in_literal = False + self.body.append(self.defs['literal_block'][1]) + + def visit_meta(self, node): + raise NotImplementedError, node.astext() + + def depart_meta(self, node): + pass + + def visit_note(self, node): + self.visit_admonition(node, 'note') + + depart_note = depart_admonition + + def indent(self, by=0.5): + # if we are in a section ".SH" there already is a .RS + step = self._indent[-1] + self._indent.append(by) + self.body.append(self.defs['indent'][0] % step) + + def dedent(self): + self._indent.pop() + self.body.append(self.defs['indent'][1]) + + def visit_option_list(self, node): + self.indent(OPTION_LIST_INDENT) + + def depart_option_list(self, node): + self.dedent() + + def visit_option_list_item(self, node): + # one item of the list + self.body.append(self.defs['option_list_item'][0]) + + def depart_option_list_item(self, node): + self.body.append(self.defs['option_list_item'][1]) + + def visit_option_group(self, node): + # as one option could have several forms it is a group + # options without parameter bold only, .B, -v + # options with parameter bold italic, .BI, -f file + # + # we do not know if .B or .BI + self.context.append('.B') # blind guess + self.context.append(len(self.body)) # to be able to insert later + self.context.append(0) # option counter + + def depart_option_group(self, node): + self.context.pop() # the counter + start_position = self.context.pop() + text = self.body[start_position:] + del self.body[start_position:] + self.body.append('%s%s\n' % (self.context.pop(), ''.join(text))) + + def visit_option(self, node): + # each form of the option will be presented separately + if self.context[-1] > 0: + self.body.append(', ') + if self.context[-3] == '.BI': + self.body.append('\\') + self.body.append(' ') + + def depart_option(self, node): + self.context[-1] += 1 + + def visit_option_string(self, node): + # do not know if .B or .BI + pass + + def depart_option_string(self, node): + pass + + def visit_option_argument(self, node): + self.context[-3] = '.BI' # bold/italic alternate + if node['delimiter'] != ' ': + self.body.append('\\fB%s ' % node['delimiter']) + elif self.body[len(self.body)-1].endswith('='): + # a blank only means no blank in output, just changing font + self.body.append(' ') + else: + # blank backslash blank, switch font then a blank + self.body.append(' \\ ') + + def depart_option_argument(self, node): + pass + + def visit_organization(self, node): + self.visit_docinfo_item(node, 'organization') + + def depart_organization(self, node): + pass + + def visit_paragraph(self, node): + # ``.PP`` : Start standard indented paragraph. + # ``.LP`` : Start block paragraph, all except the first. + # ``.P [type]`` : Start paragraph type. + # NOTE dont use paragraph starts because they reset indentation. + # ``.sp`` is only vertical space + self.ensure_eol() + self.body.append('.sp\n') + + def depart_paragraph(self, node): + self.body.append('\n') + + def visit_problematic(self, node): + self.body.append(self.defs['problematic'][0]) + + def depart_problematic(self, node): + self.body.append(self.defs['problematic'][1]) + + def visit_raw(self, node): + if node.get('format') == 'manpage': + self.body.append(node.astext() + "\n") + # Keep non-manpage raw text out of output: + raise nodes.SkipNode + + def visit_reference(self, node): + """E.g. link or email address.""" + self.body.append(self.defs['reference'][0]) + + def depart_reference(self, node): + self.body.append(self.defs['reference'][1]) + + def visit_revision(self, node): + self.visit_docinfo_item(node, 'revision') + + depart_revision = depart_docinfo_item + + def visit_row(self, node): + self._active_table.new_row() + + def depart_row(self, node): + pass + + def visit_section(self, node): + self.section_level += 1 + + def depart_section(self, node): + self.section_level -= 1 + + def visit_status(self, node): + self.visit_docinfo_item(node, 'status') + + depart_status = depart_docinfo_item + + def visit_strong(self, node): + self.body.append(self.defs['strong'][0]) + + def depart_strong(self, node): + self.body.append(self.defs['strong'][1]) + + def visit_substitution_definition(self, node): + """Internal only.""" + raise nodes.SkipNode + + def visit_substitution_reference(self, node): + self.document.reporter.warning('"substitution_reference" not supported', + base_node=node) + + def visit_subtitle(self, node): + if isinstance(node.parent, nodes.sidebar): + self.body.append(self.defs['strong'][0]) + elif isinstance(node.parent, nodes.document): + self.visit_docinfo_item(node, 'subtitle') + elif isinstance(node.parent, nodes.section): + self.body.append(self.defs['strong'][0]) + + def depart_subtitle(self, node): + # document subtitle calls SkipNode + self.body.append(self.defs['strong'][1]+'\n.PP\n') + + def visit_system_message(self, node): + # TODO add report_level + #if node['level'] < self.document.reporter['writer'].report_level: + # Level is too low to display: + # raise nodes.SkipNode + attr = {} + backref_text = '' + if node.hasattr('id'): + attr['name'] = node['id'] + if node.hasattr('line'): + line = ', line %s' % node['line'] + else: + line = '' + self.body.append('.IP "System Message: %s/%s (%s:%s)"\n' + % (node['type'], node['level'], node['source'], line)) + + def depart_system_message(self, node): + pass + + def visit_table(self, node): + self._active_table = Table() + + def depart_table(self, node): + self.ensure_eol() + self.body.extend(self._active_table.as_list()) + self._active_table = None + + def visit_target(self, node): + # targets are in-document hyper targets, without any use for man-pages. + raise nodes.SkipNode + + def visit_tbody(self, node): + pass + + def depart_tbody(self, node): + pass + + def visit_term(self, node): + self.body.append(self.defs['term'][0]) + + def depart_term(self, node): + self.body.append(self.defs['term'][1]) + + def visit_tgroup(self, node): + pass + + def depart_tgroup(self, node): + pass + + def visit_thead(self, node): + # MAYBE double line '=' + pass + + def depart_thead(self, node): + # MAYBE double line '=' + pass + + def visit_tip(self, node): + self.visit_admonition(node, 'tip') + + depart_tip = depart_admonition + + def visit_title(self, node): + if isinstance(node.parent, nodes.topic): + self.body.append(self.defs['topic-title'][0]) + elif isinstance(node.parent, nodes.sidebar): + self.body.append(self.defs['sidebar-title'][0]) + elif isinstance(node.parent, nodes.admonition): + self.body.append('.IP "') + elif self.section_level == 0: + self._docinfo['title'] = node.astext() + # document title for .TH + self._docinfo['title_upper'] = node.astext().upper() + raise nodes.SkipNode + elif self.section_level == 1: + self.body.append('.SH ') + for n in node.traverse(nodes.Text): + n.parent.replace(n, nodes.Text(n.astext().upper())) + else: + self.body.append('.SS ') + + def depart_title(self, node): + if isinstance(node.parent, nodes.admonition): + self.body.append('"') + self.body.append('\n') + + def visit_title_reference(self, node): + """inline citation reference""" + self.body.append(self.defs['title_reference'][0]) + + def depart_title_reference(self, node): + self.body.append(self.defs['title_reference'][1]) + + def visit_topic(self, node): + pass + + def depart_topic(self, node): + pass + + def visit_sidebar(self, node): + pass + + def depart_sidebar(self, node): + pass + + def visit_rubric(self, node): + pass + + def depart_rubric(self, node): + pass + + def visit_transition(self, node): + # .PP Begin a new paragraph and reset prevailing indent. + # .sp N leaves N lines of blank space. + # .ce centers the next line + self.body.append('\n.sp\n.ce\n----\n') + + def depart_transition(self, node): + self.body.append('\n.ce 0\n.sp\n') + + def visit_version(self, node): + self.visit_docinfo_item(node, 'version') + + def visit_warning(self, node): + self.visit_admonition(node, 'warning') + + depart_warning = depart_admonition + + def unimplemented_visit(self, node): + raise NotImplementedError('visiting unimplemented node type: %s' + % node.__class__.__name__) + +# vim: set fileencoding=utf-8 et ts=4 ai : diff -r 0bb67503ad4b -r ac6fec2af8c8 doc/rst2man.py --- a/doc/rst2man.py Mon May 31 21:37:01 2010 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,1114 +0,0 @@ -#!/usr/bin/env python -# -*- coding: utf-8 -*- -# $Id: manpage.py 6110 2009-08-31 14:40:33Z grubert $ -# Author: Engelbert Gruber -# Copyright: This module is put into the public domain. - -""" -Simple man page writer for reStructuredText. - -Man pages (short for "manual pages") contain system documentation on unix-like -systems. The pages are grouped in numbered sections: - - 1 executable programs and shell commands - 2 system calls - 3 library functions - 4 special files - 5 file formats - 6 games - 7 miscellaneous - 8 system administration - -Man pages are written *troff*, a text file formatting system. - -See http://www.tldp.org/HOWTO/Man-Page for a start. - -Man pages have no subsection only parts. -Standard parts - - NAME , - SYNOPSIS , - DESCRIPTION , - OPTIONS , - FILES , - SEE ALSO , - BUGS , - -and - - AUTHOR . - -A unix-like system keeps an index of the DESCRIPTIONs, which is accesable -by the command whatis or apropos. - -""" - -__docformat__ = 'reStructuredText' - -import sys -import os -import time -import re -from types import ListType - -import docutils -from docutils import nodes, utils, writers, languages -import roman - -FIELD_LIST_INDENT = 7 -DEFINITION_LIST_INDENT = 7 -OPTION_LIST_INDENT = 7 -BLOCKQOUTE_INDENT = 3.5 - -# Define two macros so man/roff can calculate the -# indent/unindent margins by itself -MACRO_DEF = (r""". -.nr rst2man-indent-level 0 -. -.de1 rstReportMargin -\\$1 \\n[an-margin] -level \\n[rst2man-indent-level] -level margin: \\n[rst2man-indent\\n[rst2man-indent-level]] -- -\\n[rst2man-indent0] -\\n[rst2man-indent1] -\\n[rst2man-indent2] -.. -.de1 INDENT -.\" .rstReportMargin pre: -. RS \\$1 -. nr rst2man-indent\\n[rst2man-indent-level] \\n[an-margin] -. nr rst2man-indent-level +1 -.\" .rstReportMargin post: -.. -.de UNINDENT -. RE -.\" indent \\n[an-margin] -.\" old: \\n[rst2man-indent\\n[rst2man-indent-level]] -.nr rst2man-indent-level -1 -.\" new: \\n[rst2man-indent\\n[rst2man-indent-level]] -.in \\n[rst2man-indent\\n[rst2man-indent-level]]u -.. -""") - -class Writer(writers.Writer): - - supported = ('manpage') - """Formats this writer supports.""" - - output = None - """Final translated form of `document`.""" - - def __init__(self): - writers.Writer.__init__(self) - self.translator_class = Translator - - def translate(self): - visitor = self.translator_class(self.document) - self.document.walkabout(visitor) - self.output = visitor.astext() - - -class Table: - def __init__(self): - self._rows = [] - self._options = ['center'] - self._tab_char = '\t' - self._coldefs = [] - def new_row(self): - self._rows.append([]) - def append_separator(self, separator): - """Append the separator for table head.""" - self._rows.append([separator]) - def append_cell(self, cell_lines): - """cell_lines is an array of lines""" - start = 0 - if len(cell_lines) > 0 and cell_lines[0] == '.sp\n': - start = 1 - self._rows[-1].append(cell_lines[start:]) - if len(self._coldefs) < len(self._rows[-1]): - self._coldefs.append('l') - def _minimize_cell(self, cell_lines): - """Remove leading and trailing blank and ``.sp`` lines""" - while (cell_lines and cell_lines[0] in ('\n', '.sp\n')): - del cell_lines[0] - while (cell_lines and cell_lines[-1] in ('\n', '.sp\n')): - del cell_lines[-1] - def as_list(self): - text = ['.TS\n'] - text.append(' '.join(self._options) + ';\n') - text.append('|%s|.\n' % ('|'.join(self._coldefs))) - for row in self._rows: - # row = array of cells. cell = array of lines. - text.append('_\n') # line above - text.append('T{\n') - for i in range(len(row)): - cell = row[i] - self._minimize_cell(cell) - text.extend(cell) - if not text[-1].endswith('\n'): - text[-1] += '\n' - if i < len(row)-1: - text.append('T}'+self._tab_char+'T{\n') - else: - text.append('T}\n') - text.append('_\n') - text.append('.TE\n') - return text - -class Translator(nodes.NodeVisitor): - """""" - - words_and_spaces = re.compile(r'\S+| +|\n') - document_start = """Man page generated from reStructeredText.""" - - def __init__(self, document): - nodes.NodeVisitor.__init__(self, document) - self.settings = settings = document.settings - lcode = settings.language_code - self.language = languages.get_language(lcode) - self.head = [] - self.body = [] - self.foot = [] - self.section_level = 0 - self.context = [] - self.topic_class = '' - self.colspecs = [] - self.compact_p = 1 - self.compact_simple = None - # the list style "*" bullet or "#" numbered - self._list_char = [] - # writing the header .TH and .SH NAME is postboned after - # docinfo. - self._docinfo = { - "title" : "", "title_upper": "", - "subtitle" : "", - "manual_section" : "", "manual_group" : "", - "author" : [], - "date" : "", - "copyright" : "", - "version" : "", - } - self._docinfo_keys = [] # a list to keep the sequence as in source. - self._docinfo_names = {} # to get name from text not normalized. - self._in_docinfo = None - self._active_table = None - self._in_literal = False - self.header_written = 0 - self._line_block = 0 - self.authors = [] - self.section_level = 0 - self._indent = [0] - # central definition of simple processing rules - # what to output on : visit, depart - # Do not use paragraph requests ``.PP`` because these set indentation. - # use ``.sp``. Remove superfluous ``.sp`` in ``astext``. - # - # Fonts are put on a stack, the top one is used. - # ``.ft P`` or ``\\fP`` pop from stack. - # ``B`` bold, ``I`` italic, ``R`` roman should be available. - # Hopefully ``C`` courier too. - self.defs = { - 'indent' : ('.INDENT %.1f\n', '.UNINDENT\n'), - 'definition_list_item' : ('.TP', ''), - 'field_name' : ('.TP\n.B ', '\n'), - 'literal' : ('\\fB', '\\fP'), - 'literal_block' : ('.sp\n.nf\n.ft C\n', '\n.ft P\n.fi\n'), - - 'option_list_item' : ('.TP\n', ''), - - 'reference' : (r'\%', r'\:'), - 'emphasis': ('\\fI', '\\fP'), - 'strong' : ('\\fB', '\\fP'), - 'term' : ('\n.B ', '\n'), - 'title_reference' : ('\\fI', '\\fP'), - - 'topic-title' : ('.SS ',), - 'sidebar-title' : ('.SS ',), - - 'problematic' : ('\n.nf\n', '\n.fi\n'), - } - # NOTE don't specify the newline before a dot-command, but ensure - # it is there. - - def comment_begin(self, text): - """Return commented version of the passed text WITHOUT end of - line/comment.""" - prefix = '.\\" ' - out_text = ''.join( - [(prefix + in_line + '\n') - for in_line in text.split('\n')]) - return out_text - - def comment(self, text): - """Return commented version of the passed text.""" - return self.comment_begin(text)+'.\n' - - def ensure_eol(self): - """Ensure the last line in body is terminated by new line.""" - if self.body[-1][-1] != '\n': - self.body.append('\n') - - def astext(self): - """Return the final formatted document as a string.""" - if not self.header_written: - # ensure we get a ".TH" as viewers require it. - self.head.append(self.header()) - # filter body - for i in xrange(len(self.body)-1, 0, -1): - # remove superfluous vertical gaps. - if self.body[i] == '.sp\n': - if self.body[i - 1][:4] in ('.BI ','.IP '): - self.body[i] = '.\n' - elif (self.body[i - 1][:3] == '.B ' and - self.body[i - 2][:4] == '.TP\n'): - self.body[i] = '.\n' - elif (self.body[i - 1] == '\n' and - self.body[i - 2][0] != '.' and - (self.body[i - 3][:7] == '.TP\n.B ' - or self.body[i - 3][:4] == '\n.B ') - ): - self.body[i] = '.\n' - return ''.join(self.head + self.body + self.foot) - - def deunicode(self, text): - text = text.replace(u'\xa0', '\\ ') - text = text.replace(u'\u2020', '\\(dg') - return text - - def visit_Text(self, node): - text = node.astext() - text = text.replace('\\','\\e') - replace_pairs = [ - (u'-', ur'\-'), - (u'\'', ur'\(aq'), - (u'´', ur'\''), - (u'`', ur'\(ga'), - ] - for (in_char, out_markup) in replace_pairs: - text = text.replace(in_char, out_markup) - # unicode - text = self.deunicode(text) - if self._in_literal: - # prevent interpretation of "." at line start - if text[0] == '.': - text = '\\&' + text - text = text.replace('\n.', '\n\\&.') - self.body.append(text) - - def depart_Text(self, node): - pass - - def list_start(self, node): - class enum_char: - enum_style = { - 'bullet' : '\\(bu', - 'emdash' : '\\(em', - } - - def __init__(self, style): - self._style = style - if node.has_key('start'): - self._cnt = node['start'] - 1 - else: - self._cnt = 0 - self._indent = 2 - if style == 'arabic': - # indentation depends on number of childrens - # and start value. - self._indent = len(str(len(node.children))) - self._indent += len(str(self._cnt)) + 1 - elif style == 'loweralpha': - self._cnt += ord('a') - 1 - self._indent = 3 - elif style == 'upperalpha': - self._cnt += ord('A') - 1 - self._indent = 3 - elif style.endswith('roman'): - self._indent = 5 - - def next(self): - if self._style == 'bullet': - return self.enum_style[self._style] - elif self._style == 'emdash': - return self.enum_style[self._style] - self._cnt += 1 - # TODO add prefix postfix - if self._style == 'arabic': - return "%d." % self._cnt - elif self._style in ('loweralpha', 'upperalpha'): - return "%c." % self._cnt - elif self._style.endswith('roman'): - res = roman.toRoman(self._cnt) + '.' - if self._style.startswith('upper'): - return res.upper() - return res.lower() - else: - return "%d." % self._cnt - def get_width(self): - return self._indent - def __repr__(self): - return 'enum_style-%s' % list(self._style) - - if node.has_key('enumtype'): - self._list_char.append(enum_char(node['enumtype'])) - else: - self._list_char.append(enum_char('bullet')) - if len(self._list_char) > 1: - # indent nested lists - self.indent(self._list_char[-2].get_width()) - else: - self.indent(self._list_char[-1].get_width()) - - def list_end(self): - self.dedent() - self._list_char.pop() - - def header(self): - tmpl = (".TH %(title_upper)s %(manual_section)s" - " \"%(date)s\" \"%(version)s\" \"%(manual_group)s\"\n" - ".SH NAME\n" - "%(title)s \- %(subtitle)s\n") - return tmpl % self._docinfo - - def append_header(self): - """append header with .TH and .SH NAME""" - # NOTE before everything - # .TH title_upper section date source manual - if self.header_written: - return - self.body.append(self.header()) - self.body.append(MACRO_DEF) - self.header_written = 1 - - def visit_address(self, node): - self.visit_docinfo_item(node, 'address') - - def depart_address(self, node): - pass - - def visit_admonition(self, node, name=None): - if name: - self.body.append('.IP %s\n' % - self.language.labels.get(name, name)) - - def depart_admonition(self, node): - self.body.append('.RE\n') - - def visit_attention(self, node): - self.visit_admonition(node, 'attention') - - depart_attention = depart_admonition - - def visit_docinfo_item(self, node, name): - if name == 'author': - self._docinfo[name].append(node.astext()) - else: - self._docinfo[name] = node.astext() - self._docinfo_keys.append(name) - raise nodes.SkipNode - - def depart_docinfo_item(self, node): - pass - - def visit_author(self, node): - self.visit_docinfo_item(node, 'author') - - depart_author = depart_docinfo_item - - def visit_authors(self, node): - # _author is called anyway. - pass - - def depart_authors(self, node): - pass - - def visit_block_quote(self, node): - # BUG/HACK: indent alway uses the _last_ indention, - # thus we need two of them. - self.indent(BLOCKQOUTE_INDENT) - self.indent(0) - - def depart_block_quote(self, node): - self.dedent() - self.dedent() - - def visit_bullet_list(self, node): - self.list_start(node) - - def depart_bullet_list(self, node): - self.list_end() - - def visit_caption(self, node): - pass - - def depart_caption(self, node): - pass - - def visit_caution(self, node): - self.visit_admonition(node, 'caution') - - depart_caution = depart_admonition - - def visit_citation(self, node): - num, text = node.astext().split(None, 1) - num = num.strip() - self.body.append('.IP [%s] 5\n' % num) - - def depart_citation(self, node): - pass - - def visit_citation_reference(self, node): - self.body.append('['+node.astext()+']') - raise nodes.SkipNode - - def visit_classifier(self, node): - pass - - def depart_classifier(self, node): - pass - - def visit_colspec(self, node): - self.colspecs.append(node) - - def depart_colspec(self, node): - pass - - def write_colspecs(self): - self.body.append("%s.\n" % ('L '*len(self.colspecs))) - - def visit_comment(self, node, - sub=re.compile('-(?=-)').sub): - self.body.append(self.comment(node.astext())) - raise nodes.SkipNode - - def visit_contact(self, node): - self.visit_docinfo_item(node, 'contact') - - depart_contact = depart_docinfo_item - - def visit_container(self, node): - pass - - def depart_container(self, node): - pass - - def visit_compound(self, node): - pass - - def depart_compound(self, node): - pass - - def visit_copyright(self, node): - self.visit_docinfo_item(node, 'copyright') - - def visit_danger(self, node): - self.visit_admonition(node, 'danger') - - depart_danger = depart_admonition - - def visit_date(self, node): - self.visit_docinfo_item(node, 'date') - - def visit_decoration(self, node): - pass - - def depart_decoration(self, node): - pass - - def visit_definition(self, node): - pass - - def depart_definition(self, node): - pass - - def visit_definition_list(self, node): - self.indent(DEFINITION_LIST_INDENT) - - def depart_definition_list(self, node): - self.dedent() - - def visit_definition_list_item(self, node): - self.body.append(self.defs['definition_list_item'][0]) - - def depart_definition_list_item(self, node): - self.body.append(self.defs['definition_list_item'][1]) - - def visit_description(self, node): - pass - - def depart_description(self, node): - pass - - def visit_docinfo(self, node): - self._in_docinfo = 1 - - def depart_docinfo(self, node): - self._in_docinfo = None - # NOTE nothing should be written before this - self.append_header() - - def visit_doctest_block(self, node): - self.body.append(self.defs['literal_block'][0]) - self._in_literal = True - - def depart_doctest_block(self, node): - self._in_literal = False - self.body.append(self.defs['literal_block'][1]) - - def visit_document(self, node): - # no blank line between comment and header. - self.body.append(self.comment(self.document_start).rstrip()+'\n') - # writing header is postboned - self.header_written = 0 - - def depart_document(self, node): - if self._docinfo['author']: - self.body.append('.SH AUTHOR\n%s\n' - % ', '.join(self._docinfo['author'])) - skip = ('author', 'copyright', 'date', - 'manual_group', 'manual_section', - 'subtitle', - 'title', 'title_upper', 'version') - for name in self._docinfo_keys: - if name == 'address': - self.body.append("\n%s:\n%s%s.nf\n%s\n.fi\n%s%s" % ( - self.language.labels.get(name, name), - self.defs['indent'][0] % 0, - self.defs['indent'][0] % BLOCKQOUTE_INDENT, - self._docinfo[name], - self.defs['indent'][1], - self.defs['indent'][1])) - elif not name in skip: - if name in self._docinfo_names: - label = self._docinfo_names[name] - else: - label = self.language.labels.get(name, name) - self.body.append("\n%s: %s\n" % (label, self._docinfo[name])) - if self._docinfo['copyright']: - self.body.append('.SH COPYRIGHT\n%s\n' - % self._docinfo['copyright']) - self.body.append(self.comment( - 'Generated by docutils manpage writer.\n')) - - def visit_emphasis(self, node): - self.body.append(self.defs['emphasis'][0]) - - def depart_emphasis(self, node): - self.body.append(self.defs['emphasis'][1]) - - def visit_entry(self, node): - # a cell in a table row - if 'morerows' in node: - self.document.reporter.warning('"table row spanning" not supported', - base_node=node) - if 'morecols' in node: - self.document.reporter.warning( - '"table cell spanning" not supported', base_node=node) - self.context.append(len(self.body)) - - def depart_entry(self, node): - start = self.context.pop() - self._active_table.append_cell(self.body[start:]) - del self.body[start:] - - def visit_enumerated_list(self, node): - self.list_start(node) - - def depart_enumerated_list(self, node): - self.list_end() - - def visit_error(self, node): - self.visit_admonition(node, 'error') - - depart_error = depart_admonition - - def visit_field(self, node): - pass - - def depart_field(self, node): - pass - - def visit_field_body(self, node): - if self._in_docinfo: - name_normalized = self._field_name.lower().replace(" ","_") - self._docinfo_names[name_normalized] = self._field_name - self.visit_docinfo_item(node, name_normalized) - raise nodes.SkipNode - - def depart_field_body(self, node): - pass - - def visit_field_list(self, node): - self.indent(FIELD_LIST_INDENT) - - def depart_field_list(self, node): - self.dedent() - - def visit_field_name(self, node): - if self._in_docinfo: - self._field_name = node.astext() - raise nodes.SkipNode - else: - self.body.append(self.defs['field_name'][0]) - - def depart_field_name(self, node): - self.body.append(self.defs['field_name'][1]) - - def visit_figure(self, node): - self.indent(2.5) - self.indent(0) - - def depart_figure(self, node): - self.dedent() - self.dedent() - - def visit_footer(self, node): - self.document.reporter.warning('"footer" not supported', - base_node=node) - - def depart_footer(self, node): - pass - - def visit_footnote(self, node): - num, text = node.astext().split(None, 1) - num = num.strip() - self.body.append('.IP [%s] 5\n' % self.deunicode(num)) - - def depart_footnote(self, node): - pass - - def footnote_backrefs(self, node): - self.document.reporter.warning('"footnote_backrefs" not supported', - base_node=node) - - def visit_footnote_reference(self, node): - self.body.append('['+self.deunicode(node.astext())+']') - raise nodes.SkipNode - - def depart_footnote_reference(self, node): - pass - - def visit_generated(self, node): - pass - - def depart_generated(self, node): - pass - - def visit_header(self, node): - raise NotImplementedError, node.astext() - - def depart_header(self, node): - pass - - def visit_hint(self, node): - self.visit_admonition(node, 'hint') - - depart_hint = depart_admonition - - def visit_subscript(self, node): - self.body.append('\\s-2\\d') - - def depart_subscript(self, node): - self.body.append('\\u\\s0') - - def visit_superscript(self, node): - self.body.append('\\s-2\\u') - - def depart_superscript(self, node): - self.body.append('\\d\\s0') - - def visit_attribution(self, node): - self.body.append('\\(em ') - - def depart_attribution(self, node): - self.body.append('\n') - - def visit_image(self, node): - self.document.reporter.warning('"image" not supported', - base_node=node) - text = [] - if 'alt' in node.attributes: - text.append(node.attributes['alt']) - if 'uri' in node.attributes: - text.append(node.attributes['uri']) - self.body.append('[image: %s]\n' % ('/'.join(text))) - raise nodes.SkipNode - - def visit_important(self, node): - self.visit_admonition(node, 'important') - - depart_important = depart_admonition - - def visit_label(self, node): - # footnote and citation - if (isinstance(node.parent, nodes.footnote) - or isinstance(node.parent, nodes.citation)): - raise nodes.SkipNode - self.document.reporter.warning('"unsupported "label"', - base_node=node) - self.body.append('[') - - def depart_label(self, node): - self.body.append(']\n') - - def visit_legend(self, node): - pass - - def depart_legend(self, node): - pass - - # WHAT should we use .INDENT, .UNINDENT ? - def visit_line_block(self, node): - self._line_block += 1 - if self._line_block == 1: - self.body.append('.sp\n') - self.body.append('.nf\n') - else: - self.body.append('.in +2\n') - - def depart_line_block(self, node): - self._line_block -= 1 - if self._line_block == 0: - self.body.append('.fi\n') - self.body.append('.sp\n') - else: - self.body.append('.in -2\n') - - def visit_line(self, node): - pass - - def depart_line(self, node): - self.body.append('\n') - - def visit_list_item(self, node): - # man 7 man argues to use ".IP" instead of ".TP" - self.body.append('.IP %s %d\n' % ( - self._list_char[-1].next(), - self._list_char[-1].get_width(),)) - - def depart_list_item(self, node): - pass - - def visit_literal(self, node): - self.body.append(self.defs['literal'][0]) - - def depart_literal(self, node): - self.body.append(self.defs['literal'][1]) - - def visit_literal_block(self, node): - self.body.append(self.defs['literal_block'][0]) - self._in_literal = True - - def depart_literal_block(self, node): - self._in_literal = False - self.body.append(self.defs['literal_block'][1]) - - def visit_meta(self, node): - raise NotImplementedError, node.astext() - - def depart_meta(self, node): - pass - - def visit_note(self, node): - self.visit_admonition(node, 'note') - - depart_note = depart_admonition - - def indent(self, by=0.5): - # if we are in a section ".SH" there already is a .RS - step = self._indent[-1] - self._indent.append(by) - self.body.append(self.defs['indent'][0] % step) - - def dedent(self): - self._indent.pop() - self.body.append(self.defs['indent'][1]) - - def visit_option_list(self, node): - self.indent(OPTION_LIST_INDENT) - - def depart_option_list(self, node): - self.dedent() - - def visit_option_list_item(self, node): - # one item of the list - self.body.append(self.defs['option_list_item'][0]) - - def depart_option_list_item(self, node): - self.body.append(self.defs['option_list_item'][1]) - - def visit_option_group(self, node): - # as one option could have several forms it is a group - # options without parameter bold only, .B, -v - # options with parameter bold italic, .BI, -f file - # - # we do not know if .B or .BI - self.context.append('.B') # blind guess - self.context.append(len(self.body)) # to be able to insert later - self.context.append(0) # option counter - - def depart_option_group(self, node): - self.context.pop() # the counter - start_position = self.context.pop() - text = self.body[start_position:] - del self.body[start_position:] - self.body.append('%s%s\n' % (self.context.pop(), ''.join(text))) - - def visit_option(self, node): - # each form of the option will be presented separately - if self.context[-1] > 0: - self.body.append(', ') - if self.context[-3] == '.BI': - self.body.append('\\') - self.body.append(' ') - - def depart_option(self, node): - self.context[-1] += 1 - - def visit_option_string(self, node): - # do not know if .B or .BI - pass - - def depart_option_string(self, node): - pass - - def visit_option_argument(self, node): - self.context[-3] = '.BI' # bold/italic alternate - if node['delimiter'] != ' ': - self.body.append('\\fB%s ' % node['delimiter']) - elif self.body[len(self.body)-1].endswith('='): - # a blank only means no blank in output, just changing font - self.body.append(' ') - else: - # blank backslash blank, switch font then a blank - self.body.append(' \\ ') - - def depart_option_argument(self, node): - pass - - def visit_organization(self, node): - self.visit_docinfo_item(node, 'organization') - - def depart_organization(self, node): - pass - - def visit_paragraph(self, node): - # ``.PP`` : Start standard indented paragraph. - # ``.LP`` : Start block paragraph, all except the first. - # ``.P [type]`` : Start paragraph type. - # NOTE dont use paragraph starts because they reset indentation. - # ``.sp`` is only vertical space - self.ensure_eol() - self.body.append('.sp\n') - - def depart_paragraph(self, node): - self.body.append('\n') - - def visit_problematic(self, node): - self.body.append(self.defs['problematic'][0]) - - def depart_problematic(self, node): - self.body.append(self.defs['problematic'][1]) - - def visit_raw(self, node): - if node.get('format') == 'manpage': - self.body.append(node.astext() + "\n") - # Keep non-manpage raw text out of output: - raise nodes.SkipNode - - def visit_reference(self, node): - """E.g. link or email address.""" - self.body.append(self.defs['reference'][0]) - - def depart_reference(self, node): - self.body.append(self.defs['reference'][1]) - - def visit_revision(self, node): - self.visit_docinfo_item(node, 'revision') - - depart_revision = depart_docinfo_item - - def visit_row(self, node): - self._active_table.new_row() - - def depart_row(self, node): - pass - - def visit_section(self, node): - self.section_level += 1 - - def depart_section(self, node): - self.section_level -= 1 - - def visit_status(self, node): - self.visit_docinfo_item(node, 'status') - - depart_status = depart_docinfo_item - - def visit_strong(self, node): - self.body.append(self.defs['strong'][0]) - - def depart_strong(self, node): - self.body.append(self.defs['strong'][1]) - - def visit_substitution_definition(self, node): - """Internal only.""" - raise nodes.SkipNode - - def visit_substitution_reference(self, node): - self.document.reporter.warning('"substitution_reference" not supported', - base_node=node) - - def visit_subtitle(self, node): - if isinstance(node.parent, nodes.sidebar): - self.body.append(self.defs['strong'][0]) - elif isinstance(node.parent, nodes.document): - self.visit_docinfo_item(node, 'subtitle') - elif isinstance(node.parent, nodes.section): - self.body.append(self.defs['strong'][0]) - - def depart_subtitle(self, node): - # document subtitle calls SkipNode - self.body.append(self.defs['strong'][1]+'\n.PP\n') - - def visit_system_message(self, node): - # TODO add report_level - #if node['level'] < self.document.reporter['writer'].report_level: - # Level is too low to display: - # raise nodes.SkipNode - attr = {} - backref_text = '' - if node.hasattr('id'): - attr['name'] = node['id'] - if node.hasattr('line'): - line = ', line %s' % node['line'] - else: - line = '' - self.body.append('.IP "System Message: %s/%s (%s:%s)"\n' - % (node['type'], node['level'], node['source'], line)) - - def depart_system_message(self, node): - pass - - def visit_table(self, node): - self._active_table = Table() - - def depart_table(self, node): - self.ensure_eol() - self.body.extend(self._active_table.as_list()) - self._active_table = None - - def visit_target(self, node): - # targets are in-document hyper targets, without any use for man-pages. - raise nodes.SkipNode - - def visit_tbody(self, node): - pass - - def depart_tbody(self, node): - pass - - def visit_term(self, node): - self.body.append(self.defs['term'][0]) - - def depart_term(self, node): - self.body.append(self.defs['term'][1]) - - def visit_tgroup(self, node): - pass - - def depart_tgroup(self, node): - pass - - def visit_thead(self, node): - # MAYBE double line '=' - pass - - def depart_thead(self, node): - # MAYBE double line '=' - pass - - def visit_tip(self, node): - self.visit_admonition(node, 'tip') - - depart_tip = depart_admonition - - def visit_title(self, node): - if isinstance(node.parent, nodes.topic): - self.body.append(self.defs['topic-title'][0]) - elif isinstance(node.parent, nodes.sidebar): - self.body.append(self.defs['sidebar-title'][0]) - elif isinstance(node.parent, nodes.admonition): - self.body.append('.IP "') - elif self.section_level == 0: - self._docinfo['title'] = node.astext() - # document title for .TH - self._docinfo['title_upper'] = node.astext().upper() - raise nodes.SkipNode - elif self.section_level == 1: - self.body.append('.SH ') - for n in node.traverse(nodes.Text): - n.parent.replace(n, nodes.Text(n.astext().upper())) - else: - self.body.append('.SS ') - - def depart_title(self, node): - if isinstance(node.parent, nodes.admonition): - self.body.append('"') - self.body.append('\n') - - def visit_title_reference(self, node): - """inline citation reference""" - self.body.append(self.defs['title_reference'][0]) - - def depart_title_reference(self, node): - self.body.append(self.defs['title_reference'][1]) - - def visit_topic(self, node): - pass - - def depart_topic(self, node): - pass - - def visit_sidebar(self, node): - pass - - def depart_sidebar(self, node): - pass - - def visit_rubric(self, node): - pass - - def depart_rubric(self, node): - pass - - def visit_transition(self, node): - # .PP Begin a new paragraph and reset prevailing indent. - # .sp N leaves N lines of blank space. - # .ce centers the next line - self.body.append('\n.sp\n.ce\n----\n') - - def depart_transition(self, node): - self.body.append('\n.ce 0\n.sp\n') - - def visit_version(self, node): - self.visit_docinfo_item(node, 'version') - - def visit_warning(self, node): - self.visit_admonition(node, 'warning') - - depart_warning = depart_admonition - - def unimplemented_visit(self, node): - raise NotImplementedError('visiting unimplemented node type: %s' - % node.__class__.__name__) - -# The following part is taken from the Docutils rst2man.py script: -if __name__ == "__main__": - from docutils.core import publish_cmdline, default_description - description = ("Generates plain unix manual documents. " + - default_description) - publish_cmdline(writer=Writer(), description=description) - -# vim: set fileencoding=utf-8 et ts=4 ai : diff -r 0bb67503ad4b -r ac6fec2af8c8 doc/runrst --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/doc/runrst Mon May 31 21:43:03 2010 +0200 @@ -0,0 +1,42 @@ +#!/usr/bin/env python +# +# runrst - register custom roles and run correct writer +# +# Copyright 2010 Matt Mackall and others +# +# This software may be used and distributed according to the terms of the +# GNU General Public License version 2 or any later version. + +"""usage: %s WRITER args... + +where WRITER is the name of a Docutils writer such as 'html' or 'manpage' +""" + +import sys +from docutils.parsers.rst import roles +from docutils.core import publish_cmdline +from docutils import nodes, utils + +def role_hg(name, rawtext, text, lineno, inliner, + options={}, content=[]): + text = "hg " + utils.unescape(text) + linktext = nodes.literal(rawtext, text) + parts = text.split() + cmd, args = parts[1], parts[2:] + if cmd == 'help' and args: + cmd = args[0] # link to 'dates' for 'hg help dates' + node = nodes.reference(rawtext, '', linktext, + refuri="hg.1.html#%s" % cmd) + return [node], [] + +roles.register_local_role("hg", role_hg) + +if __name__ == "__main__": + if len(sys.argv) < 2: + sys.stderr.write(__doc__ % sys.argv[0]) + sys.exit(1) + + writer = sys.argv[1] + del sys.argv[1] + + publish_cmdline(writer_name=writer) diff -r 0bb67503ad4b -r ac6fec2af8c8 hgeditor --- a/hgeditor Mon May 31 21:37:01 2010 +0200 +++ b/hgeditor Mon May 31 21:43:03 2010 +0200 @@ -25,7 +25,7 @@ # Remove temporary files even if we get interrupted trap "cleanup_exit" 0 # normal exit -trap "exit 255" 1 2 3 6 15 # HUP INT QUIT ABRT TERM +trap "exit 255" HUP INT QUIT ABRT TERM HGTMP="${TMPDIR-/tmp}/hgeditor.$RANDOM.$RANDOM.$RANDOM.$$" (umask 077 && mkdir "$HGTMP") || { diff -r 0bb67503ad4b -r ac6fec2af8c8 hgext/acl.py --- a/hgext/acl.py Mon May 31 21:37:01 2010 +0200 +++ b/hgext/acl.py Mon May 31 21:43:03 2010 +0200 @@ -7,55 +7,176 @@ '''hooks for controlling repository access -This hook makes it possible to allow or deny write access to portions -of a repository when receiving incoming changesets. +This hook makes it possible to allow or deny write access to given +branches and paths of a repository when receiving incoming changesets +via pretxnchangegroup and pretxncommit. The authorization is matched based on the local user name on the system where the hook runs, and not the committer of the original changeset (since the latter is merely informative). The acl hook is best used along with a restricted shell like hgsh, -preventing authenticating users from doing anything other than -pushing or pulling. The hook is not safe to use if users have -interactive shell access, as they can then disable the hook. -Nor is it safe if remote users share an account, because then there -is no way to distinguish them. +preventing authenticating users from doing anything other than pushing +or pulling. The hook is not safe to use if users have interactive +shell access, as they can then disable the hook. Nor is it safe if +remote users share an account, because then there is no way to +distinguish them. + +The order in which access checks are performed is: + +1) Deny list for branches (section ``acl.deny.branches``) +2) Allow list for branches (section ``acl.allow.branches``) +3) Deny list for paths (section ``acl.deny``) +4) Allow list for paths (section ``acl.allow``) + +The allow and deny sections take key-value pairs. + +Branch-based Access Control +--------------------------- + +Use the ``acl.deny.branches`` and ``acl.allow.branches`` sections to +have branch-based access control. Keys in these sections can be +either: + +- a branch name, or +- an asterisk, to match any branch; + +The corresponding values can be either: -To use this hook, configure the acl extension in your hgrc like this:: +- a comma-separated list containing users and groups, or +- an asterisk, to match anyone; + +Path-based Access Control +------------------------- + +Use the ``acl.deny`` and ``acl.allow`` sections to have path-based +access control. Keys in these sections accept a subtree pattern (with +a glob syntax by default). The corresponding values follow the same +syntax as the other sections above. - [extensions] - acl = +Groups +------ + +Group names must be prefixed with an ``@`` symbol. Specifying a group +name has the same effect as specifying all the users in that group. + +You can define group members in the ``acl.groups`` section. +If a group name is not defined there, and Mercurial is running under +a Unix-like system, the list of users will be taken from the OS. +Otherwise, an exception will be raised. + +Example Configuration +--------------------- + +:: [hooks] + + # Use this if you want to check access restrictions at commit time + pretxncommit.acl = python:hgext.acl.hook + + # Use this if you want to check access restrictions for pull, push, + # bundle and serve. pretxnchangegroup.acl = python:hgext.acl.hook [acl] - # Check whether the source of incoming changes is in this list - # ("serve" == ssh or http, "push", "pull", "bundle") + # Allow or deny access for incoming changes only if their source is + # listed here, let them pass otherwise. Source is "serve" for all + # remote access (http or ssh), "push", "pull" or "bundle" when the + # related commands are run locally. + # Default: serve sources = serve -The allow and deny sections take a subtree pattern as key (with a glob -syntax by default), and a comma separated list of users as the -corresponding value. The deny list is checked before the allow list -is. :: + [acl.deny.branches] + + # Everyone is denied to the frozen branch: + frozen-branch = * + + # A bad user is denied on all branches: + * = bad-user + + [acl.allow.branches] + + # A few users are allowed on branch-a: + branch-a = user-1, user-2, user-3 + + # Only one user is allowed on branch-b: + branch-b = user-1 + + # The super user is allowed on any branch: + * = super-user + + # Everyone is allowed on branch-for-tests: + branch-for-tests = * + + [acl.deny] + # This list is checked first. If a match is found, acl.allow is not + # checked. All users are granted access if acl.deny is not present. + # Format for both lists: glob pattern = user, ..., @group, ... + + # To match everyone, use an asterisk for the user: + # my/glob/pattern = * + + # user6 will not have write access to any file: + ** = user6 + + # Group "hg-denied" will not have write access to any file: + ** = @hg-denied + + # Nobody will be able to change "DONT-TOUCH-THIS.txt", despite + # everyone being able to change all other files. See below. + src/main/resources/DONT-TOUCH-THIS.txt = * [acl.allow] - # If acl.allow is not present, all users are allowed by default. - # An empty acl.allow section means no users allowed. + # if acl.allow is not present, all users are allowed by default + # empty acl.allow = no users allowed + + # User "doc_writer" has write access to any file under the "docs" + # folder: docs/** = doc_writer + + # User "jack" and group "designers" have write access to any file + # under the "images" folder: + images/** = jack, @designers + + # Everyone (except for "user6" - see acl.deny above) will have write + # access to any file under the "resources" folder (except for 1 + # file. See acl.deny): + src/main/resources/** = * + .hgtags = release_engineer - [acl.deny] - # If acl.deny is not present, no users are refused by default. - # An empty acl.deny section means all users allowed. - glob pattern = user4, user5 - ** = user6 ''' from mercurial.i18n import _ from mercurial import util, match import getpass, urllib +def _getusers(ui, group): + + # First, try to use group definition from section [acl.groups] + hgrcusers = ui.configlist('acl.groups', group) + if hgrcusers: + return hgrcusers + + ui.debug('acl: "%s" not defined in [acl.groups]\n' % group) + # If no users found in group definition, get users from OS-level group + try: + return util.groupmembers(group) + except KeyError: + raise util.Abort(_("group '%s' is undefined") % group) + +def _usermatch(ui, user, usersorgroups): + + if usersorgroups == '*': + return True + + for ug in usersorgroups.replace(',', ' ').split(): + if user == ug or ug.find('@') == 0 and user in _getusers(ui, ug[1:]): + return True + + return False + def buildmatch(ui, repo, user, key): '''return tuple of (match function, list enabled).''' if not ui.has_section(key): @@ -63,19 +184,26 @@ return None pats = [pat for pat, users in ui.configitems(key) - if users == '*' or user in users.replace(',', ' ').split()] + if _usermatch(ui, user, users)] ui.debug('acl: %s enabled, %d entries for user %s\n' % (key, len(pats), user)) + + if not repo: + if pats: + return lambda b: '*' in pats or b in pats + return lambda b: False + if pats: return match.match(repo.root, '', pats) return match.exact(repo.root, '', []) def hook(ui, repo, hooktype, node=None, source=None, **kwargs): - if hooktype != 'pretxnchangegroup': + if hooktype not in ['pretxnchangegroup', 'pretxncommit']: raise util.Abort(_('config error - hook type "%s" cannot stop ' - 'incoming changesets') % hooktype) - if source not in ui.config('acl', 'sources', 'serve').split(): + 'incoming changesets nor commits') % hooktype) + if (hooktype == 'pretxnchangegroup' and + source not in ui.config('acl', 'sources', 'serve').split()): ui.debug('acl: changes have source "%s" - skipping\n' % source) return @@ -90,12 +218,28 @@ cfg = ui.config('acl', 'config') if cfg: - ui.readconfig(cfg, sections = ['acl.allow', 'acl.deny']) + ui.readconfig(cfg, sections = ['acl.groups', 'acl.allow.branches', + 'acl.deny.branches', 'acl.allow', 'acl.deny']) + + allowbranches = buildmatch(ui, None, user, 'acl.allow.branches') + denybranches = buildmatch(ui, None, user, 'acl.deny.branches') allow = buildmatch(ui, repo, user, 'acl.allow') deny = buildmatch(ui, repo, user, 'acl.deny') for rev in xrange(repo[node], len(repo)): ctx = repo[rev] + branch = ctx.branch() + if denybranches and denybranches(branch): + raise util.Abort(_('acl: user "%s" denied on branch "%s"' + ' (changeset "%s")') + % (user, branch, ctx)) + if allowbranches and not allowbranches(branch): + raise util.Abort(_('acl: user "%s" not allowed on branch "%s"' + ' (changeset "%s")') + % (user, branch, ctx)) + ui.debug('acl: branch access granted: "%s" on branch "%s"\n' + % (ctx, branch)) + for f in ctx.files(): if deny and deny(f): ui.debug('acl: user %s denied on %s\n' % (user, f)) diff -r 0bb67503ad4b -r ac6fec2af8c8 hgext/bookmarks.py --- a/hgext/bookmarks.py Mon May 31 21:37:01 2010 +0200 +++ b/hgext/bookmarks.py Mon May 31 21:43:03 2010 +0200 @@ -12,8 +12,8 @@ changeset that is based on a changeset that has a bookmark on it, the bookmark shifts to the new changeset. -It is possible to use bookmark names in every revision lookup (e.g. hg -merge, hg update). +It is possible to use bookmark names in every revision lookup (e.g. +:hg:`merge`, :hg:`update`). By default, when several bookmarks point to the same changeset, they will all move forward together. It is possible to obtain a more @@ -88,10 +88,10 @@ Bookmarks are pointers to certain commits that move when committing. Bookmarks are local. They can be renamed, copied and - deleted. It is possible to use bookmark names in 'hg merge' and - 'hg update' to merge and update respectively to a given bookmark. + deleted. It is possible to use bookmark names in :hg:`merge` and + :hg:`update` to merge and update respectively to a given bookmark. - You can use 'hg bookmark NAME' to set a bookmark on the working + You can use :hg:`bookmark NAME` to set a bookmark on the working directory's parent revision with the given name. If you specify a revision using -r REV (where REV may be an existing bookmark), the bookmark is assigned to that revision. @@ -152,15 +152,22 @@ for bmark, n in marks.iteritems(): if ui.configbool('bookmarks', 'track.current'): current = repo._bookmarkcurrent - prefix = (bmark == current and n == cur) and '*' or ' ' + if bmark == current and n == cur: + prefix, label = '*', 'bookmarks.current' + else: + prefix, label = ' ', '' else: - prefix = (n == cur) and '*' or ' ' + if n == cur: + prefix, label = '*', 'bookmarks.current' + else: + prefix, label = ' ', '' if ui.quiet: - ui.write("%s\n" % bmark) + ui.write("%s\n" % bmark, label=label) else: ui.write(" %s %-25s %d:%s\n" % ( - prefix, bmark, repo.changelog.rev(n), hexfn(n))) + prefix, bmark, repo.changelog.rev(n), hexfn(n)), + label=label) return def _revstostrip(changelog, node): @@ -234,10 +241,10 @@ file.close() return mark - def rollback(self): + def rollback(self, *args): if os.path.exists(self.join('undo.bookmarks')): util.rename(self.join('undo.bookmarks'), self.join('bookmarks')) - return super(bookmark_repo, self).rollback() + return super(bookmark_repo, self).rollback(*args) def lookup(self, key): if key in self._bookmarks: @@ -301,7 +308,7 @@ super(bookmark_repo, self).invalidate() for attr in ('_bookmarks', '_bookmarkcurrent'): if attr in self.__dict__: - delattr(repo, attr) + delattr(self, attr) repo.__class__ = bookmark_repo @@ -332,3 +339,5 @@ ('m', 'rename', '', _('rename a given bookmark'))], _('hg bookmarks [-f] [-d] [-m NAME] [-r REV] [NAME]')), } + +colortable = {'bookmarks.current': 'green'} diff -r 0bb67503ad4b -r ac6fec2af8c8 hgext/churn.py --- a/hgext/churn.py Mon May 31 21:37:01 2010 +0200 +++ b/hgext/churn.py Mon May 31 21:43:03 2010 +0200 @@ -10,7 +10,7 @@ from mercurial.i18n import _ from mercurial import patch, cmdutil, util, templater -import sys, os +import os import time, datetime def maketemplater(ui, repo, tmpl): @@ -48,7 +48,7 @@ tmpl.show(ctx) return ui.popbuffer() - state = {'count': 0, 'pct': 0} + state = {'count': 0} rate = {} df = False if opts.get('date'): @@ -74,20 +74,13 @@ lines = changedlines(ui, repo, ctx1, ctx, fns) rate[key] = [r + l for r, l in zip(rate.get(key, (0, 0)), lines)] - if opts.get('progress'): - state['count'] += 1 - newpct = int(100.0 * state['count'] / max(len(repo), 1)) - if state['pct'] < newpct: - state['pct'] = newpct - ui.write("\r" + _("generating stats: %d%%") % state['pct']) - sys.stdout.flush() + state['count'] += 1 + ui.progress(_('analyzing'), state['count'], total=len(repo)) for ctx in cmdutil.walkchangerevs(repo, m, opts, prep): continue - if opts.get('progress'): - ui.write("\r") - sys.stdout.flush() + ui.progress(_('analyzing'), None) return rate @@ -160,8 +153,10 @@ def format(name, (added, removed)): return "%s %15s %s%s\n" % (pad(name, maxname), '+%d/-%d' % (added, removed), - '+' * charnum(added), - '-' * charnum(removed)) + ui.label('+' * charnum(added), + 'diffstat.inserted'), + ui.label('-' * charnum(removed), + 'diffstat.deleted')) else: width -= 6 def format(name, count): @@ -188,6 +183,6 @@ ('s', 'sort', False, _('sort by key (default: sort by count)')), ('', 'diffstat', False, _('display added/removed lines separately')), ('', 'aliases', '', _('file with email aliases')), - ('', 'progress', None, _('show progress'))], - _("hg churn [-d DATE] [-r REV] [--aliases FILE] [--progress] [FILE]")), + ], + _("hg churn [-d DATE] [-r REV] [--aliases FILE] [FILE]")), } diff -r 0bb67503ad4b -r ac6fec2af8c8 hgext/color.py --- a/hgext/color.py Mon May 31 21:37:01 2010 +0200 +++ b/hgext/color.py Mon May 31 21:43:03 2010 +0200 @@ -61,317 +61,231 @@ resolve.resolved = green bold bookmarks.current = green + +The color extension will try to detect whether to use ANSI codes or +Win32 console APIs, unless it is made explicit:: + + [color] + mode = ansi + +Any value other than 'ansi', 'win32', or 'auto' will disable color. + ''' import os, sys -from mercurial import cmdutil, commands, extensions +from mercurial import commands, dispatch, extensions from mercurial.i18n import _ # start and stop parameters for effects -_effect_params = {'none': 0, - 'black': 30, - 'red': 31, - 'green': 32, - 'yellow': 33, - 'blue': 34, - 'magenta': 35, - 'cyan': 36, - 'white': 37, - 'bold': 1, - 'italic': 3, - 'underline': 4, - 'inverse': 7, - 'black_background': 40, - 'red_background': 41, - 'green_background': 42, - 'yellow_background': 43, - 'blue_background': 44, - 'purple_background': 45, - 'cyan_background': 46, - 'white_background': 47} +_effects = {'none': 0, 'black': 30, 'red': 31, 'green': 32, 'yellow': 33, + 'blue': 34, 'magenta': 35, 'cyan': 36, 'white': 37, 'bold': 1, + 'italic': 3, 'underline': 4, 'inverse': 7, + 'black_background': 40, 'red_background': 41, + 'green_background': 42, 'yellow_background': 43, + 'blue_background': 44, 'purple_background': 45, + 'cyan_background': 46, 'white_background': 47} + +_styles = {'grep.match': 'red bold', + 'diff.changed': 'white', + 'diff.deleted': 'red', + 'diff.diffline': 'bold', + 'diff.extended': 'cyan bold', + 'diff.file_a': 'red bold', + 'diff.file_b': 'green bold', + 'diff.hunk': 'magenta', + 'diff.inserted': 'green', + 'diff.trailingwhitespace': 'bold red_background', + 'diffstat.deleted': 'red', + 'diffstat.inserted': 'green', + 'log.changeset': 'yellow', + 'resolve.resolved': 'green bold', + 'resolve.unresolved': 'red bold', + 'status.added': 'green bold', + 'status.clean': 'none', + 'status.copied': 'none', + 'status.deleted': 'cyan bold underline', + 'status.ignored': 'black bold', + 'status.modified': 'blue bold', + 'status.removed': 'red bold', + 'status.unknown': 'magenta bold underline'} + def render_effects(text, effects): 'Wrap text in commands to turn on each effect.' - start = [str(_effect_params[e]) for e in ['none'] + effects] + if not text: + return text + start = [str(_effects[e]) for e in ['none'] + effects.split()] start = '\033[' + ';'.join(start) + 'm' - stop = '\033[' + str(_effect_params['none']) + 'm' + stop = '\033[' + str(_effects['none']) + 'm' return ''.join([start, text, stop]) -def _colorstatuslike(abbreviations, effectdefs, orig, ui, repo, *pats, **opts): - '''run a status-like command with colorized output''' - delimiter = opts.get('print0') and '\0' or '\n' - - nostatus = opts.get('no_status') - opts['no_status'] = False - # run original command and capture its output - ui.pushbuffer() - retval = orig(ui, repo, *pats, **opts) - # filter out empty strings - lines_with_status = [line for line in ui.popbuffer().split(delimiter) if line] - - if nostatus: - lines = [l[2:] for l in lines_with_status] - else: - lines = lines_with_status - - # apply color to output and display it - for i in xrange(len(lines)): - try: - status = abbreviations[lines_with_status[i][0]] - except KeyError: - # Ignore lines with invalid codes, especially in the case of - # of unknown filenames containing newlines (issue2036). - pass - else: - effects = effectdefs[status] - if effects: - lines[i] = render_effects(lines[i], effects) - ui.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 colorstatus(orig, ui, repo, *pats, **opts): - '''run the status command with colored output''' - return _colorstatuslike(_status_abbreviations, _status_effects, - orig, ui, repo, *pats, **opts) - - -_resolve_abbreviations = { 'U': 'unresolved', - 'R': 'resolved', } - -_resolve_effects = { 'unresolved': ['red', 'bold'], - 'resolved': ['green', 'bold'], } - -def colorresolve(orig, ui, repo, *pats, **opts): - '''run the resolve command with colored output''' - if not opts.get('list'): - # only colorize for resolve -l - return orig(ui, repo, *pats, **opts) - return _colorstatuslike(_resolve_abbreviations, _resolve_effects, - orig, ui, repo, *pats, **opts) - - -_bookmark_effects = { 'current': ['green'] } - -def colorbookmarks(orig, ui, repo, *pats, **opts): - def colorize(orig, s): - lines = s.split('\n') - for i, line in enumerate(lines): - if line.startswith(" *"): - lines[i] = render_effects(line, _bookmark_effects['current']) - orig('\n'.join(lines)) - oldwrite = extensions.wrapfunction(ui, 'write', colorize) - try: - orig(ui, repo, *pats, **opts) - finally: - ui.write = oldwrite - -def colorqseries(orig, ui, repo, *dummy, **opts): - '''run the qseries command with colored output''' - ui.pushbuffer() - retval = orig(ui, repo, **opts) - patchlines = ui.popbuffer().splitlines() - patchnames = repo.mq.series - - for patch, patchname in zip(patchlines, patchnames): - if opts['missing']: - effects = _patch_effects['missing'] - # Determine if patch is applied. - elif [applied for applied in repo.mq.applied - if patchname == applied.name]: - effects = _patch_effects['applied'] - else: - effects = _patch_effects['unapplied'] - - patch = patch.replace(patchname, render_effects(patchname, effects), 1) - ui.write(patch + '\n') - return retval - -_patch_effects = { 'applied': ['blue', 'bold', 'underline'], - 'missing': ['red', 'bold'], - 'unapplied': ['black', 'bold'], } -def colorwrap(orig, *args): - '''wrap ui.write for colored diff output''' - def _colorize(s): - lines = s.split('\n') - for i, line in enumerate(lines): - stripline = line - if line and line[0] in '+-': - # highlight trailing whitespace, but only in changed lines - stripline = line.rstrip() - for prefix, style in _diff_prefixes: - if stripline.startswith(prefix): - lines[i] = render_effects(stripline, _diff_effects[style]) - break - if line != stripline: - lines[i] += render_effects( - line[len(stripline):], _diff_effects['trailingwhitespace']) - return '\n'.join(lines) - orig(*[_colorize(s) for s in args]) +def extstyles(): + for name, ext in extensions.extensions(): + _styles.update(getattr(ext, 'colortable', {})) -def colorshowpatch(orig, self, node): - '''wrap cmdutil.changeset_printer.showpatch with colored output''' - oldwrite = extensions.wrapfunction(self.ui, 'write', colorwrap) - try: - orig(self, node) - finally: - self.ui.write = oldwrite - -def colordiffstat(orig, s): - lines = s.split('\n') - for i, line in enumerate(lines): - if line and line[-1] in '+-': - name, graph = line.rsplit(' ', 1) - graph = graph.replace('-', - render_effects('-', _diff_effects['deleted'])) - graph = graph.replace('+', - render_effects('+', _diff_effects['inserted'])) - lines[i] = ' '.join([name, graph]) - orig('\n'.join(lines)) - -def colordiff(orig, ui, repo, *pats, **opts): - '''run the diff command with colored output''' - if opts.get('stat'): - wrapper = colordiffstat - else: - wrapper = colorwrap - oldwrite = extensions.wrapfunction(ui, 'write', wrapper) - try: - orig(ui, repo, *pats, **opts) - finally: - ui.write = oldwrite - -def colorchurn(orig, ui, repo, *pats, **opts): - '''run the churn command with colored output''' - if not opts.get('diffstat'): - return orig(ui, repo, *pats, **opts) - oldwrite = extensions.wrapfunction(ui, 'write', colordiffstat) - try: - orig(ui, repo, *pats, **opts) - finally: - ui.write = oldwrite - -_diff_prefixes = [('diff', 'diffline'), - ('copy', 'extended'), - ('rename', 'extended'), - ('old', 'extended'), - ('new', 'extended'), - ('deleted', 'extended'), - ('---', 'file_a'), - ('+++', 'file_b'), - ('@', 'hunk'), - ('-', 'deleted'), - ('+', 'inserted')] - -_diff_effects = {'diffline': ['bold'], - 'extended': ['cyan', 'bold'], - 'file_a': ['red', 'bold'], - 'file_b': ['green', 'bold'], - 'hunk': ['magenta'], - 'deleted': ['red'], - 'inserted': ['green'], - 'changed': ['white'], - 'trailingwhitespace': ['bold', 'red_background']} - -def extsetup(ui): - '''Initialize the extension.''' - _setupcmd(ui, 'diff', commands.table, colordiff, _diff_effects) - _setupcmd(ui, 'incoming', commands.table, None, _diff_effects) - _setupcmd(ui, 'log', commands.table, None, _diff_effects) - _setupcmd(ui, 'outgoing', commands.table, None, _diff_effects) - _setupcmd(ui, 'tip', commands.table, None, _diff_effects) - _setupcmd(ui, 'status', commands.table, colorstatus, _status_effects) - _setupcmd(ui, 'resolve', commands.table, colorresolve, _resolve_effects) - - try: - mq = extensions.find('mq') - _setupcmd(ui, 'qdiff', mq.cmdtable, colordiff, _diff_effects) - _setupcmd(ui, 'qseries', mq.cmdtable, colorqseries, _patch_effects) - except KeyError: - mq = None - - try: - rec = extensions.find('record') - _setupcmd(ui, 'record', rec.cmdtable, colordiff, _diff_effects) - except KeyError: - rec = None - - if mq and rec: - _setupcmd(ui, 'qrecord', rec.cmdtable, colordiff, _diff_effects) - try: - churn = extensions.find('churn') - _setupcmd(ui, 'churn', churn.cmdtable, colorchurn, _diff_effects) - except KeyError: - churn = None - - try: - bookmarks = extensions.find('bookmarks') - _setupcmd(ui, 'bookmarks', bookmarks.cmdtable, colorbookmarks, - _bookmark_effects) - except KeyError: - # The bookmarks extension is not enabled - pass - -def _setupcmd(ui, cmd, table, func, effectsmap): - '''patch in command to command table and load effect map''' - # check isatty() before anything else changes it (like pager) - isatty = sys.__stdout__.isatty() - - def nocolor(orig, *args, **opts): - - if (opts['no_color'] or opts['color'] == 'never' or - (opts['color'] == 'auto' and (os.environ.get('TERM') == 'dumb' - or not isatty))): - del opts['no_color'] - del opts['color'] - return orig(*args, **opts) - - oldshowpatch = extensions.wrapfunction(cmdutil.changeset_printer, - 'showpatch', colorshowpatch) - del opts['no_color'] - del opts['color'] - try: - if func is not None: - return func(orig, *args, **opts) - return orig(*args, **opts) - finally: - cmdutil.changeset_printer.showpatch = oldshowpatch - - entry = extensions.wrapcommand(table, cmd, nocolor) - entry[1].extend([ - ('', 'color', 'auto', _("when to colorize (always, auto, or never)")), - ('', 'no-color', None, _("don't colorize output (DEPRECATED)")), - ]) - - for status in effectsmap: - configkey = cmd + '.' + status - effects = ui.configlist('color', configkey) - if effects: +def configstyles(ui): + for status, cfgeffects in ui.configitems('color'): + if '.' not in status: + continue + cfgeffects = ui.configlist('color', status) + if cfgeffects: good = [] - for e in effects: - if e in _effect_params: + for e in cfgeffects: + if e in _effects: good.append(e) else: ui.warn(_("ignoring unknown color/effect %r " "(configured in color.%s)\n") - % (e, configkey)) - effectsmap[status] = good + % (e, status)) + _styles[status] = ' '.join(good) + +_buffers = None +def style(msg, label): + effects = [] + for l in label.split(): + s = _styles.get(l, '') + if s: + effects.append(s) + effects = ''.join(effects) + if effects: + return '\n'.join([render_effects(s, effects) + for s in msg.split('\n')]) + return msg + +def popbuffer(orig, labeled=False): + global _buffers + if labeled: + return ''.join(style(a, label) for a, label in _buffers.pop()) + return ''.join(a for a, label in _buffers.pop()) + +mode = 'ansi' +def write(orig, *args, **opts): + label = opts.get('label', '') + global _buffers + if _buffers: + _buffers[-1].extend([(str(a), label) for a in args]) + elif mode == 'win32': + for a in args: + win32print(a, orig, **opts) + else: + return orig(*[style(str(a), label) for a in args], **opts) + +def write_err(orig, *args, **opts): + label = opts.get('label', '') + if mode == 'win32': + for a in args: + win32print(a, orig, **opts) + else: + return orig(*[style(str(a), label) for a in args], **opts) + +def uisetup(ui): + if ui.plain(): + return + global mode + mode = ui.config('color', 'mode', 'auto') + if mode == 'auto': + if os.name == 'nt' and 'TERM' not in os.environ: + # looks line a cmd.exe console, use win32 API or nothing + mode = w32effects and 'win32' or 'none' + else: + mode = 'ansi' + if mode == 'win32': + if w32effects is None: + # only warn if color.mode is explicitly set to win32 + ui.warn(_('win32console not found, please install pywin32\n')) + return + _effects.update(w32effects) + elif mode != 'ansi': + return + + # check isatty() before anything else changes it (like pager) + isatty = sys.__stdout__.isatty() + + def colorcmd(orig, ui_, opts, cmd, cmdfunc): + if (opts['color'] == 'always' or + (opts['color'] == 'auto' and (os.environ.get('TERM') != 'dumb' + and isatty))): + global _buffers + _buffers = ui_._buffers + extensions.wrapfunction(ui_, 'popbuffer', popbuffer) + extensions.wrapfunction(ui_, 'write', write) + extensions.wrapfunction(ui_, 'write_err', write_err) + ui_.label = style + extstyles() + configstyles(ui) + return orig(ui_, opts, cmd, cmdfunc) + extensions.wrapfunction(dispatch, '_runcommand', colorcmd) + +commands.globalopts.append(('', 'color', 'auto', + _("when to colorize (always, auto, or never)"))) + +try: + import re, pywintypes + from win32console import * + + # http://msdn.microsoft.com/en-us/library/ms682088%28VS.85%29.aspx + w32effects = { + 'none': 0, + 'black': 0, + 'red': FOREGROUND_RED, + 'green': FOREGROUND_GREEN, + 'yellow': FOREGROUND_RED | FOREGROUND_GREEN, + 'blue': FOREGROUND_BLUE, + 'magenta': FOREGROUND_BLUE | FOREGROUND_RED, + 'cyan': FOREGROUND_BLUE | FOREGROUND_GREEN, + 'white': FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE, + 'bold': FOREGROUND_INTENSITY, + 'black_background': 0, + 'red_background': BACKGROUND_RED, + 'green_background': BACKGROUND_GREEN, + 'yellow_background': BACKGROUND_RED | BACKGROUND_GREEN, + 'blue_background': BACKGROUND_BLUE, + 'purple_background': BACKGROUND_BLUE | BACKGROUND_RED, + 'cyan_background': BACKGROUND_BLUE | BACKGROUND_GREEN, + 'white_background': BACKGROUND_RED | BACKGROUND_GREEN | BACKGROUND_BLUE, + 'bold_background': BACKGROUND_INTENSITY, + 'underline': COMMON_LVB_UNDERSCORE, # double-byte charsets only + 'inverse': COMMON_LVB_REVERSE_VIDEO, # double-byte charsets only + } + + stdout = GetStdHandle(STD_OUTPUT_HANDLE) + try: + origattr = stdout.GetConsoleScreenBufferInfo()['Attributes'] + except pywintypes.error: + # stdout may be defined but not support + # GetConsoleScreenBufferInfo(), when called from subprocess or + # redirected. + raise ImportError() + ansire = re.compile('\033\[([^m]*)m([^\033]*)(.*)', re.MULTILINE | re.DOTALL) + + def win32print(text, orig, **opts): + label = opts.get('label', '') + attr = 0 + + # determine console attributes based on labels + for l in label.split(): + style = _styles.get(l, '') + for effect in style.split(): + attr |= w32effects[effect] + + # hack to ensure regexp finds data + if not text.startswith('\033['): + text = '\033[m' + text + + # Look for ANSI-like codes embedded in text + m = re.match(ansire, text) + while m: + for sattr in m.group(1).split(';'): + if sattr: + val = int(sattr) + attr = val and attr|val or 0 + stdout.SetConsoleTextAttribute(attr or origattr) + orig(m.group(2), **opts) + m = re.match(ansire, m.group(3)) + + # Explicity reset original attributes + stdout.SetConsoleTextAttribute(origattr) + +except ImportError: + w32effects = None diff -r 0bb67503ad4b -r ac6fec2af8c8 hgext/convert/bzr.py --- a/hgext/convert/bzr.py Mon May 31 21:37:01 2010 +0200 +++ b/hgext/convert/bzr.py Mon May 31 21:43:03 2010 +0200 @@ -109,18 +109,16 @@ # the file is not available anymore - was deleted raise IOError(_('%s is not available in %s anymore') % (name, rev)) + mode = self._modecache[(name, rev)] if kind == 'symlink': target = revtree.get_symlink_target(fileid) if target is None: raise util.Abort(_('%s.%s symlink has no target') % (name, rev)) - return target + return target, mode else: sio = revtree.get_file(fileid) - return sio.read() - - def getmode(self, name, rev): - return self._modecache[(name, rev)] + return sio.read(), mode def getchanges(self, version): # set up caches: modecache and revtree diff -r 0bb67503ad4b -r ac6fec2af8c8 hgext/convert/common.py --- a/hgext/convert/common.py Mon May 31 21:37:01 2010 +0200 +++ b/hgext/convert/common.py Mon May 31 21:43:03 2010 +0200 @@ -77,15 +77,10 @@ raise NotImplementedError() def getfile(self, name, rev): - """Return file contents as a string. rev is the identifier returned - by a previous call to getchanges(). Raise IOError to indicate that - name was deleted in rev. - """ - raise NotImplementedError() - - def getmode(self, name, rev): - """Return file mode, eg. '', 'x', or 'l'. rev is the identifier - returned by a previous call to getchanges(). + """Return a pair (data, mode) where data is the file content + as a string and mode one of '', 'x' or 'l'. rev is the + identifier returned by a previous call to getchanges(). Raise + IOError to indicate that name was deleted in rev. """ raise NotImplementedError() @@ -192,8 +187,8 @@ changeset. 'files' is a list of (path, version) tuples, 'copies' is a dictionary mapping destinations to sources, 'source' is the source repository, and 'revmap' is a mapfile - of source revisions to converted revisions. Only getfile(), - getmode(), and lookuprev() should be called on 'source'. + of source revisions to converted revisions. Only getfile() and + lookuprev() should be called on 'source'. Note that the sink repository is not told to update itself to a particular revision (or even what that revision would be) diff -r 0bb67503ad4b -r ac6fec2af8c8 hgext/convert/convcmd.py --- a/hgext/convert/convcmd.py Mon May 31 21:37:01 2010 +0200 +++ b/hgext/convert/convcmd.py Mon May 31 21:43:03 2010 +0200 @@ -72,6 +72,25 @@ ui.note(_("convert: %s\n") % inst) raise util.Abort(_('%s: unknown repository type') % path) +class progresssource(object): + def __init__(self, ui, source, filecount): + self.ui = ui + self.source = source + self.filecount = filecount + self.retrieved = 0 + + def getfile(self, file, rev): + self.retrieved += 1 + self.ui.progress(_('retrieving file'), self.retrieved, + item=file, total=self.filecount) + return self.source.getfile(file, rev) + + def lookuprev(self, rev): + return self.source.lookuprev(rev) + + def close(self): + self.ui.progress(_('retrieving file'), None) + class converter(object): def __init__(self, ui, source, dest, revmapfile, opts): @@ -111,11 +130,13 @@ if n in known or n in self.map: continue known.add(n) + self.ui.progress(_('scanning'), len(known), unit=_('revisions')) commit = self.cachecommit(n) parents[n] = [] for p in commit.parents: parents[n].append(p) visit.append(p) + self.ui.progress(_('scanning'), None) return parents @@ -302,8 +323,10 @@ parents = [self.map.get(p, p) for p in parents] except KeyError: parents = [b[0] for b in pbranches] + source = progresssource(self.ui, self.source, len(files)) newnode = self.dest.putcommit(files, copies, parents, commit, - self.source, self.map) + source, self.map) + source.close() self.source.converted(rev, newnode) self.map[rev] = newnode @@ -321,7 +344,7 @@ c = None self.ui.status(_("converting...\n")) - for c in t: + for i, c in enumerate(t): num -= 1 desc = self.commitcache[c].desc if "\n" in desc: @@ -331,7 +354,10 @@ # 'utf-8' self.ui.status("%d %s\n" % (num, recode(desc))) self.ui.note(_("source: %s\n") % recode(c)) + self.ui.progress(_('converting'), i, unit=_('revisions'), + total=len(t)) self.copy(c) + self.ui.progress(_('converting'), None) tags = self.source.gettags() ctags = {} diff -r 0bb67503ad4b -r ac6fec2af8c8 hgext/convert/cvs.py --- a/hgext/convert/cvs.py Mon May 31 21:37:01 2010 +0200 +++ b/hgext/convert/cvs.py Mon May 31 21:43:03 2010 +0200 @@ -200,7 +200,7 @@ self._parse() return self.heads - def _getfile(self, name, rev): + def getfile(self, name, rev): def chunkedread(fp, count): # file-objects returned by socked.makefile() do not handle @@ -216,6 +216,7 @@ output.write(data) return output.getvalue() + self._parse() if rev.endswith("(DEAD)"): raise IOError @@ -255,18 +256,8 @@ else: raise util.Abort(_("unknown CVS response: %s") % line) - def getfile(self, file, rev): - self._parse() - data, mode = self._getfile(file, rev) - self.modecache[(file, rev)] = mode - return data - - def getmode(self, file, rev): - return self.modecache[(file, rev)] - def getchanges(self, rev): self._parse() - self.modecache = {} return sorted(self.files[rev].iteritems()), {} def getcommit(self, rev): diff -r 0bb67503ad4b -r ac6fec2af8c8 hgext/convert/cvsps.py --- a/hgext/convert/cvsps.py Mon May 31 21:37:01 2010 +0200 +++ b/hgext/convert/cvsps.py Mon May 31 21:43:03 2010 +0200 @@ -239,12 +239,12 @@ continue match = re_01.match(line) if match: - raise Exception(match.group(1)) + raise logerror(match.group(1)) match = re_02.match(line) if match: - raise Exception(match.group(2)) + raise logerror(match.group(2)) if re_03.match(line): - raise Exception(line) + raise logerror(line) elif state == 1: # expect 'Working file' (only when using log instead of rlog) diff -r 0bb67503ad4b -r ac6fec2af8c8 hgext/convert/darcs.py --- a/hgext/convert/darcs.py Mon May 31 21:37:01 2010 +0200 +++ b/hgext/convert/darcs.py Mon May 31 21:43:03 2010 +0200 @@ -157,11 +157,11 @@ def getfile(self, name, rev): if rev != self.lastrev: raise util.Abort(_('internal calling inconsistency')) - return open(os.path.join(self.tmppath, name), 'rb').read() - - def getmode(self, name, rev): - mode = os.lstat(os.path.join(self.tmppath, name)).st_mode - return (mode & 0111) and 'x' or '' + path = os.path.join(self.tmppath, name) + data = open(path, 'rb').read() + mode = os.lstat(path).st_mode + mode = (mode & 0111) and 'x' or '' + return data, mode def gettags(self): return self.tags diff -r 0bb67503ad4b -r ac6fec2af8c8 hgext/convert/filemap.py --- a/hgext/convert/filemap.py Mon May 31 21:37:01 2010 +0200 +++ b/hgext/convert/filemap.py Mon May 31 21:43:03 2010 +0200 @@ -100,8 +100,7 @@ # # - Filter and rename files. This is mostly wrapped by the filemapper # class above. We hide the original filename in the revision that is -# returned by getchanges to be able to find things later in getfile -# and getmode. +# returned by getchanges to be able to find things later in getfile. # # - Return only revisions that matter for the files we're interested in. # This involves rewriting the parents of the original revision to @@ -318,10 +317,9 @@ self.convertedorder.append((rev, True, None)) self._discard(*parents) - # Get the real changes and do the filtering/mapping. - # To be able to get the files later on in getfile and getmode, - # we hide the original filename in the rev part of the return - # value. + # Get the real changes and do the filtering/mapping. To be + # able to get the files later on in getfile, we hide the + # original filename in the rev part of the return value. changes, copies = self.base.getchanges(rev) newnames = {} files = [] @@ -345,10 +343,6 @@ realname, realrev = rev return self.base.getfile(realname, realrev) - def getmode(self, name, rev): - realname, realrev = rev - return self.base.getmode(realname, realrev) - def gettags(self): return self.base.gettags() diff -r 0bb67503ad4b -r ac6fec2af8c8 hgext/convert/git.py --- a/hgext/convert/git.py Mon May 31 21:37:01 2010 +0200 +++ b/hgext/convert/git.py Mon May 31 21:43:03 2010 +0200 @@ -67,10 +67,9 @@ return data def getfile(self, name, rev): - return self.catfile(rev, "blob") - - def getmode(self, name, rev): - return self.modecache[(name, rev)] + data = self.catfile(rev, "blob") + mode = self.modecache[(name, rev)] + return data, mode def getchanges(self, version): self.modecache = {} diff -r 0bb67503ad4b -r ac6fec2af8c8 hgext/convert/gnuarch.py --- a/hgext/convert/gnuarch.py Mon May 31 21:37:01 2010 +0200 +++ b/hgext/convert/gnuarch.py Mon May 31 21:43:03 2010 +0200 @@ -54,7 +54,6 @@ self.changes = {} self.parents = {} self.tags = {} - self.modecache = {} self.catlogparser = Parser() self.locale = locale.getpreferredencoding() self.archives = [] @@ -142,16 +141,9 @@ 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)] + return self._getfile(name, rev) def getchanges(self, rev): - self.modecache = {} self._update(rev) changes = [] copies = {} diff -r 0bb67503ad4b -r ac6fec2af8c8 hgext/convert/hg.py --- a/hgext/convert/hg.py Mon May 31 21:37:01 2010 +0200 +++ b/hgext/convert/hg.py Mon May 31 21:43:03 2010 +0200 @@ -134,11 +134,11 @@ files = dict(files) def getfilectx(repo, memctx, f): v = files[f] - data = source.getfile(f, v) - e = source.getmode(f, v) + data, mode = source.getfile(f, v) if f == '.hgtags': data = self._rewritetags(source, revmap, data) - return context.memfilectx(f, data, 'l' in e, 'x' in e, copies.get(f)) + return context.memfilectx(f, data, 'l' in mode, 'x' in mode, + copies.get(f)) pl = [] for p in parents: @@ -266,13 +266,11 @@ def getfile(self, name, rev): try: - return self.changectx(rev)[name].data() + fctx = self.changectx(rev)[name] + return fctx.data(), fctx.flags() except error.LookupError, err: raise IOError(err) - def getmode(self, name, rev): - return self.changectx(rev).manifest().flags(name) - def getchanges(self, rev): ctx = self.changectx(rev) parents = self.parents(ctx) diff -r 0bb67503ad4b -r ac6fec2af8c8 hgext/convert/monotone.py --- a/hgext/convert/monotone.py Mon May 31 21:37:01 2010 +0200 +++ b/hgext/convert/monotone.py Mon May 31 21:43:03 2010 +0200 @@ -192,18 +192,16 @@ return (files.items(), copies) - def getmode(self, name, rev): - self.mtnloadmanifest(rev) - node, attr = self.files.get(name, (None, "")) - return attr - 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) + data = self.mtnrun("get_file_of", name, r=rev) except: raise IOError() # file was deleted or renamed + self.mtnloadmanifest(rev) + node, attr = self.files.get(name, (None, "")) + return data, attr def getcommit(self, rev): certs = self.mtngetcerts(rev) diff -r 0bb67503ad4b -r ac6fec2af8c8 hgext/convert/p4.py --- a/hgext/convert/p4.py Mon May 31 21:37:01 2010 +0200 +++ b/hgext/convert/p4.py Mon May 31 21:43:03 2010 +0200 @@ -41,7 +41,6 @@ self.parent = {} self.encoding = "latin_1" self.depotname = {} # mapping from local name to depot name - self.modecache = {} self.re_type = re.compile( "([a-z]+)?(text|binary|symlink|apple|resource|unicode|utf\d+)" "(\+\w+)?$") @@ -183,17 +182,12 @@ if mode is None: raise IOError(0, "bad stat") - self.modecache[(name, rev)] = mode - if keywords: contents = keywords.sub("$\\1$", contents) if mode == "l" and contents.endswith("\n"): contents = contents[:-1] - return contents - - def getmode(self, name, rev): - return self.modecache[(name, rev)] + return contents, mode def getchanges(self, rev): return self.files[rev], {} diff -r 0bb67503ad4b -r ac6fec2af8c8 hgext/convert/subversion.py --- a/hgext/convert/subversion.py Mon May 31 21:37:01 2010 +0200 +++ b/hgext/convert/subversion.py Mon May 31 21:43:03 2010 +0200 @@ -371,19 +371,10 @@ return self.heads - def getfile(self, file, rev): - data, mode = self._getfile(file, rev) - self.modecache[(file, rev)] = mode - return data - - def getmode(self, file, rev): - return self.modecache[(file, rev)] - def getchanges(self, rev): if self._changescache and self._changescache[0] == rev: return self._changescache[1] self._changescache = None - self.modecache = {} (paths, parents) = self.paths[rev] if parents: files, self.removed, copies = self.expandpaths(rev, paths, parents) @@ -612,9 +603,6 @@ def expandpaths(self, rev, paths, parents): changed, removed = set(), set() - # Map of entrypath, revision for finding source of deleted - # revisions. - copyfrom = {} copies = {} new_module, revnum = self.revsplit(rev)[1:] @@ -622,7 +610,9 @@ self.module = new_module self.reparent(self.module) - for path, ent in paths: + for i, (path, ent) in enumerate(paths): + self.ui.progress(_('scanning paths'), i, item=path, + total=len(paths)) entrypath = self.getrelpath(path) kind = self._checkpath(entrypath, revnum) @@ -652,10 +642,10 @@ elif fromkind == svn.core.svn_node_dir: oroot = parentpath.strip('/') nroot = path.strip('/') - children = self._find_children(oroot, prevnum) - children = [s.replace(oroot, nroot) for s in children] - for child in children: - childpath = self.getrelpath("/" + child, pmodule) + children = self._iterfiles(oroot, prevnum) + for childpath in children: + childpath = childpath.replace(oroot, nroot) + childpath = self.getrelpath("/" + childpath, pmodule) if childpath: removed.add(self.recode(childpath)) else: @@ -674,18 +664,10 @@ if pkind == svn.core.svn_node_file: removed.add(self.recode(entrypath)) - children = sorted(self._find_children(path, revnum)) - for child in children: - # Can we move a child directory and its - # parent in the same commit? (probably can). Could - # cause problems if instead of revnum -1, - # we have to look in (copyfrom_path, revnum - 1) - entrypath = self.getrelpath("/" + child) - if entrypath: - # Need to filter out directories here... - kind = self._checkpath(entrypath, revnum) - if kind != svn.core.svn_node_dir: - changed.add(self.recode(entrypath)) + for childpath in self._iterfiles(path, revnum): + childpath = self.getrelpath("/" + childpath) + if childpath: + changed.add(self.recode(childpath)) # Handle directory copies if not ent.copyfrom_path or not parents: @@ -698,19 +680,18 @@ copyfrompath = self.getrelpath(ent.copyfrom_path, pmodule) if not copyfrompath: continue - copyfrom[path] = ent self.ui.debug("mark %s came from %s:%d\n" % (path, copyfrompath, ent.copyfrom_rev)) - children = self._find_children(ent.copyfrom_path, ent.copyfrom_rev) - children.sort() - for child in children: - entrypath = self.getrelpath("/" + child, pmodule) - if not entrypath: + children = self._iterfiles(ent.copyfrom_path, ent.copyfrom_rev) + for childpath in children: + childpath = self.getrelpath("/" + childpath, pmodule) + if not childpath: continue - copytopath = path + entrypath[len(copyfrompath):] + copytopath = path + childpath[len(copyfrompath):] copytopath = self.getrelpath(copytopath) - copies[self.recode(copytopath)] = self.recode(entrypath) + copies[self.recode(copytopath)] = self.recode(childpath) + self.ui.progress(_('scanning paths'), None) changed.update(removed) return (list(changed), removed, copies) @@ -839,10 +820,10 @@ raise util.Abort(_('svn: branch has no revision %s') % to_revnum) raise - def _getfile(self, file, rev): + def getfile(self, file, rev): # TODO: ra.get_file transmits the whole file instead of diffs. if file in self.removed: - raise IOError() + raise IOError() mode = '' try: new_module, revnum = self.revsplit(rev)[1:] @@ -871,12 +852,14 @@ data = data[len(link_prefix):] return data, mode - def _find_children(self, path, revnum): + def _iterfiles(self, path, revnum): + """Enumerate all files in path at revnum, recursively.""" path = path.strip('/') pool = Pool() rpath = '/'.join([self.baseurl, urllib.quote(path)]).strip('/') - return ['%s/%s' % (path, x) for x in - svn.client.ls(rpath, optrev(revnum), True, self.ctx, pool).keys()] + entries = svn.client.ls(rpath, optrev(revnum), True, self.ctx, pool) + return ((path + '/' + p) for p, e in entries.iteritems() + if e.kind == svn.core.svn_node_file) def getrelpath(self, path, module=None): if module is None: @@ -1111,12 +1094,11 @@ # Apply changes to working copy for f, v in files: try: - data = source.getfile(f, v) + data, mode = source.getfile(f, v) except IOError: self.delete.append(f) else: - e = source.getmode(f, v) - self.putfile(f, e, data) + self.putfile(f, mode, data) if f in copies: self.copies.append([copies[f], f]) files = [f[0] for f in files] diff -r 0bb67503ad4b -r ac6fec2af8c8 hgext/extdiff.py --- a/hgext/extdiff.py Mon May 31 21:37:01 2010 +0200 +++ b/hgext/extdiff.py Mon May 31 21:43:03 2010 +0200 @@ -14,7 +14,7 @@ files to compare. The extdiff extension also allows to configure new diff commands, so -you do not need to type "hg extdiff -p kdiff3" always. :: +you do not need to type :hg:`extdiff -p kdiff3` always. :: [extdiff] # add new command that runs GNU diff(1) in 'context diff' mode @@ -35,10 +35,28 @@ # your .vimrc vimdiff = gvim -f '+next' '+execute "DirDiff" argv(0) argv(1)' -You can use -I/-X and list of file or directory names like normal "hg -diff" command. The extdiff extension makes snapshots of only needed -files, so running the external diff program will actually be pretty -fast (at least faster than having to compare the entire tree). +Tool arguments can include variables that are expanded at runtime:: + + $parent1, $plabel1 - filename, descriptive label of first parent + $child, $clabel - filename, descriptive label of child revision + $parent2, $plabel2 - filename, descriptive label of second parent + $parent is an alias for $parent1. + +The extdiff extension will look in your [diff-tools] and [merge-tools] +sections for diff tool arguments, when none are specified in [extdiff]. + +:: + + [extdiff] + kdiff3 = + + [diff-tools] + kdiff3.diffargs=--L1 '$plabel1' --L2 '$clabel' $parent $child + +You can use -I/-X and list of file or directory names like normal +:hg:`diff` command. The extdiff extension makes snapshots of only +needed files, so running the external diff program will actually be +pretty fast (at least faster than having to compare the entire tree). ''' from mercurial.i18n import _ @@ -133,18 +151,23 @@ # Always make a copy of node1a (and node1b, if applicable) dir1a_files = mod_a | rem_a | ((mod_b | add_b) - add_a) dir1a = snapshot(ui, repo, dir1a_files, node1a, tmproot)[0] + rev1a = '@%d' % repo[node1a].rev() if do3way: dir1b_files = mod_b | rem_b | ((mod_a | add_a) - add_b) dir1b = snapshot(ui, repo, dir1b_files, node1b, tmproot)[0] + rev1b = '@%d' % repo[node1b].rev() else: dir1b = None + rev1b = '' fns_and_mtime = [] # If node2 in not the wc or there is >1 change, copy it dir2root = '' + rev2 = '' if node2: dir2 = snapshot(ui, repo, modadd, node2, tmproot)[0] + rev2 = '@%d' % repo[node2].rev() elif len(common) > 1: #we only actually need to get the files to copy back to #the working dir in this case (because the other cases @@ -156,23 +179,32 @@ dir2 = '' dir2root = repo.root + label1a = rev1a + label1b = rev1b + label2 = rev2 + # If only one change, diff the files instead of the directories # Handle bogus modifies correctly by checking if the files exist if len(common) == 1: common_file = util.localpath(common.pop()) dir1a = os.path.join(dir1a, common_file) + label1a = common_file + rev1a if not os.path.isfile(os.path.join(tmproot, dir1a)): dir1a = os.devnull if do3way: dir1b = os.path.join(dir1b, common_file) + label1b = common_file + rev1b if not os.path.isfile(os.path.join(tmproot, dir1b)): dir1b = os.devnull dir2 = os.path.join(dir2root, dir2, common_file) + label2 = common_file + rev2 # Function to quote file/dir names in the argument string. # When not operating in 3-way mode, an empty string is # returned for parent2 - replace = dict(parent=dir1a, parent1=dir1a, parent2=dir1b, child=dir2) + replace = dict(parent=dir1a, parent1=dir1a, parent2=dir1b, + plabel1=label1a, plabel2=label1b, + clabel=label2, child=dir2) def quote(match): key = match.group()[1:] if not do3way and key == 'parent2': @@ -180,7 +212,7 @@ return util.shellquote(replace[key]) # Match parent2 first, so 'parent1?' will match both parent1 and parent - regex = '\$(parent2|parent1?|child)' + regex = '\$(parent2|parent1?|child|plabel1|plabel2|clabel)' if not do3way and not re.search(regex, args): args += ' $parent1 $child' args = re.sub(regex, quote, args) @@ -252,6 +284,12 @@ path = diffopts.pop(0) else: path, diffopts = cmd, [] + # look for diff arguments in [diff-tools] then [merge-tools] + if diffopts == []: + args = ui.config('diff-tools', cmd+'.diffargs') or \ + ui.config('merge-tools', cmd+'.diffargs') + if args: + diffopts = shlex.split(args) def save(cmd, path, diffopts): '''use closure to save diff command to use''' def mydiff(ui, repo, *pats, **opts): diff -r 0bb67503ad4b -r ac6fec2af8c8 hgext/fetch.py --- a/hgext/fetch.py Mon May 31 21:37:01 2010 +0200 +++ b/hgext/fetch.py Mon May 31 21:43:03 2010 +0200 @@ -28,7 +28,7 @@ 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. + See :hg:`help dates` for a list of formats valid for -d/--date. ''' date = opts.get('date') diff -r 0bb67503ad4b -r ac6fec2af8c8 hgext/gpg.py --- a/hgext/gpg.py Mon May 31 21:37:01 2010 +0200 +++ b/hgext/gpg.py Mon May 31 21:43:03 2010 +0200 @@ -197,7 +197,7 @@ 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. + See :hg:`help dates` for a list of formats valid for -d/--date. """ mygpg = newgpg(ui, **opts) diff -r 0bb67503ad4b -r ac6fec2af8c8 hgext/hgcia.py --- a/hgext/hgcia.py Mon May 31 21:37:01 2010 +0200 +++ b/hgext/hgcia.py Mon May 31 21:43:03 2010 +0200 @@ -113,7 +113,7 @@ n = self.ctx.node() pbuf = patchbuf() - patch.export(self.cia.repo, [n], fp=pbuf) + cmdutil.export(self.cia.repo, [n], fp=pbuf) return patch.diffstat(pbuf.lines) or '' def logmsg(self): diff -r 0bb67503ad4b -r ac6fec2af8c8 hgext/hgk.py --- a/hgext/hgk.py Mon May 31 21:37:01 2010 +0200 +++ b/hgext/hgk.py Mon May 31 21:43:03 2010 +0200 @@ -17,7 +17,7 @@ the contrib directory, and the extension is shipped in the hgext repository, and needs to be enabled. -The hg view command will launch the hgk Tcl script. For this command +The :hg:`view` command will launch the hgk Tcl script. For this command to work, hgk must be in your search path. Alternately, you can specify the path to hgk in your .hgrc file:: diff -r 0bb67503ad4b -r ac6fec2af8c8 hgext/highlight/highlight.py --- a/hgext/highlight/highlight.py Mon May 31 21:37:01 2010 +0200 +++ b/hgext/highlight/highlight.py Mon May 31 21:43:03 2010 +0200 @@ -23,7 +23,7 @@ def pygmentize(field, fctx, style, tmpl): # append a to the syntax highlighting css - old_header = ''.join(tmpl('header')) + old_header = tmpl.load('header') if SYNTAX_CSS not in old_header: new_header = old_header + SYNTAX_CSS tmpl.cache['header'] = new_header diff -r 0bb67503ad4b -r ac6fec2af8c8 hgext/inotify/__init__.py --- a/hgext/inotify/__init__.py Mon May 31 21:37:01 2010 +0200 +++ b/hgext/inotify/__init__.py Mon May 31 21:43:03 2010 +0200 @@ -41,7 +41,7 @@ # to start an inotify server if it won't start. _inotifyon = True - def status(self, match, subrepos, ignored, clean, unknown=True): + def status(self, match, subrepos, ignored, clean, unknown): files = match.files() if '.' in files: files = [] diff -r 0bb67503ad4b -r ac6fec2af8c8 hgext/keyword.py --- a/hgext/keyword.py Mon May 31 21:37:01 2010 +0200 +++ b/hgext/keyword.py Mon May 31 21:43:03 2010 +0200 @@ -1,6 +1,6 @@ # keyword.py - $Keyword$ expansion for Mercurial # -# Copyright 2007-2009 Christian Ebert +# Copyright 2007-2010 Christian Ebert # # This software may be used and distributed according to the terms of the # GNU General Public License version 2 or any later version. @@ -35,8 +35,8 @@ 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. +Configuration is done in the [keyword], [keywordset] and [keywordmaps] +sections of hgrc files. Example:: @@ -45,31 +45,33 @@ **.py = x* = ignore + [keywordset] + # prefer svn- over cvs-like default keywordmaps + svn = True + NOTE: the more specific you are in your filename patterns the less you lose speed in huge repositories. For [keywordmaps] template mapping and expansion demonstration and -control run "hg kwdemo". See "hg help templates" for a list of +control run :hg:`kwdemo`. See :hg:`help templates` for a list of available templates and filters. -An additional date template filter {date|utcdate} is provided. It -returns a date like "2006/09/18 15:13:13". +Three additional date template filters are provided:: + + utcdate "2006/09/18 15:13:13" + svnutcdate "2006-09-18 15:13:13Z" + svnisodate "2006-09-18 08:13:13 -700 (Mon, 18 Sep 2006)" -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. +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 +Before changing/disabling active keywords, run :hg:`kwshrink` to avoid the risk of inadvertently 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. +:hg:`kwexpand`. Expansions spanning more than one line and incremental expansions, like CVS' $Log$, are not supported. A keyword template map "Log = @@ -79,7 +81,6 @@ from mercurial import commands, cmdutil, dispatch, filelog, revlog, extensions from mercurial import patch, localrepo, templater, templatefilters, util, match from mercurial.hgweb import webcommands -from mercurial.lock import release from mercurial.node import nullid from mercurial.i18n import _ import re, shutil, tempfile @@ -93,24 +94,31 @@ # hg commands that trigger expansion only when writing to working dir, # not when reading filelog, and unexpand when reading from working dir -restricted = ('merge record resolve qfold qimport qnew qpush qrefresh qrecord' - ' transplant') +restricted = 'merge record qrecord resolve transplant' + +# commands using dorecord +recordcommands = 'record qrecord' +# names of extensions using dorecord +recordextensions = 'record' -# provide cvs-like UTC date filter +# date like in cvs' $Date utcdate = lambda x: util.datestr((x[0], 0), '%Y/%m/%d %H:%M:%S') +# date like in svn's $Date +svnisodate = lambda x: util.datestr(x, '%Y-%m-%d %H:%M:%S %1%2 (%a, %d %b %Y)') +# date like in svn's $Id +svnutcdate = lambda x: util.datestr((x[0], 0), '%Y-%m-%d %H:%M:%SZ') # make keyword tools accessible kwtools = {'templater': None, 'hgcmd': '', 'inc': [], 'exc': ['.hg*']} -class kwtemplater(object): - ''' - Sets up keyword templates, corresponding keyword regex, and - provides keyword substitution functions. - ''' +def _defaultkwmaps(ui): + '''Returns default keywordmaps according to keywordset configuration.''' templates = { 'Revision': '{node|short}', 'Author': '{author|user}', + } + kwsets = ({ 'Date': '{date|utcdate}', 'RCSfile': '{file|basename},v', 'RCSFile': '{file|basename},v', # kept for backwards compatibility @@ -118,7 +126,21 @@ 'Source': '{root}/{file},v', 'Id': '{file|basename},v {node|short} {date|utcdate} {author|user}', 'Header': '{root}/{file},v {node|short} {date|utcdate} {author|user}', - } + }, { + 'Date': '{date|svnisodate}', + 'Id': '{file|basename},v {node|short} {date|svnutcdate} {author|user}', + 'LastChangedRevision': '{node|short}', + 'LastChangedBy': '{author|user}', + 'LastChangedDate': '{date|svnisodate}', + }) + templates.update(kwsets[ui.configbool('keywordset', 'svn')]) + return templates + +class kwtemplater(object): + ''' + Sets up keyword templates, corresponding keyword regex, and + provides keyword substitution functions. + ''' def __init__(self, ui, repo): self.ui = ui @@ -126,26 +148,31 @@ self.match = match.match(repo.root, '', [], kwtools['inc'], kwtools['exc']) self.restrict = kwtools['hgcmd'] in restricted.split() + self.record = kwtools['hgcmd'] in recordcommands.split() kwmaps = self.ui.configitems('keywordmaps') if kwmaps: # override default templates self.templates = dict((k, templater.parsestring(v, False)) for k, v in kwmaps) + else: + self.templates = _defaultkwmaps(self.ui) 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, None, '', False) + templatefilters.filters['svnisodate'] = svnisodate + templatefilters.filters['svnutcdate'] = svnutcdate def substitute(self, data, path, ctx, subfunc): '''Replaces keywords in data with expanded template.''' def kwsub(mobj): kw = mobj.group(1) - self.ct.use_template(self.templates[kw]) + ct = cmdutil.changeset_templater(self.ui, self.repo, + False, None, '', False) + ct.use_template(self.templates[kw]) self.ui.pushbuffer() - self.ct.show(ctx, root=self.repo.root, file=path) + ct.show(ctx, root=self.repo.root, file=path) ekw = templatefilters.firstline(self.ui.popbuffer()) return '$%s: %s $' % (kw, ekw) return subfunc(kwsub, data) @@ -163,23 +190,22 @@ Caveat: localrepository._link fails on Windows.''' return self.match(path) and not 'l' in flagfunc(path) - def overwrite(self, node, expand, files): + def overwrite(self, node, expand, candidates): '''Overwrites selected files expanding/shrinking keywords.''' ctx = self.repo[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, ctx.flags)] + if node is not None: # commit, record + candidates = [f for f in ctx.files() if f in mf] + candidates = [f for f in candidates if self.iskwfile(f, ctx.flags)] if candidates: self.restrict = True # do not expand when reading msg = (expand and _('overwriting %s expanding keywords\n') or _('overwriting %s shrinking keywords\n')) for f in candidates: - fp = self.repo.file(f) - data = fp.read(mf[f]) + if not self.record: + data = self.repo.file(f).read(mf[f]) + else: + data = self.repo.wread(f) if util.binary(data): continue if expand: @@ -190,7 +216,7 @@ else: found = self.re_kw.search(data) if found: - notify(msg % f) + self.ui.note(msg % f) self.repo.wwrite(f, data, mf.flags(f)) if node is None: self.repo.dirstate.normal(f) @@ -251,10 +277,8 @@ '''Bails out if [keyword] configuration is not active. Returns status of working directory.''' if kwt: - unknown = (opts.get('unknown') or opts.get('all') - or opts.get('untracked')) return repo.status(match=cmdutil.match(repo, pats, opts), clean=True, - unknown=unknown) + unknown=opts.get('unknown') or opts.get('all')) if ui.configitems('keyword'): raise util.Abort(_('[keyword] patterns cannot match')) raise util.Abort(_('no [keyword] patterns configured')) @@ -264,17 +288,15 @@ if repo.dirstate.parents()[1] != nullid: raise util.Abort(_('outstanding uncommitted merge')) kwt = kwtools['templater'] - status = _status(ui, repo, kwt, *pats, **opts) - modified, added, removed, deleted = status[:4] - if modified or added or removed or deleted: - raise util.Abort(_('outstanding uncommitted changes')) - wlock = lock = None + wlock = repo.wlock() try: - wlock = repo.wlock() - lock = repo.lock() - kwt.overwrite(None, expand, status[6]) + 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')) + kwt.overwrite(None, expand, clean) finally: - release(lock, wlock) + wlock.release() def demo(ui, repo, *args, **opts): '''print [keywordmaps] configuration and an expansion example @@ -287,7 +309,7 @@ Use -d/--default to disable current configuration. - See "hg help templates" for information on templates and filters. + See :hg:`help templates` for information on templates and filters. ''' def demoitems(section, items): ui.write('[%s]\n' % section) @@ -295,7 +317,6 @@ ui.write('%s = %s\n' % (k, v)) fn = 'demo.txt' - branchname = 'demobranch' tmpdir = tempfile.mkdtemp('', 'kwdemo.') ui.note(_('creating temporary repository at %s\n') % tmpdir) repo = localrepo.localrepository(ui, tmpdir, True) @@ -320,46 +341,34 @@ kwmaps = dict(ui.configitems('keywordmaps')) elif opts.get('default'): ui.status(_('\n\tconfiguration using default keyword template maps\n')) - kwmaps = kwtemplater.templates + kwmaps = _defaultkwmaps(ui) if uikwmaps: ui.status(_('\tdisabling current template maps\n')) for k, v in kwmaps.iteritems(): ui.setconfig('keywordmaps', k, v) else: ui.status(_('\n\tconfiguration using current keyword template maps\n')) - kwmaps = dict(uikwmaps) or kwtemplater.templates + kwmaps = dict(uikwmaps) or _defaultkwmaps(ui) uisetup(ui) reposetup(ui, repo) - for k, v in ui.configitems('extensions'): - if k.endswith('keyword'): - extension = '%s = %s' % (k, v) - break - ui.write('[extensions]\n%s\n' % extension) + ui.write('[extensions]\nkeyword =\n') demoitems('keyword', ui.configitems('keyword')) demoitems('keywordmaps', kwmaps.iteritems()) keywords = '$' + '$\n$'.join(sorted(kwmaps.keys())) + '$\n' repo.wopener(fn, 'w').write(keywords) repo.add([fn]) - path = repo.wjoin(fn) - ui.note(_('\nkeywords written to %s:\n') % path) + ui.note(_('\nkeywords written to %s:\n') % fn) 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 + repo.dirstate.setbranch('demobranch') 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')) msg = _('hg keyword configuration and expansion example') - ui.note("hg -R '%s' ci -m '%s'\n" % (tmpdir, msg)) + ui.note("hg ci -m '%s'\n" % msg) repo.commit(text=msg) ui.status(_('\n\tkeywords expanded\n')) ui.write(repo.wread(fn)) - ui.debug('\nremoving temporary repository %s\n' % tmpdir) shutil.rmtree(tmpdir, ignore_errors=True) def expand(ui, repo, *pats, **opts): @@ -382,7 +391,7 @@ execution by including only files that are actual candidates for expansion. - See "hg help keyword" on how to construct patterns both for + See :hg:`help keyword` on how to construct patterns both for inclusion and exclusion of files. With -A/--all and -v/--verbose the codes used to show the status @@ -398,7 +407,7 @@ cwd = pats and repo.getcwd() or '' modified, added, removed, deleted, unknown, ignored, clean = status files = [] - if not (opts.get('unknown') or opts.get('untracked')) or opts.get('all'): + if not opts.get('unknown') or opts.get('all'): files = sorted(modified + added + clean) wctx = repo[None] kwfiles = [f for f in files if kwt.iskwfile(f, wctx.flags)] @@ -419,7 +428,7 @@ '''revert expanded keywords in the working directory Run before changing/disabling active keywords or if you experience - problems with "hg import" or "hg merge". + problems with :hg:`import` or :hg:`merge`. kwshrink refuses to run if given files contain local changes. ''' @@ -485,22 +494,18 @@ del self.commitctx def kwcommitctx(self, ctx, error=False): - wlock = lock = None - try: - wlock = self.wlock() - lock = self.lock() - n = super(kwrepo, self).commitctx(ctx, error) + n = super(kwrepo, self).commitctx(ctx, error) + # no lock needed, only called from repo.commit() which already locks + if not kwt.record: kwt.overwrite(n, True, None) - return n - finally: - release(lock, wlock) + return n # monkeypatches def kwpatchfile_init(orig, self, ui, fname, opener, - missing=False, eol=None): + missing=False, eolmode=None): '''Monkeypatch/wrap patch.patchfile.__init__ to avoid rejects or conflicts due to expanded keywords in working dir.''' - orig(self, ui, fname, opener, missing, eol) + orig(self, ui, fname, opener, missing, eolmode) # shrink keywords read from working dir self.lines = kwt.shrinklines(self.fname, self.lines) @@ -519,6 +524,20 @@ kwt.match = util.never return orig(web, req, tmpl) + def kw_dorecord(orig, ui, repo, commitfunc, *pats, **opts): + '''Wraps record.dorecord expanding keywords after recording.''' + wlock = repo.wlock() + try: + # record returns 0 even when nothing has changed + # therefore compare nodes before and after + ctx = repo['.'] + ret = orig(ui, repo, commitfunc, *pats, **opts) + if ctx != repo['.']: + kwt.overwrite('.', True, None) + return ret + finally: + wlock.release() + repo.__class__ = kwrepo extensions.wrapfunction(patch.patchfile, '__init__', kwpatchfile_init) @@ -526,6 +545,12 @@ extensions.wrapfunction(patch, 'diff', kw_diff) for c in 'annotate changeset rev filediff diff'.split(): extensions.wrapfunction(webcommands, c, kwweb_skip) + for name in recordextensions.split(): + try: + record = extensions.find(name) + extensions.wrapfunction(record, 'dorecord', kw_dorecord) + except KeyError: + pass cmdtable = { 'kwdemo': @@ -540,9 +565,6 @@ [('A', 'all', None, _('show keyword status flags of all files')), ('i', 'ignore', None, _('show files excluded from expansion')), ('u', 'unknown', None, _('only show unknown (not tracked) files')), - ('a', 'all', None, - _('show keyword status flags of all files (DEPRECATED)')), - ('u', 'untracked', None, _('only show untracked files (DEPRECATED)')), ] + commands.walkopts, _('hg kwfiles [OPTION]... [FILE]...')), 'kwshrink': (shrink, commands.walkopts, diff -r 0bb67503ad4b -r ac6fec2af8c8 hgext/mq.py --- a/hgext/mq.py Mon May 31 21:37:01 2010 +0200 +++ b/hgext/mq.py Mon May 31 21:43:03 2010 +0200 @@ -14,7 +14,7 @@ Known patches are represented as patch files in the .hg/patches directory. Applied patches are both patch files and changesets. -Common tasks (use "hg help command" for more details):: +Common tasks (use :hg:`help command` for more details):: create new patch qnew import existing patch qimport @@ -37,6 +37,9 @@ preserving existing git patches upon qrefresh. If set to 'yes' or 'no', mq will override the [diff] section and always generate git or regular patches, possibly losing data in the second case. + +You will by default be managing a patch queue named "patches". You can +create other, independent patch queues with the :hg:`qqueue` command. ''' from mercurial.i18n import _ @@ -53,18 +56,11 @@ normname = util.normpath class statusentry(object): - def __init__(self, rev, name=None): - if not name: - fields = rev.split(':', 1) - if len(fields) == 2: - self.rev, self.name = fields - else: - self.rev, self.name = None, None - else: - self.rev, self.name = rev, name + def __init__(self, node, name): + self.node, self.name = node, name def __str__(self): - return self.rev + ':' + self.name + return hex(self.node) + ':' + self.name class patchheader(object): def __init__(self, pf, plainmode=False): @@ -79,8 +75,7 @@ break def eatempty(lines): while lines: - l = lines[-1] - if re.match('\s*$', l): + if not lines[-1].strip(): del lines[-1] else: break @@ -242,7 +237,12 @@ class queue(object): def __init__(self, ui, path, patchdir=None): self.basepath = path - self.path = patchdir or os.path.join(path, "patches") + try: + fh = open(os.path.join(path, '.queue')) + curpath = os.path.join(path, fh.read().rstrip()) + except IOError: + curpath = os.path.join(path, 'patches') + self.path = patchdir or curpath self.opener = util.opener(self.path) self.ui = ui self.applied_dirty = 0 @@ -265,8 +265,11 @@ @util.propertycache def applied(self): if os.path.exists(self.join(self.status_path)): + def parse(l): + n, name = l.split(':', 1) + return statusentry(bin(n), name) lines = self.opener(self.status_path).read().splitlines() - return [statusentry(l) for l in lines] + return [parse(l) for l in lines] return [] @util.propertycache @@ -329,16 +332,12 @@ return os.path.join(self.path, *p) def find_series(self, patch): - pre = re.compile("(\s*)([^#]+)") - index = 0 - for l in self.full_series: - m = pre.match(l) - if m: - s = m.group(2) - s = s.rstrip() - if s == patch: - return index - index += 1 + def matchpatch(l): + l = l.split('#', 1)[0] + return l.strip() == patch + for index, l in enumerate(self.full_series): + if matchpatch(l): + return index return None guard_re = re.compile(r'\s?#([-+][^-+# \t\r\n\f][^# \t\r\n\f]*)') @@ -486,19 +485,9 @@ def printdiff(self, repo, diffopts, node1, node2=None, files=None, fp=None, changes=None, opts={}): stat = opts.get('stat') - if stat: - opts['unified'] = '0' - m = cmdutil.match(repo, files, opts) - chunks = patch.diff(repo, node1, node2, m, changes, diffopts) - write = fp is None and repo.ui.write or fp.write - if stat: - width = self.ui.interactive() and util.termwidth() or 80 - write(patch.diffstat(util.iterlines(chunks), width=width, - git=diffopts.git)) - else: - for chunk in chunks: - write(chunk) + cmdutil.diffordiffstat(self.ui, repo, diffopts, node1, node2, m, + changes, stat, fp) def mergeone(self, repo, mergeq, head, patch, rev, diffopts): # first try just applying the patch @@ -544,22 +533,16 @@ (p1, p2) = repo.dirstate.parents() if p2 == nullid: return p1 - if len(self.applied) == 0: + if not self.applied: return None - return bin(self.applied[-1].rev) - pp = repo.changelog.parents(rev) - if pp[1] != nullid: - arevs = [x.rev for x in self.applied] - p0 = hex(pp[0]) - p1 = hex(pp[1]) - if p0 in arevs: - return pp[0] - if p1 in arevs: - return pp[1] - return pp[0] + return self.applied[-1].node + p1, p2 = repo.changelog.parents(rev) + if p2 != nullid and p2 in [x.node for x in self.applied]: + return p2 + return p1 def mergepatch(self, repo, mergeq, series, diffopts): - if len(self.applied) == 0: + if not self.applied: # each of the patches merged in will have two parents. This # can confuse the qrefresh, qdiff, and strip code because it # needs to know which parent is actually in the patch queue. @@ -569,7 +552,7 @@ pname = ".hg.patches.merge.marker" n = repo.commit('[mq]: merge marker', force=True) self.removeundo(repo) - self.applied.append(statusentry(hex(n), pname)) + self.applied.append(statusentry(n, pname)) self.applied_dirty = 1 head = self.qparents(repo) @@ -587,10 +570,10 @@ if not info: self.ui.warn(_("patch %s is not applied\n") % patch) return (1, None) - rev = bin(info[1]) + rev = info[1] err, head = self.mergeone(repo, mergeq, head, patch, rev, diffopts) if head: - self.applied.append(statusentry(hex(head), patch)) + self.applied.append(statusentry(head, patch)) self.applied_dirty = 1 if err: return (err, head) @@ -613,12 +596,12 @@ return (True, files, fuzz) def apply(self, repo, series, list=False, update_status=True, - strict=False, patchdir=None, merge=None, all_files={}): + strict=False, patchdir=None, merge=None, all_files=None): wlock = lock = tr = None try: wlock = repo.wlock() lock = repo.lock() - tr = repo.transaction() + tr = repo.transaction("qpush") try: ret = self._apply(repo, series, list, update_status, strict, patchdir, merge, all_files=all_files) @@ -633,12 +616,11 @@ repo.dirstate.invalidate() raise finally: - del tr - release(lock, wlock) + release(tr, lock, wlock) self.removeundo(repo) def _apply(self, repo, series, list=False, update_status=True, - strict=False, patchdir=None, merge=None, all_files={}): + strict=False, patchdir=None, merge=None, all_files=None): '''returns (error, hash) error = 1 for unable to read, 2 for patch failed, 3 for patch fuzz''' # TODO unify with commands.py @@ -671,7 +653,8 @@ if ph.haspatch: (patcherr, files, fuzz) = self.patch(repo, pf) - all_files.update(files) + if all_files is not None: + all_files.update(files) patcherr = not patcherr else: self.ui.warn(_("patch %s is empty\n") % patchname) @@ -701,7 +684,7 @@ raise util.Abort(_("repo commit failed")) if update_status: - self.applied.append(statusentry(hex(n), patchname)) + self.applied.append(statusentry(n, patchname)) if patcherr: self.ui.warn(_("patch failed, rejects left in working dir\n")) @@ -733,7 +716,7 @@ self.series_dirty = 1 def _revpatches(self, repo, revs): - firstrev = repo[self.applied[0].rev].rev() + firstrev = repo[self.applied[0].node].rev() patches = [] for i, rev in enumerate(revs): @@ -741,7 +724,7 @@ raise util.Abort(_('revision %d is not managed') % rev) ctx = repo[rev] - base = bin(self.applied[i].rev) + base = self.applied[i].node if ctx.node() != base: msg = _('cannot delete revision %d above applied patches') raise util.Abort(msg % rev) @@ -765,7 +748,6 @@ raise util.Abort(_('qdelete requires at least one revision or ' 'patch name')) - realpatches = [] for patch in patches: patch = self.lookup(patch, strict=True) info = self.isapplied(patch) @@ -773,8 +755,8 @@ raise util.Abort(_("cannot delete applied patch %s") % patch) if patch not in self.series: raise util.Abort(_("patch %s not in series file") % patch) - realpatches.append(patch) + patches = list(patches) numrevs = 0 if opts.get('rev'): if not self.applied: @@ -783,14 +765,14 @@ if len(revs) > 1 and revs[0] > revs[1]: revs.reverse() revpatches = self._revpatches(repo, revs) - realpatches += revpatches + patches += revpatches numrevs = len(revpatches) - self._cleanup(realpatches, numrevs, opts.get('keep')) + self._cleanup(patches, numrevs, opts.get('keep')) def check_toppatch(self, repo): - if len(self.applied) > 0: - top = bin(self.applied[-1].rev) + if self.applied: + top = self.applied[-1].node patch = self.applied[-1].name pp = repo.dirstate.parents() if top not in pp: @@ -870,7 +852,7 @@ raise util.Abort(_("repo commit failed")) try: self.full_series[insert:insert] = [patchfn] - self.applied.append(statusentry(hex(n), patchfn)) + self.applied.append(statusentry(n, patchfn)) self.parse_series() self.series_dirty = 1 self.applied_dirty = 1 @@ -927,7 +909,7 @@ """returns (index, rev, patch)""" for i, a in enumerate(self.applied): if a.name == patch: - return (i, a.rev, a.name) + return (i, a.node, a.name) return None # if the exact patch name does not exist, we try a few @@ -950,7 +932,7 @@ return None if matches: return matches[0] - if len(self.series) > 0 and len(self.applied) > 0: + if self.series and self.applied: if s == 'qtip': return self.series[self.series_end(True)-1] if s == 'qbase': @@ -1002,7 +984,7 @@ raise util.Abort(_("patch %s not in series") % patch) def push(self, repo, patch=None, force=False, list=False, - mergeq=None, all=False): + mergeq=None, all=False, move=False): diffopts = self.diffopts() wlock = repo.wlock() try: @@ -1058,6 +1040,15 @@ if not force: self.check_localchanges(repo) + if move: + try: + del self.full_series[self.full_series.index(patch, start)] + except ValueError: + raise util.Abort(_("patch '%s' not found") % patch) + self.full_series.insert(start, patch) + self.parse_series() + self.series_dirty = 1 + self.applied_dirty = 1 if start > 0: self.check_toppatch(repo) @@ -1068,7 +1059,7 @@ end = self.series.index(patch, start) + 1 s = self.series[start:end] - all_files = {} + all_files = set() try: if mergeq: ret = self.mergepatch(repo, mergeq, s, diffopts) @@ -1078,12 +1069,15 @@ self.ui.warn(_('cleaning up working directory...')) node = repo.dirstate.parents()[0] hg.revert(repo, node, None) - unknown = repo.status(unknown=True)[4] # only remove unknown files that we know we touched or # created while patching - for f in unknown: - if f in all_files: - util.unlink(repo.wjoin(f)) + for f in all_files: + if f not in repo.dirstate: + try: + util.unlink(repo.wjoin(f)) + except OSError, inst: + if inst.errno != errno.ENOENT: + raise self.ui.warn(_('done\n')) raise @@ -1101,10 +1095,6 @@ wlock.release() def pop(self, repo, patch=None, force=False, update=True, all=False): - def getfile(f, rev, flags): - t = repo.file(f).read(rev) - repo.wwrite(f, t, flags) - wlock = repo.wlock() try: if patch: @@ -1116,7 +1106,7 @@ if not info: raise util.Abort(_("patch %s is not applied") % patch) - if len(self.applied) == 0: + if not self.applied: # Allow qpop -a to work repeatedly, # but not qpop without an argument self.ui.warn(_("no patches applied\n")) @@ -1135,16 +1125,16 @@ if not update: parents = repo.dirstate.parents() - rr = [bin(x.rev) for x in self.applied] + rr = [x.node for x in self.applied] for p in parents: if p in rr: self.ui.warn(_("qpop: forcing dirstate update\n")) update = True else: - parents = [p.hex() for p in repo[None].parents()] + parents = [p.node() for p in repo[None].parents()] needupdate = False for entry in self.applied[start:]: - if entry.rev in parents: + if entry.node in parents: needupdate = True break update = needupdate @@ -1154,7 +1144,7 @@ self.applied_dirty = 1 end = len(self.applied) - rev = bin(self.applied[start].rev) + rev = self.applied[start].node if update: top = self.check_toppatch(repo)[0] @@ -1164,7 +1154,7 @@ node = short(rev) raise util.Abort(_('trying to pop unknown node %s') % node) - if heads != [bin(self.applied[-1].rev)]: + if heads != [self.applied[-1].node]: raise util.Abort(_("popping would remove a revision not " "managed by this patch queue")) @@ -1172,8 +1162,7 @@ # form of hg.update. if update: qp = self.qparents(repo, rev) - changes = repo.changelog.read(qp) - mmap = repo.manifest.read(changes[0]) + ctx = repo[qp] m, a, r, d = repo.status(qp, top)[:4] if d: raise util.Abort(_("deletions found between repo revs")) @@ -1184,18 +1173,16 @@ if e.errno != errno.ENOENT: raise repo.dirstate.forget(f) - for f in m: - getfile(f, mmap[f], mmap.flags(f)) - for f in r: - getfile(f, mmap[f], mmap.flags(f)) for f in m + r: + fctx = ctx[f] + repo.wwrite(f, fctx.data(), fctx.flags()) repo.dirstate.normal(f) repo.dirstate.setparents(qp, nullid) for patch in reversed(self.applied[start:end]): self.ui.status(_("popping %s\n") % patch.name) del self.applied[start:end] self.strip(repo, rev, update=False, backup='strip') - if len(self.applied): + if self.applied: self.ui.write(_("now at: %s\n") % self.applied[-1].name) else: self.ui.write(_("patch queue now empty\n")) @@ -1216,7 +1203,7 @@ self.printdiff(repo, diffopts, node1, node2, files=pats, opts=opts) def refresh(self, repo, pats=None, **opts): - if len(self.applied) == 0: + if not self.applied: self.ui.write(_("no patches applied\n")) return 1 msg = opts.get('msg', '').rstrip() @@ -1228,8 +1215,7 @@ try: self.check_toppatch(repo) - (top, patchfn) = (self.applied[-1].rev, self.applied[-1].name) - top = bin(top) + (top, patchfn) = (self.applied[-1].node, self.applied[-1].name) if repo.changelog.heads(top) != [top]: raise util.Abort(_("cannot refresh a revision with children")) @@ -1386,7 +1372,7 @@ patchf.rename() n = repo.commit(message, user, ph.date, match=match, force=True) - self.applied.append(statusentry(hex(n), patchfn)) + self.applied.append(statusentry(n, patchfn)) except: ctx = repo[cparents[0]] repo.dirstate.rebuild(ctx.node(), ctx.manifest()) @@ -1426,7 +1412,9 @@ def qseries(self, repo, missing=None, start=0, length=None, status=None, summary=False): - def displayname(pfx, patchname): + def displayname(pfx, patchname, state): + if pfx: + self.ui.write(pfx) if summary: ph = patchheader(self.join(patchname), self.plainmode) msg = ph.message and ph.message[0] or '' @@ -1436,10 +1424,12 @@ msg = util.ellipsis(msg, width) else: msg = '' - msg = "%s%s: %s" % (pfx, patchname, msg) + self.ui.write(patchname, label='qseries.' + state) + self.ui.write(': ') + self.ui.write(msg, label='qseries.message.' + state) else: - msg = pfx + patchname - self.ui.write(msg + '\n') + self.ui.write(patchname, label='qseries.' + state) + self.ui.write('\n') applied = set([p.name for p in self.applied]) if length is None: @@ -1450,17 +1440,17 @@ for i in xrange(start, start + length): patch = self.series[i] if patch in applied: - stat = 'A' + char, state = 'A', 'applied' elif self.pushable(i)[0]: - stat = 'U' + char, state = 'U', 'unapplied' else: - stat = 'G' + char, state = 'G', 'guarded' pfx = '' if self.ui.verbose: - pfx = '%*d %s ' % (idxwidth, i, stat) - elif status and status != stat: + pfx = '%*d %s ' % (idxwidth, i, char) + elif status and status != char: continue - displayname(pfx, patch) + displayname(pfx, patch, state) else: msng_list = [] for root, dirs, files in os.walk(self.path): @@ -1474,7 +1464,7 @@ msng_list.append(fl) for x in sorted(msng_list): pfx = self.ui.verbose and ('D ') or '' - displayname(pfx, x) + displayname(pfx, x, 'missing') def issaveline(self, l): if l.name == '.hg.patches.save.line': @@ -1485,8 +1475,7 @@ return hg.repository(self.ui, path=self.path, create=create) def restore(self, repo, rev, delete=None, qupdate=None): - c = repo.changelog.read(rev) - desc = c[4].strip() + desc = repo[rev].description().strip() lines = desc.splitlines() i = 0 datastart = None @@ -1502,12 +1491,11 @@ qpp = [bin(x) for x in l] elif datastart != None: l = line.rstrip() - se = statusentry(l) - file_ = se.name - if se.rev: - applied.append(se) + n, name = l.split(':', 1) + if n: + applied.append(statusentry(bin(n), name)) else: - series.append(file_) + series.append(l) if datastart is None: self.ui.warn(_("No saved patch data found\n")) return 1 @@ -1541,14 +1529,13 @@ hg.clean(r, qpp[0]) def save(self, repo, msg=None): - if len(self.applied) == 0: + if not self.applied: self.ui.warn(_("save: no patches applied, exiting\n")) return 1 if self.issaveline(self.applied[-1]): self.ui.warn(_("status is already saved\n")) return 1 - ar = [':' + x for x in self.full_series] if not msg: msg = _("hg patches saved state") else: @@ -1558,18 +1545,18 @@ pp = r.dirstate.parents() 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 "") - n = repo.commit(text, force=True) + msg += ''.join('%s\n' % x for x in self.applied) + msg += ''.join(':%s\n' % x for x in self.full_series) + n = repo.commit(msg, force=True) if not n: self.ui.warn(_("repo commit failed\n")) return 1 - self.applied.append(statusentry(hex(n),'.hg.patches.save.line')) + self.applied.append(statusentry(n, '.hg.patches.save.line')) self.applied_dirty = 1 self.removeundo(repo) def full_series_end(self): - if len(self.applied) > 0: + if self.applied: p = self.applied[-1].name end = self.find_series(p) if end is None: @@ -1584,17 +1571,15 @@ """ end = 0 def next(start): - if all_patches: + if all_patches or start >= len(self.series): return start - i = start - while i < len(self.series): + for i in xrange(start, len(self.series)): p, reason = self.pushable(i) if p: break self.explain_pushable(i) - i += 1 return i - if len(self.applied) > 0: + if self.applied: p = self.applied[-1].name try: end = self.series.index(p) @@ -1631,7 +1616,6 @@ if (len(files) > 1 or len(rev) > 1) and patchname: raise util.Abort(_('option "-n" not valid when importing multiple ' 'patches')) - i = 0 added = [] if rev: # If mq patches are applied, we can only import revisions @@ -1642,14 +1626,14 @@ raise util.Abort(_('revision %d is the root of more than one ' 'branch') % rev[-1]) if self.applied: - base = hex(repo.changelog.node(rev[0])) - if base in [n.rev for n in self.applied]: + base = repo.changelog.node(rev[0]) + if base in [n.node for n in self.applied]: raise util.Abort(_('revision %d is already managed') % rev[0]) - if heads != [bin(self.applied[-1].rev)]: + if heads != [self.applied[-1].node]: raise util.Abort(_('revision %d is not the parent of ' 'the queue') % rev[0]) - base = repo.changelog.rev(bin(self.applied[0].rev)) + base = repo.changelog.rev(self.applied[0].node) lastparent = repo.changelog.parentrevs(base)[0] else: if heads != [repo.changelog.node(rev[0])]: @@ -1676,10 +1660,10 @@ self.full_series.insert(0, patchname) patchf = self.opener(patchname, "w") - patch.export(repo, [n], fp=patchf, opts=diffopts) + cmdutil.export(repo, [n], fp=patchf, opts=diffopts) patchf.close() - se = statusentry(hex(n), patchname) + se = statusentry(n, patchname) self.applied.insert(0, se) added.append(patchname) @@ -1687,7 +1671,7 @@ self.parse_series() self.applied_dirty = 1 - for filename in files: + for i, filename in enumerate(files): if existing: if filename == '-': raise util.Abort(_('-e is incompatible with import from -')) @@ -1720,7 +1704,6 @@ self.full_series[index:index] = [patchname] self.parse_series() self.ui.warn(_("adding %s to series file\n") % patchname) - i += 1 added.append(patchname) patchname = None self.series_dirty = 1 @@ -1859,7 +1842,7 @@ qcommit to commit changes to this queue repository. This command is deprecated. Without -c, it's implied by other relevant - commands. With -c, use hg init --mq instead.""" + commands. With -c, use :hg:`init --mq` instead.""" return qinit(ui, repo, create=opts['create_repo']) def clone(ui, source, dest=None, **opts): @@ -1875,7 +1858,7 @@ default. Use -p to change. The patch directory must be a nested Mercurial repository, as - would be created by init --mq. + would be created by :hg:`init --mq`. ''' def patchdir(repo): url = repo.url() @@ -1897,7 +1880,7 @@ qbase, destrev = None, None if sr.local(): if sr.mq.applied: - qbase = bin(sr.mq.applied[0].rev) + qbase = sr.mq.applied[0].node if not hg.islocal(dest): heads = set(sr.heads()) destrev = list(heads.difference(sr.heads(qbase))) @@ -1929,7 +1912,7 @@ def commit(ui, repo, *pats, **opts): """commit changes in the queue repository (DEPRECATED) - This command is deprecated; use hg commit --mq instead.""" + This command is deprecated; use :hg:`commit --mq` instead.""" q = repo.mq r = q.qrepo() if not r: @@ -2056,9 +2039,9 @@ last refresh (thus showing what the current patch would become after a qrefresh). - Use 'hg diff' if you only want to see the changes made since the - last qrefresh, or 'hg export qtip' if you want to see changes made - by the current patch without including changes made since the + Use :hg:`diff` if you only want to see the changes made since the + last qrefresh, or :hg:`export qtip` if you want to see changes + made by the current patch without including changes made since the qrefresh. """ repo.mq.diff(repo, pats, opts) @@ -2158,7 +2141,17 @@ ''' def status(idx): guards = q.series_guards[idx] or ['unguarded'] - ui.write('%s: %s\n' % (q.series[idx], ' '.join(guards))) + ui.write('%s: ' % ui.label(q.series[idx], 'qguard.patch')) + for i, guard in enumerate(guards): + if guard.startswith('+'): + ui.write(guard, label='qguard.positive') + elif guard.startswith('-'): + ui.write(guard, label='qguard.negative') + else: + ui.write(guard, label='qguard.unguarded') + if i != len(guards) - 1: + ui.write(' ') + ui.write('\n') q = repo.mq patch = None args = list(args) @@ -2244,7 +2237,7 @@ mergeq = queue(ui, repo.join(""), newpath) ui.warn(_("merging with queue at: %s\n") % mergeq.path) ret = q.push(repo, patch, force=opts['force'], list=opts['list'], - mergeq=mergeq, all=opts.get('all')) + mergeq=mergeq, all=opts.get('all'), move=opts.get('move')) return ret def pop(ui, repo, patch=None, **opts): @@ -2384,8 +2377,8 @@ completes. Any stripped changesets are stored in ``.hg/strip-backup`` as a - bundle (see ``hg help bundle`` and ``hg help unbundle``). They can - be restored by running ``hg unbundle .hg/strip-backup/BUNDLE``, + bundle (see :hg:`help bundle` and :hg:`help unbundle`). They can + be restored by running :hg:`unbundle .hg/strip-backup/BUNDLE`, where BUNDLE is the bundle file created by the strip. Note that the local revision numbers will in general be different after the restore. @@ -2547,6 +2540,107 @@ q.save_dirty() return 0 +def qqueue(ui, repo, name=None, **opts): + '''manage multiple patch queues + + Supports switching between different patch queues, as well as creating + new patch queues and deleting existing ones. + + Omitting a queue name or specifying -l/--list will show you the registered + queues - by default the "normal" patches queue is registered. The currently + active queue will be marked with "(active)". + + To create a new queue, use -c/--create. The queue is automatically made + active, except in the case where there are applied patches from the + currently active queue in the repository. Then the queue will only be + created and switching will fail. + + To delete an existing queue, use --delete. You cannot delete the currently + active queue. + ''' + + q = repo.mq + + _defaultqueue = 'patches' + _allqueues = '.queues' + _activequeue = '.queue' + + def _getcurrent(): + return os.path.basename(q.path) + + def _noqueues(): + try: + fh = repo.opener(_allqueues, 'r') + fh.close() + except IOError: + return True + + return False + + def _getqueues(): + current = _getcurrent() + + try: + fh = repo.opener(_allqueues, 'r') + queues = [queue.strip() for queue in fh if queue.strip()] + if current not in queues: + queues.append(current) + except IOError: + queues = [_defaultqueue] + + return sorted(queues) + + def _setactive(name): + if q.applied: + raise util.Abort(_('patches applied - cannot set new queue active')) + + fh = repo.opener(_activequeue, 'w') + fh.write(name) + fh.close() + + def _addqueue(name): + fh = repo.opener(_allqueues, 'a') + fh.write('%s\n' % (name,)) + fh.close() + + if not name or opts.get('list'): + current = _getcurrent() + for queue in _getqueues(): + ui.write('%s' % (queue,)) + if queue == current: + ui.write(_(' (active)\n')) + else: + ui.write('\n') + return + + existing = _getqueues() + + if name not in existing and opts.get('delete'): + raise util.Abort(_('cannot delete queue that does not exist')) + elif name not in existing and not opts.get('create'): + raise util.Abort(_('use --create to create a new queue')) + + if opts.get('create'): + if _noqueues(): + _addqueue(_defaultqueue) + _addqueue(name) + _setactive(name) + elif opts.get('delete'): + current = _getcurrent() + + if name == current: + raise util.Abort(_('cannot delete currently active queue')) + + fh = repo.opener('.queues.new', 'w') + for queue in existing: + if queue == name: + continue + fh.write('%s\n' % (queue,)) + fh.close() + util.rename(repo.join('.queues.new'), repo.join(_allqueues)) + else: + _setactive(name) + def reposetup(ui, repo): class mqrepo(repo.__class__): @util.propertycache @@ -2555,8 +2649,8 @@ def abort_if_wdir_patched(self, errmsg, force=False): if self.mq.applied and not force: - parent = hex(self.dirstate.parents()[0]) - if parent in [s.rev for s in self.mq.applied]: + parent = self.dirstate.parents()[0] + if parent in [s.node for s in self.mq.applied]: raise util.Abort(errmsg) def commit(self, text="", user=None, date=None, match=None, @@ -2568,10 +2662,10 @@ return super(mqrepo, self).commit(text, user, date, match, force, editor, extra) - def push(self, remote, force=False, revs=None): + def push(self, remote, force=False, revs=None, newbranch=False): if self.mq.applied and not force and not revs: raise util.Abort(_('source has mq patches applied')) - return super(mqrepo, self).push(remote, force, revs) + return super(mqrepo, self).push(remote, force, revs, newbranch) def _findtags(self): '''augment tags from base class with patch tags''' @@ -2581,7 +2675,7 @@ if not q.applied: return result - mqtags = [(bin(patch.rev), patch.name) for patch in q.applied] + mqtags = [(patch.node, 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') @@ -2607,7 +2701,7 @@ return super(mqrepo, self)._branchtags(partial, lrev) cl = self.changelog - qbasenode = bin(q.applied[0].rev) + qbasenode = q.applied[0].node if qbasenode not in cl.nodemap: self.ui.warn(_('mq status file refers to unknown node %s\n') % short(qbasenode)) @@ -2672,13 +2766,29 @@ q = repo.mq r = q.qrepo() if not r: - raise util.Abort('no queue repository') + raise util.Abort(_('no queue repository')) return orig(r.ui, r, *args, **kwargs) +def summary(orig, ui, repo, *args, **kwargs): + r = orig(ui, repo, *args, **kwargs) + q = repo.mq + m = [] + a, u = len(q.applied), len(q.unapplied(repo)) + if a: + m.append(ui.label(_("%d applied"), 'qseries.applied') % a) + if u: + m.append(ui.label(_("%d unapplied"), 'qseries.unapplied') % u) + if m: + ui.write("mq: %s\n" % ', '.join(m)) + else: + ui.note(_("mq: (empty queue)\n")) + return r + def uisetup(ui): mqopt = [('', 'mq', None, _("operate on patch repository"))] extensions.wrapcommand(commands.table, 'import', mqimport) + extensions.wrapcommand(commands.table, 'summary', summary) entry = extensions.wrapcommand(commands.table, 'init', mqinit) entry[1].extend(mqopt) @@ -2736,7 +2846,7 @@ ('n', 'none', None, _('drop all guards'))], _('hg qguard [-l] [-n] [PATCH] [-- [+GUARD]... [-GUARD]...]')), 'qheader': (header, [], _('hg qheader [PATCH]')), - "^qimport": + "qimport": (qimport, [('e', 'existing', None, _('import file in patch directory')), ('n', 'name', '', _('name of patch file')), @@ -2749,7 +2859,7 @@ (init, [('c', 'create-repo', None, _('create queue repository'))], _('hg qinit [-c]')), - "qnew": + "^qnew": (new, [('e', 'edit', None, _('edit commit message')), ('f', 'force', None, _('import uncommitted changes (DEPRECATED)')), @@ -2774,8 +2884,9 @@ ('l', 'list', None, _('list patch name in commit text')), ('a', 'all', None, _('apply all patches')), ('m', 'merge', None, _('merge from another queue (DEPRECATED)')), - ('n', 'name', '', _('merge queue name (DEPRECATED)'))], - _('hg qpush [-f] [-l] [-a] [-m] [-n NAME] [PATCH | INDEX]')), + ('n', 'name', '', _('merge queue name (DEPRECATED)')), + ('', 'move', None, _('reorder patch series and apply only the patch'))], + _('hg qpush [-f] [-l] [-a] [-m] [-n NAME] [--move] [PATCH | INDEX]')), "^qrefresh": (refresh, [('e', 'edit', None, _('edit commit message')), @@ -2818,7 +2929,7 @@ [('m', 'missing', None, _('print patches not in series')), ] + seriesopts, _('hg qseries [-ms]')), - "^strip": + "strip": (strip, [('f', 'force', None, _('force removal of changesets even if the ' 'working directory has uncommitted changes')), @@ -2826,7 +2937,7 @@ ' number greater than REV which are not' ' descendants of REV (DEPRECATED)')), ('n', 'nobackup', None, _('no backups'))], - _('hg strip [-f] [-b] [-n] REV')), + _('hg strip [-f] [-n] REV')), "qtop": (top, [] + seriesopts, _('hg qtop [-s]')), "qunapplied": (unapplied, @@ -2836,4 +2947,20 @@ (finish, [('a', 'applied', None, _('finish all applied changesets'))], _('hg qfinish [-a] [REV]...')), + 'qqueue': + (qqueue, + [ + ('l', 'list', False, _('list all available queues')), + ('c', 'create', False, _('create new queue')), + ('', 'delete', False, _('delete reference to queue')), + ], + _('[OPTION] [QUEUE]')), } + +colortable = {'qguard.negative': 'red', + 'qguard.positive': 'yellow', + 'qguard.unguarded': 'green', + 'qseries.applied': 'blue bold underline', + 'qseries.guarded': 'black bold', + 'qseries.missing': 'red bold', + 'qseries.unapplied': 'black bold'} diff -r 0bb67503ad4b -r ac6fec2af8c8 hgext/pager.py --- a/hgext/pager.py Mon May 31 21:37:01 2010 +0200 +++ b/hgext/pager.py Mon May 31 21:43:03 2010 +0200 @@ -45,8 +45,8 @@ 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 +To ignore global commands like :hg:`version` or :hg:`help`, you have +to specify them in the global .hgrc ''' import sys, os, signal, shlex, errno diff -r 0bb67503ad4b -r ac6fec2af8c8 hgext/patchbomb.py --- a/hgext/patchbomb.py Mon May 31 21:37:01 2010 +0200 +++ b/hgext/patchbomb.py Mon May 31 21:43:03 2010 +0200 @@ -16,7 +16,7 @@ - The changeset description. - [Optional] The result of running diffstat on the patch. -- The patch itself, as generated by "hg export". +- The patch itself, as generated by :hg:`export`. Each message refers to the first in the series using the In-Reply-To and References headers, so they will show up as a sequence in threaded @@ -34,15 +34,16 @@ to = recipient1, recipient2, ... cc = cc1, cc2, ... bcc = bcc1, bcc2, ... + reply-to = address1, address2, ... Use ``[patchbomb]`` as configuration section name if you need to override global ``[email]`` address settings. -Then you can use the "hg email" command to mail a series of changesets -as a patchbomb. +Then you can use the :hg:`email` command to mail a series of +changesets as a patchbomb. To avoid sending patches prematurely, it is a good idea to first run -the "email" command with the "-n" option (test only). You will be +the :hg:`email` command with the "-n" option (test only). You will be prompted for an email recipient address, a subject and an introductory message describing the patches of your patchbomb. Then when all is done, patchbomb messages are displayed. If the PAGER environment @@ -105,6 +106,10 @@ raise util.Abort(_('diffstat rejected')) return s +def introneeded(opts, number): + '''is an introductory message required?''' + return number > 1 or opts.get('intro') or opts.get('desc') + def makepatch(ui, repo, patch, opts, _charsets, idx, total, patchname=None): desc = [] @@ -170,7 +175,7 @@ flag = ' ' + flag subj = desc[0].strip().rstrip('. ') - if total == 1 and not opts.get('intro'): + if not introneeded(opts, total): subj = '[PATCH%s] %s' % (flag, opts.get('subject') or subj) else: tlen = len(str(total)) @@ -182,9 +187,9 @@ def patchbomb(ui, repo, *revs, **opts): '''send changesets by email - By default, diffs are sent in the format generated by hg export, - one per message. The series starts with a "[PATCH 0 of N]" - introduction, which describes the series as a whole. + By default, diffs are sent in the format generated by + :hg:`export`, one per message. The series starts with a "[PATCH 0 + of N]" introduction, which describes the series as a whole. Each patch email has a Subject line of "[PATCH M of N] ...", using the first line of the changeset description as the subject text. @@ -192,7 +197,7 @@ description. Next, (optionally) if the diffstat program is installed and -d/--diffstat is used, the result of running diffstat on the patch. Finally, the patch itself, as generated by - "hg export". + :hg:`export`. By default the patch is included as text in the email body for easy reviewing. Using the -a/--attach option will instead create @@ -249,7 +254,7 @@ def getpatches(revs): for r in cmdutil.revrange(repo, revs): output = cStringIO.StringIO() - patch.export(repo, [r], fp=output, + cmdutil.export(repo, [r], fp=output, opts=patch.diffopts(ui, opts)) yield output.getvalue().split('\n') @@ -329,7 +334,7 @@ len(patches), name) msgs.append(msg) - if len(patches) > 1 or opts.get('intro'): + if introneeded(opts, len(patches)): tlen = len(str(len(patches))) flag = ' '.join(opts.get('flag')) @@ -386,8 +391,9 @@ msgs = getpatchmsgs(list(getpatches(revs))) def getaddrs(opt, prpt=None, default=None): - if opts.get(opt): - return mail.addrlistencode(ui, opts.get(opt), _charsets, + addrs = opts.get(opt.replace('-', '_')) + if addrs: + return mail.addrlistencode(ui, addrs, _charsets, opts.get('test')) addrs = (ui.config('email', opt) or @@ -400,6 +406,7 @@ to = getaddrs('to', 'To') cc = getaddrs('cc', 'Cc', '') bcc = getaddrs('bcc') + replyto = getaddrs('reply-to') ui.write('\n') @@ -438,10 +445,12 @@ m['Cc'] = ', '.join(cc) if bcc: m['Bcc'] = ', '.join(bcc) + if replyto: + m['Reply-To'] = ', '.join(replyto) if opts.get('test'): ui.status(_('Displaying '), subj, ' ...\n') ui.flush() - if 'PAGER' in os.environ: + if 'PAGER' in os.environ and not ui.plain(): fp = util.popen(os.environ['PAGER'], 'w') else: fp = ui @@ -489,6 +498,7 @@ ('n', 'test', None, _('print messages that would be sent')), ('m', 'mbox', '', _('write messages to mbox file instead of sending them')), + ('', 'reply-to', [], _('email addresses replies should be sent to')), ('s', 'subject', '', _('subject of first message (intro or single patch)')), ('', 'in-reply-to', '', diff -r 0bb67503ad4b -r ac6fec2af8c8 hgext/progress.py --- a/hgext/progress.py Mon May 31 21:37:01 2010 +0200 +++ b/hgext/progress.py Mon May 31 21:43:03 2010 +0200 @@ -114,7 +114,7 @@ if tail: used += len(tail) + 1 progwidth = termwidth - used - 3 - if total: + if total and pos <= total: amt = pos * progwidth // total bar = '=' * (amt - 1) if amt > 0: @@ -165,10 +165,10 @@ self.show(topic, pos, item, unit, total) return orig(topic, pos, item=item, unit=unit, total=total) - def write(self, orig, *args): + def write(self, orig, *args, **opts): if self.printed: self.clear() - return orig(*args) + return orig(*args, **opts) sharedprog = None diff -r 0bb67503ad4b -r ac6fec2af8c8 hgext/purge.py --- a/hgext/purge.py Mon May 31 21:37:01 2010 +0200 +++ b/hgext/purge.py Mon May 31 21:43:03 2010 +0200 @@ -37,7 +37,7 @@ This means that purge will delete: - - Unknown files: files marked with "?" by "hg status" + - Unknown files: files marked with "?" by :hg:`status` - Empty directories: in fact Mercurial ignores directories unless they contain files under source control management @@ -45,7 +45,7 @@ - Modified and unmodified tracked files - Ignored files (unless --all is specified) - - New files added to the repository (with "hg add") + - New files added to the repository (with :hg:`add`) If directories are given on the command line, only files in these directories are considered. diff -r 0bb67503ad4b -r ac6fec2af8c8 hgext/rebase.py --- a/hgext/rebase.py Mon May 31 21:37:01 2010 +0200 +++ b/hgext/rebase.py Mon May 31 21:43:03 2010 +0200 @@ -14,7 +14,7 @@ http://mercurial.selenic.com/wiki/RebaseExtension ''' -from mercurial import util, repair, merge, cmdutil, commands, error +from mercurial import hg, util, repair, merge, cmdutil, commands, error from mercurial import extensions, ancestor, copies, patch from mercurial.commands import templateopts from mercurial.node import nullrev @@ -69,6 +69,8 @@ If a rebase is interrupted to manually resolve a merge, it can be continued with --continue/-c or aborted with --abort/-a. + + Returns 0 on success, 1 if nothing to rebase. """ originalwd = target = None external = nullrev @@ -92,6 +94,9 @@ keepf = opts.get('keep', False) keepbranchesf = opts.get('keepbranches', False) detachf = opts.get('detach', False) + # keepopen is not meant for use on the command line, but by + # other extensions + keepopen = opts.get('keepopen', False) if contf or abortf: if contf and abortf: @@ -112,8 +117,7 @@ (originalwd, target, state, collapsef, keepf, keepbranchesf, external) = restorestatus(repo) if abortf: - abort(repo, originalwd, target, state) - return + return abort(repo, originalwd, target, state) else: if srcf and basef: raise error.ParseError('rebase', _('cannot specify both a ' @@ -131,7 +135,7 @@ if not result: # Empty state built, nothing to rebase ui.status(_('nothing to rebase\n')) - return + return 1 else: originalwd, target, state = result if collapsef: @@ -183,7 +187,7 @@ ui.note(_('rebase merging completed\n')) - if collapsef: + if collapsef and not keepopen: p1, p2 = defineparents(repo, min(state), target, state, targetancestors) commitmsg = 'Collapsed revision' @@ -205,10 +209,11 @@ ui.warn(_("warning: new changesets detected " "on source branch, not stripping\n")) else: - repair.strip(ui, repo, repo[min(rebased)].node(), "strip") + # backup the old csets by default + repair.strip(ui, repo, repo[min(rebased)].node(), "all") clearstatus(repo) - ui.status(_("rebase completed\n")) + ui.note(_("rebase completed\n")) if os.path.exists(repo.sjoin('undo')): util.unlink(repo.sjoin('undo')) if skipped: @@ -349,10 +354,10 @@ 'Update rebased mq patches - finalize and then import them' mqrebase = {} for p in repo.mq.applied: - if repo[p.rev].rev() in state: + if repo[p.node].rev() in state: repo.ui.debug('revision %d is an mq patch (%s), finalize it.\n' % - (repo[p.rev].rev(), p.name)) - mqrebase[repo[p.rev].rev()] = (p.name, isagitpatch(repo, p.name)) + (repo[p.node].rev(), p.name)) + mqrebase[repo[p.node].rev()] = (p.name, isagitpatch(repo, p.name)) if mqrebase: repo.mq.finish(repo, mqrebase.keys()) @@ -423,16 +428,19 @@ 'Restore the repository to its original state' if set(repo.changelog.descendants(target)) - set(state.values()): repo.ui.warn(_("warning: new changesets detected on target branch, " - "not stripping\n")) + "can't abort\n")) + return -1 else: # Strip from the first rebased revision merge.update(repo, repo[originalwd].rev(), False, True, False) rebased = filter(lambda x: x > -1, state.values()) if rebased: strippoint = min(rebased) - repair.strip(repo.ui, repo, repo[strippoint].node(), "strip") + # no backup of rebased cset versions needed + repair.strip(repo.ui, repo, repo[strippoint].node()) clearstatus(repo) repo.ui.status(_('rebase aborted\n')) + return 0 def buildstate(repo, dest, src, base, detach): 'Define which revisions are going to be rebased and where' @@ -449,8 +457,8 @@ # This check isn't strictly necessary, since mq detects commits over an # applied patch. But it prevents messing up the working directory when # a partially completed rebase is blocked by mq. - if 'qtip' in repo.tags() and (repo[dest].hex() in - [s.rev for s in repo.mq.applied]): + if 'qtip' in repo.tags() and (repo[dest].node() in + [s.node for s in repo.mq.applied]): raise util.Abort(_('cannot rebase onto an applied mq patch')) if src: @@ -506,7 +514,14 @@ cmdutil.bail_if_changed(repo) revsprepull = len(repo) - orig(ui, repo, *args, **opts) + origpostincoming = commands.postincoming + def _dummy(*args, **kwargs): + pass + commands.postincoming = _dummy + try: + orig(ui, repo, *args, **opts) + finally: + commands.postincoming = origpostincoming revspostpull = len(repo) if revspostpull > revsprepull: rebase(ui, repo, **opts) @@ -514,7 +529,7 @@ dest = repo[branch].rev() if dest != repo['.'].rev(): # there was nothing to rebase we force an update - merge.update(repo, dest, False, False, False) + hg.update(repo, dest) else: orig(ui, repo, *args, **opts) diff -r 0bb67503ad4b -r ac6fec2af8c8 hgext/record.py --- a/hgext/record.py Mon May 31 21:37:01 2010 +0200 +++ b/hgext/record.py Mon May 31 21:43:03 2010 +0200 @@ -293,6 +293,7 @@ _('&Quit, recording no changes'), _('&?')) r = ui.promptchoice("%s %s" % (query, resps), choices) + ui.write("\n") if r == 7: # ? doc = gettext(record.__doc__) c = doc.find('::') + 2 @@ -359,10 +360,10 @@ def record(ui, repo, *pats, **opts): '''interactively select changes to commit - If a list of files is omitted, all changes reported by "hg status" + 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. + 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 @@ -389,7 +390,7 @@ def qrecord(ui, repo, patch, *pats, **opts): '''interactively record a new patch - See 'hg help qnew' & 'hg help record' for more information and + See :hg:`help qnew` & :hg:`help record` for more information and usage. ''' @@ -526,7 +527,18 @@ os.rmdir(backupdir) except OSError: pass - return cmdutil.commit(ui, repo, recordfunc, pats, opts) + + # wrap ui.write so diff output can be labeled/colorized + def wrapwrite(orig, *args, **kw): + label = kw.pop('label', '') + for chunk, l in patch.difflabel(lambda: args): + orig(chunk, label=label + l) + oldwrite = ui.write + extensions.wrapfunction(ui, 'write', wrapwrite) + try: + return cmdutil.commit(ui, repo, recordfunc, pats, opts) + finally: + ui.write = oldwrite cmdtable = { "record": @@ -550,7 +562,7 @@ (qrecord, # add qnew options, except '--force' - [opt for opt in mq.cmdtable['qnew'][1] if opt[1] != 'force'], + [opt for opt in mq.cmdtable['^qnew'][1] if opt[1] != 'force'], _('hg qrecord [OPTION]... PATCH [FILE]...')), } diff -r 0bb67503ad4b -r ac6fec2af8c8 hgext/relink.py --- a/hgext/relink.py Mon May 31 21:37:01 2010 +0200 +++ b/hgext/relink.py Mon May 31 21:43:03 2010 +0200 @@ -145,7 +145,7 @@ except OSError, inst: ui.warn('%s: %s\n' % (tgt, str(inst))) - ui.progress(_('relinking'), None, unit=_(' files'), total=total) + ui.progress(_('relinking'), None) ui.status(_('relinked %d files (%d bytes reclaimed)\n') % (relinked, savedbytes)) diff -r 0bb67503ad4b -r ac6fec2af8c8 hgext/transplant.py --- a/hgext/transplant.py Mon May 31 21:37:01 2010 +0200 +++ b/hgext/transplant.py Mon May 31 21:43:03 2010 +0200 @@ -453,7 +453,7 @@ transplanted, otherwise you will be prompted to select the changesets you want. - hg transplant --branch REVISION --all will rebase the selected + :hg:`transplant --branch REVISION --all` will rebase the selected branch (up to the named revision) onto your current working directory. @@ -462,12 +462,12 @@ of a merged transplant, and you can merge descendants of them normally instead of transplanting them. - If no merges or revisions are provided, hg transplant will start - an interactive changeset browser. + If no merges or revisions are provided, :hg:`transplant` will + start an interactive changeset browser. If a changeset application fails, you can fix the merge by hand - and then resume where you left off by calling hg transplant - --continue/-c. + and then resume where you left off by calling :hg:`transplant + --continue/-c`. ''' def getremotechanges(repo, url): sourcerepo = ui.expandpath(url) diff -r 0bb67503ad4b -r ac6fec2af8c8 hgext/zeroconf/__init__.py --- a/hgext/zeroconf/__init__.py Mon May 31 21:37:01 2010 +0200 +++ b/hgext/zeroconf/__init__.py Mon May 31 21:43:03 2010 +0200 @@ -98,16 +98,17 @@ server.registerService(svc) class hgwebzc(hgweb_mod.hgweb): - def __init__(self, repo, name=None): - super(hgwebzc, self).__init__(repo, name) - name = self.reponame or os.path.basename(repo.root) + def __init__(self, repo, name=None, baseui=None): + super(hgwebzc, self).__init__(repo, name=name, baseui=baseui) + name = self.reponame or os.path.basename(self.repo.root) path = self.repo.ui.config("web", "prefix", "").strip('/') desc = self.repo.ui.config("web", "description", name) - publish(name, desc, path, int(repo.ui.config("web", "port", 8000))) + publish(name, desc, path, + int(self.repo.ui.config("web", "port", 8000))) class hgwebdirzc(hgwebdir_mod.hgwebdir): def __init__(self, conf, baseui=None): - super(hgwebdirzc, self).__init__(conf, baseui) + super(hgwebdirzc, self).__init__(conf, baseui=baseui) prefix = self.ui.config("web", "prefix", "").strip('/') + '/' for repo, path in self.repos: u = self.ui.copy() diff -r 0bb67503ad4b -r ac6fec2af8c8 hgweb.cgi --- a/hgweb.cgi Mon May 31 21:37:01 2010 +0200 +++ b/hgweb.cgi Mon May 31 21:43:03 2010 +0200 @@ -1,28 +1,17 @@ #!/usr/bin/env python # -# An example CGI script to use hgweb, edit as necessary +# An example hgweb CGI script, edit as necessary -# adjust python path if not a system-wide install: -#import sys -#sys.path.insert(0, "/path/to/python/lib") +# Path to repo or hgweb config to serve (see 'hg help hgweb') +config = "/path/to/repo/or/config" -# enable importing on demand to reduce startup time -from mercurial import demandimport; demandimport.enable() +# Uncomment and adjust if Mercurial is not installed system-wide: +#import sys; sys.path.insert(0, "/path/to/python/lib") # Uncomment to send python tracebacks to the browser if an error occurs: -#import cgitb -#cgitb.enable() +#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. -# Note that this will cause your .hgrc files to be interpreted in -# UTF-8 and all your repo files to be displayed using UTF-8. -# -#import os -#os.environ["HGENCODING"] = "UTF-8" - -from mercurial.hgweb.hgweb_mod import hgweb -import mercurial.hgweb.wsgicgi as wsgicgi - -application = hgweb("/path/to/repo", "repository name") +from mercurial import demandimport; demandimport.enable() +from mercurial.hgweb import hgweb, wsgicgi +application = hgweb(config) wsgicgi.launch(application) diff -r 0bb67503ad4b -r ac6fec2af8c8 hgwebdir.cgi --- a/hgwebdir.cgi Mon May 31 21:37:01 2010 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,67 +0,0 @@ -#!/usr/bin/env python -# -# An example CGI script to export multiple hgweb repos, edit as necessary - -# adjust python path if not a system-wide install: -#import sys -#sys.path.insert(0, "/path/to/python/lib") - -# enable importing on demand to reduce startup time -from mercurial import demandimport; demandimport.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. -# Note that this will cause your .hgrc files to be interpreted in -# UTF-8 and all your repo files to be displayed using UTF-8. -# -#import os -#os.environ["HGENCODING"] = "UTF-8" - -from mercurial.hgweb.hgwebdir_mod import hgwebdir -import mercurial.hgweb.wsgicgi as wsgicgi - -# The config file looks like this. You can have paths to individual -# repos, collections of repos in a directory tree, or both. -# -# [paths] -# virtual/path1 = /real/path1 -# virtual/path2 = /real/path2 -# virtual/root = /real/root/* -# / = /real/root2/* -# virtual/root2 = /real/root2/** -# -# [collections] -# /prefix/to/strip/off = /root/of/tree/full/of/repos -# -# paths example: -# -# * First two lines mount one repository into one virtual path, like -# '/real/path1' into 'virtual/path1'. -# -# * The third entry mounts every mercurial repository found in '/real/root' -# in 'virtual/root'. This format is preferred over the [collections] one, -# since using absolute paths as configuration keys is not supported on every -# platform (especially on Windows). -# -# * The fourth entry is a special case mounting all repositories in -# /'real/root2' in the root of the virtual directory. -# -# * The fifth entry recursively finds all repositories under the real root, -# and mounts them using their relative path (to given real root) under the -# virtual root. -# -# collections example: say directory tree /foo contains repos /foo/bar, -# /foo/quux/baz. Give this config section: -# [collections] -# /foo = /foo -# Then repos will list as bar and quux/baz. -# -# Alternatively you can pass a list of ('virtual/path', '/real/path') tuples -# or use a dictionary with entries like 'virtual/path': '/real/path' - -application = hgwebdir('hgweb.config') -wsgicgi.launch(application) diff -r 0bb67503ad4b -r ac6fec2af8c8 i18n/da.po --- a/i18n/da.po Mon May 31 21:37:01 2010 +0200 +++ b/i18n/da.po Mon May 31 21:43:03 2010 +0200 @@ -17,8 +17,8 @@ msgstr "" "Project-Id-Version: Mercurial\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2010-05-08 22:29+0200\n" -"PO-Revision-Date: 2010-05-08 23:40+0200\n" +"POT-Creation-Date: 2010-04-05 01:22+0200\n" +"PO-Revision-Date: 2010-04-05 01:37+0200\n" "Last-Translator: \n" "Language-Team: Danish\n" "MIME-Version: 1.0\n" @@ -384,7 +384,7 @@ #, python-format msgid "hook type %s does not pass a changeset id" -msgstr "hook type %s overfører ikke noget ændrings-ID" +msgstr "" #, python-format msgid "database error: %s" @@ -425,9 +425,8 @@ msgid "Revision %d is a merge, ignoring...\n" msgstr "Revision %d er en sammenføjning; ignorerer...\n" -#, python-format -msgid "generating stats: %d%%" -msgstr "genererer statistik: %d%%" +msgid "analyzing" +msgstr "" msgid "" "histogram of changes to the repository\n" @@ -524,11 +523,8 @@ msgid "file with email aliases" msgstr "fil med email-aliaser" -msgid "show progress" -msgstr "vis fremskridt" - -msgid "hg churn [-d DATE] [-r REV] [--aliases FILE] [--progress] [FILE]" -msgstr "hg churn [-d DATO] [-r REV] [--aliases FIL] [--progress] [FIL]" +msgid "hg churn [-d DATE] [-r REV] [--aliases FILE] [FILE]" +msgstr "hg churn [-d DATO] [-r REV] [--aliases FIL] [FIL]" msgid "" "colorize output from some commands\n" @@ -940,13 +936,6 @@ msgid "hg debugcvsps [OPTION]... [PATH]..." msgstr "hg debugcvsps [TILVALG]... [STI]..." -#, python-format -msgid "%s does not look like a Bazaar repository" -msgstr "%s ser ikke ud som et Bazaar depot" - -msgid "Bazaar modules could not be loaded" -msgstr "Bazaar-modulerne kunne ikke indlæses" - msgid "" "warning: lightweight checkouts may cause conversion failures, try with a " "regular branch instead.\n" @@ -1058,12 +1047,8 @@ msgstr "--sourcesort er ikke supporteret at denne datakilde" #, python-format -msgid "%s does not look like a CVS checkout" -msgstr "%s ser ikke ud som et CVS arbejdskatalog" - -#, python-format msgid "revision %s is not a patchset number" -msgstr "revision %s er ikke et patchset-nummer" +msgstr "" #, python-format msgid "connecting to %s\n" @@ -1096,9 +1081,6 @@ msgid "collecting CVS rlog\n" msgstr "samler CVS rlog\n" -msgid "not a CVS sandbox" -msgstr "" - #, python-format msgid "reading cvs log cache %s\n" msgstr "læser cvs log-mellemlager %s\n" @@ -1127,9 +1109,6 @@ msgid "revision must be followed by date line" msgstr "revision skal efterfølges af datolinje" -msgid "log cache overlaps with new log entries, re-run without cache." -msgstr "" - #, python-format msgid "writing cvs log cache %s\n" msgstr "skriver cvs log-mellemlager %s\n" @@ -1157,10 +1136,6 @@ msgstr "%d ændringer\n" #, python-format -msgid "%s does not look like a darcs repository" -msgstr "%s ser ikke ud som et darcs depot" - -#, python-format msgid "darcs version 2.1 or newer needed (found %r)" msgstr "kræver darcs version 2.1 eller nyere (fandt %r)" @@ -1185,26 +1160,7 @@ msgstr "kildedepot understøtter ikke --filemap" #, python-format -msgid "%s does not look like a Git repository" -msgstr "%s ser ikke ud som et Git depot" - -msgid "cannot retrieve git heads" -msgstr "kan ikke hente git-hoveder" - -#, python-format -msgid "cannot read %r object at %s" -msgstr "kan ikke læse %r objekt ved %s" - -#, python-format -msgid "cannot read changes in %s" -msgstr "kan ikke læse ændringer i %s" - -#, python-format -msgid "cannot read tags from %s" -msgstr "kan ikke læse mærkater fra %s" - -#, python-format -msgid "%s does not look like a GNU Arch repository" +msgid "%s does not look like a GNU Arch repo" msgstr "%s ser ikke ud som et GNU Arch depot" msgid "cannot find a GNU Arch tool" @@ -1224,7 +1180,7 @@ msgstr "kan ikke parse cat-log af %s" #, python-format -msgid "%s is not a local Mercurial repository" +msgid "%s is not a local Mercurial repo" msgstr "%s er ikke et lokalt Mercurial depot" #, python-format @@ -1232,10 +1188,6 @@ msgstr "initialiserer mål %s depot\n" #, python-format -msgid "could not create hg repository %s as sink" -msgstr "kunne ikke oprette hg depot %s som mål" - -#, python-format msgid "pulling from %s into %s\n" msgstr "hiver fra %s ind i %s\n" @@ -1254,17 +1206,13 @@ msgstr "ignorerer: %s\n" #, python-format -msgid "%s does not look like a monotone repository" +msgid "%s does not look like a monotone repo" msgstr "%s ser ikke ud som et monotone depot" #, python-format msgid "copying file in renamed directory from '%s' to '%s'" msgstr "kopierer fil i omdøbt katalog fra '%s' til '%s'" -#, python-format -msgid "%s does not look like a P4 repository" -msgstr "%s ser ikke ud som et P4 depot" - msgid "reading p4 views\n" msgstr "læser p4 views\n" @@ -1279,10 +1227,6 @@ "repository. Use --source-type if you know better.\n" msgstr "" -#, python-format -msgid "%s does not look like a Subversion repository" -msgstr "%s ser ikke ud som et Subversion depot" - msgid "Subversion python bindings could not be loaded" msgstr "Subversion python bindingerne kunne ikke indlæses" @@ -1349,11 +1293,11 @@ msgstr "svn: gren har ikke nogen revision %s" #, python-format -msgid "initializing svn repository %r\n" +msgid "initializing svn repo %r\n" msgstr "initialiserer svn depot %r\n" #, python-format -msgid "initializing svn working copy %r\n" +msgid "initializing svn wc %r\n" msgstr "initialiserer svn arbejdskatalog %r\n" msgid "unexpected svn output:\n" @@ -2040,7 +1984,7 @@ msgstr "genskanner på grund af ændring af .hgignore\n" msgid "cannot start: socket is already bound" -msgstr "kan ikke starte: soklen er allede bundet" +msgstr "" msgid "" "cannot start: tried linking .hg/inotify.sock to a temporary socket but .hg/" @@ -2220,9 +2164,6 @@ "\n" "nøgleord skrevet til %s:\n" -msgid "unhooked all commit hooks\n" -msgstr "" - msgid "hg keyword configuration and expansion example" msgstr "eksempel på konfiguration og ekspansion af nøgleord" @@ -2311,12 +2252,6 @@ msgid "only show unknown (not tracked) files" msgstr "vis kun ukendte (ikke-fulgte) filer" -msgid "show keyword status flags of all files (DEPRECATED)" -msgstr "vis keyword status for alle filer (FORÆLDET)" - -msgid "only show untracked files (DEPRECATED)" -msgstr "vis kun ikke-fulgte filer (FORÆLDET)" - msgid "hg kwfiles [OPTION]... [FILE]..." msgstr "hg kwfiles [TILVALG]... [FIL]..." @@ -2491,7 +2426,7 @@ msgstr "rettelse fejlede, afvisninger er efterladt i arbejdskataloget\n" msgid "fuzz found when applying patch, stopping\n" -msgstr "fandt fnidder ved anvendelsen af rettelsen, stopper\n" +msgstr "" #, python-format msgid "revision %d is not managed" @@ -2640,7 +2575,7 @@ msgstr "rettelsen %s er ikke i series filen" msgid "No saved patch data found\n" -msgstr "Fandt ingen gemt rettelsesdata\n" +msgstr "" #, python-format msgid "restoring status: %s\n" @@ -3133,30 +3068,19 @@ msgstr "kopier %s til %s\n" msgid "" -"strip a changeset and all its descendants from the repository\n" -"\n" -" The strip command removes all changesets whose local revision\n" -" number is greater than or equal to REV, and then restores any\n" -" changesets that are not descendants of REV. If the working\n" -" directory has uncommitted changes, the operation is aborted unless\n" -" the --force flag is supplied.\n" -"\n" -" If a parent of the working directory is stripped, then the working\n" -" directory will automatically be updated to the most recent\n" -" available ancestor of the stripped parent after the operation\n" -" completes.\n" -"\n" -" Any stripped changesets are stored in ``.hg/strip-backup`` as a\n" -" bundle (see ``hg help bundle`` and ``hg help unbundle``). They can\n" -" be restored by running ``hg unbundle .hg/strip-backup/BUNDLE``,\n" -" where BUNDLE is the bundle file created by the strip. Note that\n" -" the local revision numbers will in general be different after the\n" -" restore.\n" -"\n" -" Use the --nobackup option to discard the backup bundle once the\n" -" operation completes.\n" -" " -msgstr "" +"strip a revision and all its descendants from the repository\n" +"\n" +" If one of the working directory's parent revisions is stripped, the\n" +" working directory will be updated to the parent of the stripped\n" +" revision.\n" +" " +msgstr "" +"strip en revision og alle dens efterkommere fra depotet\n" +"\n" +" Hvis en af arbejdskatalogets forælder-revisioner bliver strippet,\n" +" så vil arbejdskataloget blive opdateret til forældren af den\n" +" strippede revision.\n" +" " msgid "" "set or print guarded patches to push\n" @@ -3415,8 +3339,8 @@ msgid "add \"Date: \" to patch" msgstr "tilføj \"Date: \" til rettelsen" -msgid "hg qnew [-e] [-m TEXT] [-l FILE] PATCH [FILE]..." -msgstr "hg qnew [-e] [-m TEKST] [-l FIL] RETTELSE [FIL]..." +msgid "hg qnew [-e] [-m TEXT] [-l FILE] [-f] PATCH [FILE]..." +msgstr "hg qnew [-e] [-m TEKST] [-l FIL] [-f] RETTELSE [FIL]..." msgid "hg qnext [-s]" msgstr "hg qnext [-s]" @@ -3440,7 +3364,7 @@ msgstr "" msgid "list patch name in commit text" -msgstr "put rettelsens navn ind i deponeringsbeskeden" +msgstr "" msgid "apply all patches" msgstr "anvend alle rettelser" @@ -3479,7 +3403,7 @@ msgstr "" msgid "update queue working directory" -msgstr "opdater arbejdskataloget for kø-depotet" +msgstr "" msgid "hg qrestore [-d] [-u] REV" msgstr "hg qrestore [-d] [-u] REV" @@ -3520,14 +3444,10 @@ msgid "hg qseries [-ms]" msgstr "hg qseries [-ms]" -msgid "" -"force removal of changesets even if the working directory has uncommitted " -"changes" -msgstr "" - -msgid "" -"bundle only changesets with local revision number greater than REV which are " -"not descendants of REV (DEPRECATED)" +msgid "force removal with local changes" +msgstr "gennemtving fjernelse af rettelse med lokale ændringer" + +msgid "bundle unrelated changesets" msgstr "" msgid "no backups" @@ -4357,7 +4277,7 @@ msgstr "hg qrecord [TILVALG]... RETTELSE [FIL]..." msgid "recreates hardlinks between repository clones" -msgstr "genopret hårde lænker mellem depot-kloner" +msgstr "" msgid "" "recreate hardlinks between two repositories\n" @@ -4416,7 +4336,7 @@ #, python-format msgid "relinked %d files (%d bytes reclaimed)\n" -msgstr "genlænkede %d filer (%d byte indvundet)\n" +msgstr "" msgid "[ORIGIN]" msgstr "[KILDE]" @@ -4843,6 +4763,9 @@ msgid "invalid format spec '%%%s' in output filename" msgstr "ugyldig formatspecifikation '%%%s' i output filnavn" +msgid "searching" +msgstr "søger" + #, python-format msgid "adding %s\n" msgstr "tilføjer %s\n" @@ -4974,7 +4897,7 @@ #, python-format msgid "%s: no key named '%s'" -msgstr "%s: ingen nøgle ved navn '%s'" +msgstr "" #, python-format msgid "%s: %s" @@ -5170,8 +5093,10 @@ " By default, the revision used is the parent of the working\n" " directory; use -r/--rev to specify a different revision.\n" "\n" -" To specify the type of archive to create, use -t/--type. Valid\n" -" types are:\n" +" The archive type is automatically detected based on file\n" +" extension (or override using -t/--type).\n" +"\n" +" Valid types are:\n" "\n" " :``files``: a directory full of files (default)\n" " :``tar``: tar archive, uncompressed\n" @@ -5781,10 +5706,10 @@ msgstr "Kontrollerer udvidelser...\n" msgid " One or more extensions could not be found" -msgstr " En eller flere udvidelser blev ikke fundet" +msgstr "" msgid " (check that you compiled the extensions)\n" -msgstr " (kontroller at du har kompileret udvidelserne)\n" +msgstr "" msgid "Checking templates...\n" msgstr "" @@ -5793,21 +5718,21 @@ msgstr "" msgid "Checking patch...\n" -msgstr "Kontrollerer patch...\n" +msgstr "" msgid " patch call failed:\n" -msgstr " kaldet til patch fejlede:\n" +msgstr "" msgid " unexpected patch output!\n" -msgstr " uventet output fra patch!\n" +msgstr "" msgid " patch test failed!\n" -msgstr " patch testen fejlede!\n" +msgstr "" msgid "" " (Current patch tool may be incompatible with patch, or misconfigured. " "Please check your .hgrc file)\n" -msgstr " (Det nuværende patch-værktøj er måske inkompatibelt med patch eller konfigureret forkert. Undersøg venligst din .hgrc-fil)\n" +msgstr "" msgid "" " Internal patcher failure, please report this error to http://mercurial." @@ -5828,20 +5753,20 @@ msgstr "" msgid "Checking username...\n" -msgstr "Kontrollerer brugernavn...\n" +msgstr "" msgid " (specify a username in your .hgrc file)\n" -msgstr " (angiv et brugernavn i din .hgrc-fil)\n" +msgstr "" msgid "No problems detected\n" msgstr "Fandt ingen problemer\n" #, python-format msgid "%s problems detected, please check your install!\n" -msgstr "fandt %s problemer, kontroller venligst din installation!\n" +msgstr "" msgid "dump rename information" -msgstr "dump information om omdøbninger" +msgstr "" #, python-format msgid "%s renamed from %s:%s\n" @@ -5986,10 +5911,10 @@ msgstr "" msgid "exporting patches:\n" -msgstr "eksporterer rettelser:\n" +msgstr "" msgid "exporting patch:\n" -msgstr "eksporterer rettelse:\n" +msgstr "" msgid "" "forget the specified files on the next commit\n" @@ -6160,11 +6085,11 @@ "\n" msgid "use \"hg help extensions\" for information on enabling extensions\n" -msgstr "brug \"hg help extensions\" for at få mere information om hvordan man slår udvidelser til\n" +msgstr "" #, python-format msgid "'%s' is provided by the following extension:" -msgstr "'%s' er i denne udvidelse:" +msgstr "" msgid "Mercurial Distributed SCM\n" msgstr "Mercurial Distribueret SCM\n" @@ -6180,7 +6105,7 @@ msgstr "aktiverede udvidelser:" msgid "DEPRECATED" -msgstr "FORÆLDET" +msgstr "" msgid "" "\n" @@ -6485,7 +6410,7 @@ " " msgid "can only specify an explicit filename" -msgstr "kan kun angive et eksplicit filnavn" +msgstr "" #, python-format msgid "'%s' not found in manifest!" @@ -6500,17 +6425,6 @@ " Path names are defined in the [paths] section of /etc/mercurial/hgrc\n" " and $HOME/.hgrc. If run inside a repository, .hg/hgrc is used, too.\n" "\n" -" The path names ``default`` and ``default-push`` have a special\n" -" meaning. When performing a push or pull operation, they are used\n" -" as fallbacks if no location is specified on the command-line.\n" -" When ``default-push`` is set, it will be used for push and\n" -" ``default`` will be used for pull; otherwise ``default`` is used\n" -" as the fallback for both. When cloning a repository, the clone\n" -" source is written as ``default`` in ``.hg/hgrc``. Note that\n" -" ``default`` and ``default-push`` apply to all inbound (e.g. ``hg\n" -" incoming``) and outbound (e.g. ``hg outgoing``, ``hg email`` and\n" -" ``hg bundle``) operations.\n" -"\n" " See 'hg help urls' for more information.\n" " " msgstr "" @@ -6850,46 +6764,33 @@ " Print the root directory of the current repository.\n" " " msgstr "" -"print roden (toppen) af det nuværende arbejdskatalog\n" -"\n" -" Print rod-kataloget for det nuværende depot.\n" -" " msgid "" "export the repository via HTTP\n" "\n" -" Start a local HTTP repository browser and pull server. You can use\n" -" this for ad-hoc sharing and browing of repositories. It is\n" -" recommended to use a real web server to serve a repository for\n" -" longer periods of time.\n" -"\n" -" Please note that the server does not implement access control.\n" -" This means that, by default, anybody can read from the server and\n" -" nobody can write to it by default. Set the ``web.allow_push``\n" -" option to ``*`` to allow everybody to push to the server. You\n" -" should use a real web server if you need to authenticate users.\n" +" Start a local HTTP repository browser and pull server.\n" "\n" " By default, the server logs accesses to stdout and errors to\n" " stderr. Use the -A/--accesslog and -E/--errorlog options to log to\n" " files.\n" +"\n" +" To have the server choose a free port number to listen on, specify\n" +" a port number of 0; in this case, the server will print the port\n" +" number it uses.\n" " " msgstr "" "eksporter depotet via HTTP\n" "\n" -" Start en lokal HTTP depotbrowser og pull-server. Du kan bruge\n" -" denne til ad-hoc deling og browsning af depoter. Det anbefales at\n" -" man bruger en rigtig web-server for at serve et depot for længere\n" -" tidsrum.\n" -"\n" -" Bemærk venligst at serveren ikke implementerer adgangskontrol.\n" -" Dette betyder at alle som udgangspunkt kan læse fra serveren og at\n" -" ingen kan skrive til den. Sæt ``web.allow_push`` til ``*`` for at\n" -" tillade at enhver skubber ændringer til serveren. Du skal bruge en\n" -" rigtig web-server hvis du har behov for at autentificere brugere.\n" +" Start en lokal HTTP depotbrowser og pull-server.\n" "\n" " Som standard logger serveren forespørgsler til stdout og fejl til\n" " stderr. Brug -A/--accesslog og -E/--errorlog tilvalgene for at\n" -" logge til filer." +" logge til filer.\n" +"\n" +" For at få serveren til at vælge et frit portnummer at lytte til,\n" +" angiv da portnummer 0; så vil serveren skrive det portnummer den\n" +" bruger.\n" +" " #, python-format msgid "listening at http://%s%s/%s (bound to %s:%d)\n" @@ -7363,7 +7264,7 @@ msgstr "antal linier kontekst der skal vises" msgid "output diffstat-style summary of changes" -msgstr "vis en opsummering af ændringerne i stil med diffstat" +msgstr "" msgid "guess renamed files by similarity (0<=s<=100)" msgstr "gæt omdøbte filer ud fra enshed (0<=s<=100)" @@ -7611,8 +7512,8 @@ msgid "print matching line numbers" msgstr "udskriv matchende linienumre" -msgid "only search files changed within revision range" -msgstr "søg kun i filer som er ændret i det angivne interval" +msgid "search in given revision range" +msgstr "søg i det angivne interval" msgid "[OPTION]... PATTERN [FILE]..." msgstr "[TILVALG]... MØNSTER [FIL]..." @@ -7838,7 +7739,7 @@ msgid "name of error log file to write to" msgstr "navn på fejlllog fil der skrives til" -msgid "port to listen on (default: 8000)" +msgid "port to listen on (default: 8000" msgstr "port der skal lyttes på (standard: 8000)" msgid "address to listen on (default: all interfaces)" @@ -8052,6 +7953,10 @@ msgid "hg: unknown command '%s'\n" msgstr "hg: ukendt kommando '%s'\n" +#, python-format +msgid "abort: could not import module %s!\n" +msgstr "afbrudt: kunne ikke importere modul %s!\n" + msgid "(did you forget to compile extensions?)\n" msgstr "(glemte du at kompilere udvidelserne?)\n" @@ -8709,9 +8614,8 @@ "You can customize output for any \"log-like\" command: log,\n" "outgoing, incoming, tip, parents, heads and glog.\n" "\n" -"Four styles are packaged with Mercurial: default (the style used\n" -"when no explicit preference is passed), compact, changelog,\n" -"and xml.\n" +"Three styles are packaged with Mercurial: default (the style used\n" +"when no explicit preference is passed), compact and changelog.\n" "Usage::\n" "\n" " $ hg log -r1 --style changelog\n" @@ -9252,9 +9156,6 @@ msgid "queries" msgstr "" -msgid "searching" -msgstr "søger" - msgid "already have changeset " msgstr "har allerede ændringen " @@ -9279,12 +9180,12 @@ msgid "abort: push creates new remote heads!\n" msgstr "afbrudt: skub laver nye fjern-hoveder!\n" +msgid "(did you forget to merge? use push -f to force)\n" +msgstr "(glemte du at sammenføje? brug push -f for at gennemtvinge)\n" + msgid "(you should pull and merge or use push -f to force)\n" msgstr "(du skal hive og sammenføje eller bruge -f for at gennemtvinge)\n" -msgid "(did you forget to merge? use push -f to force)\n" -msgstr "(glemte du at sammenføje? brug push -f for at gennemtvinge)\n" - #, python-format msgid "abort: push creates new remote branches: %s!\n" msgstr "afbrudt: skub laver nye grene i fjerndepotet: %s!\n" @@ -9589,6 +9490,15 @@ msgid "Unsupported line endings type: %s" msgstr "Linieendelse %s understøttes ikke" +msgid "" +"internal patcher failed\n" +"please report details to http://mercurial.selenic.com/bts/\n" +"or mercurial@selenic.com\n" +msgstr "" +"intern lappe-funktionalitet fejlede\n" +"angiv venligst fejldetaljer på http://mercurial.selenic.com/bts/\n" +"eller mercurial@selenic.com\n" + #, python-format msgid " %d files changed, %d insertions(+), %d deletions(-)\n" msgstr "%d filer ændret, %d indsættelser(+), %d sletninger(-)\n" @@ -9736,8 +9646,8 @@ msgstr "fjerner underdepot %s\n" #, python-format -msgid "pulling subrepo %s\n" -msgstr "hiver fra underdepot %s\n" +msgid "pulling subrepo %s from %s\n" +msgstr "hiver underdepot %s fra %s\n" #, python-format msgid "pushing subrepo %s\n" diff -r 0bb67503ad4b -r ac6fec2af8c8 i18n/fr.po --- a/i18n/fr.po Mon May 31 21:37:01 2010 +0200 +++ b/i18n/fr.po Mon May 31 21:43:03 2010 +0200 @@ -1,17 +1,17 @@ # French translations for Mercurial # Traductions françaises de Mercurial # Copyright (C) 2009 Matt Mackall and others -# +# # Quelques règles : # - dans l'aide d'une commande, la première ligne descriptive # commence par un verbe au présent sans majuscule # - dans l'aide d'une commande, la description des options # utilise un verbe à l'infinitif -# +# # Note : la terminologie ci-dessous est loin d'être complète, figée ou # parfaite. À compléter et à améliorer, particulièrement les # termes comportant un point d'interrogation... -# +# # Dictionnaire de termes courants : # - to apply a patch appliquer un patch # - a branch une branche @@ -46,7 +46,7 @@ # untracked non suivi, non géré, pas sous contrôle du dépôt, # hors révision ? # - the working directory le répertoire de travail -# +# # Termes très courants repris de l'anglais - à utiliser sans guillemets # pour ne pas alourdir inutilement la traduction : # - a diff un diff ? (ou également un patch ? synonymes...) @@ -54,7 +54,7 @@ # - a patch un patch # - a tag un tag # - to tag taguer -# +# # Termes anglais avec une signification très spécifique, difficile à # paraphraser sans alourdir ou perdre le sens - à utiliser avec guillemets : # - a bundle un \"bundle\" @@ -62,7 +62,7 @@ # - a changeset un \"changeset\" # - a changegroup un \"changegroup\" # - the tip la révision \"tip\" -# +# # Termes dont le classement / la traduction sont à déterminer : # - a commit un commit, un \"commit\" # - to commit \"committer\" ? (beuark, même dit tous les jours) @@ -73,7 +73,7 @@ # - to push propager ? (utilisé par svn pour commit) # publier ? pousser ?? envoyer ?? # - the series file (mq) ? -# +# # Notes : # - (cédric) je verrais bien l'ajout d'une rubrique générale dans l'aide # (par exemple 'hg help glossary') librement remplissable par chaque équipe @@ -81,7 +81,7 @@ # qui vont être rencontrés dans mercurial - et en particulier permettrait # de faire le lien avec des termes franglisants parfois utilisés # (par ex. fusionner = "merger", etc.) ... ? -# +# msgid "" msgstr "" "Project-Id-Version: Mercurial\n" @@ -100,10 +100,10 @@ msgstr " (défaut: %s)" msgid "OPTIONS" -msgstr "" +msgstr "OPTIONS" msgid "COMMANDS" -msgstr "" +msgstr "COMMANDES" #, fuzzy msgid "" @@ -454,8 +454,7 @@ "A template is a piece of text, with markup to invoke variable\n" "expansion::\n" "\n" -" $ hg log -r1 --template \"{node}\\n\"\n" -" b56ce7b07c52de7d5fd79fb89701ea538af65746\n" +" $ hg log -r1 --template \"{node}\\n\"\\n b56ce7b07c52de7d5fd79fb89701ea538af65746\n" "\n" "Strings in curly braces are called keywords. The availability of\n" "keywords depends on the exact context of the templater. These\n" @@ -492,8 +491,7 @@ "variable. You can also use a chain of filters to get the desired\n" "output::\n" "\n" -" $ hg tip --template \"{date|isodate}\\n\"\n" -" 2008-08-21 18:22 +0000\n" +" $ hg tip --template \"{date|isodate}\\n\"\\n 2008-08-21 18:22 +0000\n" "\n" "List of filters:\n" "\n" @@ -713,8 +711,9 @@ " " msgstr "" +#, fuzzy msgid "a bookmark of this name does not exist" -msgstr "" +msgstr "un signet de ce nom n'existe pas" msgid "a bookmark of the same name already exists" msgstr "" @@ -738,10 +737,10 @@ msgstr "" msgid "delete a given bookmark" -msgstr "" +msgstr "Supprimer un signet donné" msgid "rename a given bookmark" -msgstr "" +msgstr "Renommer un signet donné" msgid "hg bookmarks [-f] [-d] [-m NAME] [-r REV] [NAME]" msgstr "" @@ -821,8 +820,7 @@ " {hgweb} Base URL for browsing Mercurial repositories.\n" "\n" " Default 'changeset {node|short} in repo {root} refers '\n" -" 'to bug {bug}.\\ndetails:\\n\\t{desc|tabindent}'\n" -"\n" +" 'to bug {bug}.\\ndetails:\\n\\t{desc|tabindent}'\\n\n" "strip\n" " The number of slashes to strip from the front of {root} to produce\n" " {webroot}. Default 0.\n" @@ -864,9 +862,7 @@ " bzuser=unknown@domain.com\n" " bzdir=/opt/bugzilla-3.2\n" " template=Changeset {node|short} in {root|basename}.\n" -" {hgweb}/{webroot}/rev/{node|short}\\n\n" -" {desc}\\n\n" -" strip=5\n" +" {hgweb}/{webroot}/rev/{node|short}\\n\\n {desc}\\n\\n strip=5\n" "\n" " [web]\n" " baseurl=http://dev.domain.com/hg\n" @@ -973,8 +969,9 @@ msgid "hg children [-r REV] [FILE]" msgstr "" +#, fuzzy msgid "command to display statistics about repository history" -msgstr "" +msgstr "Commande pour afficher les statistiques de l'historique du repository" #, python-format msgid "Revision %d is a merge, ignoring...\n" @@ -1393,9 +1390,7 @@ msgid "hg debugcvsps [OPTION]... [PATH]..." msgstr "" -msgid "" -"warning: lightweight checkouts may cause conversion failures, try with a " -"regular branch instead.\n" +msgid "warning: lightweight checkouts may cause conversion failures, try with a regular branch instead.\n" msgstr "" msgid "bzr source type could not be determined\n" @@ -1439,18 +1434,19 @@ #, python-format msgid "%s: unknown repository type" -msgstr "" +msgstr "%s : type de dépôt inconnu" #, python-format msgid "unknown sort mode: %s" -msgstr "" +msgstr "mode de tri inconnu: %s" #, python-format msgid "cycle detected between %s and %s" -msgstr "" - +msgstr "cycle détecté entre %s et %s" + +#, fuzzy msgid "not all revisions were sorted" -msgstr "" +msgstr "Touts les révisions n'ont pas été triées" #, python-format msgid "Writing author map file %s\n" @@ -1475,22 +1471,24 @@ msgid "scanning source...\n" msgstr "" +#, fuzzy msgid "sorting...\n" -msgstr "" - +msgstr "tri en cours...\n" + +#, fuzzy msgid "converting...\n" -msgstr "" +msgstr "conversion en cours...\n" #, python-format msgid "source: %s\n" -msgstr "" +msgstr "source: %s\n" #, python-format msgid "assuming destination %s\n" msgstr "" msgid "more than one sort mode specified" -msgstr "" +msgstr "plus d'un mode de tri specifié" msgid "--sourcesort is not supported by this data source" msgstr "" @@ -1501,55 +1499,56 @@ #, python-format msgid "connecting to %s\n" -msgstr "" +msgstr "connexion à %s\n" msgid "CVS pserver authentication failed" -msgstr "" - -#, python-format -msgid "" -"unexpected response from CVS server (expected \"Valid-requests\", but got %r)" -msgstr "" +msgstr "l'authentification au serveur CVS a échoué" + +#, python-format, fuzzy +msgid "unexpected response from CVS server (expected \"Valid-requests\", but got %r)" +msgstr "réponse innatendue du serveur CVS (\\'Valid-requests\" attendue, mais %r reçue)" #, python-format msgid "%d bytes missing from remote file" -msgstr "" +msgstr "%d bytes manquants dans le fichier distant" #, python-format msgid "cvs server: %s\n" -msgstr "" +msgstr "serveur cvs : %s\n" #, python-format msgid "unknown CVS response: %s" -msgstr "" - +msgstr "Réponse CVS inconnue : %s" + +#, fuzzy msgid "collecting CVS rlog\n" -msgstr "" +msgstr "récupération des rlog CVS\n" #, python-format msgid "reading cvs log cache %s\n" msgstr "" -#, python-format +#, python-format, fuzzy msgid "cache has %d log entries\n" -msgstr "" +msgstr "le cache a %d entrées de log\n" #, python-format msgid "error reading cache: %r\n" -msgstr "" +msgstr "erreur lors de la lecture du cache : %r\n" #, python-format msgid "running %s\n" msgstr "" +#, fuzzy msgid "RCS file must be followed by working file" -msgstr "" +msgstr "le fichier RCS doit être suivi par le fichier de travail" msgid "must have at least some revisions" msgstr "" msgid "expected revision number" -msgstr "" +msgstr "numéro de révision attendu" msgid "revision must be followed by date line" msgstr "" @@ -1558,9 +1557,9 @@ msgid "writing cvs log cache %s\n" msgstr "" -#, python-format +#, python-format, fuzzy msgid "%d log entries\n" -msgstr "" +msgstr "%d entrées dans les log\n" msgid "creating changesets\n" msgstr "" @@ -1580,10 +1579,10 @@ #, python-format msgid "darcs version 2.1 or newer needed (found %r)" -msgstr "" +msgstr "darcs version 2.1 ou supérieur nécessaire (trouvé %r)" msgid "Python ElementTree module is not available" -msgstr "" +msgstr "Le module Python ElementTree n'est pas disponible" msgid "internal calling inconsistency" msgstr "" @@ -1614,8 +1613,7 @@ msgstr "" #, python-format -msgid "" -"tree analysis stopped because it points to an unregistered archive %s...\n" +msgid "tree analysis stopped because it points to an unregistered archive %s...\n" msgstr "" #, python-format @@ -1624,7 +1622,7 @@ #, python-format msgid "%s is not a local Mercurial repo" -msgstr "" +msgstr "%s n'est pas un dépôt local de Mercurial" #, python-format msgid "initializing destination %s repository\n" @@ -1638,7 +1636,7 @@ msgstr "" msgid "updating tags\n" -msgstr "" +msgstr "mise à jour des tags\n" #, python-format msgid "%s is not a valid start revision" @@ -1675,9 +1673,9 @@ msgid "Subversion python bindings are too old, 1.4 or later required" msgstr "" -#, python-format +#, python-format, fuzzy msgid "svn: revision %s is not an integer" -msgstr "" +msgstr "svn : la révision %s n'est pas un entier" #, python-format msgid "svn: start revision %s is not an integer" @@ -1712,7 +1710,7 @@ #, python-format msgid "no tags found at revision %d\n" -msgstr "" +msgstr "aucun tag trouvé dans la révision %d\n" #, python-format msgid "%s not found up to revision %d" @@ -1732,7 +1730,7 @@ #, python-format msgid "initializing svn repo %r\n" -msgstr "" +msgstr "initialisation du dépôt svn %r\n" #, python-format msgid "initializing svn wc %r\n" @@ -1745,7 +1743,7 @@ msgstr "" msgid "XXX TAGS NOT IMPLEMENTED YET\n" -msgstr "" +msgstr "XXX TAGS PAS ENCORE IMPLÉMENTÉ\n" msgid "" "command to allow external programs to compare revisions\n" @@ -1796,7 +1794,7 @@ msgstr "" msgid "cleaning up temp directory\n" -msgstr "" +msgstr "nettoyage du dossier temporaire\n" msgid "" "use external program to diff repository (or selected files)\n" @@ -1837,10 +1835,8 @@ " %(path)s program.\n" "\n" " When two revision arguments are given, then changes are shown between\n" -" those revisions. If only one revision is specified then that revision " -"is\n" -" compared to the working directory, and, when no revisions are " -"specified,\n" +" those revisions. If only one revision is specified then that revision is\n" +" compared to the working directory, and, when no revisions are specified,\n" " the working directory files are compared to its parent." msgstr "" @@ -1871,8 +1867,7 @@ " " msgstr "" -msgid "" -"working dir not at branch tip (use \"hg update\" to check out branch tip)" +msgid "working dir not at branch tip (use \"hg update\" to check out branch tip)" msgstr "" msgid "outstanding uncommitted merge" @@ -1884,23 +1879,18 @@ msgid "working directory is missing some files" msgstr "" -msgid "" -"multiple heads in this branch (use \"hg heads .\" and \"hg merge\" to merge)" +msgid "multiple heads in this branch (use \"hg heads .\" and \"hg merge\" to merge)" msgstr "" #, python-format msgid "pulling from %s\n" msgstr "" -msgid "" -"Other repository doesn't support revision lookup, so a rev cannot be " -"specified." -msgstr "" - -#, python-format -msgid "" -"not merging with %d other new branch heads (use \"hg heads .\" and \"hg merge" -"\" to merge them)\n" +msgid "Other repository doesn't support revision lookup, so a rev cannot be specified." +msgstr "" + +#, python-format +msgid "not merging with %d other new branch heads (use \"hg heads .\" and \"hg merge\" to merge them)\n" msgstr "" #, python-format @@ -1934,7 +1924,7 @@ msgstr "" msgid "error while verifying signature" -msgstr "" +msgstr "erreur lors de la vérification de la signature" #, python-format msgid "%s Bad signature from \"%s\"\n" @@ -1951,16 +1941,16 @@ msgid "list signed changesets" msgstr "" -#, python-format +#, python-format, fuzzy msgid "%s:%d node does not exist\n" -msgstr "" +msgstr "%s: le noeud %d n'existe pas\n" msgid "verify all the signatures there may be for a particular revision" msgstr "" #, python-format msgid "No valid signature for %s\n" -msgstr "" +msgstr "Signature invalide pour %s\n" msgid "" "add a signature for the current or given revision\n" @@ -1978,9 +1968,7 @@ msgid "Error while signing" msgstr "" -msgid "" -"working copy of .hgsigs is changed (please commit .hgsigs manually or use --" -"force)" +msgid "working copy of .hgsigs is changed (please commit .hgsigs manually or use --force)" msgstr "" msgid "unknown signature version" @@ -2071,8 +2059,7 @@ " # Append a diffstat to the log message (optional)\n" " #diffstat = False\n" " # Template to use for log messages (optional)\n" -" #template = {desc}\\n{baseurl}/rev/{node}-- {diffstat}\n" -" # Style to use (optional)\n" +" #template = {desc}\\n{baseurl}/rev/{node}-- {diffstat}\\n # Style to use (optional)\n" " #style = foo\n" " # The URL of the CIA notification service (optional)\n" " # You can use mailto: URLs to send by email, eg\n" @@ -2163,7 +2150,7 @@ msgstr "" msgid "recursive" -msgstr "" +msgstr "récursif" msgid "pretty" msgstr "" @@ -2175,7 +2162,7 @@ msgstr "" msgid "search" -msgstr "" +msgstr "recherche" msgid "hg git-diff-tree [OPTION]... NODE1 NODE2 [FILE]..." msgstr "" @@ -2190,19 +2177,19 @@ msgstr "" msgid "ignored" -msgstr "" +msgstr "ignoré" msgid "hg debug-rev-parse REV" msgstr "" msgid "header" -msgstr "" +msgstr "en-tête" msgid "topo-order" msgstr "" msgid "parents" -msgstr "" +msgstr "parent" msgid "max-count" msgstr "" @@ -2296,7 +2283,7 @@ #, python-format msgid "found %d\n" -msgstr "" +msgstr "trouvé %d\n" #, python-format msgid "*** to raise the limit from %d to %d (run as root):\n" @@ -2400,8 +2387,7 @@ "\n" " [interhg]\n" " issues = s!issue(\\d+)!issue\\1!\n" -" bugzilla = s!((?:bug|b=|(?=#?\\d{4,}))(?:\\s*#?)(\\d+))!\\1!" -"i\n" +" bugzilla = s!((?:bug|b=|(?=#?\\d{4,}))(?:\\s*#?)(\\d+))!\\1!i\n" " boldify = s!(^|\\s)#(\\d+)\\b! #\\2!\n" msgstr "" @@ -2735,8 +2721,7 @@ msgstr "le patch %s n'est pas appliqué\n" msgid "patch failed, unable to continue (try -v)\n" -msgstr "" -"l'application du patch a échoué, impossible de continuer (essayez avec -v)\n" +msgstr "l'application du patch a échoué, impossible de continuer (essayez avec -v)\n" #, python-format msgid "applying %s\n" @@ -2860,9 +2845,7 @@ #, python-format msgid "errors during apply, please fix and refresh %s\n" -msgstr "" -"des erreurs se sont produites durant l'application, veuillez corriger puis " -"rafraîchir %s\n" +msgstr "des erreurs se sont produites durant l'application, veuillez corriger puis rafraîchir %s\n" #, python-format msgid "now at: %s\n" @@ -2880,16 +2863,14 @@ msgstr "qpop: %s est déjà le dernier patch appliqué\n" msgid "qpop: forcing dirstate update\n" -msgstr "" -"qpop: mise à jour de l'état du répertoire de travail (dirstate) forcée\n" +msgstr "qpop: mise à jour de l'état du répertoire de travail (dirstate) forcée\n" #, python-format msgid "trying to pop unknown node %s" msgstr "tentative de dépilement d'un nœud inconnu : %s" msgid "popping would remove a revision not managed by this patch queue" -msgstr "" -"le dépilement enlèverait une révision non gérée par cette pile de patchs" +msgstr "le dépilement enlèverait une révision non gérée par cette pile de patchs" msgid "deletions found between repo revs" msgstr "suppressions trouvées entre des révisions du dépôt" @@ -2905,12 +2886,8 @@ msgstr "impossible de rafraîchir une révision possédant des révisions filles" # restaurer/récupérer ? pas satisfait... -msgid "" -"refresh interrupted while patch was popped! (revert --all, qpush to " -"recover)\n" -msgstr "" -"rafraîchissement interrompu alors qu'un patch était en cours de dépilement " -"(utiliser revert --all, ou qpush pour restaurer l'état)\n" +msgid "refresh interrupted while patch was popped! (revert --all, qpush to recover)\n" +msgstr "rafraîchissement interrompu alors qu'un patch était en cours de dépilement (utiliser revert --all, ou qpush pour restaurer l'état)\n" msgid "patch queue directory already exists" msgstr "la pile de patchs existe déjà" @@ -2963,9 +2940,7 @@ msgstr "l'option \"-r\" n'est pas utilisable lors de l'importation de fichiers" msgid "option \"-n\" not valid when importing multiple patches" -msgstr "" -"l'option \"-n\" n'est pas utilisable lors de l'importation de plusieurs " -"patchs" +msgstr "l'option \"-n\" n'est pas utilisable lors de l'importation de plusieurs patchs" # origine/base/racine ? #, python-format @@ -3009,8 +2984,7 @@ msgid "" "remove patches from queue\n" "\n" -" The patches must not be applied, and at least one patch is required. " -"With\n" +" The patches must not be applied, and at least one patch is required. With\n" " -k/--keep, the patch files are preserved in the patch directory.\n" "\n" " To stop managing a patch and move it into permanent history,\n" @@ -3678,9 +3652,7 @@ msgstr "hg qpush [-f] [-l] [-a] [-m] [-n NOM] [PATCH | INDEX]" msgid "refresh only files already in the patch and specified files" -msgstr "" -"ne rafraîchir que les fichiers déjà présent dans le patch et ceux " -"explicitement spécifiés" +msgstr "ne rafraîchir que les fichiers déjà présent dans le patch et ceux explicitement spécifiés" msgid "add/update author field in patch with current user" msgstr "" @@ -3812,8 +3784,7 @@ " maxdiff = 300 # max lines of diffs to include (0=none, -1=all)\n" " maxsubject = 67 # truncate subject line longer than this\n" " diffstat = True # add a diffstat before the diff content\n" -" sources = serve # notify if source of incoming changes in this " -"list\n" +" sources = serve # notify if source of incoming changes in this list\n" " # (serve == ssh or http, push, pull, bundle)\n" " merge = False # send notification for merges (default True)\n" " [email]\n" @@ -4138,10 +4109,8 @@ "\n" " hg email -b # send bundle of all patches not in default\n" " hg email -b DEST # send bundle of all patches not in DEST\n" -" hg email -b -r 3000 # bundle of all ancestors of 3000 not in " -"default\n" -" hg email -b -r 3000 DEST # bundle of all ancestors of 3000 not in " -"DEST\n" +" hg email -b -r 3000 # bundle of all ancestors of 3000 not in default\n" +" hg email -b -r 3000 DEST # bundle of all ancestors of 3000 not in DEST\n" "\n" " Before using this command, you will need to enable email in your\n" " hgrc. See the [email] section in hgrc(5) for details.\n" @@ -4201,9 +4170,7 @@ msgstr "veuillez spécifier au minimum un \"changeset\", à l'aide de -r ou -o" msgid "--outgoing mode always on with --bundle; do not re-specify --outgoing" -msgstr "" -"ne pas spécifier --outgoing, qui est systématiquement activé lorsque --" -"bundle est utilisé" +msgstr "ne pas spécifier --outgoing, qui est systématiquement activé lorsque --bundle est utilisé" msgid "too many destinations" msgstr "trop de destinations" @@ -4272,8 +4239,7 @@ msgstr "afficher les messages tels qu'ils seraient envoyés" msgid "write messages to mbox file instead of sending them" -msgstr "" -"écrire les messages dans un fichier au format \"mbox\" au lieu de les envoyer" +msgstr "écrire les messages dans un fichier au format \"mbox\" au lieu de les envoyer" msgid "subject of first message (intro or single patch)" msgstr "sujet du premier message (intro ou patch unique)" @@ -4294,9 +4260,7 @@ msgstr "envoyer les modifications non présentes dans le dépôt cible" msgid "send changes not in target as a binary bundle" -msgstr "" -"envoyer les modifications non présentes dans le dépôt cible sous forme de " -"\"bundle\" binaire" +msgstr "envoyer les modifications non présentes dans le dépôt cible sous forme de \"bundle\" binaire" msgid "name of the bundle attachment file" msgstr "nom à donner au fichier \"bundle\" envoyé" @@ -4308,9 +4272,7 @@ msgstr "procéder même si le dépôt cible n'est pas apparenté (avec -b/--bundle)" msgid "a base changeset to specify instead of a destination (with -b/--bundle)" -msgstr "" -"un \"changeset\" à utiliser comme point de départ, au lieu d'une destination " -"(avec -b/--bundle)" +msgstr "un \"changeset\" à utiliser comme point de départ, au lieu d'une destination (avec -b/--bundle)" msgid "send an introduction email for a single patch" msgstr "envoyer un courriel d'introduction pour un patch seul" @@ -4402,9 +4364,7 @@ msgstr "afficher les nom des fichiers au lieu de les supprimer" msgid "end filenames with NUL, for use with xargs (implies -p/--print)" -msgstr "" -"terminer les noms de fichiers par un caractère nul, pour utilisation avec " -"xargs (implique -p/--print)" +msgstr "terminer les noms de fichiers par un caractère nul, pour utilisation avec xargs (implique -p/--print)" msgid "hg purge [OPTION]... [DIR]..." msgstr "hg purge [OPTION]... [RÉPERTOIRE]..." @@ -4524,9 +4484,7 @@ msgid "abort an interrupted rebase" msgstr "" -msgid "" -"hg rebase [-s REV | -b REV] [-d REV] [--collapse] [--keep] [--keepbranches] " -"| [-c] | [-a]" +msgid "hg rebase [-s REV | -b REV] [-d REV] [--collapse] [--keep] [--keepbranches] | [-c] | [-a]" msgstr "" msgid "commands to interactively select changes for commit/qrefresh" @@ -4799,8 +4757,7 @@ msgid "filter changesets through FILTER" msgstr "" -msgid "" -"hg transplant [-s REPOSITORY] [-b BRANCH [-a]] [-p REV] [-m REV] [REV]..." +msgid "hg transplant [-s REPOSITORY] [-b BRANCH [-a]] [-p REV] [-m REV] [REV]..." msgstr "" msgid "" @@ -4858,8 +4815,7 @@ " ** = cleverdecode:\n" " # or ** = macdecode:\n" "\n" -"If not doing conversion, to make sure you do not commit CRLF/CR by " -"accident::\n" +"If not doing conversion, to make sure you do not commit CRLF/CR by accident::\n" "\n" " [hooks]\n" " pretxncommit.crlf = python:hgext.win32text.forbidcrlf\n" @@ -4950,9 +4906,7 @@ msgstr "le préfixe donné pour l'archive contient des composants non autorisés" msgid "cannot give prefix when archiving to files" -msgstr "" -"impossible de donner un préfixe pour une création d'archive vers un " -"répertoire (de type \"files\")" +msgstr "impossible de donner un préfixe pour une création d'archive vers un répertoire (de type \"files\")" #, python-format msgid "unknown archive type '%s'" @@ -5012,8 +4966,7 @@ #, python-format msgid "invalid format spec '%%%s' in output filename" -msgstr "" -"'%%%s'spécification de format invalide dans le nom du fichier de sortie" +msgstr "'%%%s'spécification de format invalide dans le nom du fichier de sortie" #, python-format msgid "adding %s\n" @@ -5025,9 +4978,7 @@ #, python-format msgid "recording removal of %s as rename to %s (%d%% similar)\n" -msgstr "" -"suppression de %s plannifiée, en tant que renommage de %s (similaire à %d%" -"%)\n" +msgstr "suppression de %s plannifiée, en tant que renommage de %s (similaire à %d%%)\n" #, python-format msgid "%s: not copying - file is not managed\n" @@ -5295,8 +5246,7 @@ " removed.\n" " " msgstr "" -"crée une archive du dépôt à une révision donnée, sans conserver " -"l'historique\n" +"crée une archive du dépôt à une révision donnée, sans conserver l'historique\n" "\n" " Par défaut l'archive est créée à partir de la révision parente\n" " du répertoire de travail. Une autre révision peut être spécifiée\n" @@ -5328,8 +5278,7 @@ msgstr "le répertoire racine ne peut servir de destination" msgid "cannot archive plain files to stdout" -msgstr "" -"impossible d'envoyer une archive de simples fichiers vers la sortie standard" +msgstr "impossible d'envoyer une archive de simples fichiers vers la sortie standard" msgid "" "reverse effect of earlier changeset\n" @@ -5790,14 +5739,10 @@ msgid " patch test failed!\n" msgstr "" -msgid "" -" (Current patch tool may be incompatible with patch, or misconfigured. " -"Please check your .hgrc file)\n" -msgstr "" - -msgid "" -" Internal patcher failure, please report this error to http://mercurial." -"selenic.com/bts/\n" +msgid " (Current patch tool may be incompatible with patch, or misconfigured. Please check your .hgrc file)\n" +msgstr "" + +msgid " Internal patcher failure, please report this error to http://mercurial.selenic.com/bts/\n" msgstr "" msgid "Checking commit editor...\n" @@ -6012,15 +5957,12 @@ msgstr "utiliser \"hg help\" pour la liste complète des commandes" msgid "use \"hg help\" for the full list of commands or \"hg -v\" for details" -msgstr "" -"utiliser \"hg help\" pour la liste complète des commandes ou \"hg -v\" pour " -"plus de détails" +msgstr "utiliser \"hg help\" pour la liste complète des commandes ou \"hg -v\" pour plus de détails" # la chaîne passée est préfixée par un espace #, python-format msgid "use \"hg -v help%s\" to show aliases and global options" -msgstr "" -"utiliser \"hg -v help%s\" pour afficher les alias et les options globales" +msgstr "utiliser \"hg -v help%s\" pour afficher les alias et les options globales" #, python-format msgid "use \"hg -v help %s\" to show global options" @@ -6266,9 +6208,7 @@ msgid "%s - use \"hg update\" instead" msgstr "" -msgid "" -"working dir not at a head rev - use \"hg update\" or merge with an explicit " -"rev" +msgid "working dir not at a head rev - use \"hg update\" or merge with an explicit rev" msgstr "" msgid "" @@ -7233,9 +7173,7 @@ msgid "[-nibt] [-r REV] [SOURCE]" msgstr "" -msgid "" -"directory strip option for patch. This has the same meaning as the " -"corresponding patch option" +msgid "directory strip option for patch. This has the same meaning as the corresponding patch option" msgstr "" msgid "base path" @@ -7649,9 +7587,7 @@ msgstr "** exception inconnue rencontrée, détails ci-dessous\n" msgid "** report bug details to http://mercurial.selenic.com/bts/\n" -msgstr "" -"** veuillez signaler le problème en détails sur http://www.selenic.com/" -"mercurial/bts\n" +msgstr "** veuillez signaler le problème en détails sur http://www.selenic.com/mercurial/bts\n" msgid "** or mercurial@selenic.com\n" msgstr "** ou mercurial@selenic.com\n" @@ -7690,12 +7626,8 @@ msgid "Option --cwd may not be abbreviated!" msgstr "L'option --cwd ne peut être abrégée !" -msgid "" -"Option -R has to be separated from other options (e.g. not -qR) and --" -"repository may only be abbreviated as --repo!" -msgstr "" -"L'option -R doit être séparée des autres options (autrement dit -qR est " -"invalide) et --repository ne peut être abrégé qu'en --repo !" +msgid "Option -R has to be separated from other options (e.g. not -qR) and --repository may only be abbreviated as --repo!" +msgstr "L'option -R doit être séparée des autres options (autrement dit -qR est invalide) et --repository ne peut être abrégé qu'en --repo !" #, python-format msgid "Time: real %.3f secs (user %.3f+%.3f sys %.3f+%.3f)\n" @@ -7712,9 +7644,7 @@ msgid "unrecognized profiling format '%s' - Ignored\n" msgstr "" -msgid "" -"lsprof not available - install from http://codespeak.net/svn/user/arigo/hack/" -"misc/lsprof/" +msgid "lsprof not available - install from http://codespeak.net/svn/user/arigo/hack/misc/lsprof/" msgstr "" #, python-format @@ -7836,9 +7766,7 @@ msgid "destination '%s' is not empty" msgstr "" -msgid "" -"src repository does not support revision lookup and so doesn't support clone " -"by revision" +msgid "src repository does not support revision lookup and so doesn't support clone by revision" msgstr "" msgid "clone from remote to remote not supported" @@ -7849,16 +7777,13 @@ msgstr "comparaison avec %s\n" #, python-format -msgid "" -"%d files updated, %d files merged, %d files removed, %d files unresolved\n" +msgid "%d files updated, %d files merged, %d files removed, %d files unresolved\n" msgstr "" msgid "use 'hg resolve' to retry unresolved file merges\n" msgstr "" -msgid "" -"use 'hg resolve' to retry unresolved file merges or 'hg update -C' to " -"abandon\n" +msgid "use 'hg resolve' to retry unresolved file merges or 'hg update -C' to abandon\n" msgstr "" msgid "(branch merge, don't forget to commit)\n" @@ -8126,9 +8051,7 @@ msgid "requesting all changes\n" msgstr "" -msgid "" -"Partial pull cannot be done because other repository doesn't support " -"changegroupsubset." +msgid "Partial pull cannot be done because other repository doesn't support changegroupsubset." msgstr "" #, python-format @@ -8247,9 +8170,7 @@ msgstr "" #, python-format -msgid "" -"untracked file in working directory differs from file in requested revision: " -"'%s'" +msgid "untracked file in working directory differs from file in requested revision: '%s'" msgstr "" #, python-format diff -r 0bb67503ad4b -r ac6fec2af8c8 i18n/sv.po --- a/i18n/sv.po Mon May 31 21:37:01 2010 +0200 +++ b/i18n/sv.po Mon May 31 21:43:03 2010 +0200 @@ -13,8 +13,8 @@ msgstr "" "Project-Id-Version: Mercurial\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2010-04-19 18:26+0200\n" -"PO-Revision-Date: 2010-04-19 18:57+0100\n" +"POT-Creation-Date: 2010-04-19 18:58+0200\n" +"PO-Revision-Date: 2010-04-19 19:49+0200\n" "Last-Translator: Jens Bäckman \n" "Language-Team: Swedish\n" "MIME-Version: 1.0\n" @@ -411,9 +411,8 @@ msgid "Revision %d is a merge, ignoring...\n" msgstr "" -#, python-format -msgid "generating stats: %d%%" -msgstr "" +msgid "analyzing" +msgstr "analyserar" msgid "" "histogram of changes to the repository\n" @@ -476,11 +475,8 @@ msgid "file with email aliases" msgstr "" -msgid "show progress" -msgstr "" - -msgid "hg churn [-d DATE] [-r REV] [--aliases FILE] [--progress] [FILE]" -msgstr "" +msgid "hg churn [-d DATE] [-r REV] [--aliases FILE] [FILE]" +msgstr "hg churn [-d DATUM] [-r REV] [--aliases FIL] [FIL]" msgid "" "colorize output from some commands\n" @@ -529,6 +525,15 @@ " resolve.resolved = green bold\n" "\n" " bookmarks.current = green\n" +"\n" +"The color extension will try to detect whether to use ANSI codes or\n" +"Win32 console APIs, unless it is made explicit::\n" +"\n" +" [color]\n" +" mode = ansi\n" +"\n" +"Any value other than 'ansi', 'win32', or 'auto' will disable color.\n" +"\n" msgstr "" "färglägg utmatning från vissa kommandon\n" "\n" @@ -575,17 +580,26 @@ " resolve.resolved = green bold\n" "\n" " bookmarks.current = green\n" +"\n" +"Utökningen color försöker att upptäcka om ANSI-koder eller APIer för\n" +"konsolen i Win32 om det inte anges explicit::\n" +"\n" +" [color]\n" +" mode = ansi\n" +"\n" +"Ett värde skilt från 'ansi', 'win32', eller 'auto' stänger av färg.\n" +"\n" + +#, python-format +msgid "ignoring unknown color/effect %r (configured in color.%s)\n" +msgstr "ignorerar okänd färg/effekt %r (konfigurerad i color.%s)\n" + +msgid "win32console not found, please install pywin32\n" +msgstr "win32console hittades inte, installera pywin32\n" msgid "when to colorize (always, auto, or never)" msgstr "när färgläggning ska ske (always, auto eller never)" -msgid "don't colorize output (DEPRECATED)" -msgstr "färglägg inte utmatning (FÖRLEGAD)" - -#, python-format -msgid "ignoring unknown color/effect %r (configured in color.%s)\n" -msgstr "" - msgid "import revisions from foreign VCS repositories into Mercurial" msgstr "" @@ -2055,9 +2069,6 @@ "keywords written to %s:\n" msgstr "" -msgid "unhooked all commit hooks\n" -msgstr "" - msgid "hg keyword configuration and expansion example" msgstr "exempel på nyckelordskonfiguration och -expansion i hg" @@ -2129,12 +2140,6 @@ msgid "only show unknown (not tracked) files" msgstr "visa bara okända (ospårade) filer" -msgid "show keyword status flags of all files (DEPRECATED)" -msgstr "visa statusflaggor för alla filer (FÖRLEGAD)" - -msgid "only show untracked files (DEPRECATED)" -msgstr "visa bara ospårade filer (FÖRLEGAD)" - msgid "hg kwfiles [OPTION]... [FILE]..." msgstr "" @@ -4417,6 +4422,9 @@ msgid "invalid format spec '%%%s' in output filename" msgstr "" +msgid "searching" +msgstr "söker" + #, python-format msgid "adding %s\n" msgstr "" @@ -4548,12 +4556,8 @@ msgstr "" #, python-format -msgid "%s: %s" -msgstr "" - -#, python-format msgid "Found revision %s from %s\n" -msgstr "" +msgstr "Hittade revision %s från %s\n" msgid "revision matching date not found" msgstr "" @@ -4738,8 +4742,10 @@ " By default, the revision used is the parent of the working\n" " directory; use -r/--rev to specify a different revision.\n" "\n" -" To specify the type of archive to create, use -t/--type. Valid\n" -" types are:\n" +" The archive type is automatically detected based on file\n" +" extension (or override using -t/--type).\n" +"\n" +" Valid types are:\n" "\n" " :``files``: a directory full of files (default)\n" " :``tar``: tar archive, uncompressed\n" @@ -4762,8 +4768,10 @@ " Som standard används revisonen för arbetskatalogens förälder; använd\n" " -r/--rev för att specificera en annan revision.\n" "\n" -" För att definiera vilken typ av arkiv som ska skapas, använd -t/--type.\n" -" Giltiga typer är::\n" +" Arkivtypen upptäcks automatiskt baserat på filändelsen (eller tvinga\n" +" med hjälp av -t/--type).\n" +"\n" +" Giltiga typer är:\n" "\n" " :``files``: en katalog fylld med filer (standard)\n" " :``tar``: tar-arkiv, okomprimerad\n" @@ -6630,7 +6638,7 @@ msgstr "inga ändringar behövs för %s\n" msgid "" -"roll back the last transaction\n" +"roll back the last transaction (dangerous)\n" "\n" " This command should be used with care. There is only one level of\n" " rollback, and there is no way to undo a rollback. It will also\n" @@ -6657,7 +6665,7 @@ " may fail if a rollback is performed.\n" " " msgstr "" -"återgång från den senaste transaktionen\n" +"återgång från den senaste transaktionen (farligt)\n" "\n" " Detta kommando bör användas med försiktighet. Det finns bara en nivå\n" " av återgång, och det finns inget sätt att ångra en återgång.\n" @@ -6696,22 +6704,29 @@ " " msgid "" -"export the repository via HTTP\n" +"start stand-alone webserver\n" "\n" " Start a local HTTP repository browser and pull server.\n" "\n" " By default, the server logs accesses to stdout and errors to\n" " stderr. Use the -A/--accesslog and -E/--errorlog options to log to\n" " files.\n" -" " -msgstr "" -"exportera arkivet via HTTP\n" +"\n" +" To have the server choose a free port number to listen on, specify\n" +" a port number of 0; in this case, the server will print the port\n" +" number it uses.\n" +" " +msgstr "" +"starta fristående webbserver\n" "\n" " Startar en lokal HTTP-arkivbläddrare och pull-server.\n" "\n" " Som standard loggar servern anslutningar till stdout och fel till\n" " stderr. Använd flaggorna -A/--accesslog och -E/--errorlog för att logga\n" " till filer.\n" +"\n" +" För att låta servern välja ett ledigt portnummer att lyssna på, ange 0\n" +" som portnummer; då visar servern det portnummer som används.\n" " " #, python-format @@ -6805,6 +6820,10 @@ " för att hitta inkommande och utgående ändringar. Detta kan ta lång tid.\n" " " +#, python-format +msgid "parent: %d:%s " +msgstr "förälder: %d:%s " + msgid " (empty repository)" msgstr " (tomt arkiv)" @@ -6812,22 +6831,18 @@ msgstr " (ingen revision uthämtad)" #, python-format -msgid "parent: %d:%s %s\n" -msgstr "förälder: %d:%s %s\n" - -#, python-format msgid "branch: %s\n" msgstr "gren: %s\n" #, python-format +msgid "%d modified" +msgstr "%d modifierad" + +#, python-format msgid "%d added" msgstr "%d tillagd" #, python-format -msgid "%d modified" -msgstr "%d modifierad" - -#, python-format msgid "%d removed" msgstr "%d borttagen" @@ -6836,14 +6851,14 @@ msgstr "%d raderad" #, python-format +msgid "%d unknown" +msgstr "%d okänd" + +#, python-format msgid "%d ignored" msgstr "%d ignorerad" #, python-format -msgid "%d unknown" -msgstr "%d okänd" - -#, python-format msgid "%d unresolved" msgstr "%d olöst" @@ -7002,7 +7017,7 @@ " " msgid "" -"update working directory\n" +"update working directory (or switch revisions)\n" "\n" " Update the repository's working directory to the specified\n" " changeset.\n" @@ -7038,7 +7053,7 @@ " See 'hg help dates' for a list of formats valid for -d/--date.\n" " " msgstr "" -"uppdatera arbetskatalogen\n" +"uppdatera arbetskatalogen (eller växla mellan revisioner)\n" "\n" " Uppdatera arkivets arbetskatalog till den specificerade ändringen.\n" "\n" @@ -9032,8 +9047,16 @@ msgid "no interrupted transaction available\n" msgstr "" -msgid "rolling back last transaction\n" -msgstr "återkallar den senaste transaktionen\n" +#, python-format +msgid "rolling back to revision %s (undo %s: %s)\n" +msgstr "återgår till revisionen %s (ångra %s: %s)\n" + +#, python-format +msgid "rolling back to revision %s (undo %s)\n" +msgstr "återgår till revisionen %s (ångra %s)\n" + +msgid "rolling back unknown transaction\n" +msgstr "återkallar okänd transaktion\n" #, python-format msgid "Named branch could not be reset, current branch still is: %s\n" @@ -9127,9 +9150,6 @@ msgid "queries" msgstr "frågor" -msgid "searching" -msgstr "söker" - msgid "already have changeset " msgstr "har redan ändringen " @@ -9463,6 +9483,15 @@ msgid "Unsupported line endings type: %s" msgstr "" +msgid "" +"internal patcher failed\n" +"please report details to http://mercurial.selenic.com/bts/\n" +"or mercurial@selenic.com\n" +msgstr "" +"den interna patcharen misslyckades\n" +"rapportera detaljer till http://mercurial.selenic.com/bts/\n" +"eller mercurial@selenic.com\n" + #, python-format msgid " %d files changed, %d insertions(+), %d deletions(-)\n" msgstr "" @@ -9610,8 +9639,8 @@ msgstr "" #, python-format -msgid "pulling subrepo %s\n" -msgstr "" +msgid "pulling subrepo %s from %s\n" +msgstr "drar subarkivet %s från %s\n" #, python-format msgid "pushing subrepo %s\n" @@ -9886,12 +9915,9 @@ msgid "checking changesets\n" msgstr "kontrollerar ändringar\n" -msgid "checking" -msgstr "kontrollerar" - #, python-format msgid "unpacking changeset %s" -msgstr "" +msgstr "packar upp ändringen %s" msgid "checking manifests\n" msgstr "kontrollerar manifest\n" @@ -9930,6 +9956,9 @@ msgid "cannot decode filename '%s'" msgstr "kan inte avkoda filnamnet '%s'" +msgid "checking" +msgstr "kontrollerar" + #, python-format msgid "broken revlog! (%s)" msgstr "skadad revlog! (%s)" diff -r 0bb67503ad4b -r ac6fec2af8c8 mercurial/byterange.py --- a/mercurial/byterange.py Mon May 31 21:37:01 2010 +0200 +++ b/mercurial/byterange.py Mon May 31 21:43:03 2010 +0200 @@ -25,11 +25,6 @@ import urllib2 import email.Utils -try: - from cStringIO import StringIO -except ImportError, msg: - from StringIO import StringIO - class RangeError(IOError): """Error raised when an unsatisfiable range is requested.""" pass diff -r 0bb67503ad4b -r ac6fec2af8c8 mercurial/cmdutil.py --- a/mercurial/cmdutil.py Mon May 31 21:37:01 2010 +0200 +++ b/mercurial/cmdutil.py Mon May 31 21:43:03 2010 +0200 @@ -8,8 +8,9 @@ from node import hex, nullid, nullrev, short from i18n import _ import os, sys, errno, re, glob, tempfile -import mdiff, bdiff, util, templater, patch, error, encoding, templatekw +import util, templater, patch, error, encoding, templatekw import match as _match +import similar revrangesep = ':' @@ -286,45 +287,6 @@ def matchfiles(repo, files): return _match.exact(repo.root, repo.getcwd(), files) -def findrenames(repo, added, removed, threshold): - '''find renamed files -- yields (before, after, score) tuples''' - copies = {} - ctx = repo['.'] - for r in removed: - if r not in ctx: - continue - fctx = ctx.filectx(r) - - def score(text): - if not len(text): - return 0.0 - if not fctx.cmp(text): - return 1.0 - if threshold == 1.0: - return 0.0 - orig = fctx.data() - # bdiff.blocks() returns blocks of matching lines - # count the number of bytes in each - equal = 0 - alines = mdiff.splitnewlines(text) - matches = bdiff.blocks(text, orig) - for x1, x2, y1, y2 in matches: - for line in alines[x1:x2]: - equal += len(line) - - lengths = len(text) + len(orig) - return equal * 2.0 / lengths - - for a in added: - bestscore = copies.get(a, (None, threshold))[1] - myscore = score(repo.wread(a)) - if myscore >= bestscore: - copies[a] = (r, myscore) - - for dest, v in copies.iteritems(): - source, score = v - yield source, dest, score - def addremove(repo, pats=[], opts={}, dry_run=None, similarity=None): if dry_run is None: dry_run = opts.get('dry_run') @@ -357,18 +319,25 @@ removed.append(abs) elif repo.dirstate[abs] == 'a': added.append(abs) - if not dry_run: - repo.remove(deleted) - repo.add(unknown) + copies = {} if similarity > 0: - for old, new, score in findrenames(repo, added + unknown, - removed + deleted, similarity): + for old, new, score in similar.findrenames(repo, + added + unknown, removed + deleted, similarity): if repo.ui.verbose or not m.exact(old) or not m.exact(new): repo.ui.status(_('recording removal of %s as rename to %s ' '(%d%% similar)\n') % (m.rel(old), m.rel(new), score * 100)) - if not dry_run: + copies[new] = old + + if not dry_run: + wlock = repo.wlock() + try: + repo.remove(deleted) + repo.add(unknown) + for new, old in copies.iteritems(): repo.copy(old, new) + finally: + wlock.release() def copy(ui, repo, pats, opts, rename=False): # called with the repo lock held @@ -382,12 +351,13 @@ def walkpat(pat): srcs = [] + badstates = after and '?' or '?r' m = match(repo, [pat], opts, globbed=True) for abs in repo.walk(m): state = repo.dirstate[abs] rel = m.rel(abs) exact = m.exact(abs) - if state in '?r': + if state in badstates: if exact and state == '?': ui.warn(_('%s: not copying - file is not managed\n') % rel) if exact and state == 'r': @@ -427,6 +397,12 @@ if after: if not exists: + if rename: + ui.warn(_('%s: not recording move - %s does not exist\n') % + (relsrc, reltarget)) + else: + ui.warn(_('%s: not recording copy - %s does not exist\n') % + (relsrc, reltarget)) return elif not dryrun: try: @@ -573,7 +549,7 @@ if errors: ui.warn(_('(consider using --after)\n')) - return errors + return errors != 0 def service(opts, parentfn=None, initfn=None, runfn=None, logfile=None, runargs=None, appendpid=False): @@ -647,6 +623,70 @@ if runfn: return runfn() +def export(repo, revs, template='hg-%h.patch', fp=None, switch_parent=False, + opts=None): + '''export changesets as hg patches.''' + + total = len(revs) + revwidth = max([len(str(rev)) for rev in revs]) + + def single(rev, seqno, fp): + ctx = repo[rev] + node = ctx.node() + parents = [p.node() for p in ctx.parents() if p] + branch = ctx.branch() + if switch_parent: + parents.reverse() + prev = (parents and parents[0]) or nullid + + if not fp: + fp = make_file(repo, template, node, total=total, seqno=seqno, + revwidth=revwidth, mode='ab') + if fp != sys.stdout and hasattr(fp, 'name'): + repo.ui.note("%s\n" % fp.name) + + fp.write("# HG changeset patch\n") + fp.write("# User %s\n" % ctx.user()) + fp.write("# Date %d %d\n" % ctx.date()) + if branch and (branch != 'default'): + fp.write("# Branch %s\n" % branch) + fp.write("# Node ID %s\n" % hex(node)) + fp.write("# Parent %s\n" % hex(prev)) + if len(parents) > 1: + fp.write("# Parent %s\n" % hex(parents[1])) + fp.write(ctx.description().rstrip()) + fp.write("\n\n") + + for chunk in patch.diff(repo, prev, node, opts=opts): + fp.write(chunk) + + for seqno, rev in enumerate(revs): + single(rev, seqno + 1, fp) + +def diffordiffstat(ui, repo, diffopts, node1, node2, match, + changes=None, stat=False, fp=None): + '''show diff or diffstat.''' + if fp is None: + write = ui.write + else: + def write(s, **kw): + fp.write(s) + + if stat: + diffopts.context = 0 + width = 80 + if not ui.plain(): + width = util.termwidth() + chunks = patch.diff(repo, node1, node2, match, changes, diffopts) + for chunk, label in patch.diffstatui(util.iterlines(chunks), + width=width, + git=diffopts.git): + write(chunk, label=label) + else: + for chunk, label in patch.diffui(repo, node1, node2, match, + changes, diffopts): + write(chunk, label=label) + class changeset_printer(object): '''show changeset information when templating not requested.''' @@ -682,7 +722,7 @@ if self.buffered: self.ui.pushbuffer() self._show(ctx, copies, props) - self.hunk[ctx.rev()] = self.ui.popbuffer() + self.hunk[ctx.rev()] = self.ui.popbuffer(labeled=True) else: self._show(ctx, copies, props) @@ -692,7 +732,8 @@ rev = ctx.rev() if self.ui.quiet: - self.ui.write("%d:%s\n" % (rev, short(changenode))) + self.ui.write("%d:%s\n" % (rev, short(changenode)), + label='log.node') return log = self.repo.changelog @@ -703,63 +744,77 @@ parents = [(p, hexfunc(log.node(p))) for p in self._meaningful_parentrevs(log, rev)] - self.ui.write(_("changeset: %d:%s\n") % (rev, hexfunc(changenode))) + self.ui.write(_("changeset: %d:%s\n") % (rev, hexfunc(changenode)), + label='log.changeset') branch = ctx.branch() # don't show the default branch name if branch != 'default': branch = encoding.tolocal(branch) - self.ui.write(_("branch: %s\n") % branch) + self.ui.write(_("branch: %s\n") % branch, + label='log.branch') for tag in self.repo.nodetags(changenode): - self.ui.write(_("tag: %s\n") % tag) + self.ui.write(_("tag: %s\n") % tag, + label='log.tag') for parent in parents: - self.ui.write(_("parent: %d:%s\n") % parent) + self.ui.write(_("parent: %d:%s\n") % parent, + label='log.parent') if self.ui.debugflag: mnode = ctx.manifestnode() self.ui.write(_("manifest: %d:%s\n") % - (self.repo.manifest.rev(mnode), hex(mnode))) - self.ui.write(_("user: %s\n") % ctx.user()) - self.ui.write(_("date: %s\n") % date) + (self.repo.manifest.rev(mnode), hex(mnode)), + label='ui.debug log.manifest') + self.ui.write(_("user: %s\n") % ctx.user(), + label='log.user') + self.ui.write(_("date: %s\n") % date, + label='log.date') if self.ui.debugflag: files = self.repo.status(log.parents(changenode)[0], changenode)[:3] for key, value in zip([_("files:"), _("files+:"), _("files-:")], files): if value: - self.ui.write("%-12s %s\n" % (key, " ".join(value))) + self.ui.write("%-12s %s\n" % (key, " ".join(value)), + label='ui.debug log.files') elif ctx.files() and self.ui.verbose: - self.ui.write(_("files: %s\n") % " ".join(ctx.files())) + self.ui.write(_("files: %s\n") % " ".join(ctx.files()), + label='ui.note log.files') if copies and self.ui.verbose: copies = ['%s (%s)' % c for c in copies] - self.ui.write(_("copies: %s\n") % ' '.join(copies)) + self.ui.write(_("copies: %s\n") % ' '.join(copies), + label='ui.note log.copies') extra = ctx.extra() if extra and self.ui.debugflag: for key, value in sorted(extra.items()): self.ui.write(_("extra: %s=%s\n") - % (key, value.encode('string_escape'))) + % (key, value.encode('string_escape')), + label='ui.debug log.extra') description = ctx.description().strip() if description: if self.ui.verbose: - self.ui.write(_("description:\n")) - self.ui.write(description) + self.ui.write(_("description:\n"), + label='ui.note log.description') + self.ui.write(description, + label='ui.note log.description') self.ui.write("\n\n") else: self.ui.write(_("summary: %s\n") % - description.splitlines()[0]) + description.splitlines()[0], + label='log.summary') self.ui.write("\n") self.showpatch(changenode) def showpatch(self, node): if self.patch: + stat = self.diffopts.get('stat') + diffopts = patch.diffopts(self.ui, self.diffopts) prev = self.repo.changelog.parents(node)[0] - chunks = patch.diff(self.repo, prev, node, match=self.patch, - opts=patch.diffopts(self.ui, self.diffopts)) - for chunk in chunks: - self.ui.write(chunk) + diffordiffstat(self.ui, self.repo, diffopts, prev, node, + match=self.patch, stat=stat) self.ui.write("\n") def _meaningful_parentrevs(self, log, rev): @@ -876,7 +931,7 @@ msg = _("%s: no key named '%s'") raise util.Abort(msg % (self.t.mapfile, inst.args[0])) except SyntaxError, inst: - raise util.Abort(_('%s: %s') % (self.t.mapfile, inst.args[0])) + raise util.Abort('%s: %s' % (self.t.mapfile, inst.args[0])) def show_changeset(ui, repo, opts, buffered=False, matchfn=False): """show one changeset using template or regular display. @@ -891,7 +946,7 @@ """ # options patch = False - if opts.get('patch'): + if opts.get('patch') or opts.get('stat'): patch = matchfn or matchall(repo) tmpl = opts.get('template') @@ -1044,7 +1099,7 @@ fncache.setdefault(rev, []) fncache[rev].append(file_) wanted.add(rev) - if follow and copied: + if copied: copies.append(copied) if slowpath: if follow: diff -r 0bb67503ad4b -r ac6fec2af8c8 mercurial/commands.py --- a/mercurial/commands.py Mon May 31 21:37:01 2010 +0200 +++ b/mercurial/commands.py Mon May 31 21:43:03 2010 +0200 @@ -11,9 +11,8 @@ import os, re, sys, difflib, time, tempfile import hg, util, revlog, bundlerepo, extensions, copies, error import patch, help, mdiff, url, encoding, templatekw -import archival, changegroup, cmdutil, sshserver, hbisect -from hgweb import server -import merge as merge_ +import archival, changegroup, cmdutil, sshserver, hbisect, hgweb, hgweb.server +import merge as mergemod import minirst # Commands start here, listed alphabetically @@ -25,14 +24,14 @@ repository. The files will be added to the repository at the next commit. To - undo an add before that, see hg forget. + undo an add before that, see :hg:`forget`. If no names are given, add all files to the repository. .. container:: verbose An example showing how new (unknown) files are added - automatically by ``hg add``:: + automatically by :hg:`add`:: $ ls foo.c @@ -76,6 +75,8 @@ option takes a percentage between 0 (disabled) and 100 (files must be identical) as its parameter. Detecting renamed files this way can be expensive. + + Returns 0 if all files are successfully added. """ try: sim = float(opts.get('similarity') or 0) @@ -98,6 +99,8 @@ it detects as binary. With -a, annotate will annotate the file anyway, although the results will probably be neither useful nor desirable. + + Returns 0 on success. """ if opts.get('follow'): # --follow is deprecated and now just an alias for -f/--file @@ -158,8 +161,10 @@ By default, the revision used is the parent of the working directory; use -r/--rev to specify a different revision. - To specify the type of archive to create, use -t/--type. Valid - types are: + The archive type is automatically detected based on file + extension (or override using -t/--type). + + Valid types are: :``files``: a directory full of files (default) :``tar``: tar archive, uncompressed @@ -169,12 +174,14 @@ :``zip``: zip archive, compressed using deflate The exact name of the destination archive or directory is given - using a format string; see 'hg help export' for details. + using a format string; see :hg:`help export` for details. Each member added to an archive file has a directory prefix prepended. Use -p/--prefix to specify a format string for the prefix. The default is the basename of the archive, with suffixes removed. + + Returns 0 on success. ''' ctx = repo[opts.get('rev')] @@ -184,16 +191,32 @@ dest = cmdutil.make_filename(repo, dest, node) if os.path.realpath(dest) == repo.root: raise util.Abort(_('repository root cannot be destination')) - matchfn = cmdutil.match(repo, [], opts) - kind = opts.get('type') or 'files' + + def guess_type(): + exttypes = { + 'tar': ['.tar'], + 'tbz2': ['.tbz2', '.tar.bz2'], + 'tgz': ['.tgz', '.tar.gz'], + 'zip': ['.zip'], + } + + for type, extensions in exttypes.items(): + if util.any(dest.endswith(ext) for ext in extensions): + return type + return None + + kind = opts.get('type') or guess_type() or 'files' prefix = opts.get('prefix') + if dest == '-': if kind == 'files': raise util.Abort(_('cannot archive plain files to stdout')) dest = sys.stdout if not prefix: prefix = os.path.basename(repo.root) + '-%h' + prefix = cmdutil.make_filename(repo, prefix, node) + matchfn = cmdutil.match(repo, [], opts) archival.archive(repo, dest, node, kind, not opts.get('no_decode'), matchfn, prefix) @@ -212,7 +235,9 @@ changeset afterwards. This saves you from doing the merge by hand. The result of this merge is not committed, as with a normal merge. - See 'hg help dates' for a list of formats valid for -d/--date. + See :hg:`help dates` for a list of formats valid for -d/--date. + + Returns 0 on success. ''' if rev and node: raise util.Abort(_("please specify just one revision")) @@ -306,6 +331,8 @@ status 0 means good, 125 means to skip the revision, 127 (command not found) will abort the bisection, and any other non-zero exit status means the revision is bad. + + Returns 0 on success. """ def print_result(nodes, good): displayer = cmdutil.show_changeset(ui, repo, {}) @@ -387,7 +414,8 @@ hg.clean(repo, nodes[0], show_stats=False) finally: hbisect.save_state(repo, state) - return print_result(nodes, good) + print_result(nodes, good) + return # update state node = repo.lookup(rev or '.') @@ -438,8 +466,10 @@ the parent of the working directory, negating a previous branch change. - Use the command 'hg update' to switch to an existing branch. Use - 'hg commit --close-branch' to mark this branch as closed. + Use the command :hg:`update` to switch to an existing branch. Use + :hg:`commit --close-branch` to mark this branch as closed. + + Returns 0 on success. """ if opts.get('clean'): @@ -462,12 +492,14 @@ List the repository's named branches, indicating which ones are inactive. If -c/--closed is specified, also list branches which have - been marked closed (see hg commit --close-branch). + been marked closed (see :hg:`commit --close-branch`). If -a/--active is specified, only show active branches. A branch is considered active if it contains repository heads. - Use the command 'hg update' to switch to an existing branch. + Use the command :hg:`update` to switch to an existing branch. + + Returns 0. """ hexfunc = ui.debugflag and hex or short @@ -521,6 +553,8 @@ Applying bundles preserves all changeset contents including permissions, copy/rename information, and revision history. + + Returns 0 on success, 1 if no changes found. """ revs = opts.get('rev') or None if revs: @@ -566,7 +600,7 @@ if not o: ui.status(_("no changes found\n")) - return + return 1 if revs: cg = repo.changegroupsubset(o, revs, 'bundle') @@ -595,6 +629,8 @@ :``%s``: basename of file being printed :``%d``: dirname of file being printed, or '.' if in repository root :``%p``: root-relative path name of file being printed + + Returns 0 on success. """ ctx = repo[opts.get('rev')] err = 1 @@ -619,11 +655,11 @@ The location of the source is added to the new repository's .hg/hgrc file, as the default to be used for future pulls. - See 'hg help urls' for valid source format details. + See :hg:`help urls` for valid source format details. It is possible to specify an ``ssh://`` URL as the destination, but no .hg/hgrc and working directory will be created on the remote side. - Please see 'hg help urls' for important details about ``ssh://`` URLs. + Please see :hg:`help urls` for important details about ``ssh://`` URLs. A set of changesets (tags, or branch names) to pull may be specified by listing each changeset (tag, or branch name) with -r/--rev. @@ -669,25 +705,29 @@ f) the tipmost head specified with the url#branch source syntax g) the tipmost head of the default branch h) tip + + Returns 0 on success. """ if opts.get('noupdate') and opts.get('updaterev'): raise util.Abort(_("cannot specify both --noupdate and --updaterev")) - hg.clone(cmdutil.remoteui(ui, opts), source, dest, - pull=opts.get('pull'), - stream=opts.get('uncompressed'), - rev=opts.get('rev'), - update=opts.get('updaterev') or not opts.get('noupdate'), - branch=opts.get('branch')) + r = hg.clone(cmdutil.remoteui(ui, opts), source, dest, + pull=opts.get('pull'), + stream=opts.get('uncompressed'), + rev=opts.get('rev'), + update=opts.get('updaterev') or not opts.get('noupdate'), + branch=opts.get('branch')) + + return r is None def commit(ui, repo, *pats, **opts): """commit the specified files or all outstanding changes Commit changes to the given files into the repository. Unlike a - centralized RCS, this operation is a local operation. See hg push - for a way to actively distribute your changes. - - If a list of files is omitted, all changes reported by "hg status" + centralized RCS, this operation is a local operation. See + :hg:`push` for a way to actively distribute your changes. + + 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 @@ -696,10 +736,16 @@ If no commit message is specified, the configured editor is started to prompt you for a message. - See 'hg help dates' for a list of formats valid for -d/--date. + See :hg:`help dates` for a list of formats valid for -d/--date. + + Returns 0 on success, 1 if nothing changed. """ extra = {} if opts.get('close_branch'): + if repo['.'].node() not in repo.branchheads(): + # The topo heads set is included in the branch heads set of the + # current branch, so it's sufficient to test branchheads + raise util.Abort(_('can only close branch heads')) extra['close'] = 1 e = cmdutil.commiteditor if opts.get('force_editor'): @@ -709,25 +755,30 @@ return repo.commit(message, opts.get('user'), opts.get('date'), match, editor=e, extra=extra) + branch = repo[None].branch() + bheads = repo.branchheads(branch) + node = cmdutil.commit(ui, repo, commitfunc, pats, opts) if not node: ui.status(_("nothing changed\n")) - 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 - pass - elif (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)): + return 1 + + ctx = repo[node] + parents = ctx.parents() + + if bheads and [x for x in parents + if x.node() not in bheads and x.branch() == branch]: ui.status(_('created new head\n')) + if not opts.get('close_branch'): + for r in parents: + if r.extra().get('close'): + ui.status(_('reopening closed branch head %d\n') % r) + if ui.debugflag: - ui.write(_('committed changeset %d:%s\n') % (rev, hex(node))) + ui.write(_('committed changeset %d:%s\n') % (int(ctx), ctx.hex())) elif ui.verbose: - ui.write(_('committed changeset %d:%s\n') % (rev, short(node))) + ui.write(_('committed changeset %d:%s\n') % (int(ctx), ctx)) def copy(ui, repo, *pats, **opts): """mark files as copied for the next commit @@ -741,7 +792,9 @@ operation is recorded, but no copying is performed. This command takes effect with the next commit. To undo a copy - before that, see hg revert. + before that, see :hg:`revert`. + + Returns 0 on success, 1 if errors are encountered. """ wlock = repo.wlock(False) try: @@ -768,6 +821,7 @@ ui.write("%d:%s\n" % (r.rev(a), hex(a))) def debugcommands(ui, cmd='', *args): + """list all available commands and options""" for cmd, vals in sorted(table.iteritems()): cmd = cmd.split('|')[0].strip('^') opts = ', '.join([i[1] for i in vals[1]]) @@ -784,6 +838,8 @@ otables.append(entry[1]) for t in otables: for o in t: + if "(DEPRECATED)" in o[3]: + continue if o[0]: options.append('-%s' % o[0]) options.append('--%s' % o[1]) @@ -796,6 +852,7 @@ ui.write("%s\n" % "\n".join(sorted(cmdlist))) def debugfsinfo(ui, path = "."): + """show information detected about current filesystem""" open('.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')) @@ -852,8 +909,12 @@ With --debug, the source (filename and line number) is printed for each config item. + + Returns 0 on success. """ + for f in util.rcpath(): + ui.debug(_('read config from: %s\n') % f) untrusted = bool(opts.get('untrusted')) if values: if len([v for v in values if '.' in v]) > 1: @@ -880,6 +941,8 @@ This is useful for writing repository conversion tools, but should be used with care. + + Returns 0 on success. """ if not rev2: @@ -971,7 +1034,10 @@ ui.write("}\n") def debuginstall(ui): - '''test Mercurial installation''' + '''test Mercurial installation + + Returns 0 on success. + ''' def writetemp(contents): (fd, name) = tempfile.mkstemp(prefix="hg-debuginstall-") @@ -1131,7 +1197,9 @@ anyway, probably with undesirable results. Use the -g/--git option to generate diffs in the git extended diff - format. For more information, read 'hg help diffs'. + format. For more information, read :hg:`help diffs`. + + Returns 0 on success. """ revs = opts.get('rev') @@ -1151,21 +1219,9 @@ if reverse: node1, node2 = node2, node1 - if stat: - opts['unified'] = '0' diffopts = patch.diffopts(ui, opts) - m = cmdutil.match(repo, pats, opts) - it = patch.diff(repo, node1, node2, match=m, opts=diffopts) - if stat: - width = 80 - if not ui.plain(): - width = util.termwidth() - ui.write(patch.diffstat(util.iterlines(it), width=width, - git=diffopts.git)) - else: - for chunk in it: - ui.write(chunk) + cmdutil.diffordiffstat(ui, repo, diffopts, node1, node2, m, stat=stat) def export(ui, repo, *changesets, **opts): """dump the header and diffs for one or more changesets @@ -1197,10 +1253,12 @@ diff anyway, probably with undesirable results. Use the -g/--git option to generate diffs in the git extended diff - format. See 'hg help diffs' for more information. + format. See :hg:`help diffs` for more information. With the --switch-parent option, the diff will be against the second parent. It can be useful to review a merge. + + Returns 0 on success. """ changesets += tuple(opts.get('rev', [])) if not changesets: @@ -1210,7 +1268,7 @@ ui.note(_('exporting patches:\n')) else: ui.note(_('exporting patch:\n')) - patch.export(repo, revs, template=opts.get('output'), + cmdutil.export(repo, revs, template=opts.get('output'), switch_parent=opts.get('switch_parent'), opts=patch.diffopts(ui, opts)) @@ -1224,7 +1282,9 @@ entire project history, and it does not delete them from the working directory. - To undo a forget before the next commit, see hg add. + To undo a forget before the next commit, see :hg:`add`. + + Returns 0 on success. """ if not pats: @@ -1233,17 +1293,20 @@ m = cmdutil.match(repo, pats, opts) s = repo.status(match=m, clean=True) forget = sorted(s[0] + s[1] + s[3] + s[6]) + errs = 0 for f in m.files(): if f not in repo.dirstate and not os.path.isdir(m.rel(f)): ui.warn(_('not removing %s: file is already untracked\n') % m.rel(f)) + errs = 1 for f in forget: if ui.verbose or not m.exact(f): ui.status(_('removing %s\n') % m.rel(f)) repo.remove(forget, unlink=False) + return errs def grep(ui, repo, pattern, *pats, **opts): """search for a pattern in specified files and revisions @@ -1260,6 +1323,8 @@ 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. + + Returns 0 if a match is found, 1 otherwise. """ reflags = 0 if opts.get('ignore_case'): @@ -1268,7 +1333,7 @@ regexp = re.compile(pattern, reflags) except Exception, inst: ui.warn(_("grep: invalid match pattern: %s\n") % inst) - return None + return 1 sep, eol = ':', '\n' if opts.get('print0'): sep = eol = '\0' @@ -1337,6 +1402,7 @@ iter = [('', l) for l in states] for change, l in iter: cols = [fn, str(rev)] + before, match, after = None, None, None if opts.get('line_number'): cols.append(str(l.linenum)) if opts.get('all'): @@ -1351,8 +1417,15 @@ continue filerevmatches[c] = 1 else: - cols.append(l.line) - ui.write(sep.join(cols), eol) + before = l.line[:l.colstart] + match = l.line[l.colstart:l.colend] + after = l.line[l.colend:] + ui.write(sep.join(cols)) + if before is not None: + ui.write(sep + before) + ui.write(match, label='grep.match') + ui.write(after) + ui.write(eol) found = True return found @@ -1418,6 +1491,8 @@ del matches[rev] del revfiles[rev] + return not found + def heads(ui, repo, *branchrevs, **opts): """show current repository heads or show branch heads @@ -1432,13 +1507,15 @@ associated with the specified changesets are shown. If -c/--closed is specified, also show branch heads marked closed - (see hg commit --close-branch). + (see :hg:`commit --close-branch`). If STARTREV is specified, only those heads that are descendants of STARTREV will be displayed. If -t/--topo is specified, named branch mechanics will be ignored and only changesets without children will be shown. + + Returns 0 if matching heads are found, 1 if not. """ if opts.get('rev'): @@ -1496,7 +1573,10 @@ With no arguments, print a list of commands with short help messages. Given a topic, extension, or command name, print help for that - topic.""" + topic. + + Returns 0 if successful. + """ option_lists = [] textwidth = util.termwidth() - 2 @@ -1754,6 +1834,8 @@ parent hash identifiers, followed by a "+" if there are uncommitted changes in the working directory, a list of tags for this revision and a branch name for non-default branches. + + Returns 0 if successful. """ if not repo and not source: @@ -1832,7 +1914,7 @@ text/plain body parts before first diff are added to commit message. - If the imported patch was generated by hg export, user and + If the imported patch was generated by :hg:`export`, user and description from patch override values from message headers and body. Values given on command line with -m/--message and -u/--user override these. @@ -1848,7 +1930,9 @@ To read a patch from standard input, use "-" as the patch name. If a URL is specified, the patch will be downloaded from it. - See 'hg help dates' for a list of formats valid for -d/--date. + See :hg:`help dates` for a list of formats valid for -d/--date. + + Returns 0 on success. """ patches = (patch1,) + patches @@ -1982,6 +2066,8 @@ changesets twice if the incoming is followed by a pull. See pull for valid source format details. + + Returns 0 if there are incoming changes, 1 otherwise. """ limit = cmdutil.loglimit(opts) source, branches = hg.parseurl(ui.expandpath(source), opts.get('branch')) @@ -2051,7 +2137,9 @@ If no directory is given, the current directory is used. It is possible to specify an ``ssh://`` URL as the destination. - See 'hg help urls' for more information. + See :hg:`help urls` for more information. + + Returns 0 on success. """ hg.repository(cmdutil.remoteui(ui, opts), dest, create=1) @@ -2072,6 +2160,8 @@ command, use the -0 option to both this command and "xargs". This will avoid the problem of "xargs" treating single filenames that contain whitespace as multiple filenames. + + Returns 0 if a match is found, 1 otherwise. """ end = opts.get('print0') and '\0' or '\n' rev = opts.get('rev') or None @@ -2106,7 +2196,7 @@ --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. + See :hg:`help dates` for a list of formats valid for -d/--date. By default this command prints revision number and changeset id, tags, non-trivial parents, user, date and time, and a summary for @@ -2117,6 +2207,8 @@ changesets, as it will only compare the merge changeset against its first parent. Also, only files different from BOTH parents will appear in files:. + + Returns 0 on success. """ matchfn = cmdutil.match(repo, pats, opts) @@ -2131,6 +2223,9 @@ if opts["date"]: df = util.matchdate(opts["date"]) + branches = opts.get('branch', []) + opts.get('only_branch', []) + opts['branch'] = [repo.lookupbranch(b) for b in branches] + displayer = cmdutil.show_changeset(ui, repo, opts, True, matchfn) def prep(ctx, fns): rev = ctx.rev() @@ -2140,7 +2235,7 @@ return if opts.get('only_merges') and len(parents) != 2: return - if opts.get('only_branch') and ctx.branch() not in opts['only_branch']: + if opts.get('branch') and ctx.branch() not in opts['branch']: return if df and not df(ctx.date()[0]): return @@ -2182,6 +2277,8 @@ With -v, print file permissions, symlink and executable bits. With --debug, print file revision hashes. + + Returns 0 on success. """ if rev and node: @@ -2214,6 +2311,8 @@ head revision, and the current branch contains exactly one other head, the other head is merged with by default. Otherwise, an explicit revision with which to merge with must be provided. + + Returns 0 on success, 1 if there are unresolved files. """ if opts.get('rev') and node: @@ -2270,6 +2369,8 @@ be pushed if a push was requested. See pull for details of valid destination formats. + + Returns 0 if there are outgoing changes, 1 otherwise. """ limit = cmdutil.loglimit(opts) dest = ui.expandpath(dest or 'default-push', dest or 'default') @@ -2307,6 +2408,8 @@ If a file argument is given, the revision in which the file was last changed (before the working directory revision or the argument to --rev if given) is printed. + + Returns 0 on success. """ rev = opts.get('rev') if rev: @@ -2346,8 +2449,9 @@ Show definition of symbolic path name NAME. If no name is given, show definition of all 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. + 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. The path names ``default`` and ``default-push`` have a special meaning. When performing a push or pull operation, they are used @@ -2356,11 +2460,11 @@ ``default`` will be used for pull; otherwise ``default`` is used as the fallback for both. When cloning a repository, the clone source is written as ``default`` in ``.hg/hgrc``. Note that - ``default`` and ``default-push`` apply to all inbound (e.g. ``hg - incoming``) and outbound (e.g. ``hg outgoing``, ``hg email`` and - ``hg bundle``) operations. - - See 'hg help urls' for more information. + ``default`` and ``default-push`` apply to all inbound (e.g. + :hg:`incoming`) and outbound (e.g. :hg:`outgoing`, :hg:`email` and + :hg:`bundle`) operations. + + See :hg:`help urls` for more information. """ if search: for name, path in ui.configitems("paths"): @@ -2396,13 +2500,15 @@ -R is specified). By default, this does not update the copy of the project in the working directory. - Use hg incoming if you want to see what would have been added by a - pull at the time you issued this command. If you then decide to - added those changes to the repository, you should use pull -r X - where X is the last changeset listed by hg incoming. + Use :hg:`incoming` if you want to see what would have been added + by a pull at the time you issued this command. If you then decide + to add those changes to the repository, you should use :hg:`pull + -r X` where ``X`` is the last changeset listed by :hg:`incoming`. If SOURCE is omitted, the 'default' path will be used. - See 'hg help urls' for more information. + See :hg:`help urls` for more information. + + Returns 0 on success, 1 if an update had unresolved files. """ source, branches = hg.parseurl(ui.expandpath(source), opts.get('branch')) other = hg.repository(cmdutil.remoteui(repo, opts), source) @@ -2435,14 +2541,20 @@ to use. In this situation, it is recommended to pull and merge before pushing. + Use --new-branch if you want to allow push to create a new named + branch that is not present at the destination. This allows you to + only create a new branch without forcing other changes. + Use -f/--force to override the default behavior and push all changesets on all branches. If -r/--rev is used, the specified revision and all its ancestors will be pushed to the remote repository. - Please see 'hg help urls' for important details about ``ssh://`` + Please see :hg:`help urls` for important details about ``ssh://`` URLs. If DESTINATION is omitted, a default path will be used. + + Returns 0 if push was successful, 1 if nothing to push. """ dest = ui.expandpath(dest or 'default-push', dest or 'default') dest, branches = hg.parseurl(dest, opts.get('branch')) @@ -2459,7 +2571,8 @@ if not c.sub(s).push(opts.get('force')): return False - r = repo.push(other, opts.get('force'), revs=revs) + r = repo.push(other, opts.get('force'), revs=revs, + newbranch=opts.get('new_branch')) return r == 0 def recover(ui, repo): @@ -2470,6 +2583,8 @@ This command tries to fix the repository status after an interrupted operation. It should only be necessary when Mercurial suggests it. + + Returns 0 if successful, 1 if nothing to recover or verify fails. """ if repo.recover(): return hg.verify(repo) @@ -2489,8 +2604,8 @@ The following table details the behavior of remove for different file states (columns) and option combinations (rows). The file states are Added [A], Clean [C], Modified [M] and Missing [!] (as - reported by hg status). The actions are Warn, Remove (from branch) - and Delete (from disk):: + reported by :hg:`status`). The actions are Warn, Remove (from + branch) and Delete (from disk):: A C M ! none W RD W R @@ -2499,9 +2614,12 @@ -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. + To undo a remove before that, see :hg:`revert`. + + Returns 0 on success, 1 if any warnings encountered. """ + ret = 0 after, force = opts.get('after'), opts.get('force') if not pats and not after: raise util.Abort(_('no files specified')) @@ -2513,11 +2631,13 @@ for f in m.files(): if f not in repo.dirstate and not os.path.isdir(m.rel(f)): ui.warn(_('not removing %s: file is untracked\n') % m.rel(f)) + ret = 1 def warn(files, reason): for f in files: ui.warn(_('not removing %s: file %s (use -f to force removal)\n') % (m.rel(f), reason)) + ret = 1 if force: remove, forget = modified + deleted + clean, added @@ -2535,6 +2655,7 @@ repo.forget(forget) repo.remove(remove, unlink=not after) + return ret def rename(ui, repo, *pats, **opts): """rename files; equivalent of copy + remove @@ -2548,7 +2669,9 @@ operation is recorded, but no copying is performed. This command takes effect at the next commit. To undo a rename - before that, see hg revert. + before that, see :hg:`revert`. + + Returns 0 on success, 1 if errors are encountered. """ wlock = repo.wlock(False) try: @@ -2582,6 +2705,8 @@ Note that Mercurial will not let you commit files with unresolved merge conflicts. You must use ``hg resolve -m ...`` before you can commit after a conflicting merge. + + Returns 0 on success, 1 if any files fail a resolve attempt. """ all, mark, unmark, show, nostatus = \ @@ -2595,8 +2720,9 @@ raise util.Abort(_('no files or directories specified; ' 'use --all to remerge all files')) - ms = merge_.mergestate(repo) + ms = mergemod.mergestate(repo) m = cmdutil.match(repo, pats, opts) + ret = 0 for f in ms: if m(f): @@ -2604,7 +2730,9 @@ if nostatus: ui.write("%s\n" % f) else: - ui.write("%s %s\n" % (ms[f].upper(), f)) + ui.write("%s %s\n" % (ms[f].upper(), f), + label='resolve.' + + {'u': 'unresolved', 'r': 'resolved'}[ms[f]]) elif mark: ms.mark(f, "r") elif unmark: @@ -2618,10 +2746,12 @@ util.copyfile(a, a + ".resolve") # resolve file - ms.resolve(f, wctx, mctx) + if ms.resolve(f, wctx, mctx): + ret = 1 # replace filemerge's .orig file with our resolve file util.rename(a + ".resolve", a + ".orig") + return ret def revert(ui, repo, *pats, **opts): """restore individual files or directories to an earlier state @@ -2638,8 +2768,8 @@ Using the -r/--rev 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. See 'hg help - dates' for a list of formats valid for -d/--date. + to "roll 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 @@ -2655,6 +2785,8 @@ Modified files are saved with a .orig suffix before reverting. To disable these backups, use --no-backup. + + Returns 0 on success. """ if opts["date"]: @@ -2841,8 +2973,8 @@ finally: wlock.release() -def rollback(ui, repo): - """roll back the last transaction +def rollback(ui, repo, **opts): + """roll back the last transaction (dangerous) This command should be used with care. There is only one level of rollback, and there is no way to undo a rollback. It will also @@ -2867,18 +2999,22 @@ the changes). Furthermore, a race is possible with readers of the repository; for example an in-progress pull from the repository may fail if a rollback is performed. + + Returns 0 on success, 1 if no rollback data is available. """ - repo.rollback() + return repo.rollback(opts.get('dry_run')) def root(ui, repo): """print the root (top) of the current working directory Print the root directory of the current repository. + + Returns 0 on success. """ ui.write(repo.root + "\n") def serve(ui, repo, **opts): - """export the repository via HTTP + """start stand-alone webserver Start a local HTTP repository browser and pull server. You can use this for ad-hoc sharing and browing of repositories. It is @@ -2894,6 +3030,12 @@ By default, the server logs accesses to stdout and errors to stderr. Use the -A/--accesslog and -E/--errorlog options to log to files. + + To have the server choose a free port number to listen on, specify + a port number of 0; in this case, the server will print the port + number it uses. + + Returns 0 on success. """ if opts["stdio"]: @@ -2903,25 +3045,36 @@ s = sshserver.sshserver(ui, repo) s.serve_forever() + # this way we can check if something was given in the command-line + if opts.get('port'): + opts['port'] = int(opts.get('port')) + baseui = repo and repo.baseui or ui optlist = ("name templates style address port prefix ipv6" - " accesslog errorlog webdir_conf certificate encoding") + " accesslog errorlog certificate encoding") for o in optlist.split(): - if opts.get(o, None): - baseui.setconfig("web", o, str(opts[o])) - if (repo is not None) and (repo.ui != baseui): - repo.ui.setconfig("web", o, str(opts[o])) - - if repo is None and not ui.config("web", "webdir_conf"): - raise error.RepoError(_("There is no Mercurial repository here" - " (.hg not found)")) + val = opts.get(o, '') + if val in (None, ''): # should check against default options instead + continue + baseui.setconfig("web", o, val) + if repo and repo.ui != baseui: + repo.ui.setconfig("web", o, val) + + o = opts.get('web_conf') or opts.get('webdir_conf') + if not o: + if not repo: + raise error.RepoError(_("There is no Mercurial repository" + " here (.hg not found)")) + o = repo.root + + app = hgweb.hgweb(o, baseui=ui) class service(object): def init(self): util.set_signal_handler() - self.httpd = server.create_server(baseui, repo) - - if not ui.verbose: + self.httpd = hgweb.server.create_server(ui, app) + + if opts['port'] and not ui.verbose: return if self.httpd.prefix: @@ -2942,8 +3095,12 @@ 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)) + if opts['port']: + write = ui.status + else: + write = ui.write + write(_('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() @@ -2985,6 +3142,8 @@ ? = not tracked I = ignored = origin of the previous file listed as A (added) + + Returns 0 on success. """ revs = opts.get('rev') @@ -3034,9 +3193,11 @@ format = "%%s%s" % end for f in files: - ui.write(format % repo.pathto(f, cwd)) + ui.write(format % repo.pathto(f, cwd), + label='status.' + state) if f in copy: - ui.write(' %s%s' % (repo.pathto(copy[f], cwd), end)) + ui.write(' %s%s' % (repo.pathto(copy[f], cwd), end), + label='status.copied') def summary(ui, repo, **opts): """summarize working directory state @@ -3046,38 +3207,54 @@ With the --remote option, this will check the default paths for incoming and outgoing changes. This can be time-consuming. + + Returns 0 on success. """ ctx = repo[None] parents = ctx.parents() pnode = parents[0].node() - tags = repo.tags() for p in parents: - t = ' '.join([t for t in tags if tags[t] == p.node()]) + # label with log.changeset (instead of log.parent) since this + # shows a working directory parent *changeset*: + ui.write(_('parent: %d:%s ') % (p.rev(), str(p)), + label='log.changeset') + ui.write(' '.join(p.tags()), label='log.tag') if p.rev() == -1: if not len(repo): - t += _(' (empty repository)') + ui.write(_(' (empty repository)')) else: - t += _(' (no revision checked out)') - ui.write(_('parent: %d:%s %s\n') % (p.rev(), str(p), t)) + ui.write(_(' (no revision checked out)')) + ui.write('\n') if p.description(): - ui.status(' ' + p.description().splitlines()[0].strip() + '\n') + ui.status(' ' + p.description().splitlines()[0].strip() + '\n', + label='log.summary') branch = ctx.branch() bheads = repo.branchheads(branch) m = _('branch: %s\n') % branch if branch != 'default': - ui.write(m) + ui.write(m, label='log.branch') else: - ui.status(m) + ui.status(m, label='log.branch') st = list(repo.status(unknown=True))[:6] - ms = merge_.mergestate(repo) + + ms = mergemod.mergestate(repo) st.append([f for f in ms if ms[f] == 'u']) - labels = [_('%d modified'), _('%d added'), _('%d removed'), - _('%d deleted'), _('%d unknown'), _('%d ignored'), - _('%d unresolved')] + + subs = [s for s in ctx.substate if ctx.sub(s).dirty()] + st.append(subs) + + labels = [ui.label(_('%d modified'), 'status.modified'), + ui.label(_('%d added'), 'status.added'), + ui.label(_('%d removed'), 'status.removed'), + ui.label(_('%d deleted'), 'status.deleted'), + ui.label(_('%d unknown'), 'status.unknown'), + ui.label(_('%d ignored'), 'status.ignored'), + ui.label(_('%d unresolved'), 'resolve.unresolved'), + ui.label(_('%d subrepos'), 'status.modified')] t = [] for s, l in zip(st, labels): if s: @@ -3090,7 +3267,10 @@ t += _(' (merge)') elif branch != parents[0].branch(): t += _(' (new branch)') - elif (not st[0] and not st[1] and not st[2]): + elif (parents[0].extra().get('close') and + pnode in repo.branchheads(branch, closed=True)): + t += _(' (head closed)') + elif (not st[0] and not st[1] and not st[2] and not st[7]): t += _(' (clean)') cleanworkdir = True elif pnode not in bheads: @@ -3169,11 +3349,16 @@ 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. + See :hg:`help dates` for a list of formats valid for -d/--date. + + Since tag names have priority over branch names during revision + lookup, using an existing branch name as a tag name is discouraged. + + Returns 0 on success. """ rev_ = "." - names = (name1,) + names + names = [t.strip() for t in (name1,) + names] if len(names) != len(set(names)): raise util.Abort(_('tag names must be unique')) for n in names: @@ -3217,6 +3402,9 @@ if date: date = util.parsedate(date) + if opts.get('edit'): + message = ui.edit(message, ui.username()) + repo.tag(names, r, message, opts.get('local'), opts.get('user'), date) def tags(ui, repo): @@ -3224,6 +3412,8 @@ This lists both regular and local tags. When the -v/--verbose switch is used, a third column "local" is printed for local tags. + + Returns 0 on success. """ hexfunc = ui.debugflag and hex or short @@ -3259,6 +3449,8 @@ 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. + + Returns 0 on success. """ displayer = cmdutil.show_changeset(ui, repo, opts) displayer.show(repo[len(repo) - 1]) @@ -3269,6 +3461,8 @@ Apply one or more compressed changegroup files generated by the bundle command. + + Returns 0 on success, 1 if an update has unresolved files. """ fnames = (fname1,) + fnames @@ -3284,7 +3478,7 @@ return postincoming(ui, repo, modheads, opts.get('update'), None) def update(ui, repo, node=None, rev=None, clean=False, date=None, check=False): - """update working directory + """update working directory (or switch revisions) Update the repository's working directory to the specified changeset. @@ -3311,12 +3505,14 @@ 3. With the -C/--clean option, uncommitted changes are discarded and the working directory is updated to the requested changeset. - Use null as the changeset to remove the working directory (like 'hg - clone -U'). - - If you want to update just one file to an older changeset, use 'hg revert'. - - See 'hg help dates' for a list of formats valid for -d/--date. + Use null as the changeset to remove the working directory (like + :hg:`clone -U`). + + If you want to update just one file to an older changeset, use :hg:`revert`. + + See :hg:`help dates` for a list of formats valid for -d/--date. + + Returns 0 on success, 1 if there are unresolved files. """ if rev and node: raise util.Abort(_("please specify just one revision")) @@ -3352,6 +3548,8 @@ integrity, validating the hashes and checksums of each entry in the changelog, manifest, and tracked files, as well as the integrity of their crosslinks and indices. + + Returns 0 on success, 1 if errors are encountered. """ return hg.verify(repo) @@ -3423,6 +3621,7 @@ ('g', 'git', None, _('use git extended diff format')), ('l', 'limit', '', _('limit number of changes displayed')), ('M', 'no-merges', None, _('do not show merges')), + ('', 'stat', None, _('output diffstat-style summary of changes')), ] + templateopts diffopts = [ @@ -3636,7 +3835,7 @@ ('c', 'closed', False, _('show normal and closed branch heads')), ] + templateopts, - _('[-ac] [-r STARTREV] [REV]...')), + _('[-ac] [-r REV] [REV]...')), "help": (help_, [], _('[TOPIC]')), "identify|id": (identify, @@ -3702,8 +3901,10 @@ ('', 'removed', None, _('include revisions where files were removed')), ('m', 'only-merges', None, _('show only merges')), ('u', 'user', [], _('revisions committed by user')), - ('b', 'only-branch', [], - _('show only changesets within the given named branch')), + ('', 'only-branch', [], + _('show only changesets within the given named branch (DEPRECATED)')), + ('b', 'branch', [], + _('show changesets within the given named branch')), ('P', 'prune', [], _('do not display revision or any of its ancestors')), ] + logopts + walkopts, @@ -3755,6 +3956,7 @@ _('a changeset intended to be included in the destination')), ('b', 'branch', [], _('a specific branch you would like to push')), + ('', 'new-branch', False, _('allow pushing a new branch')), ] + remoteopts, _('[-f] [-r REV]... [-e CMD] [--remotecmd CMD] [DEST]')), "recover": (recover, []), @@ -3789,7 +3991,7 @@ ('', 'no-backup', None, _('do not save backup copies of files')), ] + walkopts + dryrunopts, _('[OPTION]... [-r REV] [NAME]...')), - "rollback": (rollback, []), + "rollback": (rollback, dryrunopts), "root": (root, []), "^serve": (serve, @@ -3797,15 +3999,18 @@ ('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 listen on (default: 8000)')), + # use string type, then we can check if something was passed + ('p', 'port', '', _('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 directory)')), - ('', 'webdir-conf', '', _('name of the webdir config file' + ('', 'web-conf', '', _('name of the hgweb config file' ' (serve more than one repository)')), + ('', 'webdir-conf', '', _('name of the hgweb config file' + ' (DEPRECATED)')), ('', 'pid-file', '', _('name of file to write process ID to')), ('', 'stdio', None, _('for remote clients')), ('t', 'templates', '', _('web templates to use')), @@ -3845,6 +4050,7 @@ ('r', 'rev', '', _('revision to tag')), ('', 'remove', None, _('remove a tag')), # -l/--local is already there, commitopts cannot be used + ('e', 'edit', None, _('edit commit message')), ('m', 'message', '', _('use as commit message')), ] + commitopts2, _('[-f] [-l] [-m TEXT] [-d DATE] [-u USER] [-r REV] NAME...')), diff -r 0bb67503ad4b -r ac6fec2af8c8 mercurial/config.py --- a/mercurial/config.py Mon May 31 21:37:01 2010 +0200 +++ b/mercurial/config.py Mon May 31 21:43:03 2010 +0200 @@ -6,7 +6,7 @@ # GNU General Public License version 2 or any later version. from i18n import _ -import error +import error, util import re, os class sortdict(dict): @@ -96,7 +96,7 @@ cont = False m = includere.match(l) if m: - inc = m.group(1) + inc = util.expandpath(m.group(1)) base = os.path.dirname(src) inc = os.path.normpath(os.path.join(base, inc)) if include: diff -r 0bb67503ad4b -r ac6fec2af8c8 mercurial/context.py --- a/mercurial/context.py Mon May 31 21:37:01 2010 +0200 +++ b/mercurial/context.py Mon May 31 21:43:03 2010 +0200 @@ -7,7 +7,7 @@ from node import nullid, nullrev, short, hex from i18n import _ -import ancestor, bdiff, error, util, subrepo +import ancestor, bdiff, error, util, subrepo, patch import os, errno propertycache = util.propertycache @@ -204,6 +204,16 @@ def sub(self, path): return subrepo.subrepo(self, path) + def diff(self, ctx2=None, match=None, **opts): + """Returns a diff generator for the given contexts and matcher""" + if ctx2 is None: + ctx2 = self.p1() + if ctx2 is not None and not isinstance(ctx2, changectx): + ctx2 = self._repo[ctx2] + diffopts = patch.diffopts(self._repo.ui, opts) + return patch.diff(self._repo, ctx2.node(), self.node(), + match=match, opts=diffopts) + class filectx(object): """A filecontext object makes access to data related to a particular filerevision convenient.""" @@ -539,15 +549,14 @@ class workingctx(changectx): """A workingctx object makes access to data related to the current working directory convenient. - parents - a pair of parent nodeids, or None to use the dirstate. date - any valid date string or (unixtime, offset), or None. user - username string, or None. extra - a dictionary of extra values, or None. changes - a list of file lists as returned by localrepo.status() or None to use the repository status. """ - def __init__(self, repo, parents=None, text="", user=None, date=None, - extra=None, changes=None): + def __init__(self, repo, text="", user=None, date=None, extra=None, + changes=None): self._repo = repo self._rev = None self._node = None @@ -556,10 +565,15 @@ self._date = util.parsedate(date) if user: self._user = user - if parents: - self._parents = [changectx(self._repo, p) for p in parents] if changes: - self._status = list(changes) + self._status = list(changes[:4]) + self._unknown = changes[4] + self._ignored = changes[5] + self._clean = changes[6] + else: + self._unknown = None + self._ignored = None + self._clean = None self._extra = {} if extra: @@ -587,6 +601,9 @@ def _manifest(self): """generate a manifest corresponding to the working directory""" + if self._unknown is None: + self.status(unknown=True) + man = self._parents[0].manifest().copy() copied = self._repo.dirstate.copies() if len(self._parents) > 1: @@ -601,7 +618,8 @@ f = copied.get(f, f) return getman(f).flags(f) ff = self._repo.dirstate.flagfunc(cf) - modified, added, removed, deleted, unknown = self._status[:5] + modified, added, removed, deleted = self._status + unknown = self._unknown for i, l in (("a", added), ("m", modified), ("u", unknown)): for f in l: orig = copied.get(f, f) @@ -619,7 +637,7 @@ @propertycache def _status(self): - return self._repo.status(unknown=True) + return self._repo.status()[:4] @propertycache def _user(self): @@ -637,6 +655,22 @@ self._parents = [changectx(self._repo, x) for x in p] return self._parents + def status(self, ignored=False, clean=False, unknown=False): + """Explicit status query + Unless this method is used to query the working copy status, the + _status property will implicitly read the status using its default + arguments.""" + stat = self._repo.status(ignored=ignored, clean=clean, unknown=unknown) + self._unknown = self._ignored = self._clean = None + if unknown: + self._unknown = stat[4] + if ignored: + self._ignored = stat[5] + if clean: + self._clean = stat[6] + self._status = stat[:4] + return stat + def manifest(self): return self._manifest def user(self): @@ -657,9 +691,14 @@ def deleted(self): return self._status[3] def unknown(self): - return self._status[4] + assert self._unknown is not None # must call status first + return self._unknown + def ignored(self): + assert self._ignored is not None # must call status first + return self._ignored def clean(self): - return self._status[5] + assert self._clean is not None # must call status first + return self._clean def branch(self): return self._extra['branch'] def extra(self): @@ -713,7 +752,11 @@ def dirty(self, missing=False): "check whether a working directory is modified" - + # check subrepos first + for s in self.substate: + if self.sub(s).dirty(): + return True + # check current working dir return (self.p2() or self.branch() != self.p1().branch() or self.modified() or self.added() or self.removed() or (missing and self.deleted())) @@ -873,8 +916,10 @@ return self._status[3] def unknown(self): return self._status[4] + def ignored(self): + return self._status[5] def clean(self): - return self._status[5] + return self._status[6] def branch(self): return self._extra['branch'] def extra(self): @@ -890,12 +935,16 @@ """get a file context from the working directory""" return self._filectxfn(self._repo, self, path) + def commit(self): + """commit context to the repo""" + return self._repo.commitctx(self) + class memfilectx(object): """memfilectx represents an in-memory file to commit. See memctx for more details. """ - def __init__(self, path, data, islink, isexec, copied): + def __init__(self, path, data, islink=False, isexec=False, copied=None): """ path is the normalized file path relative to repository root. data is the file content as a string. diff -r 0bb67503ad4b -r ac6fec2af8c8 mercurial/dirstate.py --- a/mercurial/dirstate.py Mon May 31 21:37:01 2010 +0200 +++ b/mercurial/dirstate.py Mon May 31 21:43:03 2010 +0200 @@ -11,7 +11,6 @@ import struct, os, stat, errno import cStringIO -_unknown = ('?', 0, 0, 0) _format = ">cllll" propertycache = util.propertycache @@ -286,14 +285,15 @@ '''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. + # in state 'm' (-1) or coming from other parent (-2) 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) + self.otherparent(f) if source: self.copy(source, f) return @@ -305,8 +305,11 @@ if f in self._copymap: del self._copymap[f] - def normaldirty(self, f): - '''Mark a file normal, but dirty.''' + def otherparent(self, f): + '''Mark as coming from the other parent, always dirty.''' + if self._pl[1] == nullid: + raise util.Abort(_("setting %r to other parent " + "only allowed in merges") % f) self._dirty = True self._addpath(f) self._map[f] = ('n', 0, -2, -1) @@ -327,10 +330,11 @@ self._droppath(f) size = 0 if self._pl[1] != nullid and f in self._map: + # backup the previous state entry = self._map[f] - if entry[0] == 'm': + if entry[0] == 'm': # merge size = -1 - elif entry[0] == 'n' and entry[2] == -2: + elif entry[0] == 'n' and entry[2] == -2: # other parent size = -2 self._map[f] = ('r', 0, size, 0) if size == 0 and f in self._copymap: @@ -639,7 +643,7 @@ if (size >= 0 and (size != st.st_size or ((mode ^ st.st_mode) & 0100 and self._checkexec)) - or size == -2 + or size == -2 # other parent or fn in self._copymap): madd(fn) elif time != int(st.st_mtime): diff -r 0bb67503ad4b -r ac6fec2af8c8 mercurial/dispatch.py --- a/mercurial/dispatch.py Mon May 31 21:37:01 2010 +0200 +++ b/mercurial/dispatch.py Mon May 31 21:43:03 2010 +0200 @@ -9,7 +9,7 @@ import os, sys, atexit, signal, pdb, socket, errno, shlex, time import util, commands, hg, fancyopts, extensions, hook, error import cmdutil, encoding -import ui as _ui +import ui as uimod def run(): "run the command in sys.argv" @@ -18,7 +18,7 @@ def dispatch(args): "run the command specified in args" try: - u = _ui.ui() + u = uimod.ui() if '--traceback' in args: u.setconfig('ui', 'traceback', 'on') except util.Abort, inst: diff -r 0bb67503ad4b -r ac6fec2af8c8 mercurial/filelog.py --- a/mercurial/filelog.py Mon May 31 21:37:01 2010 +0200 +++ b/mercurial/filelog.py Mon May 31 21:43:03 2010 +0200 @@ -33,9 +33,7 @@ def add(self, text, meta, transaction, link, p1=None, p2=None): if meta or text.startswith('\1\n'): - mt = "" - if meta: - mt = ["%s: %s\n" % (k, v) for k, v in sorted(meta.iteritems())] + mt = ["%s: %s\n" % (k, v) for k, v in sorted(meta.iteritems())] text = "\1\n%s\1\n%s" % ("".join(mt), text) return self.addrevision(text, transaction, link, p1, p2) diff -r 0bb67503ad4b -r ac6fec2af8c8 mercurial/filemerge.py --- a/mercurial/filemerge.py Mon May 31 21:37:01 2010 +0200 +++ b/mercurial/filemerge.py Mon May 31 21:43:03 2010 +0200 @@ -7,7 +7,7 @@ from node import short from i18n import _ -import util, simplemerge, match +import util, simplemerge, match, error import os, tempfile, re, filecmp def _toolstr(ui, tool, part, default=""): @@ -16,6 +16,9 @@ def _toolbool(ui, tool, part, default=False): return ui.configbool("merge-tools", tool + "." + part, default) +def _toollist(ui, tool, part, default=[]): + return ui.configlist("merge-tools", tool + "." + part, default) + _internal = ['internal:' + s for s in 'fail local other merge prompt dump'.split()] @@ -176,7 +179,18 @@ ui.debug("my %s other %s ancestor %s\n" % (fcd, fco, fca)) # do we attempt to simplemerge first? - if _toolbool(ui, tool, "premerge", not (binary or symlink)): + try: + premerge = _toolbool(ui, tool, "premerge", not (binary or symlink)) + except error.ConfigError: + premerge = _toolstr(ui, tool, "premerge").lower() + valid = 'keep'.split() + if premerge not in valid: + _valid = ', '.join(["'" + v + "'" for v in valid]) + raise error.ConfigError(_("%s.premerge not valid " + "('%s' is neither boolean nor %s)") % + (tool, premerge, _valid)) + + if premerge: r = simplemerge.simplemerge(ui, a, b, c, quiet=True) if not r: ui.debug(" premerge successful\n") @@ -184,7 +198,8 @@ os.unlink(b) os.unlink(c) return 0 - util.copyfile(back, a) # restore from backup and try again + if premerge != 'keep': + util.copyfile(back, a) # restore from backup and try again env = dict(HG_FILE=fd, HG_MY_NODE=short(mynode), @@ -211,11 +226,20 @@ lambda x: '"%s"' % util.localpath(replace[x.group()[1:]]), args) r = util.system(toolpath + ' ' + args, cwd=repo.root, environ=env) - if not r and _toolbool(ui, tool, "checkconflicts"): + if not r and (_toolbool(ui, tool, "checkconflicts") or + 'conflicts' in _toollist(ui, tool, "check")): if re.match("^(<<<<<<< .*|=======|>>>>>>> .*)$", fcd.data()): r = 1 - if not r and _toolbool(ui, tool, "checkchanged"): + checked = False + if 'prompt' in _toollist(ui, tool, "check"): + checked = True + if ui.promptchoice(_("was merge of '%s' successful (yn)?") % fd, + (_("&Yes"), _("&No")), 1): + r = 1 + + if not r and not checked and (_toolbool(ui, tool, "checkchanged") or + 'changed' in _toollist(ui, tool, "check")): if filecmp.cmp(repo.wjoin(fd), back): if ui.promptchoice(_(" output file %s appears unchanged\n" "was merge successful (yn)?") % fd, diff -r 0bb67503ad4b -r ac6fec2af8c8 mercurial/graphmod.py --- a/mercurial/graphmod.py Mon May 31 21:37:01 2010 +0200 +++ b/mercurial/graphmod.py Mon May 31 21:43:03 2010 +0200 @@ -115,7 +115,7 @@ edges.append((ecol, next.index(eid), colors[eid])) elif eid == cur: for p in parents: - edges.append((ecol, next.index(p), colors[p])) + edges.append((ecol, next.index(p), color)) # Yield and move on yield (cur, type, data, (col, color), edges) diff -r 0bb67503ad4b -r ac6fec2af8c8 mercurial/help.py --- a/mercurial/help.py Mon May 31 21:37:01 2010 +0200 +++ b/mercurial/help.py Mon May 31 21:43:03 2010 +0200 @@ -25,7 +25,7 @@ break start = line[:3] - if start == '\"\"\"' or start == "\'\'\'": + if start == '"""' or start == "'''": line = line[3:] while line: if line.rstrip().endswith(start): @@ -97,4 +97,5 @@ loaddoc('templates')), (['urls'], _('URL Paths'), loaddoc('urls')), (["extensions"], _("Using additional features"), extshelp), + (["hgweb"], _("Configuring hgweb"), loaddoc('hgweb')), ) diff -r 0bb67503ad4b -r ac6fec2af8c8 mercurial/help/config.txt --- a/mercurial/help/config.txt Mon May 31 21:37:01 2010 +0200 +++ b/mercurial/help/config.txt Mon May 31 21:43:03 2010 +0200 @@ -5,12 +5,13 @@ - ``\.hg\hgrc`` - ``%USERPROFILE%\.hgrc`` -- ``%USERPROFILE%\Mercurial.ini`` +- ``%USERPROFILE%\mercurial.ini`` - ``%HOME%\.hgrc`` -- ``%HOME%\Mercurial.ini`` -- ``C:\Mercurial\Mercurial.ini`` -- ``HKEY_LOCAL_MACHINE\SOFTWARE\Mercurial`` -- ``\Mercurial.ini`` +- ``%HOME%\mercurial.ini`` +- ``C:\mercurial\mercurial.ini`` (unless regkey or hgrc.d\ or mercurial.ini found) +- ``HKEY_LOCAL_MACHINE\SOFTWARE\Mercurial`` (unless hgrc.d\ or mercurial.ini found) +- ``\hgrc.d\*.rc`` (unless mercurial.ini found) +- ``\mercurial.ini`` On Unix, these files are read: @@ -29,7 +30,7 @@ username = Firstname Lastname verbose = True -This above entries will be referred to as ``ui.username`` and +The above entries will be referred to as ``ui.username`` and ``ui.verbose``, respectively. Please see the hgrc man page for a full description of the possible configuration values: diff -r 0bb67503ad4b -r ac6fec2af8c8 mercurial/help/diffs.txt --- a/mercurial/help/diffs.txt Mon May 31 21:37:01 2010 +0200 +++ b/mercurial/help/diffs.txt Mon May 31 21:43:03 2010 +0200 @@ -16,7 +16,7 @@ format. This means that when generating diffs from a Mercurial repository -(e.g. with "hg export"), you should be careful about things like file +(e.g. with :hg:`export`), you should be careful about things like file copies and renames or other things mentioned above, because when applying a standard diff to a different repository, this extra information is lost. Mercurial's internal operations (like push and diff -r 0bb67503ad4b -r ac6fec2af8c8 mercurial/help/hgweb.txt --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/mercurial/help/hgweb.txt Mon May 31 21:43:03 2010 +0200 @@ -0,0 +1,46 @@ +Mercurial's internal web server, hgweb, can serve either a single +repository, or a collection of them. In the latter case, a special +configuration file can be used to specify the repository paths to use +and global web configuration options. + +This file uses the same syntax as hgrc configuration files, but only +the following sections are recognized: + + - web + - paths + - collections + +The ``web`` section can specify all the settings described in the web +section of the hgrc documentation. + +The ``paths`` section provides mappings of physical repository +paths to virtual ones. For instance:: + + [paths] + projects/a = /foo/bar + projects/b = /baz/quux + web/root = /real/root/* + / = /real/root2/* + virtual/root2 = /real/root2/** + +- The first two entries make two repositories in different directories + appear under the same directory in the web interface +- The third entry maps every Mercurial repository found in '/real/root' + into 'web/root'. This format is preferred over the [collections] one, + since using absolute paths as configuration keys is not supported on every + platform (especially on Windows). +- The fourth entry is a special case mapping all repositories in + '/real/root2' in the root of the virtual directory. +- The fifth entry recursively finds all repositories under the real + root, and maps their relative paths under the virtual root. + +The ``collections`` section provides mappings of trees of physical +repositories paths to virtual ones, though the paths syntax is generally +preferred. For instance:: + + [collections] + /foo = /foo + +Here, the left side will be stripped off all repositories found in the +right side. Thus ``/foo/bar`` and ``foo/quux/baz`` will be listed as +``bar`` and ``quux/baz`` respectively. diff -r 0bb67503ad4b -r ac6fec2af8c8 mercurial/help/urls.txt --- a/mercurial/help/urls.txt Mon May 31 21:37:01 2010 +0200 +++ b/mercurial/help/urls.txt Mon May 31 21:43:03 2010 +0200 @@ -7,12 +7,12 @@ ssh://[user[:pass]@]host[:port]/[path][#revision] Paths in the local filesystem can either point to Mercurial -repositories or to bundle files (as created by 'hg bundle' or 'hg -incoming --bundle'). +repositories or to bundle files (as created by :hg:`bundle` or :hg:` +incoming --bundle`). An optional identifier after # indicates a particular branch, tag, or -changeset to use from the remote repository. See also 'hg help -revisions'. +changeset to use from the remote repository. See also :hg:`help +revisions`. Some features, such as pushing to http:// and https:// URLs are only possible if the feature is explicitly enabled on the remote Mercurial @@ -47,7 +47,7 @@ ... You can then use the alias for any command that uses a URL (for -example 'hg pull alias1' will be treated as 'hg pull URL1'). +example :hg:`pull alias1` will be treated as :hg:`pull URL1`). Two path aliases are special because they are used as defaults when you do not provide the URL to a command: diff -r 0bb67503ad4b -r ac6fec2af8c8 mercurial/hg.py --- a/mercurial/hg.py Mon May 31 21:37:01 2010 +0200 +++ b/mercurial/hg.py Mon May 31 21:43:03 2010 +0200 @@ -10,8 +10,8 @@ from lock import release import localrepo, bundlerepo, httprepo, sshrepo, statichttprepo import lock, util, extensions, error, encoding, node -import merge as _merge -import verify as _verify +import merge as mergemod +import verify as verifymod import errno, os, shutil def _local(path): @@ -277,6 +277,7 @@ % dest) raise + hardlink = None for f in src_repo.store.copylist(): src = os.path.join(src_repo.sharedpath, f) dst = os.path.join(dest_path, f) @@ -287,7 +288,7 @@ if dst.endswith('data'): # lock to avoid premature writing to the target dest_lock = lock.lock(os.path.join(dstbase, "lock")) - util.copyfiles(src, dst) + hardlink = util.copyfiles(src, dst, hardlink) # we need to re-init the repo after manually copying the data # into it @@ -359,7 +360,7 @@ def update(repo, node): """update the working directory to node, merging linear changes""" - stats = _merge.update(repo, node, False, False, None) + stats = mergemod.update(repo, node, False, False, None) _showstats(repo, stats) if stats[3]: repo.ui.status(_("use 'hg resolve' to retry unresolved file merges\n")) @@ -370,14 +371,14 @@ def clean(repo, node, show_stats=True): """forcibly switch the working directory to node, clobbering changes""" - stats = _merge.update(repo, node, False, True, None) + stats = mergemod.update(repo, node, False, True, None) if show_stats: _showstats(repo, stats) return stats[3] > 0 def merge(repo, node, force=None, remind=True): """branch merge with node, resolving changes""" - stats = _merge.update(repo, node, True, force, False) + stats = mergemod.update(repo, node, True, force, False) _showstats(repo, stats) if stats[3]: repo.ui.status(_("use 'hg resolve' to retry unresolved file merges " @@ -388,8 +389,8 @@ def revert(repo, node, choose): """revert changes to revision in node without updating dirstate""" - return _merge.update(repo, node, False, True, choose)[3] > 0 + return mergemod.update(repo, node, False, True, choose)[3] > 0 def verify(repo): """verify the consistency of a repository""" - return _verify.verify(repo) + return verifymod.verify(repo) diff -r 0bb67503ad4b -r ac6fec2af8c8 mercurial/hgweb/__init__.py --- a/mercurial/hgweb/__init__.py Mon May 31 21:37:01 2010 +0200 +++ b/mercurial/hgweb/__init__.py Mon May 31 21:43:03 2010 +0200 @@ -6,11 +6,26 @@ # This software may be used and distributed according to the terms of the # GNU General Public License version 2 or any later version. +import os import hgweb_mod, hgwebdir_mod -def hgweb(*args, **kwargs): - return hgweb_mod.hgweb(*args, **kwargs) +def hgweb(config, name=None, baseui=None): + '''create an hgweb wsgi object -def hgwebdir(*args, **kwargs): - return hgwebdir_mod.hgwebdir(*args, **kwargs) + config can be one of: + - repo object (single repo view) + - path to repo (single repo view) + - path to config file (multi-repo view) + - dict of virtual:real pairs (multi-repo view) + - list of virtual:real tuples (multi-repo view) + ''' + if ((isinstance(config, str) and not os.path.isdir(config)) or + isinstance(config, dict) or isinstance(config, list)): + # create a multi-dir interface + return hgwebdir_mod.hgwebdir(config, baseui=baseui) + return hgweb_mod.hgweb(config, name=name, baseui=baseui) + +def hgwebdir(config, baseui=None): + return hgwebdir_mod.hgwebdir(config, baseui=baseui) + diff -r 0bb67503ad4b -r ac6fec2af8c8 mercurial/hgweb/hgweb_mod.py --- a/mercurial/hgweb/hgweb_mod.py Mon May 31 21:37:01 2010 +0200 +++ b/mercurial/hgweb/hgweb_mod.py Mon May 31 21:43:03 2010 +0200 @@ -10,7 +10,6 @@ from mercurial import ui, hg, hook, error, encoding, templater from common import get_mtime, ErrorResponse, permhooks from common import HTTP_OK, HTTP_BAD_REQUEST, HTTP_NOT_FOUND, HTTP_SERVER_ERROR -from common import HTTP_UNAUTHORIZED, HTTP_METHOD_NOT_ALLOWED from request import wsgirequest import webcommands, protocol, webutil @@ -22,15 +21,18 @@ } class hgweb(object): - def __init__(self, repo, name=None): + def __init__(self, repo, name=None, baseui=None): if isinstance(repo, str): - u = ui.ui() - u.setconfig('ui', 'report_untrusted', 'off') - u.setconfig('ui', 'interactive', 'off') + if baseui: + u = baseui.copy() + else: + u = ui.ui() self.repo = hg.repository(u, repo) else: self.repo = repo + self.repo.ui.setconfig('ui', 'report_untrusted', 'off') + self.repo.ui.setconfig('ui', 'interactive', 'off') hook.redirect(True) self.mtime = -1 self.reponame = name diff -r 0bb67503ad4b -r ac6fec2af8c8 mercurial/hgweb/hgwebdir_mod.py --- a/mercurial/hgweb/hgwebdir_mod.py Mon May 31 21:37:01 2010 +0200 +++ b/mercurial/hgweb/hgwebdir_mod.py Mon May 31 21:43:03 2010 +0200 @@ -196,11 +196,8 @@ yield {"type" : i[0], "extension": i[1], "node": nodeid, "url": url} - sortdefault = None, False - def entries(sortcolumn="", descending=False, subdir="", **map): + def rawentries(subdir="", **map): - rows = [] - parity = paritygen(self.stripecount) descend = self.ui.configbool('web', 'descend', True) for name, path in self.repos: @@ -252,19 +249,19 @@ lastchange=d, lastchange_sort=d[1]-d[0], archives=archivelist(u, "tip", url)) - if (not sortcolumn or (sortcolumn, descending) == sortdefault): - # fast path for unsorted output - row['parity'] = parity.next() - yield row - else: - rows.append((row["%s_sort" % sortcolumn], row)) - if rows: - rows.sort() - if descending: - rows.reverse() - for key, row in rows: - row['parity'] = parity.next() - yield row + yield row + + sortdefault = None, False + def entries(sortcolumn="", descending=False, subdir="", **map): + rows = rawentries(subdir=subdir, **map) + + if sortcolumn and sortdefault != (sortcolumn, descending): + sortkey = '%s_sort' % sortcolumn + rows = sorted(rows, key=lambda x: x[sortkey], + reverse=descending) + for row, parity in zip(rows, paritygen(self.stripecount)): + row['parity'] = parity + yield row self.refresh() sortable = ["name", "description", "contact", "lastchange"] diff -r 0bb67503ad4b -r ac6fec2af8c8 mercurial/hgweb/server.py --- a/mercurial/hgweb/server.py Mon May 31 21:37:01 2010 +0200 +++ b/mercurial/hgweb/server.py Mon May 31 21:43:03 2010 +0200 @@ -7,9 +7,7 @@ # GNU General Public License version 2 or any later version. import os, sys, errno, urllib, BaseHTTPServer, socket, SocketServer, traceback -from mercurial import hg, util, error -from hgweb_mod import hgweb -from hgwebdir_mod import hgwebdir +from mercurial import util, error from mercurial.i18n import _ def _splitURI(uri): @@ -195,104 +193,85 @@ self.close_connection = True pass -def create_server(ui, repo): - use_threads = True - - def openlog(opt, default): - if opt and opt != '-': - return open(opt, 'a') - return default - - if repo is None: - myui = ui +try: + from threading import activeCount + _mixin = SocketServer.ThreadingMixIn +except ImportError: + if hasattr(os, "fork"): + _mixin = SocketServer.ForkingMixIn else: - 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") - accesslog = openlog(myui.config("web", "accesslog", "-"), sys.stdout) - errorlog = openlog(myui.config("web", "errorlog", "-"), sys.stderr) + class _mixin: + pass + +def openlog(opt, default): + if opt and opt != '-': + return open(opt, 'a') + return default - if use_threads: - try: - from threading import activeCount - except ImportError: - use_threads = False +class MercurialHTTPServer(object, _mixin, BaseHTTPServer.HTTPServer): - if use_threads: - _mixin = SocketServer.ThreadingMixIn - else: - if hasattr(os, "fork"): - _mixin = SocketServer.ForkingMixIn - else: - class _mixin: - pass + # SO_REUSEADDR has broken semantics on windows + if os.name == 'nt': + allow_reuse_address = 0 - class MercurialHTTPServer(object, _mixin, BaseHTTPServer.HTTPServer): + def __init__(self, ui, app, addr, handler, **kwargs): + BaseHTTPServer.HTTPServer.__init__(self, addr, handler, **kwargs) + self.daemon_threads = True + self.application = app - # SO_REUSEADDR has broken semantics on windows - if os.name == 'nt': - allow_reuse_address = 0 + ssl_cert = ui.config('web', 'certificate') + if ssl_cert: + try: + from OpenSSL import SSL + ctx = SSL.Context(SSL.SSLv23_METHOD) + except ImportError: + raise util.Abort(_("SSL support is unavailable")) + ctx.use_privatekey_file(ssl_cert) + ctx.use_certificate_file(ssl_cert) + sock = socket.socket(self.address_family, self.socket_type) + self.socket = SSL.Connection(ctx, sock) + self.server_bind() + self.server_activate() - def __init__(self, *args, **kargs): - BaseHTTPServer.HTTPServer.__init__(self, *args, **kargs) - self.accesslog = accesslog - self.errorlog = errorlog - self.daemon_threads = True - def make_handler(): - if webdir_conf: - hgwebobj = hgwebdir(webdir_conf, ui) - elif repo is not None: - hgwebobj = hgweb(hg.repository(repo.ui, repo.root)) - else: - raise error.RepoError(_("There is no Mercurial repository" - " here (.hg not found)")) - return hgwebobj - self.application = make_handler() + prefix = ui.config('web', 'prefix', '') + if prefix: + prefix = '/' + prefix.strip('/') + self.prefix = prefix - if ssl_cert: - try: - from OpenSSL import SSL - ctx = SSL.Context(SSL.SSLv23_METHOD) - except ImportError: - raise util.Abort(_("SSL support is unavailable")) - ctx.use_privatekey_file(ssl_cert) - ctx.use_certificate_file(ssl_cert) - sock = socket.socket(self.address_family, self.socket_type) - self.socket = SSL.Connection(ctx, sock) - self.server_bind() - self.server_activate() + alog = openlog(ui.config('web', 'accesslog', '-'), sys.stdout) + elog = openlog(ui.config('web', 'errorlog', '-'), sys.stderr) + self.accesslog = alog + self.errorlog = elog + + self.addr, self.port = self.socket.getsockname()[0:2] + self.fqaddr = socket.getfqdn(addr[0]) - 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) +class IPv6HTTPServer(MercurialHTTPServer): + address_family = getattr(socket, 'AF_INET6', None) + def __init__(self, *args, **kwargs): + if self.address_family is None: + raise error.RepoError(_('IPv6 is not available on this system')) + super(IPv6HTTPServer, self).__init__(*args, **kwargs) - def __init__(self, *args, **kwargs): - if self.address_family is None: - raise error.RepoError(_('IPv6 is not available on this system')) - super(IPv6HTTPServer, self).__init__(*args, **kwargs) +def create_server(ui, app): - if ssl_cert: + if ui.config('web', 'certificate'): handler = _shgwebhandler else: handler = _hgwebhandler + if ui.configbool('web', 'ipv6'): + cls = IPv6HTTPServer + else: + cls = MercurialHTTPServer + # ugly hack due to python issue5853 (for threaded use) import mimetypes; mimetypes.init() + address = ui.config('web', 'address', '') + port = int(ui.config('web', 'port', 8000)) try: - if use_ipv6: - return IPv6HTTPServer((address, port), handler) - else: - return MercurialHTTPServer((address, port), handler) + return cls(ui, app, (address, port), handler) except socket.error, inst: raise util.Abort(_("cannot start server at '%s:%d': %s") % (address, port, inst.args[1])) diff -r 0bb67503ad4b -r ac6fec2af8c8 mercurial/httprepo.py --- a/mercurial/httprepo.py Mon May 31 21:37:01 2010 +0200 +++ b/mercurial/httprepo.py Mon May 31 21:43:03 2010 +0200 @@ -208,6 +208,12 @@ return util.chunkbuffer(zgenerator(f)) def unbundle(self, cg, heads, source): + '''Send cg (a readable file-like object representing the + changegroup to push, typically a chunkbuffer object) to the + remote server as a bundle. Return an integer response code: + non-zero indicates a successful push (see + localrepository.addchangegroup()), and zero indicates either + error or nothing to push.''' # have to stream bundle to a temp file because we do not have # http 1.1 chunked transfer. diff -r 0bb67503ad4b -r ac6fec2af8c8 mercurial/localrepo.py --- a/mercurial/localrepo.py Mon May 31 21:37:01 2010 +0200 +++ b/mercurial/localrepo.py Mon May 31 21:43:03 2010 +0200 @@ -11,9 +11,10 @@ import changelog, dirstate, filelog, manifest, context import lock, transaction, store, encoding import util, extensions, hook, error -import match as match_ -import merge as merge_ -import tags as tags_ +import match as matchmod +import merge as mergemod +import tags as tagsmod +import url as urlmod from lock import release import weakref, stat, errno, os, time, inspect propertycache = util.propertycache @@ -163,9 +164,13 @@ if c in allchars: raise util.Abort(_('%r cannot be used in a tag name') % c) + branches = self.branchmap() for name in names: self.hook('pretag', throw=True, node=hex(node), tag=name, local=local) + if name in branches: + self.ui.warn(_("warning: tag %s conflicts with existing" + " branch name\n") % name) def writetags(fp, names, munge, prevtags): fp.seek(0, 2) @@ -207,7 +212,7 @@ if '.hgtags' not in self.dirstate: self.add(['.hgtags']) - m = match_.exact(self.root, '', ['.hgtags']) + m = matchmod.exact(self.root, '', ['.hgtags']) tagnode = self.commit(message, user, date, extra=extra, match=m) for name in names: @@ -268,8 +273,8 @@ alltags = {} # map tag name to (node, hist) tagtypes = {} - tags_.findglobaltags(self.ui, self, alltags, tagtypes) - tags_.readlocaltags(self.ui, self, alltags, tagtypes) + tagsmod.findglobaltags(self.ui, self, alltags, tagtypes) + tagsmod.readlocaltags(self.ui, self, alltags, tagtypes) # Build the return dicts. Have to re-encode tag names because # the tags module always uses UTF-8 (in order not to lose info @@ -314,6 +319,8 @@ self.nodetagscache = {} for t, n in self.tags().iteritems(): self.nodetagscache.setdefault(n, []).append(t) + for tags in self.nodetagscache.itervalues(): + tags.sort() return self.nodetagscache.get(node, []) def _branchtags(self, partial, lrev): @@ -410,9 +417,8 @@ for branch, newnodes in newbranches.iteritems(): bheads = partial.setdefault(branch, []) bheads.extend(newnodes) - if len(bheads) < 2: + if len(bheads) <= 1: continue - newbheads = [] # starting from tip means fewer passes over reachable while newnodes: latest = newnodes.pop() @@ -420,9 +426,8 @@ continue minbhrev = self[min([self[bh].rev() for bh in bheads])].node() reachable = self.changelog.reachable(latest, minbhrev) + reachable.remove(latest) bheads = [b for b in bheads if b not in reachable] - newbheads.insert(0, latest) - bheads.extend(newbheads) partial[branch] = bheads def lookup(self, key): @@ -456,6 +461,14 @@ pass raise error.RepoLookupError(_("unknown revision '%s'") % key) + def lookupbranch(self, key, remote=None): + repo = remote or self + if key in repo.branchmap(): + return key + + repo = (remote and remote.local()) and remote or self + return repo[key].branch() + def local(self): return True @@ -503,7 +516,7 @@ for pat, cmd in self.ui.configitems(filter): if cmd == '!': continue - mf = match_.match(self.root, '', [pat]) + mf = matchmod.match(self.root, '', [pat]) fn = None params = cmd for name, filterfn in self._datafilters.iteritems(): @@ -554,7 +567,7 @@ def wwritedata(self, filename, data): return self._filter("decode", filename, data) - def transaction(self): + def transaction(self, desc): tr = self._transref and self._transref() or None if tr and tr.running(): return tr.nest() @@ -571,10 +584,12 @@ ds = "" self.opener("journal.dirstate", "w").write(ds) self.opener("journal.branch", "w").write(self.dirstate.branch()) + self.opener("journal.desc", "w").write("%d\n%s\n" % (len(self), desc)) renames = [(self.sjoin("journal"), self.sjoin("undo")), (self.join("journal.dirstate"), self.join("undo.dirstate")), - (self.join("journal.branch"), self.join("undo.branch"))] + (self.join("journal.branch"), self.join("undo.branch")), + (self.join("journal.desc"), self.join("undo.desc"))] tr = transaction.transaction(self.ui.warn, self.sopener, self.sjoin("journal"), aftertrans(renames), @@ -597,13 +612,26 @@ finally: lock.release() - def rollback(self): + def rollback(self, dryrun=False): wlock = lock = None try: wlock = self.wlock() lock = self.lock() if os.path.exists(self.sjoin("undo")): - self.ui.status(_("rolling back last transaction\n")) + try: + args = self.opener("undo.desc", "r").read().splitlines() + if len(args) >= 3 and self.ui.verbose: + desc = _("rolling back to revision %s" + " (undo %s: %s)\n") % ( + int(args[0]) - 1, args[1], args[2]) + elif len(args) >= 2: + desc = _("rolling back to revision %s (undo %s)\n") % ( + int(args[0]) - 1, args[1]) + except IOError: + desc = _("rolling back unknown transaction\n") + self.ui.status(desc) + if dryrun: + return transaction.rollback(self.sopener, self.sjoin("undo"), self.ui.warn) util.rename(self.join("undo.dirstate"), self.join("dirstate")) @@ -619,6 +647,7 @@ self.destroyed() else: self.ui.warn(_("no rollback information available\n")) + return 1 finally: release(lock, wlock) @@ -767,7 +796,7 @@ raise util.Abort('%s: %s' % (f, msg)) if not match: - match = match_.always(self.root, '') + match = matchmod.always(self.root, '') if not force: vdirs = [] @@ -776,10 +805,10 @@ wlock = self.wlock() try: - p1, p2 = self.dirstate.parents() wctx = self[None] + merge = len(wctx.parents()) > 1 - if (not force and p2 != nullid and match and + if (not force and merge and match and (match.files() or match.anypats())): raise util.Abort(_('cannot partially commit a merge ' '(do not specify files or patterns)')) @@ -819,19 +848,18 @@ elif f not in self.dirstate: fail(f, _("file not tracked!")) - if (not force and not extra.get("close") and p2 == nullid + if (not force and not extra.get("close") and not merge and not (changes[0] or changes[1] or changes[2]) - and self[None].branch() == self['.'].branch()): + and wctx.branch() == wctx.p1().branch()): return None - ms = merge_.mergestate(self) + ms = mergemod.mergestate(self) for f in changes[0]: if f in ms and ms[f] == 'u': raise util.Abort(_("unresolved merge conflicts " "(see hg resolve)")) - cctx = context.workingctx(self, (p1, p2), text, user, date, - extra, changes) + cctx = context.workingctx(self, text, user, date, extra, changes) if editor: cctx._text = editor(self, cctx, subs) edited = (text != cctx._text) @@ -840,8 +868,10 @@ if subs or removedsubs: state = wctx.substate.copy() for s in subs: - self.ui.status(_('committing subrepository %s\n') % s) - sr = wctx.sub(s).commit(cctx._text, user, date) + sub = wctx.sub(s) + self.ui.status(_('committing subrepository %s\n') % + subrepo.relpath(sub)) + sr = sub.commit(cctx._text, user, date) state[s] = (state[s][0], sr) subrepo.writestate(self, state) @@ -852,8 +882,9 @@ msgfile.write(cctx._text) msgfile.close() + p1, p2 = self.dirstate.parents() + hookp1, hookp2 = hex(p1), (p2 != nullid and hex(p2) or '') try: - hookp1, hookp2 = hex(p1), (p2 != nullid and hex(p2) or '') self.hook("precommit", throw=True, parent1=hookp1, parent2=hookp2) ret = self.commitctx(cctx, True) except: @@ -890,7 +921,7 @@ lock = self.lock() try: - tr = self.transaction() + tr = self.transaction("commit") trp = weakref.proxy(tr) # check in files @@ -940,7 +971,8 @@ self.branchtags() return n finally: - del tr + if tr: + tr.release() lock.release() def destroyed(self): @@ -996,7 +1028,7 @@ working = ctx2.rev() is None parentworking = working and ctx1 == self['.'] - match = match or match_.always(self.root, self.getcwd()) + match = match or matchmod.always(self.root, self.getcwd()) listignored, listclean, listunknown = ignored, clean, unknown # load earliest manifest first for caching reasons @@ -1398,7 +1430,7 @@ self.ui.debug("found new changesets starting at " + " ".join([short(f) for f in fetch]) + "\n") - self.ui.progress(_('searching'), None, unit=_('queries')) + self.ui.progress(_('searching'), None) self.ui.debug("%d total queries\n" % reqcnt) return base.keys(), list(fetch), heads @@ -1478,7 +1510,14 @@ finally: lock.release() - def push(self, remote, force=False, revs=None): + def push(self, remote, force=False, revs=None, newbranch=False): + '''Push outgoing changesets (limited by revs) from the current + repository to remote. Return an integer: + - 0 means HTTP error *or* nothing to push + - 1 means we pushed and remote head count is unchanged *or* + we have outgoing changesets but refused to push + - other values as described by addchangegroup() + ''' # there are two ways to push to remote repo: # # addchangegroup assumes local user can lock remote @@ -1488,16 +1527,23 @@ # servers, http servers). if remote.capable('unbundle'): - return self.push_unbundle(remote, force, revs) - return self.push_addchangegroup(remote, force, revs) + return self.push_unbundle(remote, force, revs, newbranch) + return self.push_addchangegroup(remote, force, revs, newbranch) - def prepush(self, remote, force, revs): + def prepush(self, remote, force, revs, newbranch): '''Analyze the local and remote repositories and determine which - changesets need to be pushed to the remote. Return a tuple - (changegroup, remoteheads). changegroup is a readable file-like - object whose read() returns successive changegroup chunks ready to - be sent over the wire. remoteheads is the list of remote heads. - ''' + changesets need to be pushed to the remote. Return value depends + on circumstances: + + If we are not going to push anything, return a tuple (None, + outgoing) where outgoing is 0 if there are no outgoing + changesets and 1 if there are, but we refuse to push them + (e.g. would create new remote heads). + + Otherwise, return a tuple (changegroup, remoteheads), where + changegroup is a readable file-like object whose read() returns + successive changegroup chunks ready to be sent over the wire and + remoteheads is the list of remote heads.''' common = {} remote_heads = remote.heads() inc = self.findincoming(remote, common, remote_heads, force=force) @@ -1543,13 +1589,15 @@ # 2. Check for new branches on the remote. remotemap = remote.branchmap() newbranches = branches - set(remotemap) - if newbranches: # new branch requires --force + if newbranches and not newbranch: # new branch requires --new-branch branchnames = ', '.join("%s" % b for b in newbranches) self.ui.warn(_("abort: push creates " "new remote branches: %s!\n") % branchnames) - self.ui.status(_("(use 'hg push -f' to force)\n")) + self.ui.status(_("(use 'hg push --new-branch' to create new " + "remote branches)\n")) return None, 0 + branches.difference_update(newbranches) # 3. Construct the initial oldmap and newmap dicts. # They contain information about the remote heads before and @@ -1611,29 +1659,41 @@ cg = self.changegroupsubset(update, revs, 'push') return cg, remote_heads - def push_addchangegroup(self, remote, force, revs): + def push_addchangegroup(self, remote, force, revs, newbranch): + '''Push a changegroup by locking the remote and sending the + addchangegroup command to it. Used for local and old SSH repos. + Return an integer: see push(). + ''' lock = remote.lock() try: - ret = self.prepush(remote, force, revs) + ret = self.prepush(remote, force, revs, newbranch) if ret[0] is not None: cg, remote_heads = ret + # here, we return an integer indicating remote head count change return remote.addchangegroup(cg, 'push', self.url()) + # and here we return 0 for "nothing to push" or 1 for + # "something to push but I refuse" return ret[1] finally: lock.release() - def push_unbundle(self, remote, force, revs): + def push_unbundle(self, remote, force, revs, newbranch): + '''Push a changegroup by unbundling it on the remote. Used for new + SSH and HTTP repos. Return an integer: see push().''' # local repo finds heads on server, finds out what revs it # must push. once revs transferred, if server finds it has # different heads (someone else won commit/push race), server # aborts. - ret = self.prepush(remote, force, revs) + ret = self.prepush(remote, force, revs, newbranch) if ret[0] is not None: cg, remote_heads = ret if force: remote_heads = ['force'] + # ssh: return remote's addchangegroup() + # http: return remote's addchangegroup() or 0 for error return remote.unbundle(cg, remote_heads, 'push') + # as in push_addchangegroup() return ret[1] def changegroupinfo(self, nodes, source): @@ -1831,7 +1891,7 @@ yield chnk self.ui.progress(_('bundling changes'), cnt, unit=_('chunks')) cnt += 1 - self.ui.progress(_('bundling changes'), None, unit=_('chunks')) + self.ui.progress(_('bundling changes'), None) # Figure out which manifest nodes (of the ones we think might be @@ -1859,7 +1919,7 @@ yield chnk self.ui.progress(_('bundling manifests'), cnt, unit=_('chunks')) cnt += 1 - self.ui.progress(_('bundling manifests'), None, unit=_('chunks')) + self.ui.progress(_('bundling manifests'), None) # These are no longer needed, dereference and toss the memory for # them. @@ -1908,7 +1968,7 @@ del msng_filenode_set[fname] # Signal that no more groups are left. yield changegroup.closechunk() - self.ui.progress(_('bundling files'), None, unit=_('chunks')) + self.ui.progress(_('bundling files'), None) if msng_cl_lst: self.hook('outgoing', node=hex(msng_cl_lst[0]), source=source) @@ -1960,7 +2020,7 @@ self.ui.progress(_('bundling changes'), cnt, unit=_('chunks')) cnt += 1 yield chnk - self.ui.progress(_('bundling changes'), None, unit=_('chunks')) + self.ui.progress(_('bundling changes'), None) mnfst = self.manifest nodeiter = gennodelst(mnfst) @@ -1969,7 +2029,7 @@ self.ui.progress(_('bundling manifests'), cnt, unit=_('chunks')) cnt += 1 yield chnk - self.ui.progress(_('bundling manifests'), None, unit=_('chunks')) + self.ui.progress(_('bundling manifests'), None) cnt = 0 for fname in sorted(changedfiles): @@ -1987,7 +2047,7 @@ _('bundling files'), cnt, item=fname, unit=_('chunks')) cnt += 1 yield chnk - self.ui.progress(_('bundling files'), None, unit=_('chunks')) + self.ui.progress(_('bundling files'), None) yield changegroup.closechunk() @@ -1997,12 +2057,14 @@ return util.chunkbuffer(gengroup()) def addchangegroup(self, source, srctype, url, emptyok=False): - """add changegroup to repo. + """Add the changegroup returned by source.read() to this repo. + srctype is a string like 'push', 'pull', or 'unbundle'. url is + the URL of the repo where this changegroup is coming from. - return values: + Return an integer summarizing the change to this repo: - nothing changed or no source: 0 - more heads than before: 1+added heads (2..n) - - less heads than before: -1-removed heads (-2..-n) + - fewer heads than before: -1-removed heads (-2..-n) - number of heads stays the same: 1 """ def csmap(x): @@ -2018,6 +2080,7 @@ self.hook('prechangegroup', throw=True, source=srctype, url=url) changesets = files = revisions = 0 + efiles = set() # write changelog data to temp files so concurrent readers will not see # inconsistent view @@ -2025,7 +2088,7 @@ cl.delayupdate() oldheads = len(cl.heads()) - tr = self.transaction() + tr = self.transaction("\n".join([srctype, urlmod.hidepassword(url)])) try: trp = weakref.proxy(tr) # pull off the changeset group @@ -2035,8 +2098,10 @@ step = _('changesets') count = 1 ui = self.ui + total = None def __call__(self): - self.ui.progress(self.step, self.count, unit=_('chunks')) + self.ui.progress(self.step, self.count, unit=_('chunks'), + total=self.total) self.count += 1 pr = prog() chunkiter = changegroup.chunkiter(source, progress=pr) @@ -2044,12 +2109,16 @@ raise util.Abort(_("received changelog group is empty")) clend = len(cl) changesets = clend - clstart + for c in xrange(clstart, clend): + efiles.update(self[c].files()) + efiles = len(efiles) self.ui.progress(_('changesets'), None) # pull off the manifest group self.ui.status(_("adding manifests\n")) pr.step = _('manifests') pr.count = 1 + pr.total = changesets # manifests <= changesets chunkiter = changegroup.chunkiter(source, progress=pr) # no need to check for empty manifest group here: # if the result of the merge of 1 and 2 is the same in 3 and 4, @@ -2072,14 +2141,16 @@ self.ui.status(_("adding file changes\n")) pr.step = 'files' pr.count = 1 + pr.total = efiles while 1: f = changegroup.getchunk(source) if not f: break self.ui.debug("adding %s revisions\n" % f) + pr() fl = self.file(f) o = len(fl) - chunkiter = changegroup.chunkiter(source, progress=pr) + chunkiter = changegroup.chunkiter(source) if fl.addgroup(chunkiter, revmap, trp) is None: raise util.Abort(_("received file revlog group is empty")) revisions += len(fl) - o @@ -2124,7 +2195,7 @@ tr.close() finally: - del tr + tr.release() if changesets > 0: # forcefully update the on-disk branch cache diff -r 0bb67503ad4b -r ac6fec2af8c8 mercurial/mdiff.py --- a/mercurial/mdiff.py Mon May 31 21:37:01 2010 +0200 +++ b/mercurial/mdiff.py Mon May 31 21:43:03 2010 +0200 @@ -125,12 +125,12 @@ else: al = splitnewlines(a) bl = splitnewlines(b) - l = list(bunidiff(a, b, al, bl, "a/" + fn1, "b/" + fn2, opts=opts)) + l = list(_unidiff(a, b, al, bl, opts=opts)) if not l: return "" - # difflib uses a space, rather than a tab - l[0] = "%s%s" % (l[0][:-2], datetag(ad)) - l[1] = "%s%s" % (l[1][:-2], datetag(bd)) + + l.insert(0, "--- a/%s%s" % (fn1, datetag(ad))) + l.insert(1, "+++ b/%s%s" % (fn2, datetag(bd))) for ln in xrange(len(l)): if l[ln][-1] != '\n': @@ -141,11 +141,10 @@ return "".join(l) -# somewhat self contained replacement for difflib.unified_diff +# creates a headerless unified diff # t1 and t2 are the text to be diffed # l1 and l2 are the text broken up into lines -# header1 and header2 are the filenames for the diff output -def bunidiff(t1, t2, l1, l2, header1, header2, opts=defaultopts): +def _unidiff(t1, t2, l1, l2, opts=defaultopts): def contextend(l, len): ret = l + opts.context if ret > len: @@ -158,10 +157,7 @@ return 0 return ret - def yieldhunk(hunk, header): - if header: - for x in header: - yield x + def yieldhunk(hunk): (astart, a2, bstart, b2, delta) = hunk aend = contextend(a2, len(l1)) alen = aend - astart @@ -184,8 +180,6 @@ for x in xrange(a2, aend): yield ' ' + l1[x] - header = ["--- %s\t\n" % header1, "+++ %s\t\n" % header2] - if opts.showfunc: funcre = re.compile('\w') @@ -236,11 +230,8 @@ astart = hunk[1] bstart = hunk[3] else: - for x in yieldhunk(hunk, header): + for x in yieldhunk(hunk): yield x - # we only want to yield the header if the files differ, and - # we only want to yield it once. - header = None if prev: # we've joined the previous hunk, record the new ending points. hunk[1] = a2 @@ -255,7 +246,7 @@ delta[len(delta):] = ['+' + x for x in new] if hunk: - for x in yieldhunk(hunk, header): + for x in yieldhunk(hunk): yield x def patchtext(bin): diff -r 0bb67503ad4b -r ac6fec2af8c8 mercurial/merge.py --- a/mercurial/merge.py Mon May 31 21:37:01 2010 +0200 +++ b/mercurial/merge.py Mon May 31 21:43:03 2010 +0200 @@ -364,7 +364,7 @@ repo.dirstate.normallookup(f) elif m == "g": # get if branchmerge: - repo.dirstate.normaldirty(f) + repo.dirstate.otherparent(f) else: repo.dirstate.normal(f) elif m == "m": # merge @@ -386,7 +386,8 @@ # of that file some time in the past. Thus our # merge will appear as a normal local file # modification. - repo.dirstate.normallookup(fd) + if f2 == fd: # file not locally copied/moved + repo.dirstate.normallookup(fd) if move: repo.dirstate.forget(f) elif m == "d": # directory rename @@ -491,6 +492,7 @@ ### calculate phase action = [] + wc.status(unknown=True) # prime cache if not force: _checkunknown(wc, p2) if not util.checkcase(repo.path): @@ -507,8 +509,8 @@ stats = applyupdates(repo, action, wc, p2) if not partial: + repo.dirstate.setparents(fp1, fp2) recordupdates(repo, action, branchmerge) - repo.dirstate.setparents(fp1, fp2) if not branchmerge and not fastforward: repo.dirstate.setbranch(p2.branch()) finally: diff -r 0bb67503ad4b -r ac6fec2af8c8 mercurial/minirst.py --- a/mercurial/minirst.py Mon May 31 21:37:01 2010 +0200 +++ b/mercurial/minirst.py Mon May 31 21:43:03 2010 +0200 @@ -225,6 +225,8 @@ return blocks, pruned +_sectionre = re.compile(r"""^([-=`:.'"~^_*+#])\1+$""") + def findsections(blocks): """Finds sections. @@ -240,18 +242,33 @@ # +------------------------------+ if (block['type'] == 'paragraph' and len(block['lines']) == 2 and - block['lines'][1] == '-' * len(block['lines'][0])): + len(block['lines'][0]) == len(block['lines'][1]) and + _sectionre.match(block['lines'][1])): + block['underline'] = block['lines'][1][0] block['type'] = 'section' + del block['lines'][1] return blocks def inlineliterals(blocks): for b in blocks: - if b['type'] == 'paragraph': + if b['type'] in ('paragraph', 'section'): b['lines'] = [l.replace('``', '"') for l in b['lines']] return blocks +def hgrole(blocks): + for b in blocks: + if b['type'] in ('paragraph', 'section'): + # Turn :hg:`command` into "hg command". This also works + # when there is a line break in the command and relies on + # the fact that we have no stray back-quotes in the input + # (run the blocks through inlineliterals first). + b['lines'] = [l.replace(':hg:`', '"hg ').replace('`', '"') + for l in b['lines']] + return blocks + + def addmargins(blocks): """Adds empty blocks for vertical spacing. @@ -261,7 +278,7 @@ i = 1 while i < len(blocks): if (blocks[i]['type'] == blocks[i - 1]['type'] and - blocks[i]['type'] in ('bullet', 'option', 'field', 'definition')): + blocks[i]['type'] in ('bullet', 'option', 'field')): i += 1 else: blocks.insert(i, dict(lines=[''], indent=0, type='margin')) @@ -280,7 +297,8 @@ indent += ' ' return indent + ('\n' + indent).join(block['lines']) if block['type'] == 'section': - return indent + ('\n' + indent).join(block['lines']) + underline = len(block['lines'][0]) * block['underline'] + return "%s%s\n%s%s" % (indent, block['lines'][0],indent, underline) if block['type'] == 'definition': term = indent + block['lines'][0] hang = len(block['lines'][-1]) - len(block['lines'][-1].lstrip()) @@ -289,7 +307,7 @@ return "%s\n%s" % (term, textwrap.fill(text, width=width, initial_indent=defindent, subsequent_indent=defindent)) - initindent = subindent = indent + subindent = indent if block['type'] == 'bullet': if block['lines'][0].startswith('| '): # Remove bullet for line blocks and add no extra @@ -321,7 +339,7 @@ text = ' '.join(map(str.strip, block['lines'])) return textwrap.fill(text, width=width, - initial_indent=initindent, + initial_indent=indent, subsequent_indent=subindent) @@ -332,10 +350,11 @@ b['indent'] += indent blocks = findliteralblocks(blocks) blocks, pruned = prunecontainers(blocks, keep or []) + blocks = findsections(blocks) blocks = inlineliterals(blocks) + blocks = hgrole(blocks) blocks = splitparagraphs(blocks) blocks = updatefieldlists(blocks) - blocks = findsections(blocks) blocks = addmargins(blocks) text = '\n'.join(formatblock(b, width) for b in blocks) if keep is None: diff -r 0bb67503ad4b -r ac6fec2af8c8 mercurial/patch.py --- a/mercurial/patch.py Mon May 31 21:37:01 2010 +0200 +++ b/mercurial/patch.py Mon May 31 21:43:03 2010 +0200 @@ -6,11 +6,12 @@ # This software may be used and distributed according to the terms of the # GNU General Public License version 2 or any later version. +import cStringIO, email.Parser, os, re +import tempfile, zlib + from i18n import _ from node import hex, nullid, short import base85, cmdutil, mdiff, util, diffhelpers, copies -import cStringIO, email.Parser, os, re -import sys, tempfile, zlib gitre = re.compile('diff --git a/(.*) b/(.*)') @@ -302,6 +303,9 @@ isexec = mode & 0100 self.mode = (islink, isexec) + def __repr__(self): + return "" % (self.op, self.path) + def readgitpatch(lr): """extract git-style metadata about patches from """ @@ -343,8 +347,6 @@ gp.path = line[8:] elif line.startswith('deleted file'): gp.op = 'DELETE' - # is the deleted file a symlink? - gp.setmode(int(line[-6:], 8)) elif line.startswith('new file mode '): gp.op = 'ADD' gp.setmode(int(line[-6:], 8)) @@ -905,24 +907,25 @@ return s return s[:i] +def pathstrip(path, strip): + pathlen = len(path) + i = 0 + if strip == 0: + return '', path.rstrip() + count = strip + while count > 0: + i = path.find('/', i) + if i == -1: + raise PatchError(_("unable to strip away %d of %d dirs from %s") % + (count, strip, path)) + i += 1 + # consume '//' in the path + while i < pathlen - 1 and path[i] == '/': + i += 1 + count -= 1 + return path[:i].lstrip(), path[i:].rstrip() + def selectfile(afile_orig, bfile_orig, hunk, strip): - def pathstrip(path, count=1): - pathlen = len(path) - i = 0 - if count == 0: - return '', path.rstrip() - while count > 0: - i = path.find('/', i) - if i == -1: - raise PatchError(_("unable to strip away %d dirs from %s") % - (count, path)) - i += 1 - # consume '//' in the path - while i < pathlen - 1 and path[i] == '/': - i += 1 - count -= 1 - return path[:i].lstrip(), path[i:].rstrip() - nulla = afile_orig == "/dev/null" nullb = bfile_orig == "/dev/null" abase, afile = pathstrip(afile_orig, strip) @@ -1125,9 +1128,9 @@ if (empty is None and not gitworkdone) or empty: raise NoHunks + def applydiff(ui, fp, changed, strip=1, sourcefile=None, eolmode='strict'): - """ - Reads a patch from fp and tries to apply it. + """Reads a patch from fp and tries to apply it. The dict 'changed' is filled in with all of the filenames changed by the patch. Returns 0 for a clean patch, -1 if any rejects were @@ -1136,12 +1139,22 @@ If 'eolmode' is 'strict', the patch content and patched file are read in binary mode. Otherwise, line endings are ignored when patching then normalized according to 'eolmode'. + + Callers probably want to call 'updatedir' after this to apply + certain categories of changes not done by this function. """ + return _applydiff( + ui, fp, patchfile, copyfile, + changed, strip=strip, sourcefile=sourcefile, eolmode=eolmode) + + +def _applydiff(ui, fp, patcher, copyfn, changed, strip=1, + sourcefile=None, eolmode='strict'): rejects = 0 err = 0 current_file = None - gitpatches = None - opener = util.opener(os.getcwd()) + cwd = os.getcwd() + opener = util.opener(cwd) def closefile(): if not current_file: @@ -1153,8 +1166,7 @@ if state == 'hunk': if not current_file: continue - current_hunk = values - ret = current_file.apply(current_hunk) + ret = current_file.apply(values) if ret >= 0: changed.setdefault(current_file.fname, None) if ret > 0: @@ -1164,24 +1176,25 @@ afile, bfile, first_hunk = values try: if sourcefile: - current_file = patchfile(ui, sourcefile, opener, - eolmode=eolmode) + current_file = patcher(ui, sourcefile, opener, + eolmode=eolmode) else: current_file, missing = selectfile(afile, bfile, first_hunk, strip) - current_file = patchfile(ui, current_file, opener, - missing, eolmode) + current_file = patcher(ui, current_file, opener, + missing=missing, eolmode=eolmode) except PatchError, err: ui.warn(str(err) + '\n') - current_file, current_hunk = None, None + current_file = None rejects += 1 continue elif state == 'git': - gitpatches = values - cwd = os.getcwd() - for gp in gitpatches: + for gp in values: + gp.path = pathstrip(gp.path, strip - 1)[1] + if gp.oldpath: + gp.oldpath = pathstrip(gp.oldpath, strip - 1)[1] if gp.op in ('COPY', 'RENAME'): - copyfile(gp.oldpath, gp.path, cwd) + copyfn(gp.oldpath, gp.path, cwd) changed[gp.path] = gp else: raise util.Abort(_('unsupported parser state: %s') % state) @@ -1192,20 +1205,6 @@ return -1 return err -def diffopts(ui, opts=None, untrusted=False): - def get(key, name=None, getter=ui.configbool): - return ((opts and opts.get(key)) or - getter('diff', name or key, None, untrusted=untrusted)) - return mdiff.diffopts( - text=opts and opts.get('text'), - git=get('git'), - nodates=get('nodates'), - showfunc=get('show_function', 'showfunc'), - ignorews=get('ignore_all_space', 'ignorews'), - ignorewsamount=get('ignore_space_change', 'ignorewsamount'), - ignoreblanklines=get('ignore_blank_lines', 'ignoreblanklines'), - context=get('unified', getter=ui.config)) - def updatedir(ui, repo, patches, similarity=0): '''Update dirstate after patch application according to metadata''' if not patches: @@ -1240,8 +1239,7 @@ if gp.op == 'ADD' and not os.path.exists(dst): flags = (isexec and 'x' or '') + (islink and 'l' or '') repo.wwrite(gp.path, '', flags) - elif gp.op != 'DELETE': - util.set_flags(dst, islink, isexec) + util.set_flags(dst, islink, isexec) cmdutil.addremove(repo, cfiles, similarity=similarity) files = patches.keys() files.extend([r for r in removes if r not in files]) @@ -1337,6 +1335,10 @@ try: return internalpatch(patchname, ui, strip, cwd, files, eolmode) except NoHunks: + ui.warn(_('internal patcher failed\n' + 'please report details to ' + 'http://mercurial.selenic.com/bts/\n' + 'or mercurial@selenic.com\n')) patcher = (util.find_exe('gpatch') or util.find_exe('patch') or 'patch') ui.debug('no valid hunks found; trying with %r instead\n' % @@ -1393,6 +1395,20 @@ class GitDiffRequired(Exception): pass +def diffopts(ui, opts=None, untrusted=False): + def get(key, name=None, getter=ui.configbool): + return ((opts and opts.get(key)) or + getter('diff', name or key, None, untrusted=untrusted)) + return mdiff.diffopts( + text=opts and opts.get('text'), + git=get('git'), + nodates=get('nodates'), + showfunc=get('show_function', 'showfunc'), + ignorews=get('ignore_all_space', 'ignorews'), + ignorewsamount=get('ignore_space_change', 'ignorewsamount'), + ignoreblanklines=get('ignore_blank_lines', 'ignoreblanklines'), + context=get('unified', getter=ui.config)) + def diff(repo, node1=None, node2=None, match=None, changes=None, opts=None, losedatafn=None): '''yields diff of changes to files between two nodes, or node and @@ -1465,6 +1481,43 @@ else: return difffn(opts, None) +def difflabel(func, *args, **kw): + '''yields 2-tuples of (output, label) based on the output of func()''' + prefixes = [('diff', 'diff.diffline'), + ('copy', 'diff.extended'), + ('rename', 'diff.extended'), + ('old', 'diff.extended'), + ('new', 'diff.extended'), + ('deleted', 'diff.extended'), + ('---', 'diff.file_a'), + ('+++', 'diff.file_b'), + ('@@', 'diff.hunk'), + ('-', 'diff.deleted'), + ('+', 'diff.inserted')] + + for chunk in func(*args, **kw): + lines = chunk.split('\n') + for i, line in enumerate(lines): + if i != 0: + yield ('\n', '') + stripline = line + if line and line[0] in '+-': + # highlight trailing whitespace, but only in changed lines + stripline = line.rstrip() + for prefix, label in prefixes: + if stripline.startswith(prefix): + yield (stripline, label) + break + else: + yield (line, '') + if line != stripline: + yield (line[len(stripline):], 'diff.trailingwhitespace') + +def diffui(*args, **kw): + '''like diff(), but yields 2-tuples of (output, label) for ui.write()''' + return difflabel(diff, *args, **kw) + + def _addmodehdr(header, omode, nmode): if omode != nmode: header.append('old mode %s\n' % omode) @@ -1568,47 +1621,6 @@ if text: yield text -def export(repo, revs, template='hg-%h.patch', fp=None, switch_parent=False, - opts=None): - '''export changesets as hg patches.''' - - total = len(revs) - revwidth = max([len(str(rev)) for rev in revs]) - - def single(rev, seqno, fp): - ctx = repo[rev] - node = ctx.node() - parents = [p.node() for p in ctx.parents() if p] - branch = ctx.branch() - if switch_parent: - parents.reverse() - prev = (parents and parents[0]) or nullid - - if not fp: - fp = cmdutil.make_file(repo, template, node, total=total, - seqno=seqno, revwidth=revwidth, - mode='ab') - if fp != sys.stdout and hasattr(fp, 'name'): - repo.ui.note("%s\n" % fp.name) - - fp.write("# HG changeset patch\n") - fp.write("# User %s\n" % ctx.user()) - fp.write("# Date %d %d\n" % ctx.date()) - if branch and (branch != 'default'): - fp.write("# Branch %s\n" % branch) - fp.write("# Node ID %s\n" % hex(node)) - fp.write("# Parent %s\n" % hex(prev)) - if len(parents) > 1: - fp.write("# Parent %s\n" % hex(parents[1])) - fp.write(ctx.description().rstrip()) - fp.write("\n\n") - - for chunk in diff(repo, prev, node, opts=opts): - fp.write(chunk) - - for seqno, rev in enumerate(revs): - single(rev, seqno + 1, fp) - def diffstatdata(lines): filename, adds, removes = None, 0, 0 for line in lines: @@ -1676,3 +1688,22 @@ % (len(stats), totaladds, totalremoves)) return ''.join(output) + +def diffstatui(*args, **kw): + '''like diffstat(), but yields 2-tuples of (output, label) for + ui.write() + ''' + + for line in diffstat(*args, **kw).splitlines(): + if line and line[-1] in '+-': + name, graph = line.rsplit(' ', 1) + yield (name + ' ', '') + m = re.search(r'\++', graph) + if m: + yield (m.group(0), 'diffstat.inserted') + m = re.search(r'-+', graph) + if m: + yield (m.group(0), 'diffstat.deleted') + else: + yield (line, '') + yield ('\n', '') diff -r 0bb67503ad4b -r ac6fec2af8c8 mercurial/posix.py --- a/mercurial/posix.py Mon May 31 21:37:01 2010 +0200 +++ b/mercurial/posix.py Mon May 31 21:43:03 2010 +0200 @@ -7,7 +7,7 @@ from i18n import _ import osutil -import os, sys, errno, stat, getpass, pwd, grp, fcntl +import os, sys, errno, stat, getpass, pwd, grp posixfile = open nulldev = '/dev/null' @@ -118,6 +118,7 @@ return st1.st_dev == st2.st_dev if sys.platform == 'darwin': + import fcntl # only needed on darwin, missing on jython def realpath(path): ''' Returns the true, canonical file system path equivalent to the given @@ -258,6 +259,12 @@ except KeyError: return str(gid) +def groupmembers(name): + """Return the list of members of the group with the given + name, KeyError if the group does not exist. + """ + return list(grp.getgrnam(name).gr_mem) + def spawndetached(args): return os.spawnvp(os.P_NOWAIT | getattr(os, 'P_DETACH', 0), args[0], args) diff -r 0bb67503ad4b -r ac6fec2af8c8 mercurial/pure/mpatch.py --- a/mercurial/pure/mpatch.py Mon May 31 21:37:01 2010 +0200 +++ b/mercurial/pure/mpatch.py Mon May 31 21:43:03 2010 +0200 @@ -112,7 +112,7 @@ outlen += length if bin != binend: - raise Exception("patch cannot be decoded") + raise ValueError("patch cannot be decoded") outlen += orig - last return outlen diff -r 0bb67503ad4b -r ac6fec2af8c8 mercurial/pure/osutil.py --- a/mercurial/pure/osutil.py Mon May 31 21:37:01 2010 +0200 +++ b/mercurial/pure/osutil.py Mon May 31 21:43:03 2010 +0200 @@ -6,25 +6,25 @@ # GNU General Public License version 2 or any later version. import os -import stat as _stat +import stat as statmod posixfile = open def _mode_to_kind(mode): - if _stat.S_ISREG(mode): - return _stat.S_IFREG - if _stat.S_ISDIR(mode): - return _stat.S_IFDIR - if _stat.S_ISLNK(mode): - return _stat.S_IFLNK - if _stat.S_ISBLK(mode): - return _stat.S_IFBLK - if _stat.S_ISCHR(mode): - return _stat.S_IFCHR - if _stat.S_ISFIFO(mode): - return _stat.S_IFIFO - if _stat.S_ISSOCK(mode): - return _stat.S_IFSOCK + if statmod.S_ISREG(mode): + return statmod.S_IFREG + if statmod.S_ISDIR(mode): + return statmod.S_IFDIR + if statmod.S_ISLNK(mode): + return statmod.S_IFLNK + if statmod.S_ISBLK(mode): + return statmod.S_IFBLK + if statmod.S_ISCHR(mode): + return statmod.S_IFCHR + if statmod.S_ISFIFO(mode): + return statmod.S_IFIFO + if statmod.S_ISSOCK(mode): + return statmod.S_IFSOCK return mode def listdir(path, stat=False, skip=None): @@ -49,7 +49,7 @@ names.sort() for fn in names: st = os.lstat(prefix + fn) - if fn == skip and _stat.S_ISDIR(st.st_mode): + if fn == skip and statmod.S_ISDIR(st.st_mode): return [] if stat: result.append((fn, _mode_to_kind(st.st_mode), st)) diff -r 0bb67503ad4b -r ac6fec2af8c8 mercurial/repair.py --- a/mercurial/repair.py Mon May 31 21:37:01 2010 +0200 +++ b/mercurial/repair.py Mon May 31 21:43:03 2010 +0200 @@ -18,7 +18,6 @@ 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): @@ -106,40 +105,55 @@ extranodes = _collectextranodes(repo, files, striprev) # create a changegroup for all the branches we need to keep + backupfile = None if backup == "all": - _bundle(repo, [node], cl.heads(), node, 'backup') + backupfile = _bundle(repo, [node], cl.heads(), node, 'backup') + repo.ui.status(_("saved backup bundle to %s\n") % backupfile) if saveheads or extranodes: chgrpfile = _bundle(repo, savebases, saveheads, node, 'temp', extranodes) mfst = repo.manifest - tr = repo.transaction() + tr = repo.transaction("strip") offset = len(tr.entries) - tr.startgroup() - cl.strip(striprev, tr) - mfst.strip(striprev, tr) - for fn in files: - repo.file(fn).strip(striprev, tr) - tr.endgroup() - try: - for i in xrange(offset, len(tr.entries)): - file, troffset, ignore = tr.entries[i] - repo.sopener(file, 'a').truncate(troffset) - tr.close() + tr.startgroup() + cl.strip(striprev, tr) + mfst.strip(striprev, tr) + for fn in files: + repo.file(fn).strip(striprev, tr) + tr.endgroup() + + try: + for i in xrange(offset, len(tr.entries)): + file, troffset, ignore = tr.entries[i] + repo.sopener(file, 'a').truncate(troffset) + tr.close() + except: + tr.abort() + raise + + if saveheads or extranodes: + ui.note(_("adding branch\n")) + f = open(chgrpfile, "rb") + gen = changegroup.readbundle(f, chgrpfile) + if not repo.ui.verbose: + # silence internal shuffling chatter + repo.ui.pushbuffer() + repo.addchangegroup(gen, 'strip', 'bundle:' + chgrpfile, True) + if not repo.ui.verbose: + repo.ui.popbuffer() + f.close() + if backup != "strip": + os.unlink(chgrpfile) except: - tr.abort() + if backupfile: + ui.warn("strip failed, full bundle stored in '%s'\n" % backupfile) + elif saveheads: + ui.warn("strip failed, partial bundle stored in '%s'\n" + % chgrpfile) raise - if saveheads or extranodes: - ui.status(_("adding branch\n")) - f = open(chgrpfile, "rb") - gen = changegroup.readbundle(f, chgrpfile) - repo.addchangegroup(gen, 'strip', 'bundle:' + chgrpfile, True) - f.close() - if backup != "strip": - os.unlink(chgrpfile) - repo.destroyed() diff -r 0bb67503ad4b -r ac6fec2af8c8 mercurial/revlog.py --- a/mercurial/revlog.py Mon May 31 21:37:01 2010 +0200 +++ b/mercurial/revlog.py Mon May 31 21:43:03 2010 +0200 @@ -444,7 +444,10 @@ i = '' try: f = self.opener(self.indexfile) - i = f.read(_prereadsize) + if "nonlazy" in getattr(self.opener, 'options', {}): + i = f.read() + else: + i = f.read(_prereadsize) if len(i) > 0: v = struct.unpack(versionformat, i[:4])[0] except IOError, inst: @@ -848,6 +851,32 @@ c.append(self.node(r)) return c + def descendant(self, start, end): + for i in self.descendants(start): + if i == end: + return True + elif i > end: + break + return False + + def ancestor(self, a, b): + """calculate the least common ancestor of nodes a and b""" + + # fast path, check if it is a descendant + a, b = self.rev(a), self.rev(b) + start, end = sorted((a, b)) + if self.descendant(start, end): + return self.node(start) + + def parents(rev): + return [p for p in self.parentrevs(rev) if p != nullrev] + + c = ancestor.ancestor(a, b, parents) + if c is None: + return nullid + + return self.node(c) + def _match(self, id): if isinstance(id, (long, int)): # rev @@ -1122,32 +1151,6 @@ self._cache = (node, curr, text) return node - def descendant(self, start, end): - for i in self.descendants(start): - if i == end: - return True - elif i > end: - break - return False - - def ancestor(self, a, b): - """calculate the least common ancestor of nodes a and b""" - - # fast path, check if it is a descendant - a, b = self.rev(a), self.rev(b) - start, end = sorted((a, b)) - if self.descendant(start, end): - return self.node(start) - - def parents(rev): - return [p for p in self.parentrevs(rev) if p != nullrev] - - c = ancestor.ancestor(a, b, parents) - if c is None: - return nullid - - return self.node(c) - def group(self, nodelist, lookup, infocollect=None): """Calculate a delta group, yielding a sequence of changegroup chunks (strings). diff -r 0bb67503ad4b -r ac6fec2af8c8 mercurial/similar.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/mercurial/similar.py Mon May 31 21:43:03 2010 +0200 @@ -0,0 +1,103 @@ +# similar.py - mechanisms for finding similar files +# +# Copyright 2005-2007 Matt Mackall +# +# This software may be used and distributed according to the terms of the +# GNU General Public License version 2 or any later version. + +from i18n import _ +import util +import mdiff +import bdiff + +def _findexactmatches(repo, added, removed): + '''find renamed files that have no changes + + Takes a list of new filectxs and a list of removed filectxs, and yields + (before, after) tuples of exact matches. + ''' + numfiles = len(added) + len(removed) + + # Get hashes of removed files. + hashes = {} + for i, fctx in enumerate(removed): + repo.ui.progress(_('searching for exact renames'), i, total=numfiles) + h = util.sha1(fctx.data()).digest() + hashes[h] = fctx + + # For each added file, see if it corresponds to a removed file. + for i, fctx in enumerate(added): + repo.ui.progress(_('searching for exact renames'), i + len(removed), + total=numfiles) + h = util.sha1(fctx.data()).digest() + if h in hashes: + yield (hashes[h], fctx) + + # Done + repo.ui.progress(_('searching for exact renames'), None) + +def _findsimilarmatches(repo, added, removed, threshold): + '''find potentially renamed files based on similar file content + + Takes a list of new filectxs and a list of removed filectxs, and yields + (before, after, score) tuples of partial matches. + ''' + copies = {} + for i, r in enumerate(removed): + repo.ui.progress(_('searching for similar files'), i, total=len(removed)) + + # lazily load text + @util.cachefunc + def data(): + orig = r.data() + return orig, mdiff.splitnewlines(orig) + + def score(text): + orig, lines = data() + # bdiff.blocks() returns blocks of matching lines + # count the number of bytes in each + equal = 0 + matches = bdiff.blocks(text, orig) + for x1, x2, y1, y2 in matches: + for line in lines[y1:y2]: + equal += len(line) + + lengths = len(text) + len(orig) + return equal * 2.0 / lengths + + for a in added: + bestscore = copies.get(a, (None, threshold))[1] + myscore = score(a.data()) + if myscore >= bestscore: + copies[a] = (r, myscore) + repo.ui.progress(_('searching'), None) + + for dest, v in copies.iteritems(): + source, score = v + yield source, dest, score + +def findrenames(repo, added, removed, threshold): + '''find renamed files -- yields (before, after, score) tuples''' + parentctx = repo['.'] + workingctx = repo[None] + + # Zero length files will be frequently unrelated to each other, and + # tracking the deletion/addition of such a file will probably cause more + # harm than good. We strip them out here to avoid matching them later on. + addedfiles = set([workingctx[fp] for fp in added + if workingctx[fp].size() > 0]) + removedfiles = set([parentctx[fp] for fp in removed + if fp in parentctx and parentctx[fp].size() > 0]) + + # Find exact matches. + for (a, b) in _findexactmatches(repo, + sorted(addedfiles), sorted(removedfiles)): + addedfiles.remove(b) + yield (a.path(), b.path(), 1.0) + + # If the user requested similar files to be matched, search for them also. + if threshold < 1.0: + for (a, b, score) in _findsimilarmatches(repo, + sorted(addedfiles), sorted(removedfiles), threshold): + yield (a.path(), b.path(), score) + diff -r 0bb67503ad4b -r ac6fec2af8c8 mercurial/sshrepo.py --- a/mercurial/sshrepo.py Mon May 31 21:37:01 2010 +0200 +++ b/mercurial/sshrepo.py Mon May 31 21:43:03 2010 +0200 @@ -217,6 +217,10 @@ return self.do_cmd("changegroupsubset", bases=bases, heads=heads) def unbundle(self, cg, heads, source): + '''Send cg (a readable file-like object representing the + changegroup to push, typically a chunkbuffer object) to the + remote server as a bundle. Return an integer indicating the + result of the push (see localrepository.addchangegroup()).''' d = self.call("unbundle", heads=' '.join(map(hex, heads))) if d: # remote may send "unsynced changes" @@ -242,6 +246,9 @@ self.abort(error.ResponseError(_("unexpected response:"), r)) def addchangegroup(self, cg, source, url): + '''Send a changegroup to the remote server. Return an integer + similar to unbundle(). DEPRECATED, since it requires locking the + remote.''' d = self.call("addchangegroup") if d: self.abort(error.RepoError(_("push refused: %s") % d)) diff -r 0bb67503ad4b -r ac6fec2af8c8 mercurial/statichttprepo.py --- a/mercurial/statichttprepo.py Mon May 31 21:37:01 2010 +0200 +++ b/mercurial/statichttprepo.py Mon May 31 21:43:03 2010 +0200 @@ -77,6 +77,7 @@ return httprangereader(f, urlopener) return o + opener.options = {'nonlazy': 1} return opener class statichttprepository(localrepo.localrepository): diff -r 0bb67503ad4b -r ac6fec2af8c8 mercurial/streamclone.py --- a/mercurial/streamclone.py Mon May 31 21:37:01 2010 +0200 +++ b/mercurial/streamclone.py Mon May 31 21:43:03 2010 +0200 @@ -6,7 +6,6 @@ # GNU General Public License version 2 or any later version. import util, error -from i18n import _ from mercurial import store diff -r 0bb67503ad4b -r ac6fec2af8c8 mercurial/subrepo.py --- a/mercurial/subrepo.py Mon May 31 21:37:01 2010 +0200 +++ b/mercurial/subrepo.py Mon May 31 21:43:03 2010 +0200 @@ -5,7 +5,7 @@ # This software may be used and distributed according to the terms of the # GNU General Public License version 2 or any later version. -import errno, os, re, xml.dom.minidom, shutil +import errno, os, re, xml.dom.minidom, shutil, urlparse, posixpath from i18n import _ import config, util, node, error hg = None @@ -126,6 +126,14 @@ # record merged .hgsubstate writestate(repo, sm) +def relpath(sub): + if not hasattr(sub, '_repo'): + return sub._path + parent = sub._repo + while hasattr(parent, '_subparent'): + parent = parent._subparent + return sub._repo.root[len(parent.root)+1:] + def _abssource(repo, push=False): if hasattr(repo, '_subparent'): source = repo._subsource @@ -135,8 +143,12 @@ if '://' in parent: if parent[-1] == '/': parent = parent[:-1] - return parent + '/' + source - return os.path.join(parent, repo._subsource) + r = urlparse.urlparse(parent + '/' + source) + r = urlparse.urlunparse((r[0], r[1], + posixpath.normpath(r[2]), + r[3], r[4], r[5])) + return r + return posixpath.normpath(os.path.join(parent, repo._subsource)) if push and repo.ui.config('paths', 'default-push'): return repo.ui.config('paths', 'default-push', repo.root) return repo.ui.config('paths', 'default', repo.root) @@ -210,7 +222,7 @@ return w.dirty() # working directory changed def commit(self, text, user, date): - self._repo.ui.debug("committing subrepo %s\n" % self._path) + self._repo.ui.debug("committing subrepo %s\n" % relpath(self)) n = self._repo.commit(text, user, date) if not n: return self._repo['.'].hex() # different version checked out @@ -219,7 +231,7 @@ def remove(self): # we can't fully delete the repository as it may contain # local-only history - self._repo.ui.note(_('removing subrepo %s\n') % self._path) + self._repo.ui.note(_('removing subrepo %s\n') % relpath(self)) hg.clean(self._repo, node.nullid, False) def _get(self, state): @@ -228,8 +240,9 @@ self._repo.lookup(revision) except error.RepoError: self._repo._subsource = source - self._repo.ui.status(_('pulling subrepo %s\n') % self._path) srcurl = _abssource(self._repo) + self._repo.ui.status(_('pulling subrepo %s from %s\n') + % (relpath(self), srcurl)) other = hg.repository(self._repo.ui, srcurl) self._repo.pull(other) @@ -245,12 +258,12 @@ dst = self._repo[state[1]] anc = dst.ancestor(cur) if anc == cur: - self._repo.ui.debug("updating subrepo %s\n" % self._path) + self._repo.ui.debug("updating subrepo %s\n" % relpath(self)) hg.update(self._repo, state[1]) elif anc == dst: - self._repo.ui.debug("skipping subrepo %s\n" % self._path) + self._repo.ui.debug("skipping subrepo %s\n" % relpath(self)) else: - self._repo.ui.debug("merging subrepo %s\n" % self._path) + self._repo.ui.debug("merging subrepo %s\n" % relpath(self)) hg.merge(self._repo, state[1], remind=False) def push(self, force): @@ -261,8 +274,9 @@ if not c.sub(s).push(force): return False - self._repo.ui.status(_('pushing subrepo %s\n') % self._path) dsturl = _abssource(self._repo, True) + self._repo.ui.status(_('pushing subrepo %s to %s\n') % + (relpath(self), dsturl)) other = hg.repository(self._repo.ui, dsturl) return self._repo.push(other, force) diff -r 0bb67503ad4b -r ac6fec2af8c8 mercurial/tags.py --- a/mercurial/tags.py Mon May 31 21:37:01 2010 +0200 +++ b/mercurial/tags.py Mon May 31 21:43:03 2010 +0200 @@ -339,4 +339,3 @@ cachefile.write("%s %s\n" % (hex(node), name)) cachefile.rename() - cachefile.close() diff -r 0bb67503ad4b -r ac6fec2af8c8 mercurial/templatefilters.py --- a/mercurial/templatefilters.py Mon May 31 21:37:01 2010 +0200 +++ b/mercurial/templatefilters.py Mon May 31 21:43:03 2010 +0200 @@ -14,15 +14,13 @@ return "".join([stringify(t) for t in thing if t is not None]) return str(thing) -agescales = [("second", 1), - ("minute", 60), - ("hour", 3600), - ("day", 3600 * 24), +agescales = [("year", 3600 * 24 * 365), + ("month", 3600 * 24 * 30), ("week", 3600 * 24 * 7), - ("month", 3600 * 24 * 30), - ("year", 3600 * 24 * 365)] - -agescales.reverse() + ("day", 3600 * 24), + ("hour", 3600), + ("minute", 60), + ("second", 1)] def age(date): '''turn a (timestamp, tzoff) tuple into an age string.''' diff -r 0bb67503ad4b -r ac6fec2af8c8 mercurial/templater.py --- a/mercurial/templater.py Mon May 31 21:37:01 2010 +0200 +++ b/mercurial/templater.py Mon May 31 21:43:03 2010 +0200 @@ -6,12 +6,30 @@ # GNU General Public License version 2 or any later version. from i18n import _ -import re, sys, os +import sys, os import util, config, templatefilters path = ['templates', '../templates'] stringify = templatefilters.stringify +def _flatten(thing): + '''yield a single stream from a possibly nested set of iterators''' + if isinstance(thing, str): + yield thing + elif not hasattr(thing, '__iter__'): + if i is not None: + yield str(thing) + else: + for i in thing: + if isinstance(i, str): + yield i + elif not hasattr(i, '__iter__'): + if i is not None: + yield str(i) + elif i is not None: + for j in _flatten(i): + yield j + def parsestring(s, quoted=True): '''parse a string using simple c-like syntax. string must be in quotes if quoted is True.''' @@ -42,97 +60,107 @@ filter uses function to transform value. syntax is {key|filter1|filter2|...}.''' - template_re = re.compile(r'{([\w\|%]+)}') + def __init__(self, loader, filters={}, defaults={}): + self._loader = loader + self._filters = filters + self._defaults = defaults + self._cache = {} - def __init__(self, loader, filters={}, defaults={}): - self.loader = loader - self.filters = filters - self.defaults = defaults - self.cache = {} + def process(self, t, mapping): + '''Perform expansion. t is name of map element to expand. + mapping contains added elements for use during expansion. Is a + generator.''' + return _flatten(self._process(self._load(t), mapping)) + + def _load(self, t): + '''load, parse, and cache a template''' + if t not in self._cache: + self._cache[t] = self._parse(self._loader(t)) + return self._cache[t] - def process(self, t, map): - '''Perform expansion. t is name of map element to expand. map contains - added elements for use during expansion. Is a generator.''' - tmpl = self.loader(t) - iters = [self._process(tmpl, map)] - while iters: - try: - item = iters[0].next() - except StopIteration: - iters.pop(0) - continue - if isinstance(item, str): - yield item - elif item is None: - yield '' - elif hasattr(item, '__iter__'): - iters.insert(0, iter(item)) - else: - yield str(item) + def _get(self, mapping, key): + v = mapping.get(key) + if v is None: + v = self._defaults.get(key, '') + if hasattr(v, '__call__'): + v = v(**mapping) + return v - def _format(self, expr, get, map): - key, format = expr.split('%') - v = get(key) + def _filter(self, mapping, parts): + filters, val = parts + x = self._get(mapping, val) + for f in filters: + x = f(x) + return x + + def _format(self, mapping, args): + key, parsed = args + v = self._get(mapping, key) if not hasattr(v, '__iter__'): - raise SyntaxError(_("error expanding '%s%%%s'") % (key, format)) - lm = map.copy() + raise SyntaxError(_("error expanding '%s%%%s'") + % (key, format)) + lm = mapping.copy() for i in v: if isinstance(i, dict): lm.update(i) - yield self.process(format, lm) + yield self._process(parsed, lm) else: # v is not an iterable of dicts, this happen when 'key' # has been fully expanded already and format is useless. # If so, return the expanded value. yield i - def _filter(self, expr, get, map): - if expr not in self.cache: - parts = expr.split('|') - val = parts[0] - try: - filters = [self.filters[f] for f in parts[1:]] - except KeyError, i: - raise SyntaxError(_("unknown filter '%s'") % i[0]) - def apply(get): - x = get(val) - for f in filters: - x = f(x) - return x - self.cache[expr] = apply - return self.cache[expr](get) + def _parse(self, tmpl): + '''preparse a template''' + parsed = [] + pos, stop = 0, len(tmpl) + while pos < stop: + n = tmpl.find('{', pos) + if n < 0: + parsed.append((None, tmpl[pos:stop])) + break + if n > 0 and tmpl[n - 1] == '\\': + # escaped + parsed.append((None, tmpl[pos:n - 1] + "{")) + pos = n + 1 + continue + if n > pos: + parsed.append((None, tmpl[pos:n])) - def _process(self, tmpl, map): - '''Render a template. Returns a generator.''' - - def get(key): - v = map.get(key) - if v is None: - v = self.defaults.get(key, '') - if hasattr(v, '__call__'): - v = v(**map) - return v - - while tmpl: - m = self.template_re.search(tmpl) - if not m: - yield tmpl + pos = n + n = tmpl.find('}', pos) + if n < 0: + # no closing + parsed.append((None, tmpl[pos:stop])) break - start, end = m.span(0) - variants = m.groups() - expr = variants[0] or variants[1] - - if start: - yield tmpl[:start] - tmpl = tmpl[end:] + expr = tmpl[pos + 1:n] + pos = n + 1 if '%' in expr: - yield self._format(expr, get, map) + key, t = expr.split('%') + parsed.append((self._format, (key.strip(), + self._load(t.strip())))) elif '|' in expr: - yield self._filter(expr, get, map) + parts = expr.split('|') + val = parts[0].strip() + try: + filters = [self._filters[f.strip()] for f in parts[1:]] + except KeyError, i: + raise SyntaxError(_("unknown filter '%s'") % i[0]) + parsed.append((self._filter, (filters, val))) else: - yield get(expr) + parsed.append((self._get, expr.strip())) + + return parsed + + def _process(self, parsed, mapping): + '''Render a template. Returns a generator.''' + for f, e in parsed: + if f: + yield f(mapping, e) + else: + yield e engines = {'default': engine} @@ -188,14 +216,14 @@ (self.map[t][1], inst.args[1])) return self.cache[t] - def __call__(self, t, **map): + def __call__(self, t, **mapping): ttype = t in self.map and self.map[t][0] or 'default' proc = self.engines.get(ttype) if proc is None: proc = engines[ttype](self.load, self.filters, self.defaults) self.engines[ttype] = proc - stream = proc.process(t, map) + stream = proc.process(t, mapping) if self.minchunk: stream = util.increasingchunks(stream, min=self.minchunk, max=self.maxchunk) diff -r 0bb67503ad4b -r ac6fec2af8c8 mercurial/templates/gitweb/graph.tmpl --- a/mercurial/templates/gitweb/graph.tmpl Mon May 31 21:37:01 2010 +0200 +++ b/mercurial/templates/gitweb/graph.tmpl Mon May 31 21:43:03 2010 +0200 @@ -50,7 +50,7 @@ var graph = new Graph(); graph.scale({bg_height}); -graph.edge = function(x0, y0, x1, y1, color) { +graph.edge = function(x0, y0, x1, y1, color) \{ this.setColor(color, 0.0, 0.65); this.ctx.beginPath(); @@ -65,7 +65,7 @@ revlink += ' _TAGS'; revlink += '_DATE, by _USER'; -graph.vertex = function(x, y, color, parity, cur) { +graph.vertex = function(x, y, color, parity, cur) \{ this.ctx.beginPath(); color = this.setColor(color, 0.25, 0.75); @@ -84,17 +84,17 @@ item = item.replace(/_DATE/, cur[5]); var tagspan = ''; - if (cur[7].length || (cur[6][0] != 'default' || cur[6][1])) { + if (cur[7].length || (cur[6][0] != 'default' || cur[6][1])) \{ tagspan = ''; - if (cur[6][1]) { + if (cur[6][1]) \{ tagspan += ''; tagspan += cur[6][0] + ' '; - } else if (!cur[6][1] && cur[6][0] != 'default') { + } else if (!cur[6][1] && cur[6][0] != 'default') \{ tagspan += ''; tagspan += cur[6][0] + ' '; } - if (cur[7].length) { - for (var t in cur[7]) { + if (cur[7].length) \{ + for (var t in cur[7]) \{ var tag = cur[7][t]; tagspan += '' + tag + ' '; } diff -r 0bb67503ad4b -r ac6fec2af8c8 mercurial/templates/monoblue/graph.tmpl --- a/mercurial/templates/monoblue/graph.tmpl Mon May 31 21:37:01 2010 +0200 +++ b/mercurial/templates/monoblue/graph.tmpl Mon May 31 21:43:03 2010 +0200 @@ -48,7 +48,7 @@ var graph = new Graph(); graph.scale({bg_height}); - graph.edge = function(x0, y0, x1, y1, color) { + graph.edge = function(x0, y0, x1, y1, color) \{ this.setColor(color, 0.0, 0.65); this.ctx.beginPath(); @@ -62,7 +62,7 @@ revlink += '_DESC'; revlink += '_TAGS_DATE, by _USER'; - graph.vertex = function(x, y, color, parity, cur) { + graph.vertex = function(x, y, color, parity, cur) \{ this.ctx.beginPath(); color = this.setColor(color, 0.25, 0.75); @@ -81,17 +81,17 @@ item = item.replace(/_DATE/, cur[5]); var tagspan = ''; - if (cur[7].length || (cur[6][0] != 'default' || cur[6][1])) { + if (cur[7].length || (cur[6][0] != 'default' || cur[6][1])) \{ tagspan = ''; - if (cur[6][1]) { + if (cur[6][1]) \{ tagspan += ''; tagspan += cur[6][0] + ' '; - } else if (!cur[6][1] && cur[6][0] != 'default') { + } else if (!cur[6][1] && cur[6][0] != 'default') \{ tagspan += ''; tagspan += cur[6][0] + ' '; } - if (cur[7].length) { - for (var t in cur[7]) { + if (cur[7].length) \{ + for (var t in cur[7]) \{ var tag = cur[7][t]; tagspan += '' + tag + ' '; } diff -r 0bb67503ad4b -r ac6fec2af8c8 mercurial/templates/paper/graph.tmpl --- a/mercurial/templates/paper/graph.tmpl Mon May 31 21:37:01 2010 +0200 +++ b/mercurial/templates/paper/graph.tmpl Mon May 31 21:43:03 2010 +0200 @@ -59,7 +59,7 @@ var graph = new Graph(); graph.scale({bg_height}); -graph.edge = function(x0, y0, x1, y1, color) { +graph.edge = function(x0, y0, x1, y1, color) \{ this.setColor(color, 0.0, 0.65); this.ctx.beginPath(); @@ -73,7 +73,7 @@ revlink += '_DESC'; revlink += '_TAGS_DATE, by _USER'; -graph.vertex = function(x, y, color, parity, cur) { +graph.vertex = function(x, y, color, parity, cur) \{ this.ctx.beginPath(); color = this.setColor(color, 0.25, 0.75); @@ -92,17 +92,17 @@ item = item.replace(/_DATE/, cur[5]); var tagspan = ''; - if (cur[7].length || (cur[6][0] != 'default' || cur[6][1])) { + if (cur[7].length || (cur[6][0] != 'default' || cur[6][1])) \{ tagspan = ''; - if (cur[6][1]) { + if (cur[6][1]) \{ tagspan += ''; tagspan += cur[6][0] + ' '; - } else if (!cur[6][1] && cur[6][0] != 'default') { + } else if (!cur[6][1] && cur[6][0] != 'default') \{ tagspan += ''; tagspan += cur[6][0] + ' '; } - if (cur[7].length) { - for (var t in cur[7]) { + if (cur[7].length) \{ + for (var t in cur[7]) \{ var tag = cur[7][t]; tagspan += '' + tag + ' '; } diff -r 0bb67503ad4b -r ac6fec2af8c8 mercurial/templates/spartan/graph.tmpl --- a/mercurial/templates/spartan/graph.tmpl Mon May 31 21:37:01 2010 +0200 +++ b/mercurial/templates/spartan/graph.tmpl Mon May 31 21:43:03 2010 +0200 @@ -43,7 +43,7 @@ var graph = new Graph(); graph.scale({bg_height}); -graph.edge = function(x0, y0, x1, y1, color) { +graph.edge = function(x0, y0, x1, y1, color) \{ this.setColor(color, 0.0, 0.65); this.ctx.beginPath(); @@ -57,7 +57,7 @@ revlink += '_DESC'; revlink += '_DATE, by _USER'; -graph.vertex = function(x, y, color, parity, cur) { +graph.vertex = function(x, y, color, parity, cur) \{ this.ctx.beginPath(); color = this.setColor(color, 0.25, 0.75); diff -r 0bb67503ad4b -r ac6fec2af8c8 mercurial/transaction.py --- a/mercurial/transaction.py Mon May 31 21:37:01 2010 +0200 +++ b/mercurial/transaction.py Mon May 31 21:43:03 2010 +0200 @@ -43,6 +43,7 @@ class transaction(object): def __init__(self, report, opener, journal, after=None, createmode=None): self.count = 1 + self.usages = 1 self.report = report self.opener = opener self.after = after @@ -108,8 +109,16 @@ @active def nest(self): self.count += 1 + self.usages += 1 return self + def release(self): + if self.count > 0: + self.usages -= 1 + # of the transaction scopes are left without being closed, fail + if self.count > 0 and self.usages == 0: + self._abort() + def running(self): return self.count > 0 @@ -136,6 +145,7 @@ def _abort(self): self.count = 0 + self.usages = 0 self.file.close() try: diff -r 0bb67503ad4b -r ac6fec2af8c8 mercurial/ui.py --- a/mercurial/ui.py Mon May 31 21:37:01 2010 +0200 +++ b/mercurial/ui.py Mon May 31 21:43:03 2010 +0200 @@ -154,11 +154,83 @@ def configlist(self, section, name, default=None, untrusted=False): """Return a list of comma/space separated strings""" + + def _parse_plain(parts, s, offset): + whitespace = False + while offset < len(s) and (s[offset].isspace() or s[offset] == ','): + whitespace = True + offset += 1 + if offset >= len(s): + return None, parts, offset + if whitespace: + parts.append('') + if s[offset] == '"' and not parts[-1]: + return _parse_quote, parts, offset + 1 + elif s[offset] == '"' and parts[-1][-1] == '\\': + parts[-1] = parts[-1][:-1] + s[offset] + return _parse_plain, parts, offset + 1 + parts[-1] += s[offset] + return _parse_plain, parts, offset + 1 + + def _parse_quote(parts, s, offset): + if offset < len(s) and s[offset] == '"': # "" + parts.append('') + offset += 1 + while offset < len(s) and (s[offset].isspace() or + s[offset] == ','): + offset += 1 + return _parse_plain, parts, offset + + while offset < len(s) and s[offset] != '"': + if (s[offset] == '\\' and offset + 1 < len(s) + and s[offset + 1] == '"'): + offset += 1 + parts[-1] += '"' + else: + parts[-1] += s[offset] + offset += 1 + + if offset >= len(s): + real_parts = _configlist(parts[-1]) + if not real_parts: + parts[-1] = '"' + else: + real_parts[0] = '"' + real_parts[0] + parts = parts[:-1] + parts.extend(real_parts) + return None, parts, offset + + offset += 1 + while offset < len(s) and s[offset] in [' ', ',']: + offset += 1 + + if offset < len(s): + if offset + 1 == len(s) and s[offset] == '"': + parts[-1] += '"' + offset += 1 + else: + parts.append('') + else: + return None, parts, offset + + return _parse_plain, parts, offset + + def _configlist(s): + s = s.rstrip(' ,') + if not s: + return None + parser, parts, offset = _parse_plain, [''], 0 + while parser: + parser, parts, offset = parser(parts, s, offset) + return parts + result = self.config(section, name, untrusted=untrusted) if result is None: result = default or [] if isinstance(result, basestring): - result = result.replace(",", " ").split() + result = _configlist(result) + if result is None: + result = default or [] return result def has_section(self, section, untrusted=False): @@ -194,6 +266,8 @@ user = os.environ.get("HGUSER") if user is None: user = self.config("ui", "username") + if user is not None: + user = os.path.expandvars(user) if user is None: user = os.environ.get("EMAIL") if user is None and self.configbool("ui", "askusername"): @@ -239,17 +313,42 @@ def pushbuffer(self): self._buffers.append([]) - def popbuffer(self): + def popbuffer(self, labeled=False): + '''pop the last buffer and return the buffered output + + If labeled is True, any labels associated with buffered + output will be handled. By default, this has no effect + on the output returned, but extensions and GUI tools may + handle this argument and returned styled output. If output + is being buffered so it can be captured and parsed or + processed, labeled should not be set to True. + ''' return "".join(self._buffers.pop()) - def write(self, *args): + def write(self, *args, **opts): + '''write args to output + + By default, this method simply writes to the buffer or stdout, + but extensions or GUI tools may override this method, + write_err(), popbuffer(), and label() to style output from + various parts of hg. + + An optional keyword argument, "label", can be passed in. + This should be a string containing label names separated by + space. Label names take the form of "topic.type". For example, + ui.debug() issues a label of "ui.debug". + + When labeling output for a specific command, a label of + "cmdname.type" is recommended. For example, status issues + a label of "status.modified" for modified files. + ''' if self._buffers: self._buffers[-1].extend([str(a) for a in args]) else: for a in args: sys.stdout.write(str(a)) - def write_err(self, *args): + def write_err(self, *args, **opts): try: if not getattr(sys.stdout, 'closed', False): sys.stdout.flush() @@ -335,17 +434,37 @@ return getpass.getpass(prompt or _('password: ')) except EOFError: raise util.Abort(_('response expected')) - def status(self, *msg): + def status(self, *msg, **opts): + '''write status message to output (if ui.quiet is False) + + This adds an output label of "ui.status". + ''' if not self.quiet: - self.write(*msg) - def warn(self, *msg): - self.write_err(*msg) - def note(self, *msg): + opts['label'] = opts.get('label', '') + ' ui.status' + self.write(*msg, **opts) + def warn(self, *msg, **opts): + '''write warning message to output (stderr) + + This adds an output label of "ui.warning". + ''' + opts['label'] = opts.get('label', '') + ' ui.warning' + self.write_err(*msg, **opts) + def note(self, *msg, **opts): + '''write note to output (if ui.verbose is True) + + This adds an output label of "ui.note". + ''' if self.verbose: - self.write(*msg) - def debug(self, *msg): + opts['label'] = opts.get('label', '') + ' ui.note' + self.write(*msg, **opts) + def debug(self, *msg, **opts): + '''write debug message to output (if ui.debugflag is True) + + This adds an output label of "ui.debug". + ''' if self.debugflag: - self.write(*msg) + opts['label'] = opts.get('label', '') + ' ui.debug' + self.write(*msg, **opts) def edit(self, text, user): (fd, name) = tempfile.mkstemp(prefix="hg-editor-", suffix=".txt", text=True) @@ -417,3 +536,15 @@ % (topic, item, pos, total, unit, pct)) else: self.debug('%s:%s %s%s\n' % (topic, item, pos, unit)) + + def label(self, msg, label): + '''style msg based on supplied label + + Like ui.write(), this just returns msg unchanged, but extensions + and GUI tools can override it to allow styling output without + writing it. + + ui.write(s, 'label') is equivalent to + ui.write(ui.label(s, 'label')). + ''' + return msg diff -r 0bb67503ad4b -r ac6fec2af8c8 mercurial/util.py --- a/mercurial/util.py Mon May 31 21:37:01 2010 +0200 +++ b/mercurial/util.py Mon May 31 21:43:03 2010 +0200 @@ -36,6 +36,13 @@ _fastsha1 = sha1 = _sha1 return _sha1(s) +import __builtin__ + +def fakebuffer(sliceable, offset=0): + return sliceable[offset:] +if not hasattr(__builtin__, 'buffer'): + __builtin__.buffer = fakebuffer + import subprocess closefds = os.name == 'posix' @@ -451,7 +458,7 @@ for name, kind in osutil.listdir(src): srcname = os.path.join(src, name) dstname = os.path.join(dst, name) - copyfiles(srcname, dstname, hardlink) + hardlink = copyfiles(srcname, dstname, hardlink) else: if hardlink: try: @@ -462,6 +469,8 @@ else: shutil.copy(src, dst) + return hardlink + class path_auditor(object): '''ensure that a filesystem path contains no banned components. the following properties of a path are checked: @@ -765,7 +774,7 @@ file. When rename is called, the copy is renamed to the original name, making the changes visible. """ - def __init__(self, name, mode, createmode): + def __init__(self, name, mode='w+b', createmode=None): self.__name = name self._fp = None self.temp = mktempcopy(name, emptyok=('w' in mode), diff -r 0bb67503ad4b -r ac6fec2af8c8 mercurial/verify.py --- a/mercurial/verify.py Mon May 31 21:37:01 2010 +0200 +++ b/mercurial/verify.py Mon May 31 21:43:03 2010 +0200 @@ -122,7 +122,7 @@ checklog(cl, "changelog", 0) total = len(repo) for i in repo: - ui.progress(_('checking'), i, total=total) + ui.progress(_('changesets'), i, total=total) n = cl.node(i) checkentry(cl, i, n, seen, [i], "changelog") @@ -133,14 +133,14 @@ filelinkrevs.setdefault(f, []).append(i) except Exception, inst: exc(i, _("unpacking changeset %s") % short(n), inst) - ui.progress(_('checking'), None) + ui.progress(_('changesets'), None) ui.status(_("checking manifests\n")) seen = {} checklog(mf, "manifest", 0) total = len(mf) for i in mf: - ui.progress(_('checking'), i, total=total) + ui.progress(_('manifests'), i, total=total) n = mf.node(i) lr = checkentry(mf, i, n, seen, mflinkrevs.get(n, []), "manifest") if n in mflinkrevs: @@ -156,7 +156,7 @@ filenodes.setdefault(f, {}).setdefault(fn, lr) except Exception, inst: exc(lr, _("reading manifest delta %s") % short(n), inst) - ui.progress(_('checking'), None) + ui.progress(_('manifests'), None) ui.status(_("crosschecking files in changesets and manifests\n")) diff -r 0bb67503ad4b -r ac6fec2af8c8 mercurial/win32.py --- a/mercurial/win32.py Mon May 31 21:37:01 2010 +0200 +++ b/mercurial/win32.py Mon May 31 21:43:03 2010 +0200 @@ -16,7 +16,7 @@ import win32api import errno, os, sys, pywintypes, win32con, win32file, win32process -import winerror, win32gui +import winerror, win32gui, win32console import osutil, encoding from win32com.shell import shell, shellcon @@ -189,3 +189,16 @@ pid = win32process.GetCurrentProcessId() win32gui.EnumWindows(callback, pid) + +def termwidth_(): + try: + # Query stderr to avoid problems with redirections + screenbuf = win32console.GetStdHandle(win32console.STD_ERROR_HANDLE) + try: + window = screenbuf.GetConsoleScreenBufferInfo()['Window'] + width = window.Right - window.Left + return width + finally: + screenbuf.Detach() + except pywintypes.error: + return 79 diff -r 0bb67503ad4b -r ac6fec2af8c8 mercurial/windows.py --- a/mercurial/windows.py Mon May 31 21:37:01 2010 +0200 +++ b/mercurial/windows.py Mon May 31 21:43:03 2010 +0200 @@ -363,6 +363,10 @@ # current line but on the new one. Keep room for it. return 79 +def groupmembers(name): + # Don't support groups on Windows for now + raise KeyError() + try: # override functions with win32 versions if possible from win32 import * diff -r 0bb67503ad4b -r ac6fec2af8c8 setup.py --- a/setup.py Mon May 31 21:37:01 2010 +0200 +++ b/setup.py Mon May 31 21:43:03 2010 +0200 @@ -37,7 +37,6 @@ import tempfile from distutils.core import setup, Extension from distutils.dist import Distribution -from distutils.command.install_data import install_data from distutils.command.build import build from distutils.command.build_py import build_py from distutils.spawn import spawn, find_executable diff -r 0bb67503ad4b -r ac6fec2af8c8 tests/get-with-headers.py --- a/tests/get-with-headers.py Mon May 31 21:37:01 2010 +0200 +++ b/tests/get-with-headers.py Mon May 31 21:43:03 2010 +0200 @@ -3,7 +3,7 @@ """This does HTTP GET requests given a host:port and path and returns a subset of the headers plus the body of the result.""" -import httplib, sys, re +import httplib, sys try: import msvcrt, os diff -r 0bb67503ad4b -r ac6fec2af8c8 tests/helpers.sh --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tests/helpers.sh Mon May 31 21:43:03 2010 +0200 @@ -0,0 +1,17 @@ +#/bin/sh + +hideport() { sed "s/localhost:$HGPORT/localhost:\$HGPORT/"; } + +repr() { python -c "import sys; print repr(sys.stdin.read()).replace('\\n', '\n')"; } + +hidehex() { python -c 'import sys, re; print re.replace("\b[0-9A-Fa-f]{12,40}", "X" * 12)'; } + +hidetmp() { sed "s/$HGTMP/\$HGTMP/"; } + +hidebackup() { sed 's/\(saved backup bundle to \).*/\1/'; } + +cleanrebase() { + sed -e 's/\(Rebase status stored to\).*/\1/' \ + -e 's/\(Rebase status restored from\).*/\1/' \ + -e 's/\(saved backup bundle to \).*/\1/'; +} diff -r 0bb67503ad4b -r ac6fec2af8c8 tests/hghave --- a/tests/hghave Mon May 31 21:37:01 2010 +0200 +++ b/tests/hghave Mon May 31 21:43:03 2010 +0200 @@ -120,12 +120,12 @@ def has_git(): return matchoutput('git --version 2>&1', r'^git version') -def has_rst2html(): - for name in ('rst2html', 'rst2html.py'): - name = name + ' --version 2>&1' - if matchoutput(name, r'^rst2html(?:\.py)? \(Docutils'): - return True - return False +def has_docutils(): + try: + from docutils.core import publish_cmdline + return True + except ImportError: + return False def has_svn(): return matchoutput('svn --version 2>&1', r'^svn, version') and \ @@ -198,7 +198,7 @@ "outer-repo": (has_outer_repo, "outer repo"), "p4": (has_p4, "Perforce server and client"), "pygments": (has_pygments, "Pygments source highlighting library"), - "rst2html": (has_rst2html, "Docutils rst2html tool"), + "docutils": (has_docutils, "Docutils text processing library"), "svn": (has_svn, "subversion client and admin tools"), "svn-bindings": (has_svn_bindings, "subversion python bindings"), "symlink": (has_symlink, "symbolic links"), diff -r 0bb67503ad4b -r ac6fec2af8c8 tests/killdaemons.py --- a/tests/killdaemons.py Mon May 31 21:37:01 2010 +0200 +++ b/tests/killdaemons.py Mon May 31 21:43:03 2010 +0200 @@ -1,6 +1,6 @@ #!/usr/bin/env python -import os, sys, time, errno, signal +import os, time, errno, signal # Kill off any leftover daemon processes try: diff -r 0bb67503ad4b -r ac6fec2af8c8 tests/run-tests.py --- a/tests/run-tests.py Mon May 31 21:37:01 2010 +0200 +++ b/tests/run-tests.py Mon May 31 21:43:03 2010 +0200 @@ -46,9 +46,8 @@ import errno import optparse import os -import signal +import shutil import subprocess -import shutil import signal import sys import tempfile @@ -70,6 +69,9 @@ SKIPPED_PREFIX = 'skipped: ' FAILED_PREFIX = 'hghave check failed: ' PYTHON = sys.executable +IMPL_PATH = 'PYTHONPATH' +if 'java' in sys.platform: + IMPL_PATH = 'JYTHONPATH' requiredtools = ["python", "diff", "grep", "unzip", "gunzip", "bunzip2", "sed"] @@ -81,34 +83,43 @@ def parseargs(): parser = optparse.OptionParser("%prog [options] [tests]") + + # keep these sorted + parser.add_option("--blacklist", action="append", + help="skip tests listed in the specified blacklist file") parser.add_option("-C", "--annotate", action="store_true", help="output files annotated with coverage") parser.add_option("--child", type="int", help="run as child process, summary to given fd") parser.add_option("-c", "--cover", action="store_true", help="print a test coverage report") + parser.add_option("-d", "--debug", action="store_true", + help="debug mode: write output of test scripts to console" + " rather than capturing and diff'ing it (disables timeout)") parser.add_option("-f", "--first", action="store_true", help="exit on the first test failure") + parser.add_option("--inotify", action="store_true", + help="enable inotify extension when running tests") 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" " (default: $%s or %d)" % defaults['jobs']) - parser.add_option("-k", "--keywords", - help="run tests matching keywords") parser.add_option("--keep-tmpdir", action="store_true", help="keep temporary directory after running tests") - parser.add_option("--tmpdir", type="string", - help="run tests in the given temporary directory" - " (implies --keep-tmpdir)") - parser.add_option("-d", "--debug", action="store_true", - help="debug mode: write output of test scripts to console" - " rather than capturing and diff'ing it (disables timeout)") - parser.add_option("-R", "--restart", action="store_true", - help="restart at last error") + parser.add_option("-k", "--keywords", + help="run tests matching keywords") + parser.add_option("-l", "--local", action="store_true", + help="shortcut for --with-hg=/../hg") + parser.add_option("-n", "--nodiff", action="store_true", + help="skip showing test changes") parser.add_option("-p", "--port", type="int", help="port on which servers should listen" " (default: $%s or %d)" % defaults['port']) + parser.add_option("--pure", action="store_true", + help="use pure Python code instead of C extensions") + parser.add_option("-R", "--restart", action="store_true", + help="restart at last error") parser.add_option("-r", "--retest", action="store_true", help="retest failed tests") parser.add_option("-S", "--noskips", action="store_true", @@ -116,30 +127,29 @@ parser.add_option("-t", "--timeout", type="int", 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" + " (implies --keep-tmpdir)") parser.add_option("-v", "--verbose", action="store_true", help="output verbose messages") - parser.add_option("-n", "--nodiff", action="store_true", - help="skip showing test changes") + parser.add_option("--view", type="string", + help="external diff viewer") parser.add_option("--with-hg", type="string", metavar="HG", help="test using specified hg script rather than a " "temporary installation") - parser.add_option("--local", action="store_true", - help="shortcut for --with-hg=/../hg") - parser.add_option("--pure", action="store_true", - help="use pure Python code instead of C extensions") parser.add_option("-3", "--py3k-warnings", action="store_true", help="enable Py3k warnings on Python 2.6+") - parser.add_option("--inotify", action="store_true", - help="enable inotify extension when running tests") - parser.add_option("--blacklist", action="append", - help="skip tests listed in the specified blacklist file") for option, default in defaults.items(): defaults[option] = int(os.environ.get(*default)) parser.set_defaults(**defaults) (options, args) = parser.parse_args() + # jython is always pure + if 'java' in sys.platform or '__pypy__' in sys.modules: + options.pure = True + if options.with_hg: if not (os.path.isfile(options.with_hg) and os.access(options.with_hg, os.X_OK)): @@ -565,6 +575,7 @@ mark = '.' skipped = (ret == SKIPPED_STATUS) + # If we're not in --debug mode and reference output file exists, # check test output against it. if options.debug: @@ -576,6 +587,13 @@ else: refout = [] + if (ret != 0 or out != refout) and not skipped and not options.debug: + # Save errors to a file for diagnosis + f = open(err, "wb") + for line in out: + f.write(line) + f.close() + if skipped: mark = 's' if out is None: # debug mode: nothing to parse @@ -597,7 +615,10 @@ else: fail("output changed") if not options.nodiff: - showdiff(refout, out, ref, err) + if options.view: + os.system("%s %s %s" % (options.view, ref, err)) + else: + showdiff(refout, out, ref, err) ret = 1 elif ret: mark = '!' @@ -607,13 +628,6 @@ sys.stdout.write(mark) sys.stdout.flush() - if ret != 0 and not skipped and not options.debug: - # Save errors to a file for diagnosis - f = open(err, "wb") - for line in out: - f.write(line) - f.close() - killdaemons() os.chdir(TESTDIR) @@ -843,6 +857,7 @@ os.environ["EMAIL"] = "Foo Bar " os.environ['CDPATH'] = '' os.environ['COLUMNS'] = '80' + os.environ['GREP_OPTIONS'] = '' os.environ['http_proxy'] = '' # unset env related to hooks @@ -914,10 +929,10 @@ # it, in case external libraries are only available via current # PYTHONPATH. (In particular, the Subversion bindings on OS X # are in /opt/subversion.) - oldpypath = os.environ.get('PYTHONPATH') + oldpypath = os.environ.get(IMPL_PATH) if oldpypath: pypath.append(oldpypath) - os.environ['PYTHONPATH'] = os.pathsep.join(pypath) + os.environ[IMPL_PATH] = os.pathsep.join(pypath) COVERAGE_FILE = os.path.join(TESTDIR, ".coverage") @@ -938,7 +953,7 @@ vlog("# Using TESTDIR", TESTDIR) vlog("# Using HGTMP", HGTMP) vlog("# Using PATH", os.environ["PATH"]) - vlog("# Using PYTHONPATH", os.environ["PYTHONPATH"]) + vlog("# Using", IMPL_PATH, os.environ[IMPL_PATH]) try: if len(tests) > 1 and options.jobs > 1: diff -r 0bb67503ad4b -r ac6fec2af8c8 tests/svn/branches.svndump --- a/tests/svn/branches.svndump Mon May 31 21:37:01 2010 +0200 +++ b/tests/svn/branches.svndump Mon May 31 21:43:03 2010 +0200 @@ -1,6 +1,6 @@ SVN-fs-dump-format-version: 2 -UUID: 9ccdebd5-40da-43b4-a9c9-c2857851ccaf +UUID: 644ede6c-2b81-4367-9dc8-d786514f2cde Revision-number: 0 Prop-content-length: 56 @@ -9,7 +9,7 @@ K 8 svn:date V 27 -2010-05-19T19:36:21.767874Z +2010-05-19T20:16:07.429098Z PROPS-END Revision-number: 1 @@ -27,7 +27,7 @@ K 8 svn:date V 27 -2010-05-19T19:36:21.795557Z +2010-05-19T20:16:07.461283Z PROPS-END Node-path: branches @@ -63,7 +63,7 @@ K 8 svn:date V 27 -2010-05-19T19:36:22.069417Z +2010-05-19T20:16:08.121436Z PROPS-END Node-path: branches/notinbranch @@ -155,7 +155,7 @@ K 8 svn:date V 27 -2010-05-19T19:36:25.067407Z +2010-05-19T20:16:11.113124Z PROPS-END Node-path: branches/old @@ -188,7 +188,7 @@ K 8 svn:date V 27 -2010-05-19T19:36:27.042834Z +2010-05-19T20:16:13.060877Z PROPS-END Node-path: trunk/a @@ -218,7 +218,7 @@ K 8 svn:date V 27 -2010-05-19T19:36:28.045031Z +2010-05-19T20:16:14.066212Z PROPS-END Node-path: branches/old/b @@ -248,7 +248,7 @@ K 8 svn:date V 27 -2010-05-19T19:36:30.049884Z +2010-05-19T20:16:16.069449Z PROPS-END Node-path: branches/old/c @@ -286,7 +286,7 @@ K 8 svn:date V 27 -2010-05-19T19:36:31.044711Z +2010-05-19T20:16:17.070868Z PROPS-END Node-path: branches/old/b @@ -317,7 +317,7 @@ K 8 svn:date V 27 -2010-05-19T19:36:34.043552Z +2010-05-19T20:16:20.063098Z PROPS-END Node-path: branches/old2 @@ -346,7 +346,7 @@ K 8 svn:date V 27 -2010-05-19T19:36:36.042680Z +2010-05-19T20:16:22.062931Z PROPS-END Node-path: branches/old @@ -375,7 +375,7 @@ K 8 svn:date V 27 -2010-05-19T19:36:37.043714Z +2010-05-19T20:16:23.075562Z PROPS-END Node-path: trunk/a @@ -404,7 +404,7 @@ K 8 svn:date V 27 -2010-05-19T19:36:39.044703Z +2010-05-19T20:16:25.107655Z PROPS-END Node-path: branches/old3 diff -r 0bb67503ad4b -r ac6fec2af8c8 tests/test-acl --- a/tests/test-acl Mon May 31 21:37:01 2010 +0200 +++ b/tests/test-acl Mon May 31 21:43:03 2010 +0200 @@ -7,7 +7,7 @@ echo "Pushing as user $user" echo 'hgrc = """' - sed -e 1,2d b/.hg/hgrc + sed -e 1,2d b/.hg/hgrc | grep -v fakegroups.py echo '"""' if test -f acl.config; then echo 'acl.config = """' @@ -24,6 +24,30 @@ echo } +init_config() +{ +cat > fakegroups.py < $config <> acl.config do_push barney +# asterisk + +init_config + +echo 'asterisk test' +echo '[acl.allow]' >> $config +echo "** = fred" >> $config +echo "fred is always allowed" +do_push fred + +echo '[acl.deny]' >> $config +echo "foo/Bar/** = *" >> $config +echo "no one is allowed inside foo/Bar/" +do_push fred + +# Groups + +init_config + +echo 'OS-level groups' +echo '[acl.allow]' >> $config +echo "** = @group1" >> $config +echo "@group1 is always allowed" +do_push fred + +echo '[acl.deny]' >> $config +echo "foo/Bar/** = @group1" >> $config +echo "@group is allowed inside anything but foo/Bar/" +do_push fred + +echo 'Invalid group' +# Disable the fakegroups trick to get real failures +grep -v fakegroups $config > config.tmp +mv config.tmp $config +echo '[acl.allow]' >> $config +echo "** = @unlikelytoexist" >> $config +do_push fred 2>&1 | grep unlikelytoexist + +true diff -r 0bb67503ad4b -r ac6fec2af8c8 tests/test-acl.out --- a/tests/test-acl.out Mon May 31 21:37:01 2010 +0200 +++ b/tests/test-acl.out Mon May 31 21:43:03 2010 +0200 @@ -59,19 +59,19 @@ changesets: 3 chunks add changeset 911600dab2ae adding manifests -manifests: 1 chunks -manifests: 2 chunks -manifests: 3 chunks +manifests: 1/3 chunks (33.33%) +manifests: 2/3 chunks (66.67%) +manifests: 3/3 chunks (100.00%) adding file changes adding foo/Bar/file.txt revisions -files: 1 chunks +files: 1/3 chunks (33.33%) adding foo/file.txt revisions -files: 2 chunks +files: 2/3 chunks (66.67%) adding quux/file.py revisions -files: 3 chunks +files: 3/3 chunks (100.00%) added 3 changesets with 3 changes to 3 files updating the branch cache -rolling back last transaction +rolling back to revision 0 (undo push) 0:6675d58eff77 Extension disabled for lack of acl.sources @@ -129,21 +129,21 @@ changesets: 3 chunks add changeset 911600dab2ae adding manifests -manifests: 1 chunks -manifests: 2 chunks -manifests: 3 chunks +manifests: 1/3 chunks (33.33%) +manifests: 2/3 chunks (66.67%) +manifests: 3/3 chunks (100.00%) adding file changes adding foo/Bar/file.txt revisions -files: 1 chunks +files: 1/3 chunks (33.33%) adding foo/file.txt revisions -files: 2 chunks +files: 2/3 chunks (66.67%) adding quux/file.py revisions -files: 3 chunks +files: 3/3 chunks (100.00%) added 3 changesets with 3 changes to 3 files calling hook pretxnchangegroup.acl: hgext.acl.hook acl: changes have source "push" - skipping updating the branch cache -rolling back last transaction +rolling back to revision 0 (undo push) 0:6675d58eff77 No [acl.allow]/[acl.deny] @@ -203,25 +203,30 @@ changesets: 3 chunks add changeset 911600dab2ae adding manifests -manifests: 1 chunks -manifests: 2 chunks -manifests: 3 chunks +manifests: 1/3 chunks (33.33%) +manifests: 2/3 chunks (66.67%) +manifests: 3/3 chunks (100.00%) adding file changes adding foo/Bar/file.txt revisions -files: 1 chunks +files: 1/3 chunks (33.33%) adding foo/file.txt revisions -files: 2 chunks +files: 2/3 chunks (66.67%) adding quux/file.py revisions -files: 3 chunks +files: 3/3 chunks (100.00%) added 3 changesets with 3 changes to 3 files calling hook pretxnchangegroup.acl: hgext.acl.hook +acl: acl.allow.branches not enabled +acl: acl.deny.branches not enabled acl: acl.allow not enabled acl: acl.deny not enabled +acl: branch access granted: "ef1ea85a6374" on branch "default" acl: allowing changeset ef1ea85a6374 +acl: branch access granted: "f9cafe1212c8" on branch "default" acl: allowing changeset f9cafe1212c8 +acl: branch access granted: "911600dab2ae" on branch "default" acl: allowing changeset 911600dab2ae updating the branch cache -rolling back last transaction +rolling back to revision 0 (undo push) 0:6675d58eff77 Empty [acl.allow] @@ -282,20 +287,23 @@ changesets: 3 chunks add changeset 911600dab2ae adding manifests -manifests: 1 chunks -manifests: 2 chunks -manifests: 3 chunks +manifests: 1/3 chunks (33.33%) +manifests: 2/3 chunks (66.67%) +manifests: 3/3 chunks (100.00%) adding file changes adding foo/Bar/file.txt revisions -files: 1 chunks +files: 1/3 chunks (33.33%) adding foo/file.txt revisions -files: 2 chunks +files: 2/3 chunks (66.67%) adding quux/file.py revisions -files: 3 chunks +files: 3/3 chunks (100.00%) added 3 changesets with 3 changes to 3 files calling hook pretxnchangegroup.acl: hgext.acl.hook +acl: acl.allow.branches not enabled +acl: acl.deny.branches not enabled acl: acl.allow enabled, 0 entries for user fred acl: acl.deny not enabled +acl: branch access granted: "ef1ea85a6374" on branch "default" acl: user fred not allowed on foo/file.txt error: pretxnchangegroup.acl hook failed: acl: access denied for changeset ef1ea85a6374 transaction abort! @@ -362,22 +370,27 @@ changesets: 3 chunks add changeset 911600dab2ae adding manifests -manifests: 1 chunks -manifests: 2 chunks -manifests: 3 chunks +manifests: 1/3 chunks (33.33%) +manifests: 2/3 chunks (66.67%) +manifests: 3/3 chunks (100.00%) adding file changes adding foo/Bar/file.txt revisions -files: 1 chunks +files: 1/3 chunks (33.33%) adding foo/file.txt revisions -files: 2 chunks +files: 2/3 chunks (66.67%) adding quux/file.py revisions -files: 3 chunks +files: 3/3 chunks (100.00%) added 3 changesets with 3 changes to 3 files calling hook pretxnchangegroup.acl: hgext.acl.hook +acl: acl.allow.branches not enabled +acl: acl.deny.branches not enabled acl: acl.allow enabled, 1 entries for user fred acl: acl.deny not enabled +acl: branch access granted: "ef1ea85a6374" on branch "default" acl: allowing changeset ef1ea85a6374 +acl: branch access granted: "f9cafe1212c8" on branch "default" acl: allowing changeset f9cafe1212c8 +acl: branch access granted: "911600dab2ae" on branch "default" acl: user fred not allowed on quux/file.py error: pretxnchangegroup.acl hook failed: acl: access denied for changeset 911600dab2ae transaction abort! @@ -445,20 +458,23 @@ changesets: 3 chunks add changeset 911600dab2ae adding manifests -manifests: 1 chunks -manifests: 2 chunks -manifests: 3 chunks +manifests: 1/3 chunks (33.33%) +manifests: 2/3 chunks (66.67%) +manifests: 3/3 chunks (100.00%) adding file changes adding foo/Bar/file.txt revisions -files: 1 chunks +files: 1/3 chunks (33.33%) adding foo/file.txt revisions -files: 2 chunks +files: 2/3 chunks (66.67%) adding quux/file.py revisions -files: 3 chunks +files: 3/3 chunks (100.00%) added 3 changesets with 3 changes to 3 files calling hook pretxnchangegroup.acl: hgext.acl.hook +acl: acl.allow.branches not enabled +acl: acl.deny.branches not enabled acl: acl.allow enabled, 0 entries for user barney acl: acl.deny enabled, 0 entries for user barney +acl: branch access granted: "ef1ea85a6374" on branch "default" acl: user barney not allowed on foo/file.txt error: pretxnchangegroup.acl hook failed: acl: access denied for changeset ef1ea85a6374 transaction abort! @@ -527,22 +543,27 @@ changesets: 3 chunks add changeset 911600dab2ae adding manifests -manifests: 1 chunks -manifests: 2 chunks -manifests: 3 chunks +manifests: 1/3 chunks (33.33%) +manifests: 2/3 chunks (66.67%) +manifests: 3/3 chunks (100.00%) adding file changes adding foo/Bar/file.txt revisions -files: 1 chunks +files: 1/3 chunks (33.33%) adding foo/file.txt revisions -files: 2 chunks +files: 2/3 chunks (66.67%) adding quux/file.py revisions -files: 3 chunks +files: 3/3 chunks (100.00%) added 3 changesets with 3 changes to 3 files calling hook pretxnchangegroup.acl: hgext.acl.hook +acl: acl.allow.branches not enabled +acl: acl.deny.branches not enabled acl: acl.allow enabled, 1 entries for user fred acl: acl.deny enabled, 1 entries for user fred +acl: branch access granted: "ef1ea85a6374" on branch "default" acl: allowing changeset ef1ea85a6374 +acl: branch access granted: "f9cafe1212c8" on branch "default" acl: allowing changeset f9cafe1212c8 +acl: branch access granted: "911600dab2ae" on branch "default" acl: user fred not allowed on quux/file.py error: pretxnchangegroup.acl hook failed: acl: access denied for changeset 911600dab2ae transaction abort! @@ -612,21 +633,25 @@ changesets: 3 chunks add changeset 911600dab2ae adding manifests -manifests: 1 chunks -manifests: 2 chunks -manifests: 3 chunks +manifests: 1/3 chunks (33.33%) +manifests: 2/3 chunks (66.67%) +manifests: 3/3 chunks (100.00%) adding file changes adding foo/Bar/file.txt revisions -files: 1 chunks +files: 1/3 chunks (33.33%) adding foo/file.txt revisions -files: 2 chunks +files: 2/3 chunks (66.67%) adding quux/file.py revisions -files: 3 chunks +files: 3/3 chunks (100.00%) added 3 changesets with 3 changes to 3 files calling hook pretxnchangegroup.acl: hgext.acl.hook +acl: acl.allow.branches not enabled +acl: acl.deny.branches not enabled acl: acl.allow enabled, 1 entries for user fred acl: acl.deny enabled, 2 entries for user fred +acl: branch access granted: "ef1ea85a6374" on branch "default" acl: allowing changeset ef1ea85a6374 +acl: branch access granted: "f9cafe1212c8" on branch "default" acl: user fred denied on foo/Bar/file.txt error: pretxnchangegroup.acl hook failed: acl: access denied for changeset f9cafe1212c8 transaction abort! @@ -696,20 +721,23 @@ changesets: 3 chunks add changeset 911600dab2ae adding manifests -manifests: 1 chunks -manifests: 2 chunks -manifests: 3 chunks +manifests: 1/3 chunks (33.33%) +manifests: 2/3 chunks (66.67%) +manifests: 3/3 chunks (100.00%) adding file changes adding foo/Bar/file.txt revisions -files: 1 chunks +files: 1/3 chunks (33.33%) adding foo/file.txt revisions -files: 2 chunks +files: 2/3 chunks (66.67%) adding quux/file.py revisions -files: 3 chunks +files: 3/3 chunks (100.00%) added 3 changesets with 3 changes to 3 files calling hook pretxnchangegroup.acl: hgext.acl.hook +acl: acl.allow.branches not enabled +acl: acl.deny.branches not enabled acl: acl.allow enabled, 0 entries for user barney acl: acl.deny enabled, 0 entries for user barney +acl: branch access granted: "ef1ea85a6374" on branch "default" acl: user barney not allowed on foo/file.txt error: pretxnchangegroup.acl hook failed: acl: access denied for changeset ef1ea85a6374 transaction abort! @@ -781,25 +809,30 @@ changesets: 3 chunks add changeset 911600dab2ae adding manifests -manifests: 1 chunks -manifests: 2 chunks -manifests: 3 chunks +manifests: 1/3 chunks (33.33%) +manifests: 2/3 chunks (66.67%) +manifests: 3/3 chunks (100.00%) adding file changes adding foo/Bar/file.txt revisions -files: 1 chunks +files: 1/3 chunks (33.33%) adding foo/file.txt revisions -files: 2 chunks +files: 2/3 chunks (66.67%) adding quux/file.py revisions -files: 3 chunks +files: 3/3 chunks (100.00%) added 3 changesets with 3 changes to 3 files calling hook pretxnchangegroup.acl: hgext.acl.hook +acl: acl.allow.branches not enabled +acl: acl.deny.branches not enabled acl: acl.allow enabled, 1 entries for user barney acl: acl.deny enabled, 0 entries for user barney +acl: branch access granted: "ef1ea85a6374" on branch "default" acl: allowing changeset ef1ea85a6374 +acl: branch access granted: "f9cafe1212c8" on branch "default" acl: allowing changeset f9cafe1212c8 +acl: branch access granted: "911600dab2ae" on branch "default" acl: allowing changeset 911600dab2ae updating the branch cache -rolling back last transaction +rolling back to revision 0 (undo push) 0:6675d58eff77 wilma can change files with a .txt extension @@ -867,22 +900,27 @@ changesets: 3 chunks add changeset 911600dab2ae adding manifests -manifests: 1 chunks -manifests: 2 chunks -manifests: 3 chunks +manifests: 1/3 chunks (33.33%) +manifests: 2/3 chunks (66.67%) +manifests: 3/3 chunks (100.00%) adding file changes adding foo/Bar/file.txt revisions -files: 1 chunks +files: 1/3 chunks (33.33%) adding foo/file.txt revisions -files: 2 chunks +files: 2/3 chunks (66.67%) adding quux/file.py revisions -files: 3 chunks +files: 3/3 chunks (100.00%) added 3 changesets with 3 changes to 3 files calling hook pretxnchangegroup.acl: hgext.acl.hook +acl: acl.allow.branches not enabled +acl: acl.deny.branches not enabled acl: acl.allow enabled, 1 entries for user wilma acl: acl.deny enabled, 0 entries for user wilma +acl: branch access granted: "ef1ea85a6374" on branch "default" acl: allowing changeset ef1ea85a6374 +acl: branch access granted: "f9cafe1212c8" on branch "default" acl: allowing changeset f9cafe1212c8 +acl: branch access granted: "911600dab2ae" on branch "default" acl: user wilma not allowed on quux/file.py error: pretxnchangegroup.acl hook failed: acl: access denied for changeset 911600dab2ae transaction abort! @@ -957,16 +995,16 @@ changesets: 3 chunks add changeset 911600dab2ae adding manifests -manifests: 1 chunks -manifests: 2 chunks -manifests: 3 chunks +manifests: 1/3 chunks (33.33%) +manifests: 2/3 chunks (66.67%) +manifests: 3/3 chunks (100.00%) adding file changes adding foo/Bar/file.txt revisions -files: 1 chunks +files: 1/3 chunks (33.33%) adding foo/file.txt revisions -files: 2 chunks +files: 2/3 chunks (66.67%) adding quux/file.py revisions -files: 3 chunks +files: 3/3 chunks (100.00%) added 3 changesets with 3 changes to 3 files calling hook pretxnchangegroup.acl: hgext.acl.hook error: pretxnchangegroup.acl hook raised an exception: [Errno 2] No such file or directory: '../acl.config' @@ -1046,22 +1084,27 @@ changesets: 3 chunks add changeset 911600dab2ae adding manifests -manifests: 1 chunks -manifests: 2 chunks -manifests: 3 chunks +manifests: 1/3 chunks (33.33%) +manifests: 2/3 chunks (66.67%) +manifests: 3/3 chunks (100.00%) adding file changes adding foo/Bar/file.txt revisions -files: 1 chunks +files: 1/3 chunks (33.33%) adding foo/file.txt revisions -files: 2 chunks +files: 2/3 chunks (66.67%) adding quux/file.py revisions -files: 3 chunks +files: 3/3 chunks (100.00%) added 3 changesets with 3 changes to 3 files calling hook pretxnchangegroup.acl: hgext.acl.hook +acl: acl.allow.branches not enabled +acl: acl.deny.branches not enabled acl: acl.allow enabled, 1 entries for user betty acl: acl.deny enabled, 0 entries for user betty +acl: branch access granted: "ef1ea85a6374" on branch "default" acl: allowing changeset ef1ea85a6374 +acl: branch access granted: "f9cafe1212c8" on branch "default" acl: allowing changeset f9cafe1212c8 +acl: branch access granted: "911600dab2ae" on branch "default" acl: user betty not allowed on quux/file.py error: pretxnchangegroup.acl hook failed: acl: access denied for changeset 911600dab2ae transaction abort! @@ -1142,24 +1185,380 @@ changesets: 3 chunks add changeset 911600dab2ae adding manifests -manifests: 1 chunks -manifests: 2 chunks -manifests: 3 chunks +manifests: 1/3 chunks (33.33%) +manifests: 2/3 chunks (66.67%) +manifests: 3/3 chunks (100.00%) +adding file changes +adding foo/Bar/file.txt revisions +files: 1/3 chunks (33.33%) +adding foo/file.txt revisions +files: 2/3 chunks (66.67%) +adding quux/file.py revisions +files: 3/3 chunks (100.00%) +added 3 changesets with 3 changes to 3 files +calling hook pretxnchangegroup.acl: hgext.acl.hook +acl: acl.allow.branches not enabled +acl: acl.deny.branches not enabled +acl: acl.allow enabled, 1 entries for user barney +acl: acl.deny enabled, 0 entries for user barney +acl: branch access granted: "ef1ea85a6374" on branch "default" +acl: allowing changeset ef1ea85a6374 +acl: branch access granted: "f9cafe1212c8" on branch "default" +acl: allowing changeset f9cafe1212c8 +acl: branch access granted: "911600dab2ae" on branch "default" +acl: allowing changeset 911600dab2ae +updating the branch cache +rolling back to revision 0 (undo push) +0:6675d58eff77 + +asterisk test +fred is always allowed +Pushing as user fred +hgrc = """ +[acl] +sources = push +[extensions] +[acl.allow] +** = fred +""" +pushing to ../b +searching for changes +common changesets up to 6675d58eff77 +invalidating branch cache (tip differs) +3 changesets found +list of changesets: +ef1ea85a6374b77d6da9dcda9541f498f2d17df7 +f9cafe1212c8c6fa1120d14a556e18cc44ff8bdd +911600dab2ae7a9baff75958b84fe606851ce955 +adding changesets +bundling changes: 0 chunks +bundling changes: 1 chunks +bundling changes: 2 chunks +bundling changes: 3 chunks +bundling changes: 4 chunks +bundling changes: 5 chunks +bundling changes: 6 chunks +bundling changes: 7 chunks +bundling changes: 8 chunks +bundling changes: 9 chunks +bundling manifests: 0 chunks +bundling manifests: 1 chunks +bundling manifests: 2 chunks +bundling manifests: 3 chunks +bundling manifests: 4 chunks +bundling manifests: 5 chunks +bundling manifests: 6 chunks +bundling manifests: 7 chunks +bundling manifests: 8 chunks +bundling manifests: 9 chunks +bundling files: foo/Bar/file.txt 0 chunks +bundling files: foo/Bar/file.txt 1 chunks +bundling files: foo/Bar/file.txt 2 chunks +bundling files: foo/Bar/file.txt 3 chunks +bundling files: foo/file.txt 4 chunks +bundling files: foo/file.txt 5 chunks +bundling files: foo/file.txt 6 chunks +bundling files: foo/file.txt 7 chunks +bundling files: quux/file.py 8 chunks +bundling files: quux/file.py 9 chunks +bundling files: quux/file.py 10 chunks +bundling files: quux/file.py 11 chunks +changesets: 1 chunks +add changeset ef1ea85a6374 +changesets: 2 chunks +add changeset f9cafe1212c8 +changesets: 3 chunks +add changeset 911600dab2ae +adding manifests +manifests: 1/3 chunks (33.33%) +manifests: 2/3 chunks (66.67%) +manifests: 3/3 chunks (100.00%) +adding file changes +adding foo/Bar/file.txt revisions +files: 1/3 chunks (33.33%) +adding foo/file.txt revisions +files: 2/3 chunks (66.67%) +adding quux/file.py revisions +files: 3/3 chunks (100.00%) +added 3 changesets with 3 changes to 3 files +calling hook pretxnchangegroup.acl: hgext.acl.hook +acl: acl.allow.branches not enabled +acl: acl.deny.branches not enabled +acl: acl.allow enabled, 1 entries for user fred +acl: acl.deny not enabled +acl: branch access granted: "ef1ea85a6374" on branch "default" +acl: allowing changeset ef1ea85a6374 +acl: branch access granted: "f9cafe1212c8" on branch "default" +acl: allowing changeset f9cafe1212c8 +acl: branch access granted: "911600dab2ae" on branch "default" +acl: allowing changeset 911600dab2ae +updating the branch cache +rolling back to revision 0 (undo push) +0:6675d58eff77 + +no one is allowed inside foo/Bar/ +Pushing as user fred +hgrc = """ +[acl] +sources = push +[extensions] +[acl.allow] +** = fred +[acl.deny] +foo/Bar/** = * +""" +pushing to ../b +searching for changes +common changesets up to 6675d58eff77 +invalidating branch cache (tip differs) +3 changesets found +list of changesets: +ef1ea85a6374b77d6da9dcda9541f498f2d17df7 +f9cafe1212c8c6fa1120d14a556e18cc44ff8bdd +911600dab2ae7a9baff75958b84fe606851ce955 +adding changesets +bundling changes: 0 chunks +bundling changes: 1 chunks +bundling changes: 2 chunks +bundling changes: 3 chunks +bundling changes: 4 chunks +bundling changes: 5 chunks +bundling changes: 6 chunks +bundling changes: 7 chunks +bundling changes: 8 chunks +bundling changes: 9 chunks +bundling manifests: 0 chunks +bundling manifests: 1 chunks +bundling manifests: 2 chunks +bundling manifests: 3 chunks +bundling manifests: 4 chunks +bundling manifests: 5 chunks +bundling manifests: 6 chunks +bundling manifests: 7 chunks +bundling manifests: 8 chunks +bundling manifests: 9 chunks +bundling files: foo/Bar/file.txt 0 chunks +bundling files: foo/Bar/file.txt 1 chunks +bundling files: foo/Bar/file.txt 2 chunks +bundling files: foo/Bar/file.txt 3 chunks +bundling files: foo/file.txt 4 chunks +bundling files: foo/file.txt 5 chunks +bundling files: foo/file.txt 6 chunks +bundling files: foo/file.txt 7 chunks +bundling files: quux/file.py 8 chunks +bundling files: quux/file.py 9 chunks +bundling files: quux/file.py 10 chunks +bundling files: quux/file.py 11 chunks +changesets: 1 chunks +add changeset ef1ea85a6374 +changesets: 2 chunks +add changeset f9cafe1212c8 +changesets: 3 chunks +add changeset 911600dab2ae +adding manifests +manifests: 1/3 chunks (33.33%) +manifests: 2/3 chunks (66.67%) +manifests: 3/3 chunks (100.00%) adding file changes adding foo/Bar/file.txt revisions -files: 1 chunks +files: 1/3 chunks (33.33%) adding foo/file.txt revisions -files: 2 chunks +files: 2/3 chunks (66.67%) adding quux/file.py revisions -files: 3 chunks +files: 3/3 chunks (100.00%) +added 3 changesets with 3 changes to 3 files +calling hook pretxnchangegroup.acl: hgext.acl.hook +acl: acl.allow.branches not enabled +acl: acl.deny.branches not enabled +acl: acl.allow enabled, 1 entries for user fred +acl: acl.deny enabled, 1 entries for user fred +acl: branch access granted: "ef1ea85a6374" on branch "default" +acl: allowing changeset ef1ea85a6374 +acl: branch access granted: "f9cafe1212c8" on branch "default" +acl: user fred denied on foo/Bar/file.txt +error: pretxnchangegroup.acl hook failed: acl: access denied for changeset f9cafe1212c8 +transaction abort! +rollback completed +abort: acl: access denied for changeset f9cafe1212c8 +no rollback information available +0:6675d58eff77 + +OS-level groups +@group1 is always allowed +Pushing as user fred +hgrc = """ +[acl] +sources = push +[extensions] +[acl.allow] +** = @group1 +""" +pushing to ../b +searching for changes +common changesets up to 6675d58eff77 +3 changesets found +list of changesets: +ef1ea85a6374b77d6da9dcda9541f498f2d17df7 +f9cafe1212c8c6fa1120d14a556e18cc44ff8bdd +911600dab2ae7a9baff75958b84fe606851ce955 +adding changesets +bundling changes: 0 chunks +bundling changes: 1 chunks +bundling changes: 2 chunks +bundling changes: 3 chunks +bundling changes: 4 chunks +bundling changes: 5 chunks +bundling changes: 6 chunks +bundling changes: 7 chunks +bundling changes: 8 chunks +bundling changes: 9 chunks +bundling manifests: 0 chunks +bundling manifests: 1 chunks +bundling manifests: 2 chunks +bundling manifests: 3 chunks +bundling manifests: 4 chunks +bundling manifests: 5 chunks +bundling manifests: 6 chunks +bundling manifests: 7 chunks +bundling manifests: 8 chunks +bundling manifests: 9 chunks +bundling files: foo/Bar/file.txt 0 chunks +bundling files: foo/Bar/file.txt 1 chunks +bundling files: foo/Bar/file.txt 2 chunks +bundling files: foo/Bar/file.txt 3 chunks +bundling files: foo/file.txt 4 chunks +bundling files: foo/file.txt 5 chunks +bundling files: foo/file.txt 6 chunks +bundling files: foo/file.txt 7 chunks +bundling files: quux/file.py 8 chunks +bundling files: quux/file.py 9 chunks +bundling files: quux/file.py 10 chunks +bundling files: quux/file.py 11 chunks +changesets: 1 chunks +add changeset ef1ea85a6374 +changesets: 2 chunks +add changeset f9cafe1212c8 +changesets: 3 chunks +add changeset 911600dab2ae +adding manifests +manifests: 1/3 chunks (33.33%) +manifests: 2/3 chunks (66.67%) +manifests: 3/3 chunks (100.00%) +adding file changes +adding foo/Bar/file.txt revisions +files: 1/3 chunks (33.33%) +adding foo/file.txt revisions +files: 2/3 chunks (66.67%) +adding quux/file.py revisions +files: 3/3 chunks (100.00%) added 3 changesets with 3 changes to 3 files calling hook pretxnchangegroup.acl: hgext.acl.hook -acl: acl.allow enabled, 1 entries for user barney -acl: acl.deny enabled, 0 entries for user barney +acl: acl.allow.branches not enabled +acl: acl.deny.branches not enabled +acl: "group1" not defined in [acl.groups] +acl: acl.allow enabled, 1 entries for user fred +acl: acl.deny not enabled +acl: branch access granted: "ef1ea85a6374" on branch "default" acl: allowing changeset ef1ea85a6374 +acl: branch access granted: "f9cafe1212c8" on branch "default" acl: allowing changeset f9cafe1212c8 +acl: branch access granted: "911600dab2ae" on branch "default" acl: allowing changeset 911600dab2ae updating the branch cache -rolling back last transaction +rolling back to revision 0 (undo push) 0:6675d58eff77 +@group is allowed inside anything but foo/Bar/ +Pushing as user fred +hgrc = """ +[acl] +sources = push +[extensions] +[acl.allow] +** = @group1 +[acl.deny] +foo/Bar/** = @group1 +""" +pushing to ../b +searching for changes +common changesets up to 6675d58eff77 +invalidating branch cache (tip differs) +3 changesets found +list of changesets: +ef1ea85a6374b77d6da9dcda9541f498f2d17df7 +f9cafe1212c8c6fa1120d14a556e18cc44ff8bdd +911600dab2ae7a9baff75958b84fe606851ce955 +adding changesets +bundling changes: 0 chunks +bundling changes: 1 chunks +bundling changes: 2 chunks +bundling changes: 3 chunks +bundling changes: 4 chunks +bundling changes: 5 chunks +bundling changes: 6 chunks +bundling changes: 7 chunks +bundling changes: 8 chunks +bundling changes: 9 chunks +bundling manifests: 0 chunks +bundling manifests: 1 chunks +bundling manifests: 2 chunks +bundling manifests: 3 chunks +bundling manifests: 4 chunks +bundling manifests: 5 chunks +bundling manifests: 6 chunks +bundling manifests: 7 chunks +bundling manifests: 8 chunks +bundling manifests: 9 chunks +bundling files: foo/Bar/file.txt 0 chunks +bundling files: foo/Bar/file.txt 1 chunks +bundling files: foo/Bar/file.txt 2 chunks +bundling files: foo/Bar/file.txt 3 chunks +bundling files: foo/file.txt 4 chunks +bundling files: foo/file.txt 5 chunks +bundling files: foo/file.txt 6 chunks +bundling files: foo/file.txt 7 chunks +bundling files: quux/file.py 8 chunks +bundling files: quux/file.py 9 chunks +bundling files: quux/file.py 10 chunks +bundling files: quux/file.py 11 chunks +changesets: 1 chunks +add changeset ef1ea85a6374 +changesets: 2 chunks +add changeset f9cafe1212c8 +changesets: 3 chunks +add changeset 911600dab2ae +adding manifests +manifests: 1/3 chunks (33.33%) +manifests: 2/3 chunks (66.67%) +manifests: 3/3 chunks (100.00%) +adding file changes +adding foo/Bar/file.txt revisions +files: 1/3 chunks (33.33%) +adding foo/file.txt revisions +files: 2/3 chunks (66.67%) +adding quux/file.py revisions +files: 3/3 chunks (100.00%) +added 3 changesets with 3 changes to 3 files +calling hook pretxnchangegroup.acl: hgext.acl.hook +acl: acl.allow.branches not enabled +acl: acl.deny.branches not enabled +acl: "group1" not defined in [acl.groups] +acl: acl.allow enabled, 1 entries for user fred +acl: "group1" not defined in [acl.groups] +acl: acl.deny enabled, 1 entries for user fred +acl: branch access granted: "ef1ea85a6374" on branch "default" +acl: allowing changeset ef1ea85a6374 +acl: branch access granted: "f9cafe1212c8" on branch "default" +acl: user fred denied on foo/Bar/file.txt +error: pretxnchangegroup.acl hook failed: acl: access denied for changeset f9cafe1212c8 +transaction abort! +rollback completed +abort: acl: access denied for changeset f9cafe1212c8 +no rollback information available +0:6675d58eff77 + +Invalid group +** = @unlikelytoexist +acl: "unlikelytoexist" not defined in [acl.groups] +error: pretxnchangegroup.acl hook failed: group 'unlikelytoexist' is undefined +abort: group 'unlikelytoexist' is undefined diff -r 0bb67503ad4b -r ac6fec2af8c8 tests/test-archive --- a/tests/test-archive Mon May 31 21:37:01 2010 +0200 +++ b/tests/test-archive Mon May 31 21:43:03 2010 +0200 @@ -74,6 +74,20 @@ hg archive -t tgz -p %b-%h test-%h.tar.gz gzip -dc test-$QTIP.tar.gz | tar tf - 2>/dev/null | sed "s/$QTIP/TIP/" +hg archive autodetected_test.tar +tar tf autodetected_test.tar + +# The '-t' should override autodetection +hg archive -t tar autodetect_override_test.zip +tar tf autodetect_override_test.zip + +for ext in tar tar.gz tgz tar.bz2 tbz2 zip; do + hg archive auto_test.$ext + if [ -d auto_test.$ext ]; then + echo "extension $ext was not autodetected." + fi +done + cat > md5comp.py < a hg add a hg commit -m 0 diff -r 0bb67503ad4b -r ac6fec2af8c8 tests/test-bookmarks-current --- a/tests/test-bookmarks-current Mon May 31 21:37:01 2010 +0200 +++ b/tests/test-bookmarks-current Mon May 31 21:43:03 2010 +0200 @@ -17,6 +17,10 @@ echo % list bookmarks hg bookmark +echo % list bookmarks with color +hg --config extensions.color= --config color.mode=ansi \ + bookmark --color=always + echo % update to bookmark X hg update X diff -r 0bb67503ad4b -r ac6fec2af8c8 tests/test-bookmarks-current.out --- a/tests/test-bookmarks-current.out Mon May 31 21:37:01 2010 +0200 +++ b/tests/test-bookmarks-current.out Mon May 31 21:43:03 2010 +0200 @@ -3,6 +3,8 @@ % set bookmark X % list bookmarks * X -1:000000000000 +% list bookmarks with color + * X -1:000000000000 % update to bookmark X 0 files updated, 0 files merged, 0 files removed, 0 files unresolved % list bookmarks diff -r 0bb67503ad4b -r ac6fec2af8c8 tests/test-bookmarks-rebase --- a/tests/test-bookmarks-rebase Mon May 31 21:37:01 2010 +0200 +++ b/tests/test-bookmarks-rebase Mon May 31 21:43:03 2010 +0200 @@ -1,15 +1,11 @@ #!/bin/sh +. $TESTDIR/helpers.sh + echo "[extensions]" >> $HGRCPATH echo "rebase=" >> $HGRCPATH echo "bookmarks=" >> $HGRCPATH -cleanoutput () { - sed -e 's/\(Rebase status stored to\).*/\1/' \ - -e 's/\(Rebase status restored from\).*/\1/' \ - -e 's/\(saving bundle to \).*/\1/' -} - echo % initialize repository hg init @@ -33,6 +29,6 @@ hg bookmark echo % rebase -hg rebase -s two -d one 2>&1 | cleanoutput +hg rebase -s two -d one 2>&1 | cleanrebase hg log diff -r 0bb67503ad4b -r ac6fec2af8c8 tests/test-bookmarks-rebase.out --- a/tests/test-bookmarks-rebase.out Mon May 31 21:37:01 2010 +0200 +++ b/tests/test-bookmarks-rebase.out Mon May 31 21:43:03 2010 +0200 @@ -9,17 +9,11 @@ * two 3:2ae46b1d99a7 one 1:925d80f479bb % rebase -saving bundle to -adding branch -adding changesets -adding manifests -adding file changes -added 1 changesets with 1 changes to 1 files (-1 heads) -rebase completed +saved backup bundle to changeset: 3:9163974d1cb5 +tag: one tag: tip tag: two -tag: one parent: 1:925d80f479bb parent: 2:db815d6d32e6 user: test diff -r 0bb67503ad4b -r ac6fec2af8c8 tests/test-bookmarks-strip --- a/tests/test-bookmarks-strip Mon May 31 21:37:01 2010 +0200 +++ b/tests/test-bookmarks-strip Mon May 31 21:43:03 2010 +0200 @@ -1,5 +1,6 @@ #!/bin/sh +. $TESTDIR/helpers.sh echo "[extensions]" >> $HGRCPATH echo "bookmarks=" >> $HGRCPATH echo "mq=" >> $HGRCPATH @@ -37,7 +38,7 @@ hg book echo % strip to revision 1 -hg strip 1 2>&1 | sed 's/\(saving bundle to \).*/\1/' +hg strip 1 | hidebackup echo % list bookmarks hg book diff -r 0bb67503ad4b -r ac6fec2af8c8 tests/test-bookmarks-strip.out --- a/tests/test-bookmarks-strip.out Mon May 31 21:37:01 2010 +0200 +++ b/tests/test-bookmarks-strip.out Mon May 31 21:43:03 2010 +0200 @@ -12,13 +12,7 @@ test 1:16b24da7e457 test2 1:16b24da7e457 % strip to revision 1 -saving bundle to -saving bundle to -adding branch -adding changesets -adding manifests -adding file changes -added 1 changesets with 1 changes to 1 files +saved backup bundle to % list bookmarks * test 1:9f1b7e78eff8 * test2 1:9f1b7e78eff8 diff -r 0bb67503ad4b -r ac6fec2af8c8 tests/test-bookmarks.out --- a/tests/test-bookmarks.out Mon May 31 21:37:01 2010 +0200 +++ b/tests/test-bookmarks.out Mon May 31 21:43:03 2010 +0200 @@ -3,6 +3,8 @@ % bookmark rev -1 % list bookmarks * X -1:000000000000 +% list bookmarks with color + * X -1:000000000000 % bookmark X moved to rev 0 * X 0:f7b1eb17ad24 % look up bookmark diff -r 0bb67503ad4b -r ac6fec2af8c8 tests/test-branch-option.out --- a/tests/test-branch-option.out Mon May 31 21:37:01 2010 +0200 +++ b/tests/test-branch-option.out Mon May 31 21:43:03 2010 +0200 @@ -2,7 +2,6 @@ adding foo 1 files updated, 0 files merged, 0 files removed, 0 files unresolved marked working directory as branch c -created new head requesting all changes adding changesets adding manifests @@ -14,7 +13,6 @@ marked working directory as branch b 1 files updated, 0 files merged, 0 files removed, 0 files unresolved marked working directory as branch b -created new head in rev c branch a 1:dd6e60a716c6 2:f25d57ab0566 diff -r 0bb67503ad4b -r ac6fec2af8c8 tests/test-branches.out --- a/tests/test-branches.out Mon May 31 21:37:01 2010 +0200 +++ b/tests/test-branches.out Mon May 31 21:43:03 2010 +0200 @@ -3,7 +3,6 @@ reset working directory to branch a 0 files updated, 0 files merged, 1 files removed, 0 files unresolved marked working directory as branch b -created new head 1 files updated, 0 files merged, 2 files removed, 0 files unresolved marked working directory as branch c abort: the name 'tip' is reserved @@ -171,6 +170,7 @@ date: Thu Jan 01 00:00:09 1970 +0000 summary: prune bad branch +reopening closed branch head 12 --- branch b is back in action b 13:6ac12926b8c3 a branch name much longer than the default justification used by branches 7:10ff5895aa57 diff -r 0bb67503ad4b -r ac6fec2af8c8 tests/test-bundle-r.out --- a/tests/test-bundle-r.out Mon May 31 21:37:01 2010 +0200 +++ b/tests/test-bundle-r.out Mon May 31 21:43:03 2010 +0200 @@ -154,7 +154,7 @@ crosschecking files in changesets and manifests checking files 4 files, 9 changesets, 7 total revisions -rolling back last transaction +rolling back to revision 4 (undo pull) % should fail abort: --base is incompatible with specifying a destination abort: repository default-push not found! @@ -187,7 +187,7 @@ crosschecking files in changesets and manifests checking files 4 files, 9 changesets, 7 total revisions -rolling back last transaction +rolling back to revision 2 (undo unbundle) % 2 2:d62976ca1e50 adding changesets @@ -202,7 +202,7 @@ crosschecking files in changesets and manifests checking files 2 files, 5 changesets, 5 total revisions -rolling back last transaction +rolling back to revision 2 (undo unbundle) adding changesets adding manifests adding file changes @@ -215,7 +215,7 @@ crosschecking files in changesets and manifests checking files 3 files, 7 changesets, 6 total revisions -rolling back last transaction +rolling back to revision 2 (undo unbundle) adding changesets adding manifests adding file changes diff -r 0bb67503ad4b -r ac6fec2af8c8 tests/test-bundle.out --- a/tests/test-bundle.out Mon May 31 21:37:01 2010 +0200 +++ b/tests/test-bundle.out Mon May 31 21:43:03 2010 +0200 @@ -37,7 +37,7 @@ 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 +rolling back to revision -1 (undo pull) ====== Pull full.hg into empty again (using --cwd) pulling from ../full.hg requesting all changes @@ -55,7 +55,7 @@ searching for changes no changes found ====== Rollback empty -rolling back last transaction +rolling back to revision -1 (undo pull) ====== Pull full.hg into empty again (using -R) pulling from full.hg requesting all changes @@ -123,7 +123,7 @@ 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 +rolling back to revision -1 (undo pull) ====== 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) diff -r 0bb67503ad4b -r ac6fec2af8c8 tests/test-check-code --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tests/test-check-code Mon May 31 21:43:03 2010 +0200 @@ -0,0 +1,26 @@ +#!/bin/sh +#cd `dirname $0` +cat > correct.py < wrong.py < quote.py < def toto( arg1, arg2): + gratuitous whitespace in () or [] +./wrong.py:2: + > del(arg2) + del isn't a function +./wrong.py:3: + > return ( 5+6, 9) + missing whitespace in expression + gratuitous whitespace in () or [] +./quote.py:5: + > '"""', 42+1, """and + missing whitespace in expression diff -r 0bb67503ad4b -r ac6fec2af8c8 tests/test-churn --- a/tests/test-churn Mon May 31 21:37:01 2010 +0200 +++ b/tests/test-churn Mon May 31 21:43:03 2010 +0200 @@ -52,6 +52,9 @@ hg rm d/g/f2.txt hg ci -Am "removed d/g/f2.txt" -u user1 -d 14:00 d/g/f2.txt hg churn --diffstat +echo % churn --diffstat with color +hg --config extensions.color= churn --config color.mode=ansi \ + --diffstat --color=always echo % changeset number churn hg churn -c diff -r 0bb67503ad4b -r ac6fec2af8c8 tests/test-churn.out --- a/tests/test-churn.out Mon May 31 21:37:01 2010 +0200 +++ b/tests/test-churn.out Mon May 31 21:43:03 2010 +0200 @@ -32,6 +32,10 @@ user1 +3/-1 +++++++++++++++++++++++++++++++++++++++++-------------- user3 +3/-0 +++++++++++++++++++++++++++++++++++++++++ user2 +2/-0 +++++++++++++++++++++++++++ +% churn --diffstat with color +user1 +3/-1 +++++++++++++++++++++++++++++++++++++++++-------------- +user3 +3/-0 +++++++++++++++++++++++++++++++++++++++++ +user2 +2/-0 +++++++++++++++++++++++++++ % changeset number churn user1 4 *************************************************************** user3 3 *********************************************** diff -r 0bb67503ad4b -r ac6fec2af8c8 tests/test-clone-update-order.out --- a/tests/test-clone-update-order.out Mon May 31 21:37:01 2010 +0200 +++ b/tests/test-clone-update-order.out Mon May 31 21:43:03 2010 +0200 @@ -4,11 +4,9 @@ 0 files updated, 0 files merged, 1 files removed, 0 files unresolved marked working directory as branch mine adding world -created new head 0 files updated, 0 files merged, 1 files removed, 0 files unresolved marked working directory as branch other adding bye -created new head 1 files updated, 0 files merged, 1 files removed, 0 files unresolved % test -U -u abort: cannot specify both --noupdate and --updaterev diff -r 0bb67503ad4b -r ac6fec2af8c8 tests/test-clone.out --- a/tests/test-clone.out Mon May 31 21:37:01 2010 +0200 +++ b/tests/test-clone.out Mon May 31 21:43:03 2010 +0200 @@ -66,7 +66,6 @@ % add branch 'stable' to repo a for later tests marked working directory as branch stable -created new head % tag ref2 % updating back to ref2 diff -r 0bb67503ad4b -r ac6fec2af8c8 tests/test-command-template.out --- a/tests/test-command-template.out Mon May 31 21:37:01 2010 +0200 +++ b/tests/test-command-template.out Mon May 31 21:43:03 2010 +0200 @@ -1,4 +1,3 @@ -created new head 0 files updated, 0 files merged, 4 files removed, 0 files unresolved created new head # default style is like normal output diff -r 0bb67503ad4b -r ac6fec2af8c8 tests/test-convert-cvs.out --- a/tests/test-convert-cvs.out Mon May 31 21:37:01 2010 +0200 +++ b/tests/test-convert-cvs.out Mon May 31 21:43:03 2010 +0200 @@ -45,7 +45,7 @@ 2 Initial revision 1 import filtering out empty revision -rolling back last transaction +rolling back to revision 0 (undo commit) 0 ci0 updating tags c diff -r 0bb67503ad4b -r ac6fec2af8c8 tests/test-convert-datesort.out --- a/tests/test-convert-datesort.out Mon May 31 21:37:01 2010 +0200 +++ b/tests/test-convert-datesort.out Mon May 31 21:43:03 2010 +0200 @@ -3,7 +3,6 @@ 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 diff -r 0bb67503ad4b -r ac6fec2af8c8 tests/test-convert-svn-move --- a/tests/test-convert-svn-move Mon May 31 21:37:01 2010 +0200 +++ b/tests/test-convert-svn-move Mon May 31 21:43:03 2010 +0200 @@ -51,3 +51,20 @@ echo '% try updating' hg up -qC default cd .. + +echo '% test convert progress bar' + +echo "progress=" >> $HGRCPATH +echo "[progress]" >> $HGRCPATH +echo "assume-tty=1" >> $HGRCPATH +echo "delay=0" >> $HGRCPATH +echo "refresh=0" >> $HGRCPATH + +cat > filtercr.py <&1 | python filtercr.py diff -r 0bb67503ad4b -r ac6fec2af8c8 tests/test-convert-svn-move.out --- a/tests/test-convert-svn-move.out Mon May 31 21:37:01 2010 +0200 +++ b/tests/test-convert-svn-move.out Mon May 31 21:43:03 2010 +0200 @@ -80,3 +80,44 @@ 644 @ dlink2 644 @ dlink3 % try updating +% test convert progress bar + +scanning [ <=> ] 1 +scanning [ <=> ] 2 +scanning [ <=> ] 3 +scanning [ <=> ] 4 + +converting [ ] 0/4 +retrieving file [==========> ] 1/5 +retrieving file [=====================> ] 2/5 +retrieving file [=================================> ] 3/5 +retrieving file [============================================> ] 4/5 +retrieving file [========================================================>] 5/5 + +converting [==============> ] 1/4 +scanning paths [ ] 0/1 + +retrieving file [========================================================>] 1/1 + +converting [==============================> ] 2/4 +scanning paths [ ] 0/2 +scanning paths [============================> ] 1/2 + +retrieving file [=============> ] 1/4 +retrieving file [===========================> ] 2/4 +retrieving file [=========================================> ] 3/4 +retrieving file [========================================================>] 4/4 + +converting [=============================================> ] 3/4 +scanning paths [ ] 0/1 + +retrieving file [========================================================>] 1/1 + +initializing destination hg-progress repository +scanning source... +sorting... +converting... +3 initial +2 clobber symlink +1 clobber1 +0 clobber2 diff -r 0bb67503ad4b -r ac6fec2af8c8 tests/test-convert.out --- a/tests/test-convert.out Mon May 31 21:37:01 2010 +0200 +++ b/tests/test-convert.out Mon May 31 21:43:03 2010 +0200 @@ -103,8 +103,10 @@ ignore integrity errors when reading. Use it to fix Mercurial repositories with missing revlogs, by converting from and to Mercurial. + --config convert.hg.saverev=False (boolean) store original revision ID in changeset (forces target IDs to change) + --config convert.hg.startrev=0 (hg revision identifier) convert start revision and its descendants @@ -125,25 +127,30 @@ --config convert.cvsps.cache=True (boolean) Set to False to disable remote log caching, for testing and debugging purposes. + --config convert.cvsps.fuzz=60 (integer) Specify the maximum time (in seconds) that is allowed between commits with identical user and log message in a single changeset. When very large files were checked in as part of a changeset then the default may not be long enough. + --config convert.cvsps.mergeto='{{mergetobranch ([-\w]+)}}' Specify a regular expression to which commit log messages are matched. If a match occurs, then the conversion process will insert a dummy revision merging the branch on which this log message occurs to the branch indicated in the regex. + --config convert.cvsps.mergefrom='{{mergefrombranch ([-\w]+)}}' Specify a regular expression to which commit log messages are matched. If a match occurs, then the conversion process will add the most recent revision on the branch indicated in the regex as the second parent of the changeset. + --config hook.cvslog Specify a Python function to be called at the end of gathering the CVS log. The function is passed a list with the log entries, and can modify the entries in-place, or add or delete them. + --config hook.cvschangesets Specify a Python function to be called after the changesets are calculated from the the CVS log. The function is passed a list with @@ -170,8 +177,10 @@ --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 @@ -202,8 +211,10 @@ --config convert.hg.clonebranches=False (boolean) dispatch source branches in separate clones. + --config convert.hg.tagsbranch=default (branch name) tag revisions branch name + --config convert.hg.usebranchnames=True (boolean) preserve branch names diff -r 0bb67503ad4b -r ac6fec2af8c8 tests/test-copy2 --- a/tests/test-copy2 Mon May 31 21:37:01 2010 +0200 +++ b/tests/test-copy2 Mon May 31 21:43:03 2010 +0200 @@ -19,6 +19,9 @@ hg st -A hg commit -m1 +echo "# copy --after to a nonexistant target filename" +hg cp -A foo dummy + echo "# dry-run; should show that foo is clean" hg copy --dry-run foo bar hg st -A diff -r 0bb67503ad4b -r ac6fec2af8c8 tests/test-copy2.out --- a/tests/test-copy2.out Mon May 31 21:37:01 2010 +0200 +++ b/tests/test-copy2.out Mon May 31 21:43:03 2010 +0200 @@ -11,6 +11,8 @@ # should print a warning that this is not a real copy; foo is added bar has not been committed yet, so no copy data will be stored for foo. A foo +# copy --after to a nonexistant target filename +foo: not recording copy - dummy does not exist # dry-run; should show that foo is clean C foo # should show copy diff -r 0bb67503ad4b -r ac6fec2af8c8 tests/test-debugcomplete.out --- a/tests/test-debugcomplete.out Mon May 31 21:37:01 2010 +0200 +++ b/tests/test-debugcomplete.out Mon May 31 21:43:03 2010 +0200 @@ -143,7 +143,7 @@ --traceback --verbose --version ---webdir-conf +--web-conf -6 -A -E @@ -171,12 +171,12 @@ export: output, switch-parent, rev, text, git, nodates forget: include, exclude init: ssh, remotecmd -log: follow, follow-first, date, copies, keyword, rev, removed, only-merges, user, only-branch, prune, patch, git, limit, no-merges, style, template, include, exclude +log: follow, follow-first, date, copies, keyword, rev, removed, only-merges, user, only-branch, branch, prune, patch, git, limit, no-merges, stat, style, template, include, exclude merge: force, rev, preview pull: update, force, rev, branch, ssh, remotecmd -push: force, rev, branch, ssh, remotecmd +push: force, rev, branch, new-branch, ssh, remotecmd remove: after, force, include, exclude -serve: accesslog, daemon, daemon-pipefds, errorlog, port, address, prefix, name, webdir-conf, pid-file, stdio, templates, style, ipv6, certificate +serve: accesslog, daemon, daemon-pipefds, errorlog, port, address, prefix, name, web-conf, webdir-conf, pid-file, stdio, templates, style, ipv6, certificate status: all, modified, added, removed, deleted, clean, unknown, ignored, no-status, copies, print0, rev, change, include, exclude summary: remote update: clean, check, date, rev @@ -210,20 +210,20 @@ help: identify: rev, num, id, branch, tags import: strip, base, force, no-commit, exact, import-branch, message, logfile, date, user, similarity -incoming: force, newest-first, bundle, rev, branch, patch, git, limit, no-merges, style, template, ssh, remotecmd +incoming: force, newest-first, bundle, rev, branch, patch, git, limit, no-merges, stat, style, template, ssh, remotecmd locate: rev, print0, fullpath, include, exclude manifest: rev -outgoing: force, rev, newest-first, branch, patch, git, limit, no-merges, style, template, ssh, remotecmd +outgoing: force, rev, newest-first, branch, patch, git, limit, no-merges, stat, style, template, ssh, remotecmd parents: rev, style, template paths: recover: rename: after, force, include, exclude, dry-run resolve: all, list, mark, unmark, no-status, include, exclude revert: all, date, rev, no-backup, include, exclude, dry-run -rollback: +rollback: dry-run root: showconfig: untrusted -tag: force, local, rev, remove, message, date, user +tag: force, local, rev, remove, edit, message, date, user tags: tip: patch, git, style, template unbundle: update diff -r 0bb67503ad4b -r ac6fec2af8c8 tests/test-diff-color.out --- a/tests/test-diff-color.out Mon May 31 21:37:01 2010 +0200 +++ b/tests/test-diff-color.out Mon May 31 21:43:03 2010 +0200 @@ -31,7 +31,8 @@ old mode 100644 new mode 100755 1 hunks, 2 lines changed -examine changes to 'a'? [Ynsfdaq?] @@ -2,7 +2,7 @@ +examine changes to 'a'? [Ynsfdaq?] +@@ -2,7 +2,7 @@ c a a @@ -41,13 +42,15 @@ a c record this change to 'a'? [Ynsfdaq?] -rolling back last transaction + +rolling back to revision 0 (undo commit) % qrecord diff --git a/a b/a old mode 100644 new mode 100755 1 hunks, 2 lines changed -examine changes to 'a'? [Ynsfdaq?] @@ -2,7 +2,7 @@ +examine changes to 'a'? [Ynsfdaq?] +@@ -2,7 +2,7 @@ c a a @@ -57,3 +60,4 @@ a c record this change to 'a'? [Ynsfdaq?] + diff -r 0bb67503ad4b -r ac6fec2af8c8 tests/test-dispatch.out --- a/tests/test-dispatch.out Mon May 31 21:37:01 2010 +0200 +++ b/tests/test-dispatch.out Mon May 31 21:43:03 2010 +0200 @@ -17,6 +17,8 @@ "%d" dirname of file being printed, or '.' if in repository root "%p" root-relative path name of file being printed + Returns 0 on success. + options: -o --output print output to file with formatted name diff -r 0bb67503ad4b -r ac6fec2af8c8 tests/test-eolfilename.out --- a/tests/test-eolfilename.out Mon May 31 21:37:01 2010 +0200 +++ b/tests/test-eolfilename.out Mon May 31 21:43:03 2010 +0200 @@ -15,6 +15,6 @@ o % test issue2039 ? foo -bar +bar ? foo -bar.baz +bar.baz diff -r 0bb67503ad4b -r ac6fec2af8c8 tests/test-fetch.out --- a/tests/test-fetch.out Mon May 31 21:37:01 2010 +0200 +++ b/tests/test-fetch.out Mon May 31 21:43:03 2010 +0200 @@ -86,7 +86,6 @@ 1 files updated, 0 files merged, 0 files removed, 0 files unresolved marked working directory as branch b adding b -created new head % pull in change on foreign branch updating to branch default @@ -157,7 +156,6 @@ 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 -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 diff -r 0bb67503ad4b -r ac6fec2af8c8 tests/test-fncache.out --- a/tests/test-fncache.out Mon May 31 21:37:01 2010 +0200 +++ b/tests/test-fncache.out Mon May 31 21:43:03 2010 +0200 @@ -54,6 +54,7 @@ .hg/requires .hg/undo .hg/undo.branch +.hg/undo.desc .hg/undo.dirstate % non fncache repo adding tst.d/Foo @@ -70,4 +71,5 @@ .hg/store/data/tst.d.hg/_foo.i .hg/store/undo .hg/undo.branch +.hg/undo.desc .hg/undo.dirstate diff -r 0bb67503ad4b -r ac6fec2af8c8 tests/test-gendoc --- a/tests/test-gendoc Mon May 31 21:37:01 2010 +0200 +++ b/tests/test-gendoc Mon May 31 21:43:03 2010 +0200 @@ -1,7 +1,6 @@ #!/bin/sh -"$TESTDIR/hghave" rst2html || exit 80 -RST2HTML=`which rst2html 2> /dev/null || which rst2html.py` +"$TESTDIR/hghave" docutils || exit 80 HGENCODING=UTF-8 export HGENCODING @@ -14,8 +13,8 @@ echo "" >> gendoc-$LOCALE.txt LC_ALL=$LOCALE python $TESTDIR/../doc/gendoc.py >> gendoc-$LOCALE.txt || exit - # We run rst2html over the file without adding "--halt warning" to - # make it report all errors instead of stopping on the first one. - echo "checking for parse errors with rst2html" - $RST2HTML gendoc-$LOCALE.txt /dev/null + # We call runrst without adding "--halt warning" to make it report + # all errors instead of stopping on the first one. + echo "checking for parse errors" + python $TESTDIR/../doc/runrst html gendoc-$LOCALE.txt /dev/null done diff -r 0bb67503ad4b -r ac6fec2af8c8 tests/test-gendoc.out --- a/tests/test-gendoc.out Mon May 31 21:37:01 2010 +0200 +++ b/tests/test-gendoc.out Mon May 31 21:43:03 2010 +0200 @@ -1,33 +1,33 @@ % extracting documentation from C -checking for parse errors with rst2html +checking for parse errors % extracting documentation from da -checking for parse errors with rst2html +checking for parse errors % extracting documentation from de -checking for parse errors with rst2html +checking for parse errors % extracting documentation from el -checking for parse errors with rst2html +checking for parse errors % extracting documentation from fr -checking for parse errors with rst2html +checking for parse errors % extracting documentation from it -checking for parse errors with rst2html +checking for parse errors % extracting documentation from ja -checking for parse errors with rst2html +checking for parse errors % extracting documentation from pt_BR -checking for parse errors with rst2html +checking for parse errors % extracting documentation from sv -checking for parse errors with rst2html +checking for parse errors % extracting documentation from zh_CN -checking for parse errors with rst2html +checking for parse errors % extracting documentation from zh_TW -checking for parse errors with rst2html +checking for parse errors diff -r 0bb67503ad4b -r ac6fec2af8c8 tests/test-globalopts.out --- a/tests/test-globalopts.out Mon May 31 21:37:01 2010 +0200 +++ b/tests/test-globalopts.out Mon May 31 21:43:03 2010 +0200 @@ -186,9 +186,9 @@ rename rename files; equivalent of copy + remove resolve various operations to help finish a merge revert restore individual files or directories to an earlier state - rollback roll back the last transaction + rollback roll back the last transaction (dangerous) root print the root (top) of the current working directory - serve export the repository via HTTP + serve start stand-alone webserver showconfig show combined config settings from all hgrc files status show changed files in the working directory summary summarize working directory state @@ -196,7 +196,7 @@ tags list repository tags tip show the tip revision unbundle apply one or more changegroup files - update update working directory + update update working directory (or switch revisions) verify verify the integrity of the repository version output version and copyright information @@ -212,6 +212,7 @@ templating Template Usage urls URL Paths extensions Using additional features + hgweb Configuring hgweb use "hg -v help" to show aliases and global options Mercurial Distributed SCM @@ -255,9 +256,9 @@ rename rename files; equivalent of copy + remove resolve various operations to help finish a merge revert restore individual files or directories to an earlier state - rollback roll back the last transaction + rollback roll back the last transaction (dangerous) root print the root (top) of the current working directory - serve export the repository via HTTP + serve start stand-alone webserver showconfig show combined config settings from all hgrc files status show changed files in the working directory summary summarize working directory state @@ -265,7 +266,7 @@ tags list repository tags tip show the tip revision unbundle apply one or more changegroup files - update update working directory + update update working directory (or switch revisions) verify verify the integrity of the repository version output version and copyright information @@ -281,6 +282,7 @@ templating Template Usage urls URL Paths extensions Using additional features + hgweb Configuring hgweb use "hg -v help" to show aliases and global options %% not tested: --debugger diff -r 0bb67503ad4b -r ac6fec2af8c8 tests/test-grep --- a/tests/test-grep Mon May 31 21:37:01 2010 +0200 +++ b/tests/test-grep Mon May 31 21:43:03 2010 +0200 @@ -21,6 +21,9 @@ hg grep '**test**' echo % simple hg grep port port +echo % simple with color +hg --config extensions.color= grep --config color.mode=ansi \ + --color=always port port echo % all hg grep --traceback --all -nu port port echo % other diff -r 0bb67503ad4b -r ac6fec2af8c8 tests/test-grep.out --- a/tests/test-grep.out Mon May 31 21:37:01 2010 +0200 +++ b/tests/test-grep.out Mon May 31 21:43:03 2010 +0200 @@ -4,6 +4,10 @@ port:4:export port:4:vaportight port:4:import/export +% simple with color +port:4:export +port:4:vaportight +port:4:import/export % all port:4:4:-:spam:import/export port:3:4:+:eggs:import/export diff -r 0bb67503ad4b -r ac6fec2af8c8 tests/test-help.out --- a/tests/test-help.out Mon May 31 21:37:01 2010 +0200 +++ b/tests/test-help.out Mon May 31 21:43:03 2010 +0200 @@ -15,10 +15,10 @@ 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 + serve start stand-alone webserver status show changed files in the working directory summary summarize working directory state - update update working directory + update update working directory (or switch revisions) use "hg help" for the full list of commands or "hg -v" for details add add the specified files on the next commit @@ -34,10 +34,10 @@ 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 + serve start stand-alone webserver status show changed files in the working directory summary summarize working directory state - update update working directory + update update working directory (or switch revisions) Mercurial Distributed SCM list of commands: @@ -79,9 +79,9 @@ rename rename files; equivalent of copy + remove resolve various operations to help finish a merge revert restore individual files or directories to an earlier state - rollback roll back the last transaction + rollback roll back the last transaction (dangerous) root print the root (top) of the current working directory - serve export the repository via HTTP + serve start stand-alone webserver showconfig show combined config settings from all hgrc files status show changed files in the working directory summary summarize working directory state @@ -89,7 +89,7 @@ tags list repository tags tip show the tip revision unbundle apply one or more changegroup files - update update working directory + update update working directory (or switch revisions) verify verify the integrity of the repository version output version and copyright information @@ -105,6 +105,7 @@ templating Template Usage urls URL Paths extensions Using additional features + hgweb Configuring hgweb use "hg -v help" to show aliases and global options add add the specified files on the next commit @@ -144,9 +145,9 @@ rename rename files; equivalent of copy + remove resolve various operations to help finish a merge revert restore individual files or directories to an earlier state - rollback roll back the last transaction + rollback roll back the last transaction (dangerous) root print the root (top) of the current working directory - serve export the repository via HTTP + serve start stand-alone webserver showconfig show combined config settings from all hgrc files status show changed files in the working directory summary summarize working directory state @@ -154,7 +155,7 @@ tags list repository tags tip show the tip revision unbundle apply one or more changegroup files - update update working directory + update update working directory (or switch revisions) verify verify the integrity of the repository version output version and copyright information @@ -170,6 +171,7 @@ templating Template Usage urls URL Paths extensions Using additional features + hgweb Configuring hgweb %% test short command list with verbose option Mercurial Distributed SCM (version xxx) @@ -206,13 +208,13 @@ remove, rm: remove the specified files on the next commit serve: - export the repository via HTTP + start stand-alone webserver status, st: show changed files in the working directory summary, sum: summarize working directory state update, up, checkout, co: - update working directory + update working directory (or switch revisions) global options: -R --repository repository root directory or name of overlay bundle file @@ -239,7 +241,7 @@ Schedule files to be version controlled and added to the repository. The files will be added to the repository at the next commit. To undo an - add before that, see hg forget. + add before that, see "hg forget". If no names are given, add all files to the repository. @@ -260,7 +262,7 @@ Schedule files to be version controlled and added to the repository. The files will be added to the repository at the next commit. To undo an - add before that, see hg forget. + add before that, see "hg forget". If no names are given, add all files to the repository. @@ -312,7 +314,7 @@ Schedule files to be version controlled and added to the repository. The files will be added to the repository at the next commit. To undo an - add before that, see hg forget. + add before that, see "hg forget". If no names are given, add all files to the repository. @@ -333,7 +335,7 @@ Schedule files to be version controlled and added to the repository. The files will be added to the repository at the next commit. To undo an - add before that, see hg forget. + add before that, see "hg forget". If no names are given, add all files to the repository. @@ -365,6 +367,8 @@ manifest, and tracked files, as well as the integrity of their crosslinks and indices. + Returns 0 on success, 1 if errors are encountered. + use "hg -v help verify" to show global options hg diff [OPTION]... ([-c REV] | [-r REV1 [-r REV2]]) [FILE]... @@ -391,7 +395,9 @@ with undesirable results. Use the -g/--git option to generate diffs in the git extended diff format. - For more information, read 'hg help diffs'. + For more information, read "hg help diffs". + + Returns 0 on success. options: @@ -447,6 +453,8 @@ I = ignored = origin of the previous file listed as A (added) + Returns 0 on success. + options: -A --all show status of all files @@ -487,10 +495,10 @@ 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 + serve start stand-alone webserver status show changed files in the working directory summary summarize working directory state - update update working directory + update update working directory (or switch revisions) use "hg help" for the full list of commands or "hg -v" for details hg: unknown command 'skjdfks' @@ -511,10 +519,10 @@ 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 + serve start stand-alone webserver status show changed files in the working directory summary summarize working directory state - update update working directory + update update working directory (or switch revisions) use "hg help" for the full list of commands or "hg -v" for details %% test command with no help text @@ -565,9 +573,9 @@ rename rename files; equivalent of copy + remove resolve various operations to help finish a merge revert restore individual files or directories to an earlier state - rollback roll back the last transaction + rollback roll back the last transaction (dangerous) root print the root (top) of the current working directory - serve export the repository via HTTP + serve start stand-alone webserver showconfig show combined config settings from all hgrc files status show changed files in the working directory summary summarize working directory state @@ -575,7 +583,7 @@ tags list repository tags tip show the tip revision unbundle apply one or more changegroup files - update update working directory + update update working directory (or switch revisions) verify verify the integrity of the repository version output version and copyright information @@ -595,6 +603,7 @@ templating Template Usage urls URL Paths extensions Using additional features + hgweb Configuring hgweb use "hg -v help" to show aliases and global options %% test list of commands with command with no help text diff -r 0bb67503ad4b -r ac6fec2af8c8 tests/test-hgrc --- a/tests/test-hgrc Mon May 31 21:37:01 2010 +0200 +++ b/tests/test-hgrc Mon May 31 21:43:03 2010 +0200 @@ -23,8 +23,33 @@ > $HGRCPATH hg showconfig foo -echo '%include /no-such-file' > $HGRCPATH +FAKEPATH=/path/to/nowhere +export FAKEPATH +echo '%include $FAKEPATH/no-such-file' > $HGRCPATH hg version 2>&1 | sed -e "s|$HGRCPATH|\$HGRCPATH|" +unset FAKEPATH + +echo "% username expansion" +olduser=$HGUSER +unset HGUSER + +FAKEUSER='John Doe' +export FAKEUSER +echo '[ui]' > $HGRCPATH +echo 'username = $FAKEUSER' >> $HGRCPATH + +hg init usertest +cd usertest +touch bar +hg commit --addremove --quiet -m "added bar" +hg log --template "{author}\n" +cd .. + +hg showconfig | sed -e "s:$p:...:" + +unset FAKEUSER +HGUSER=$olduser +export HGUSER # HGPLAIN cd .. diff -r 0bb67503ad4b -r ac6fec2af8c8 tests/test-hgrc.out --- a/tests/test-hgrc.out Mon May 31 21:37:01 2010 +0200 +++ b/tests/test-hgrc.out Mon May 31 21:43:03 2010 +0200 @@ -9,8 +9,12 @@ hg: config error at $HGRCPATH:2: ' x = y' foo.bar=a\nb\nc\nde\nfg foo.baz=bif cb -hg: config error at $HGRCPATH:1: cannot include /no-such-file (No such file or directory) +hg: config error at $HGRCPATH:1: cannot include /path/to/nowhere/no-such-file (No such file or directory) +% username expansion +John Doe +ui.username=$FAKEUSER % customized hgrc +read config from: .../.hgrc .../.hgrc:13: alias.log=log -g .../.hgrc:11: defaults.identify=-n .../.hgrc:2: ui.debug=true @@ -22,6 +26,7 @@ .../.hgrc:8: ui.style=~/.hgstyle .../.hgrc:9: ui.logtemplate={node} % plain hgrc +read config from: .../.hgrc none: ui.traceback=True none: ui.verbose=False none: ui.debug=True diff -r 0bb67503ad4b -r ac6fec2af8c8 tests/test-hook.out --- a/tests/test-hook.out Mon May 31 21:37:01 2010 +0200 +++ b/tests/test-hook.out Mon May 31 21:43:03 2010 +0200 @@ -101,7 +101,7 @@ adding file changes added 1 changesets with 1 changes to 1 files (run 'hg update' to get a working copy) -rolling back last transaction +rolling back to revision 3 (undo pull) preoutgoing hook: HG_SOURCE=pull preoutgoing.forbid hook: HG_SOURCE=pull pulling from ../a diff -r 0bb67503ad4b -r ac6fec2af8c8 tests/test-hup.out --- a/tests/test-hup.out Mon May 31 21:37:01 2010 +0200 +++ b/tests/test-hup.out Mon May 31 21:43:03 2010 +0200 @@ -4,4 +4,4 @@ transaction abort! rollback completed killed! -.hg/00changelog.i .hg/journal.branch .hg/journal.dirstate .hg/requires .hg/store .hg/store/00changelog.i .hg/store/00changelog.i.a +.hg/00changelog.i .hg/journal.branch .hg/journal.desc .hg/journal.dirstate .hg/requires .hg/store .hg/store/00changelog.i .hg/store/00changelog.i.a diff -r 0bb67503ad4b -r ac6fec2af8c8 tests/test-import.out --- a/tests/test-import.out Mon May 31 21:37:01 2010 +0200 +++ b/tests/test-import.out Mon May 31 21:43:03 2010 +0200 @@ -182,7 +182,7 @@ applying ../patch1 applying ../patch2 applied 1d4bd90af0e4 -rolling back last transaction +rolling back to revision 1 (undo commit) parent: 1 % hg import in a subdirectory requesting all changes diff -r 0bb67503ad4b -r ac6fec2af8c8 tests/test-inherit-mode.out --- a/tests/test-inherit-mode.out Mon May 31 21:37:01 2010 +0200 +++ b/tests/test-inherit-mode.out Mon May 31 21:43:03 2010 +0200 @@ -26,6 +26,7 @@ 00660 ./.hg/store/fncache 00660 ./.hg/store/undo 00660 ./.hg/undo.branch +00660 ./.hg/undo.desc 00660 ./.hg/undo.dirstate 00700 ./dir/ 00600 ./dir/bar @@ -54,4 +55,5 @@ 00660 ../push/.hg/store/fncache 00660 ../push/.hg/store/undo 00660 ../push/.hg/undo.branch +00660 ../push/.hg/undo.desc 00660 ../push/.hg/undo.dirstate diff -r 0bb67503ad4b -r ac6fec2af8c8 tests/test-issue619.out diff -r 0bb67503ad4b -r ac6fec2af8c8 tests/test-keyword --- a/tests/test-keyword Mon May 31 21:37:01 2010 +0200 +++ b/tests/test-keyword Mon May 31 21:43:03 2010 +0200 @@ -5,7 +5,10 @@ keyword = mq = notify = +record = transplant = +[ui] +interactive = true EOF # demo before [keyword] files are set up @@ -21,7 +24,7 @@ cat <> $HGRCPATH [keyword] -* = +** = b = ignore [hooks] commit= @@ -139,12 +142,29 @@ echo % compare changenodes in a c cat a c -echo % qinit -c -hg qinit -c +echo % record +python -c \ +'l=open("a").readlines();l.insert(1,"foo\n");l.append("bar\n");open("a","w").writelines(l);' +hg record -d '1 10' -m rectest< @@ -158,6 +191,12 @@ % cat $Id: c,v 40a904bbbe4c 1970/01/01 00:00:01 user $ tests for different changenodes +% hg cat +$Id: c,v 40a904bbbe4c 1970/01/01 00:00:01 user $ +tests for different changenodes +% keyword should not be expanded in filelog +$Id$ +tests for different changenodes % qpop and move on popping mqtest.diff patch queue now empty @@ -198,7 +237,7 @@ +do not process $Id: +xxx $ % rollback -rolling back last transaction +rolling back to revision 1 (undo commit) % status A c % update -C @@ -206,9 +245,9 @@ % custom keyword expansion % try with kwdemo [extensions] -keyword = +keyword = [keyword] -* = +** = b = ignore demo.txt = [keywordmaps] @@ -266,7 +305,7 @@ % status ? c % rollback -rolling back last transaction +rolling back to revision 2 (undo commit) % status R a ? c @@ -308,7 +347,7 @@ ignore $Id$ % rollback -rolling back last transaction +rolling back to revision 2 (undo commit) % clean update 1 files updated, 0 files merged, 0 files removed, 0 files unresolved % kwexpand/kwshrink on selected files @@ -423,7 +462,6 @@ % test restricted mode with transplant -b 1 files updated, 0 files merged, 0 files removed, 0 files unresolved marked working directory as branch foo -created new head 2 files updated, 0 files merged, 0 files removed, 0 files unresolved applying 4aa30d025d50 4aa30d025d50 transplanted to 5a4da427c162 diff -r 0bb67503ad4b -r ac6fec2af8c8 tests/test-log --- a/tests/test-log Mon May 31 21:37:01 2010 +0200 +++ b/tests/test-log Mon May 31 21:43:03 2010 +0200 @@ -118,6 +118,13 @@ echo '% log -d -1' hg log -d -1 +echo '% log -p -l2 --color=always' +hg --config extensions.color= --config color.mode=ansi \ + log -p -l2 --color=always + +echo '% log -r tip --stat' +hg log -r tip --stat + cd .. hg init usertest @@ -132,4 +139,46 @@ hg log -u "user1" -u "user2" hg log -u "user3" +cd .. + +hg init branches +cd branches + +echo a > a +hg ci -A -m "commit on default" +hg branch test +echo b > b +hg ci -A -m "commit on test" + +hg up default +echo c > c +hg ci -A -m "commit on default" +hg up test +echo c > c +hg ci -A -m "commit on test" + +echo '% log -b default' +hg log -b default + +echo '% log -b test' +hg log -b test + +echo '% log -b dummy' +hg log -b dummy + +echo '% log -b .' +hg log -b . + +echo '% log -b default -b test' +hg log -b default -b test + +echo '% log -b default -b .' +hg log -b default -b . + +echo '% log -b . -b test' +hg log -b . -b test + +echo '% log -b 2' +hg log -b 2 + exit 0 diff -r 0bb67503ad4b -r ac6fec2af8c8 tests/test-log.out --- a/tests/test-log.out Mon May 31 21:37:01 2010 +0200 +++ b/tests/test-log.out Mon May 31 21:43:03 2010 +0200 @@ -279,6 +279,43 @@ summary: r1 % log -d -1 +% log -p -l2 --color=always +changeset: 6:2404bbcab562 +tag: tip +user: test +date: Thu Jan 01 00:00:01 1970 +0000 +summary: b1.1 + +diff -r 302e9dd6890d -r 2404bbcab562 b1 +--- a/b1 Thu Jan 01 00:00:01 1970 +0000 ++++ b/b1 Thu Jan 01 00:00:01 1970 +0000 +@@ -1,1 +1,2 @@ + b1 ++postm + +changeset: 5:302e9dd6890d +parent: 3:e62f78d544b4 +parent: 4:ddb82e70d1a1 +user: test +date: Thu Jan 01 00:00:01 1970 +0000 +summary: m12 + +diff -r e62f78d544b4 -r 302e9dd6890d b2 +--- /dev/null Thu Jan 01 00:00:00 1970 +0000 ++++ b/b2 Thu Jan 01 00:00:01 1970 +0000 +@@ -0,0 +1,1 @@ ++b2 + +% log -r tip --stat +changeset: 6:2404bbcab562 +tag: tip +user: test +date: Thu Jan 01 00:00:01 1970 +0000 +summary: b1.1 + + b1 | 1 + + 1 files changed, 1 insertions(+), 0 deletions(-) + adding a adding b changeset: 0:29a4c94f1924 @@ -297,3 +334,133 @@ date: Thu Jan 01 00:00:00 1970 +0000 summary: a +adding a +marked working directory as branch test +adding b +0 files updated, 0 files merged, 1 files removed, 0 files unresolved +adding c +1 files updated, 0 files merged, 1 files removed, 0 files unresolved +adding c +% log -b default +changeset: 2:c3a4f03cc9a7 +parent: 0:24427303d56f +user: test +date: Thu Jan 01 00:00:00 1970 +0000 +summary: commit on default + +changeset: 0:24427303d56f +user: test +date: Thu Jan 01 00:00:00 1970 +0000 +summary: commit on default + +% log -b test +changeset: 3:f5d8de11c2e2 +branch: test +tag: tip +parent: 1:d32277701ccb +user: test +date: Thu Jan 01 00:00:00 1970 +0000 +summary: commit on test + +changeset: 1:d32277701ccb +branch: test +user: test +date: Thu Jan 01 00:00:00 1970 +0000 +summary: commit on test + +% log -b dummy +abort: unknown revision 'dummy'! +% log -b . +changeset: 3:f5d8de11c2e2 +branch: test +tag: tip +parent: 1:d32277701ccb +user: test +date: Thu Jan 01 00:00:00 1970 +0000 +summary: commit on test + +changeset: 1:d32277701ccb +branch: test +user: test +date: Thu Jan 01 00:00:00 1970 +0000 +summary: commit on test + +% log -b default -b test +changeset: 3:f5d8de11c2e2 +branch: test +tag: tip +parent: 1:d32277701ccb +user: test +date: Thu Jan 01 00:00:00 1970 +0000 +summary: commit on test + +changeset: 2:c3a4f03cc9a7 +parent: 0:24427303d56f +user: test +date: Thu Jan 01 00:00:00 1970 +0000 +summary: commit on default + +changeset: 1:d32277701ccb +branch: test +user: test +date: Thu Jan 01 00:00:00 1970 +0000 +summary: commit on test + +changeset: 0:24427303d56f +user: test +date: Thu Jan 01 00:00:00 1970 +0000 +summary: commit on default + +% log -b default -b . +changeset: 3:f5d8de11c2e2 +branch: test +tag: tip +parent: 1:d32277701ccb +user: test +date: Thu Jan 01 00:00:00 1970 +0000 +summary: commit on test + +changeset: 2:c3a4f03cc9a7 +parent: 0:24427303d56f +user: test +date: Thu Jan 01 00:00:00 1970 +0000 +summary: commit on default + +changeset: 1:d32277701ccb +branch: test +user: test +date: Thu Jan 01 00:00:00 1970 +0000 +summary: commit on test + +changeset: 0:24427303d56f +user: test +date: Thu Jan 01 00:00:00 1970 +0000 +summary: commit on default + +% log -b . -b test +changeset: 3:f5d8de11c2e2 +branch: test +tag: tip +parent: 1:d32277701ccb +user: test +date: Thu Jan 01 00:00:00 1970 +0000 +summary: commit on test + +changeset: 1:d32277701ccb +branch: test +user: test +date: Thu Jan 01 00:00:00 1970 +0000 +summary: commit on test + +% log -b 2 +changeset: 2:c3a4f03cc9a7 +parent: 0:24427303d56f +user: test +date: Thu Jan 01 00:00:00 1970 +0000 +summary: commit on default + +changeset: 0:24427303d56f +user: test +date: Thu Jan 01 00:00:00 1970 +0000 +summary: commit on default + diff -r 0bb67503ad4b -r ac6fec2af8c8 tests/test-merge-default.out --- a/tests/test-merge-default.out Mon May 31 21:37:01 2010 +0200 +++ b/tests/test-merge-default.out Mon May 31 21:43:03 2010 +0200 @@ -30,7 +30,6 @@ abort: there is nothing to merge - use "hg update" instead 1 files updated, 0 files merged, 0 files removed, 0 files unresolved marked working directory as branch foobranch -created new head % should fail because merge with other branch abort: branch 'foobranch' has one head - please merge with an explicit rev (run 'hg heads' to see all heads) diff -r 0bb67503ad4b -r ac6fec2af8c8 tests/test-merge3 --- a/tests/test-merge3 Mon May 31 21:37:01 2010 +0200 +++ b/tests/test-merge3 Mon May 31 21:43:03 2010 +0200 @@ -8,3 +8,4 @@ hg add b rm b hg commit -A -m"comment #1" -d "1000000 0" +exit 0 diff -r 0bb67503ad4b -r ac6fec2af8c8 tests/test-minirst.py --- a/tests/test-minirst.py Mon May 31 21:37:01 2010 +0200 +++ b/tests/test-minirst.py Mon May 31 21:43:03 2010 +0200 @@ -178,3 +178,22 @@ debugformat('containers (debug)', containers, 60, keep=['debug']) debugformat('containers (verbose debug)', containers, 60, keep=['verbose', 'debug']) + +roles = """Please see :hg:`add`.""" +debugformat('roles', roles, 60) + + +sections = """ +Title +===== + +Section +------- + +Subsection +'''''''''' + +Markup: ``foo`` and :hg:`help` +------------------------------ +""" +debugformat('sections', sections, 20) diff -r 0bb67503ad4b -r ac6fec2af8c8 tests/test-minirst.py.out --- a/tests/test-minirst.py.out Mon May 31 21:37:01 2010 +0200 +++ b/tests/test-minirst.py.out Mon May 31 21:43:03 2010 +0200 @@ -25,10 +25,12 @@ ---------------------------------------------------------------------- A Term Definition. The indented lines make up the definition. + Another Term Another definition. The final line in the definition determines the indentation, so this will be indented with four spaces. + A Nested/Indented Term Definition. ---------------------------------------------------------------------- @@ -39,6 +41,7 @@ Definition. The indented lines make up the definition. + Another Term Another definition. The final line in the @@ -46,6 +49,7 @@ indentation, so this will be indented with four spaces. + A Nested/Indented Term Definition. ---------------------------------------------------------------------- @@ -294,3 +298,23 @@ [] ---------------------------------------------------------------------- +roles formatted to fit within 60 characters: +---------------------------------------------------------------------- +Please see "hg add". +---------------------------------------------------------------------- + +sections formatted to fit within 20 characters: +---------------------------------------------------------------------- +Title +===== + +Section +------- + +Subsection +'''''''''' + +Markup: "foo" and "hg help" +--------------------------- +---------------------------------------------------------------------- + diff -r 0bb67503ad4b -r ac6fec2af8c8 tests/test-mq --- a/tests/test-mq Mon May 31 21:37:01 2010 +0200 +++ b/tests/test-mq Mon May 31 21:43:03 2010 +0200 @@ -1,5 +1,7 @@ #!/bin/sh +. $TESTDIR/helpers.sh + checkundo() { if [ -f .hg/store/undo ]; then @@ -170,7 +172,9 @@ COLUMNS=20 hg qseries --config ui.interactive=true -vs hg qpop hg qseries -vs +hg sum | grep mq hg qpush +hg sum | grep mq echo % qapplied hg qapplied @@ -219,6 +223,19 @@ hg qpop test2.patch-2 hg qpush test1b.patch+1 +echo % qpush --move +hg qpop -a +hg qpush --move test2.patch # move to front +hg qpush --move test1b.patch +hg qpush --move test.patch # noop move +hg qseries -v +hg qpop -a +hg qpush --move test.patch # cleaning up +hg qpush --move test1b.patch +hg qpush --move bogus # nonexistent patch +hg qpush --move test.patch # already applied +hg qpush + echo % pop, qapplied, qunapplied hg qseries -v echo % qapplied -1 test.patch @@ -303,16 +320,16 @@ cd ../../b echo x>x hg ci -Ama -hg strip tip 2>&1 | sed 's/\(saving bundle to \).*/\1/' +hg strip tip | hidebackup 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/' +hg strip tip | hidebackup echo % --force strip with local changes -hg strip -f tip 2>&1 | sed 's/\(saving bundle to \).*/\1/' +hg strip -f tip | hidebackup echo '% cd b; hg qrefresh' hg init refresh @@ -487,7 +504,7 @@ HGMERGE=true hg merge hg ci -m merge hg log -hg strip 1 2>&1 | sed 's/\(saving bundle to \).*/\1/' +hg strip 1 | hidebackup checkundo strip hg log cd .. diff -r 0bb67503ad4b -r ac6fec2af8c8 tests/test-mq-guards --- a/tests/test-mq-guards Mon May 31 21:37:01 2010 +0200 +++ b/tests/test-mq-guards Mon May 31 21:43:03 2010 +0200 @@ -99,6 +99,9 @@ hg qselect 1 2 3 echo % list patches and guards hg qguard -l +echo % list patches and guards with color +hg --config extensions.color= qguard --config color.mode=ansi \ + -l --color=always echo % list series hg qseries -v echo % list guards @@ -125,6 +128,8 @@ echo % should show new.patch and b.patch as Guarded, c.patch as Applied echo % and d.patch as Unapplied hg qseries -v +echo % qseries again, but with color +hg --config extensions.color= qseries -v --color=always hg qguard d.patch +2 echo % new.patch, b.patch: Guarded. c.patch: Applied. d.patch: Guarded. @@ -159,3 +164,5 @@ echo the guards file was not ignored in the past hg qdelete -k b.patch hg qseries -m +echo % hg qseries -m with color +hg --config extensions.color= qseries -m --color=always diff -r 0bb67503ad4b -r ac6fec2af8c8 tests/test-mq-guards.out --- a/tests/test-mq-guards.out Mon May 31 21:37:01 2010 +0200 +++ b/tests/test-mq-guards.out Mon May 31 21:43:03 2010 +0200 @@ -84,6 +84,10 @@ a.patch: +1 +2 -3 b.patch: +2 c.patch: unguarded +% list patches and guards with color +a.patch: +1 +2 -3 +b.patch: +2 +c.patch: unguarded % list series 0 G a.patch 1 U b.patch @@ -126,6 +130,11 @@ 1 G b.patch 2 A c.patch 3 U d.patch +% qseries again, but with color +0 G new.patch +1 G b.patch +2 A c.patch +3 U d.patch % new.patch, b.patch: Guarded. c.patch: Applied. d.patch: Guarded. 0 G new.patch 1 G b.patch @@ -206,3 +215,5 @@ % hg qseries -m: only b.patch should be shown the guards file was not ignored in the past b.patch +% hg qseries -m with color +b.patch diff -r 0bb67503ad4b -r ac6fec2af8c8 tests/test-mq-qdiff --- a/tests/test-mq-qdiff Mon May 31 21:37:01 2010 +0200 +++ b/tests/test-mq-qdiff Mon May 31 21:43:03 2010 +0200 @@ -68,3 +68,5 @@ echo a >> lines hg qdiff +echo % qdiff --stat +hg qdiff --stat diff -r 0bb67503ad4b -r ac6fec2af8c8 tests/test-mq-qdiff.out --- a/tests/test-mq-qdiff.out Mon May 31 21:37:01 2010 +0200 +++ b/tests/test-mq-qdiff.out Mon May 31 21:43:03 2010 +0200 @@ -122,3 +122,6 @@ 8 9 +a +% qdiff --stat + lines | 7 +++++-- + 1 files changed, 5 insertions(+), 2 deletions(-) diff -r 0bb67503ad4b -r ac6fec2af8c8 tests/test-mq-qimport.out --- a/tests/test-mq-qimport.out Mon May 31 21:37:01 2010 +0200 +++ b/tests/test-mq-qimport.out Mon May 31 21:43:03 2010 +0200 @@ -6,10 +6,10 @@ now at: email % hg tip -v changeset: 0:1a706973a7d8 +tag: email +tag: qbase tag: qtip tag: tip -tag: email -tag: qbase user: Username in patch date: Thu Jan 01 00:00:00 1970 +0000 files: x diff -r 0bb67503ad4b -r ac6fec2af8c8 tests/test-mq-qqueue --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tests/test-mq-qqueue Mon May 31 21:43:03 2010 +0200 @@ -0,0 +1,47 @@ +#!/bin/sh + +echo "[extensions]" >> $HGRCPATH +echo "mq=" >> $HGRCPATH + +hg init foo +cd foo +echo a > a +hg ci -qAm a + +echo %% default queue +hg qqueue + +echo b > a +hg qnew -fgDU somestuff + +echo %% applied patches in default queue +hg qap + +echo %% try to change patch \(create succeeds, switch fails\) +hg qqueue foo --create +hg qqueue + +echo %% empty default queue +hg qpop + +echo %% switch queue +hg qqueue foo +hg qqueue + +echo %% unapplied patches +hg qun +echo c > a +hg qnew -fgDU otherstuff + +echo %% fail switching back +hg qqueue patches + +echo %% fail deleting current +hg qqueue foo --delete + +echo %% switch back and delete foo +hg qpop -a +hg qqueue patches +hg qqueue foo --delete +hg qqueue +cd .. diff -r 0bb67503ad4b -r ac6fec2af8c8 tests/test-mq-qqueue.out --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tests/test-mq-qqueue.out Mon May 31 21:43:03 2010 +0200 @@ -0,0 +1,23 @@ +%% default queue +patches (active) +%% applied patches in default queue +somestuff +%% try to change patch (create succeeds, switch fails) +abort: patches applied - cannot set new queue active +foo +patches (active) +%% empty default queue +popping somestuff +patch queue now empty +%% switch queue +foo (active) +patches +%% unapplied patches +%% fail switching back +abort: patches applied - cannot set new queue active +%% fail deleting current +abort: cannot delete currently active queue +%% switch back and delete foo +popping otherstuff +patch queue now empty +patches (active) diff -r 0bb67503ad4b -r ac6fec2af8c8 tests/test-mq-qrefresh.out --- a/tests/test-mq-qrefresh.out Mon May 31 21:37:01 2010 +0200 +++ b/tests/test-mq-qrefresh.out Mon May 31 21:43:03 2010 +0200 @@ -271,12 +271,6 @@ +a % issue2025: qrefresh does not honor filtering options when tip != qtip % refresh with tip != qtip -saving bundle... -adding branch -adding changesets -adding manifests -adding file changes -added 1 changesets with 1 changes to 1 files % status after refresh M a % b after refresh diff -r 0bb67503ad4b -r ac6fec2af8c8 tests/test-mq-safety.out --- a/tests/test-mq-safety.out Mon May 31 21:37:01 2010 +0200 +++ b/tests/test-mq-safety.out Mon May 31 21:43:03 2010 +0200 @@ -19,7 +19,6 @@ 0 files updated, 0 files merged, 1 files removed, 0 files unresolved marked working directory as branch b adding c -created new head 1 files updated, 0 files merged, 0 files removed, 0 files unresolved (branch merge, don't forget to commit) 0 files updated, 0 files merged, 1 files removed, 0 files unresolved diff -r 0bb67503ad4b -r ac6fec2af8c8 tests/test-mq-strip --- a/tests/test-mq-strip Mon May 31 21:37:01 2010 +0200 +++ b/tests/test-mq-strip Mon May 31 21:43:03 2010 +0200 @@ -1,5 +1,7 @@ #!/bin/sh +. $TESTDIR/helpers.sh + echo "[extensions]" >> $HGRCPATH echo "mq=" >> $HGRCPATH @@ -7,7 +9,7 @@ hg up -C $1 echo % before update $1, strip $2 hg parents - hg strip $2 2>&1 | sed 's/\(saving bundle to \).*/\1/' + hg strip $2 | hidebackup echo % after update $1, strip $2 hg parents hg unbundle -q .hg/strip-backup/* @@ -48,6 +50,6 @@ hg merge 4 echo % before strip of merge parent hg parents -hg strip 4 2>&1 | sed 's/\(saving bundle to \).*/\1/' +hg strip 4 2>&1 | hidebackup echo % after strip of merge parent hg parents diff -r 0bb67503ad4b -r ac6fec2af8c8 tests/test-mq-strip.out --- a/tests/test-mq-strip.out Mon May 31 21:37:01 2010 +0200 +++ b/tests/test-mq-strip.out Mon May 31 21:43:03 2010 +0200 @@ -37,7 +37,7 @@ summary: e 1 files updated, 0 files merged, 0 files removed, 0 files unresolved -saving bundle to +saved backup bundle to % after update 4, strip 4 changeset: 3:65bd5f99a4a3 tag: tip @@ -55,7 +55,7 @@ summary: e 1 files updated, 0 files merged, 0 files removed, 0 files unresolved -saving bundle to +saved backup bundle to % after update 4, strip 3 changeset: 1:ef3a871183d7 user: test @@ -69,7 +69,7 @@ date: Thu Jan 01 00:00:00 1970 +0000 summary: b -saving bundle to +saved backup bundle to % after update 1, strip 4 changeset: 1:ef3a871183d7 user: test @@ -84,13 +84,7 @@ date: Thu Jan 01 00:00:00 1970 +0000 summary: e -saving bundle to -saving bundle to -adding branch -adding changesets -adding manifests -adding file changes -added 2 changesets with 2 changes to 1 files +saved backup bundle to % after update 4, strip 2 changeset: 3:443431ffac4f tag: tip @@ -108,7 +102,7 @@ summary: c 1 files updated, 0 files merged, 0 files removed, 0 files unresolved -saving bundle to +saved backup bundle to % after update 4, strip 1 changeset: 0:9ab35a2d17cb tag: tip @@ -118,7 +112,7 @@ 0 files updated, 0 files merged, 1 files removed, 0 files unresolved % before update null, strip 4 -saving bundle to +saved backup bundle to % after update null, strip 4 changeset: 4:264128213d29 tag: tip @@ -164,7 +158,7 @@ summary: c 1 files updated, 0 files merged, 0 files removed, 0 files unresolved -saving bundle to +saved backup bundle to % after strip of merge parent changeset: 1:ef3a871183d7 user: test diff -r 0bb67503ad4b -r ac6fec2af8c8 tests/test-mq.out --- a/tests/test-mq.out Mon May 31 21:37:01 2010 +0200 +++ b/tests/test-mq.out Mon May 31 21:43:03 2010 +0200 @@ -32,6 +32,9 @@ will override the [diff] section and always generate git or regular patches, possibly losing data in the second case. +You will by default be managing a patch queue named "patches". You can create +other, independent patch queues with the "hg qqueue" command. + list of commands: qapplied print the patches already applied @@ -49,6 +52,7 @@ qpop pop the current patch off the stack qprev print the name of the previous patch qpush push the next patch onto the stack + qqueue manage multiple patch queues qrefresh update the current patch qrename rename a patch qselect set or print guarded patches to push @@ -151,8 +155,10 @@ now at: test.patch 0 A test.patch: foo bar 1 U test2.patch: +mq: 1 applied, 1 unapplied applying test2.patch now at: test2.patch +mq: 2 applied % qapplied test.patch test2.patch @@ -193,6 +199,32 @@ applying test1b.patch applying test2.patch now at: test2.patch +% qpush --move +popping test2.patch +popping test1b.patch +popping test.patch +patch queue now empty +applying test2.patch +now at: test2.patch +applying test1b.patch +now at: test1b.patch +applying test.patch +now at: test.patch +0 A test2.patch +1 A test1b.patch +2 A test.patch +popping test.patch +popping test1b.patch +popping test2.patch +patch queue now empty +applying test.patch +now at: test.patch +applying test1b.patch +now at: test1b.patch +abort: patch bogus not in series +abort: cannot push to a previous patch: test.patch +applying test2.patch +now at: test2.patch % pop, qapplied, qunapplied 0 A test.patch 1 A test1b.patch @@ -306,7 +338,7 @@ % strip adding x 0 files updated, 0 files merged, 1 files removed, 0 files unresolved -saving bundle to +saved backup bundle to adding changesets adding manifests adding file changes @@ -317,7 +349,7 @@ abort: local changes found % --force strip with local changes 0 files updated, 0 files merged, 1 files removed, 0 files unresolved -saving bundle to +saved backup bundle to % cd b; hg qrefresh adding a foo @@ -357,8 +389,8 @@ ? foo.rej % mq tags 0 qparent -1 qbase foo -2 qtip bar tip +1 foo qbase +2 bar qtip tip % bad node in status popping bar now at: foo @@ -399,11 +431,6 @@ 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 diff --git a/bar b/bar new file mode 100644 --- /dev/null @@ -429,11 +456,6 @@ % test file move chains in the slow path 1 files updated, 0 files merged, 2 files removed, 0 files unresolved 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 diff --git a/foo b/bleh rename from foo rename to bleh @@ -501,13 +523,7 @@ summary: add foo 1 files updated, 0 files merged, 0 files removed, 0 files unresolved -saving bundle to -saving bundle to -adding branch -adding changesets -adding manifests -adding file changes -added 1 changesets with 1 changes to 1 files +saved backup bundle to changeset: 1:20cbbe65cff7 tag: tip user: test @@ -577,10 +593,10 @@ hello +world changeset: 1:bf5fc3f07a0a +tag: empty +tag: qbase tag: qtip tag: tip -tag: empty -tag: qbase user: test date: Thu Jan 01 00:00:00 1970 +0000 summary: imported patch empty diff -r 0bb67503ad4b -r ac6fec2af8c8 tests/test-newbranch.out --- a/tests/test-newbranch.out Mon May 31 21:37:01 2010 +0200 +++ b/tests/test-newbranch.out Mon May 31 21:43:03 2010 +0200 @@ -13,7 +13,6 @@ 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 @@ -62,7 +61,7 @@ default bar % test for invalid branch cache -rolling back last transaction +rolling back to revision 4 (undo commit) changeset: 4:4909a3732169 branch: foo tag: tip @@ -135,7 +134,6 @@ 0 files updated, 0 files merged, 1 files removed, 0 files unresolved marked working directory as branch test adding e -created new head changeset: 4:3a1e01ed1df4 branch: test tag: tip diff -r 0bb67503ad4b -r ac6fec2af8c8 tests/test-notify.out --- a/tests/test-notify.out Mon May 31 21:37:01 2010 +0200 +++ b/tests/test-notify.out Mon May 31 21:43:03 2010 +0200 @@ -95,10 +95,10 @@ +a (run 'hg update' to get a working copy) % fail for config file is missing -rolling back last transaction +rolling back to revision 0 (undo pull) pull failed % pull -rolling back last transaction +rolling back to revision 0 (undo pull) pulling from ../a searching for changes adding changesets @@ -129,7 +129,7 @@ +a (run 'hg update' to get a working copy) % pull -rolling back last transaction +rolling back to revision 0 (undo pull) pulling from ../a searching for changes adding changesets diff -r 0bb67503ad4b -r ac6fec2af8c8 tests/test-patchbomb --- a/tests/test-patchbomb Mon May 31 21:37:01 2010 +0200 +++ b/tests/test-patchbomb Mon May 31 21:43:03 2010 +0200 @@ -126,10 +126,23 @@ hg email --date '1970-1-1 0:1' -n --intro -f quux -t foo -c bar -s test \ -r 2 | fixheaders +echo "% test --desc without --intro for a single patch" +echo foo > intro.text +hg email --date '1970-1-1 0:1' -n --desc intro.text -f quux -t foo -c bar \ + -s test -r 2 | fixheaders + echo "% test intro for multiple patches" hg email --date '1970-1-1 0:1' -n --intro -f quux -t foo -c bar -s test \ -r 0:1 | fixheaders +echo "% test reply-to via config" +hg email --date '1970-1-1 0:1' -n -f quux -t foo -c bar -s test -r 2 \ + --config patchbomb.reply-to='baz@example.com' | fixheaders + +echo "% test reply-to via command line" +hg email --date '1970-1-1 0:1' -n -f quux -t foo -c bar -s test -r 2 \ + --reply-to baz --reply-to fred | fixheaders + echo "% tagging csets" hg tag -r0 zero zero.foo hg tag -r1 one one.patch diff -r 0bb67503ad4b -r ac6fec2af8c8 tests/test-patchbomb.out --- a/tests/test-patchbomb.out Mon May 31 21:37:01 2010 +0200 +++ b/tests/test-patchbomb.out Mon May 31 21:43:03 2010 +0200 @@ -940,6 +940,52 @@ @@ -0,0 +1,1 @@ +c +% test --desc without --intro for a single patch +This patch series consists of 1 patches. + + +Displaying [PATCH 0 of 1] test ... +Content-Type: text/plain; charset="us-ascii" +MIME-Version: 1.0 +Content-Transfer-Encoding: 7bit +Subject: [PATCH 0 of 1] test +Message-Id: = 0: total = loops + if opts.get('total', None): + total = int(opts.get('total')) loops = abs(loops) for i in range(loops): @@ -17,7 +19,8 @@ commands.norepo += " loop" cmdtable = { - "loop": (loop, [], 'hg loop LOOPS'), + "loop": (loop, [('', 'total', '', 'override for total')], + 'hg loop LOOPS'), } EOF @@ -55,5 +58,8 @@ echo '% test format options and indeterminate progress' hg -y --config 'progress.format=number item bar' loop -- -2 2>&1 | python filtercr.py +echo "% make sure things don't fall over if count > total" +hg -y loop --total 4 6 2>&1 | python filtercr.py + echo '% test immediate progress completion' hg -y loop 0 2>&1 | python filtercr.py diff -r 0bb67503ad4b -r ac6fec2af8c8 tests/test-progress.out --- a/tests/test-progress.out Mon May 31 21:37:01 2010 +0200 +++ b/tests/test-progress.out Mon May 31 21:43:03 2010 +0200 @@ -17,4 +17,12 @@ 0 loop.0 [ <=> ] 1 loop.1 [ <=> ] + % make sure things don't fall over if count > total + +loop [ ] 0/4 +loop [================> ] 1/4 +loop [=================================> ] 2/4 +loop [==================================================> ] 3/4 +loop [===================================================================>] 4/4 +loop [ <=> ] 5/4 % test immediate progress completion diff -r 0bb67503ad4b -r ac6fec2af8c8 tests/test-pull --- a/tests/test-pull Mon May 31 21:37:01 2010 +0200 +++ b/tests/test-pull Mon May 31 21:43:03 2010 +0200 @@ -11,13 +11,14 @@ cat hg.pid >> $DAEMON_PIDS cd .. -hg clone --pull http://localhost:$HGPORT/ copy | sed -e "s,:$HGPORT/,:\$HGPORT/," +hg clone --pull http://foo:bar@localhost:$HGPORT/ copy | sed -e "s,:$HGPORT/,:\$HGPORT/," cd copy hg verify hg co cat foo hg manifest --debug hg pull | sed -e "s,:$HGPORT/,:\$HGPORT/," +hg rollback --dry-run --verbose | sed -e "s,:$HGPORT/,:\$HGPORT/," echo % issue 622 cd .. diff -r 0bb67503ad4b -r ac6fec2af8c8 tests/test-pull-branch.out --- a/tests/test-pull-branch.out Mon May 31 21:37:01 2010 +0200 +++ b/tests/test-pull-branch.out Mon May 31 21:43:03 2010 +0200 @@ -11,7 +11,6 @@ % create branch B 1 files updated, 0 files merged, 0 files removed, 0 files unresolved marked working directory as branch branchB -created new head % a new branch is there pulling from ../t searching for changes diff -r 0bb67503ad4b -r ac6fec2af8c8 tests/test-pull-r.out --- a/tests/test-pull-r.out Mon May 31 21:37:01 2010 +0200 +++ b/tests/test-pull-r.out Mon May 31 21:43:03 2010 +0200 @@ -19,7 +19,7 @@ abort: unknown revision 'missing'! % pull multiple revisions with update 0:bbd179dfa0a7 -rolling back last transaction +rolling back to revision -1 (undo pull) % pull -r 0 changeset: 0:bbd179dfa0a7 tag: tip diff -r 0bb67503ad4b -r ac6fec2af8c8 tests/test-pull.out --- a/tests/test-pull.out Mon May 31 21:37:01 2010 +0200 +++ b/tests/test-pull.out Mon May 31 21:43:03 2010 +0200 @@ -19,9 +19,10 @@ 0 files updated, 0 files merged, 0 files removed, 0 files unresolved foo 2ed2a3912a0b24502043eae84ee4b279c18b90dd 644 foo -pulling from http://localhost:$HGPORT/ +pulling from http://foo:***@localhost:$HGPORT/ searching for changes no changes found +rolling back to revision -1 (undo pull: http://foo:***@localhost:$HGPORT/) % issue 622 pulling from ../test requesting all changes diff -r 0bb67503ad4b -r ac6fec2af8c8 tests/test-push-http.out --- a/tests/test-push-http.out Mon May 31 21:37:01 2010 +0200 +++ b/tests/test-push-http.out Mon May 31 21:43:03 2010 +0200 @@ -25,7 +25,7 @@ remote: added 1 changesets with 1 changes to 1 files % serve errors changegroup hook: HG_NODE=ba677d0156c1196c1a699fa53f390dcfc3ce3872 HG_SOURCE=serve HG_URL=remote:http -rolling back last transaction +rolling back to revision 0 (undo serve) % expect authorization error: all users denied abort: authorization failed pushing to http://localhost:$HGPORT/ diff -r 0bb67503ad4b -r ac6fec2af8c8 tests/test-push-warn --- a/tests/test-push-warn Mon May 31 21:37:01 2010 +0200 +++ b/tests/test-push-warn Mon May 31 21:43:03 2010 +0200 @@ -135,6 +135,18 @@ hg -q ci -d "1000000 0" -m 12 hg push -r 11 -r 12 ../f; echo $? +echo % failed push of new named branch +echo 12 > foo +hg -q ci -d "1000000 0" -m 12a +hg -q up 11 +echo 13 > foo +hg -q branch e +hg -q ci -d "1000000 0" -m 13d +hg push -r 12 -r 13 ../f; echo $? + +echo % using --new-branch to push new named branch +hg push --new-branch -r 12 -r 13 ../f; echo $? + echo % checking prepush logic does not allow silently pushing multiple new heads cd .. hg init h diff -r 0bb67503ad4b -r ac6fec2af8c8 tests/test-push-warn.out --- a/tests/test-push-warn.out Mon May 31 21:37:01 2010 +0200 +++ b/tests/test-push-warn.out Mon May 31 21:43:03 2010 +0200 @@ -89,23 +89,23 @@ pushing to ../f searching for changes abort: push creates new remote branches: c! -(use 'hg push -f' to force) +(use 'hg push --new-branch' to create new remote branches) 1 pushing to ../f searching for changes abort: push creates new remote branches: c! -(use 'hg push -f' to force) +(use 'hg push --new-branch' to create new remote branches) 1 % multiple new branches pushing to ../f searching for changes abort: push creates new remote branches: c, d! -(use 'hg push -f' to force) +(use 'hg push --new-branch' to create new remote branches) 1 pushing to ../f searching for changes abort: push creates new remote branches: c, d! -(use 'hg push -f' to force) +(use 'hg push --new-branch' to create new remote branches) 1 % fail on multiple head push pushing to ../f @@ -144,6 +144,20 @@ adding file changes added 2 changesets with 2 changes to 1 files 0 +% failed push of new named branch +pushing to ../f +searching for changes +abort: push creates new remote branches: e! +(use 'hg push --new-branch' to create new remote branches) +1 +% using --new-branch to push new named branch +pushing to ../f +searching for changes +adding changesets +adding manifests +adding file changes +added 1 changesets with 1 changes to 1 files +0 % checking prepush logic does not allow silently pushing multiple new heads adding init adding a @@ -172,7 +186,7 @@ pushing to j searching for changes abort: push creates new remote branches: b! -(use 'hg push -f' to force) +(use 'hg push --new-branch' to create new remote branches) % prepush -r should not allow you to sneak in new heads pushing to ../l @@ -190,7 +204,6 @@ adding b1 0 files updated, 0 files merged, 1 files removed, 0 files unresolved adding a2 -created new head %% glog of local @ 2: A a2 | @@ -222,7 +235,6 @@ adding b 0 files updated, 0 files merged, 1 files removed, 0 files unresolved adding a1 -created new head updating to branch A 2 files updated, 0 files merged, 0 files removed, 0 files unresolved 1 files updated, 0 files merged, 1 files removed, 0 files unresolved @@ -263,7 +275,6 @@ 0 files updated, 0 files merged, 1 files removed, 0 files unresolved marked working directory as branch B adding b -created new head updating to branch B 1 files updated, 0 files merged, 0 files removed, 0 files unresolved 1 files updated, 0 files merged, 1 files removed, 0 files unresolved diff -r 0bb67503ad4b -r ac6fec2af8c8 tests/test-qrecord.out --- a/tests/test-qrecord.out Mon May 31 21:37:01 2010 +0200 +++ b/tests/test-qrecord.out Mon May 31 21:43:03 2010 +0200 @@ -17,10 +17,10 @@ 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 + serve start stand-alone webserver status show changed files in the working directory summary summarize working directory state - update update working directory + update update working directory (or switch revisions) use "hg help" for the full list of commands or "hg -v" for details % help (mq present) @@ -28,7 +28,7 @@ interactively record a new patch - See 'hg help qnew' & 'hg help record' for more information and usage. + See "hg help qnew" & "hg help record" for more information and usage. options: @@ -80,34 +80,40 @@ % 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 @@ +examine changes to '1.txt'? [Ynsfdaq?] +@@ -1,3 +1,3 @@ 1 -2 +2 2 3 -record change 1/6 to '1.txt'? [Ynsfdaq?] @@ -3,3 +3,3 @@ +record change 1/6 to '1.txt'? [Ynsfdaq?] +@@ -3,3 +3,3 @@ 3 -4 +4 4 5 -record change 2/6 to '1.txt'? [Ynsfdaq?] diff --git a/2.txt b/2.txt +record change 2/6 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 @@ +examine changes to '2.txt'? [Ynsfdaq?] +@@ -1,5 +1,5 @@ a -b +b b c d e -record change 4/6 to '2.txt'? [Ynsfdaq?] diff --git a/dir/a.txt b/dir/a.txt +record change 4/6 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: a.patch +tag: qbase tag: qtip tag: tip -tag: a.patch -tag: qbase user: test date: Thu Jan 01 00:00:00 1970 +0000 summary: aaa @@ -157,27 +163,31 @@ % 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 @@ +examine changes to '1.txt'? [Ynsfdaq?] +@@ -1,5 +1,5 @@ 1 2 2 3 -4 +4 4 5 -record change 1/3 to '1.txt'? [Ynsfdaq?] diff --git a/dir/a.txt b/dir/a.txt +record change 1/3 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 @@ +examine changes to 'dir/a.txt'? [Ynsfdaq?] +@@ -1,4 +1,4 @@ -hello world +hello world! someone up record change 3/3 to 'dir/a.txt'? [Ynsfdaq?] + % after qrecord b.patch 'tip' changeset: 2:b056198bf878 +tag: b.patch tag: qtip tag: tip -tag: b.patch user: test date: Thu Jan 01 00:00:00 1970 +0000 summary: bbb diff -r 0bb67503ad4b -r ac6fec2af8c8 tests/test-rebase-abort --- a/tests/test-rebase-abort Mon May 31 21:37:01 2010 +0200 +++ b/tests/test-rebase-abort Mon May 31 21:43:03 2010 +0200 @@ -1,15 +1,11 @@ #!/bin/sh +. $TESTDIR/helpers.sh + echo "[extensions]" >> $HGRCPATH echo "graphlog=" >> $HGRCPATH echo "rebase=" >> $HGRCPATH -cleanoutput () { - sed -e 's/\(Rebase status stored to\).*/\1/' \ - -e 's/\(Rebase status restored from\).*/\1/' \ - -e 's/\(saving bundle to \).*/\1/' -} - hg init a cd a echo 'c1' >common @@ -35,10 +31,10 @@ echo echo '% Conflicting rebase' -hg rebase -s 3 -d 2 2>&1 | cleanoutput +hg rebase -s 3 -d 2 | cleanrebase echo echo '% Abort' -hg rebase --abort 2>&1 | cleanoutput +hg rebase --abort | cleanrebase hg glog --template '{rev}: {desc}\n' diff -r 0bb67503ad4b -r ac6fec2af8c8 tests/test-rebase-abort.out --- a/tests/test-rebase-abort.out Mon May 31 21:37:01 2010 +0200 +++ b/tests/test-rebase-abort.out Mon May 31 21:43:03 2010 +0200 @@ -12,12 +12,13 @@ % Conflicting rebase -merging common warning: conflicts during merge. merging common failed! abort: fix unresolved conflicts with hg resolve then run hg rebase --continue +merging common % Abort +saved backup bundle to rebase aborted @ 4: L2 | diff -r 0bb67503ad4b -r ac6fec2af8c8 tests/test-rebase-cache --- a/tests/test-rebase-cache Mon May 31 21:37:01 2010 +0200 +++ b/tests/test-rebase-cache Mon May 31 21:43:03 2010 +0200 @@ -1,5 +1,7 @@ #!/bin/sh +. $TESTDIR/helpers.sh + createrepo() { rm -rf repo hg init repo @@ -52,7 +54,7 @@ echo echo '% Rebase part of branch2 (5-6) onto branch3 (8)' -hg --config extensions.hgext.rebase= rebase --detach -s 5 -d 8 2>&1 | sed 's/\(saving bundle to \).*/\1/' +hg --config extensions.hgext.rebase= rebase --detach -s 5 -d 8 | hidebackup echo echo '% Branches' @@ -70,7 +72,7 @@ createrepo > /dev/null 2>&1 hg --config extensions.hgext.graphlog= glog --template '{rev}:{node|short} {desc} branch: {branches}\n' -hg --config extensions.hgext.rebase= rebase --detach -s 8 -d 6 2>&1 | sed 's/\(saving bundle to \).*/\1/' +hg --config extensions.hgext.rebase= rebase --detach -s 8 -d 6 | hidebackup echo echo '% Branches' @@ -89,7 +91,7 @@ createrepo > /dev/null 2>&1 hg --config extensions.hgext.graphlog= glog --template '{rev}:{node|short} {desc} branch: {branches}\n' -hg --config extensions.hgext.rebase= rebase --detach -s 7 -d 6 2>&1 | sed 's/\(saving bundle to \).*/\1/' +hg --config extensions.hgext.rebase= rebase --detach -s 7 -d 6 | hidebackup echo echo '% Branches' diff -r 0bb67503ad4b -r ac6fec2af8c8 tests/test-rebase-cache.out --- a/tests/test-rebase-cache.out Mon May 31 21:37:01 2010 +0200 +++ b/tests/test-rebase-cache.out Mon May 31 21:43:03 2010 +0200 @@ -32,13 +32,7 @@ 0:1994f17a630e A branch: % Rebase part of branch2 (5-6) onto branch3 (8) -saving bundle to -adding branch -adding changesets -adding manifests -adding file changes -added 4 changesets with 3 changes to 3 files (+1 heads) -rebase completed +saved backup bundle to % Branches branch3 8:c9bfa9beb84e @@ -90,13 +84,7 @@ |/ o 0:1994f17a630e A branch: -saving bundle to -adding branch -adding changesets -adding manifests -adding file changes -added 1 changesets with 1 changes to 1 files -rebase completed +saved backup bundle to % Branches branch2 8:b44d3024f247 @@ -149,13 +137,7 @@ |/ o 0:1994f17a630e A branch: -saving bundle to -adding branch -adding changesets -adding manifests -adding file changes -added 1 changesets with 1 changes to 1 files -rebase completed +saved backup bundle to % Branches branch2 7:b44d3024f247 diff -r 0bb67503ad4b -r ac6fec2af8c8 tests/test-rebase-check-restore --- a/tests/test-rebase-check-restore Mon May 31 21:37:01 2010 +0200 +++ b/tests/test-rebase-check-restore Mon May 31 21:43:03 2010 +0200 @@ -1,5 +1,7 @@ #!/bin/sh +. $TESTDIR/helpers.sh + echo "[extensions]" >> $HGRCPATH echo "graphlog=" >> $HGRCPATH echo "rebase=" >> $HGRCPATH @@ -41,28 +43,28 @@ echo "% - Rebasing B onto E - check keep" createrepo > /dev/null 2>&1 hg glog --template '{rev}:{desc}:{branches}\n' -hg rebase -s 1 -d 4 --keep 2>&1 | sed 's/\(saving bundle to \).*/\1/' +hg rebase -s 1 -d 4 --keep | hidebackup echo echo "% - Solve the conflict and go on" echo 'conflict solved' > A rm A.orig hg resolve -m A -hg rebase --continue 2>&1 | sed 's/\(saving bundle to \).*/\1/' +hg rebase --continue | hidebackup hg glog --template '{rev}:{desc}:{branches}\n' echo echo "% - Rebase F onto E - check keepbranches" createrepo > /dev/null 2>&1 hg glog --template '{rev}:{desc}:{branches}\n' -hg rebase -s 5 -d 4 --keepbranches 2>&1 | sed 's/\(saving bundle to \).*/\1/' +hg rebase -s 5 -d 4 --keepbranches | hidebackup echo echo "% - Solve the conflict and go on" echo 'conflict solved' > A rm A.orig hg resolve -m A -hg rebase --continue 2>&1 | sed 's/\(saving bundle to \).*/\1/' +hg rebase --continue 2>&1 | hidebackup hg glog --template '{rev}:{desc}:{branches}\n' exit 0 diff -r 0bb67503ad4b -r ac6fec2af8c8 tests/test-rebase-check-restore.out --- a/tests/test-rebase-check-restore.out Mon May 31 21:37:01 2010 +0200 +++ b/tests/test-rebase-check-restore.out Mon May 31 21:43:03 2010 +0200 @@ -12,13 +12,12 @@ |/ o 0:A: -merging A warning: conflicts during merge. merging A failed! abort: fix unresolved conflicts with hg resolve then run hg rebase --continue +merging A % - Solve the conflict and go on -rebase completed @ 7:C: | o 6:B: @@ -49,19 +48,13 @@ |/ o 0:A: -merging A warning: conflicts during merge. merging A failed! abort: fix unresolved conflicts with hg resolve then run hg rebase --continue +merging A % - Solve the conflict and go on -saving bundle to -adding branch -adding changesets -adding manifests -adding file changes -added 1 changesets with 1 changes to 1 files -rebase completed +saved backup bundle to @ 5:F:notdefault | o 4:E: diff -r 0bb67503ad4b -r ac6fec2af8c8 tests/test-rebase-collapse --- a/tests/test-rebase-collapse Mon May 31 21:37:01 2010 +0200 +++ b/tests/test-rebase-collapse Mon May 31 21:43:03 2010 +0200 @@ -1,5 +1,7 @@ #!/bin/sh +. $TESTDIR/helpers.sh + echo "[extensions]" >> $HGRCPATH echo "graphlog=" >> $HGRCPATH echo "rebase=" >> $HGRCPATH @@ -43,7 +45,7 @@ hg glog --template '{rev}: {desc}\n' echo '% Rebasing B onto H' hg up -C 3 -hg rebase --collapse --keepbranches 2>&1 | sed 's/\(saving bundle to \).*/\1/' +hg rebase --collapse --keepbranches | hidebackup hg glog --template '{rev}: {desc}\n' echo "Expected A, B, C, D, F, H" hg manifest @@ -51,7 +53,7 @@ createrepo > /dev/null 2>&1 echo echo '% Rebasing G onto H' -hg rebase --base 6 --collapse 2>&1 | sed 's/\(saving bundle to \).*/\1/' +hg rebase --base 6 --collapse | hidebackup hg glog --template '{rev}: {desc}\n' echo "Expected A, E, F, H" hg manifest @@ -91,7 +93,7 @@ echo echo '% Rebase and collapse - E onto H' -hg rebase -s 4 --collapse 2>&1 | sed 's/\(saving bundle to \).*/\1/' +hg rebase -s 4 --collapse | hidebackup hg glog --template '{rev}: {desc}\n' echo "Expected A, B, C, E, F, H" hg manifest @@ -130,7 +132,7 @@ echo echo '% Rebase and collapse - E onto I' -hg rebase -s 4 --collapse 2>&1 | sed 's/\(saving bundle to \).*/\1/' +hg rebase -s 4 --collapse | hidebackup hg glog --template '{rev}: {desc}\n' @@ -166,7 +168,7 @@ echo echo '% Rebase and collapse - B onto F' -hg rebase -s 1 --collapse 2>&1 | sed 's/\(saving bundle to \).*/\1/' +hg rebase -s 1 --collapse | hidebackup hg glog --template '{rev}: {desc}\n' diff -r 0bb67503ad4b -r ac6fec2af8c8 tests/test-rebase-collapse.out --- a/tests/test-rebase-collapse.out Mon May 31 21:37:01 2010 +0200 +++ b/tests/test-rebase-collapse.out Mon May 31 21:43:03 2010 +0200 @@ -16,13 +16,7 @@ % Rebasing B onto H 3 files updated, 0 files merged, 2 files removed, 0 files unresolved -saving bundle to -adding branch -adding changesets -adding manifests -adding file changes -added 5 changesets with 6 changes to 6 files (+1 heads) -rebase completed +saved backup bundle to @ 5: Collapsed revision | * B | * C @@ -46,13 +40,7 @@ H % Rebasing G onto H -saving bundle to -adding branch -adding changesets -adding manifests -adding file changes -added 3 changesets with 3 changes to 3 files (+1 heads) -rebase completed +saved backup bundle to @ 6: Collapsed revision | * E | * G @@ -95,13 +83,7 @@ abort: unable to collapse, there is more than one external parent % Rebase and collapse - E onto H -saving bundle to -adding branch -adding changesets -adding manifests -adding file changes -added 2 changesets with 3 changes to 3 files -rebase completed +saved backup bundle to @ 5: Collapsed revision |\ * E | | * F @@ -145,13 +127,7 @@ % Rebase and collapse - E onto I merging E -saving bundle to -adding branch -adding changesets -adding manifests -adding file changes -added 2 changesets with 3 changes to 3 files -rebase completed +saved backup bundle to @ 5: Collapsed revision |\ * E | | * F @@ -191,13 +167,7 @@ % Rebase and collapse - B onto F -saving bundle to -adding branch -adding changesets -adding manifests -adding file changes -added 2 changesets with 4 changes to 4 files -rebase completed +saved backup bundle to @ 2: Collapsed revision | * B | * C diff -r 0bb67503ad4b -r ac6fec2af8c8 tests/test-rebase-conflicts --- a/tests/test-rebase-conflicts Mon May 31 21:37:01 2010 +0200 +++ b/tests/test-rebase-conflicts Mon May 31 21:43:03 2010 +0200 @@ -1,15 +1,11 @@ #!/bin/sh +. $TESTDIR/helpers.sh + echo "[extensions]" >> $HGRCPATH echo "graphlog=" >> $HGRCPATH echo "rebase=" >> $HGRCPATH -cleanoutput () { - sed -e 's/\(Rebase status stored to\).*/\1/' \ - -e 's/\(Rebase status restored from\).*/\1/' \ - -e 's/\(saving bundle to \).*/\1/' -} - hg init a cd a echo 'c1' >common @@ -53,7 +49,7 @@ echo '% Conclude rebase' echo 'resolved merge' >common hg resolve -m common -hg rebase --continue 2>&1 | cleanoutput +hg rebase --continue | cleanrebase hg glog --template '{rev}: {desc}\n' diff -r 0bb67503ad4b -r ac6fec2af8c8 tests/test-rebase-conflicts.out --- a/tests/test-rebase-conflicts.out Mon May 31 21:37:01 2010 +0200 +++ b/tests/test-rebase-conflicts.out Mon May 31 21:43:03 2010 +0200 @@ -26,13 +26,7 @@ abort: unresolved merge conflicts (see hg resolve) % Conclude rebase -saving bundle to -adding branch -adding changesets -adding manifests -adding file changes -added 3 changesets with 3 changes to 3 files -rebase completed +saved backup bundle to @ 5: L3 | o 4: L2 diff -r 0bb67503ad4b -r ac6fec2af8c8 tests/test-rebase-detach --- a/tests/test-rebase-detach Mon May 31 21:37:01 2010 +0200 +++ b/tests/test-rebase-detach Mon May 31 21:43:03 2010 +0200 @@ -1,5 +1,7 @@ #!/bin/sh +. $TESTDIR/helpers.sh + echo "[extensions]" >> $HGRCPATH echo "graphlog=" >> $HGRCPATH echo "rebase=" >> $HGRCPATH @@ -33,7 +35,7 @@ createrepo > /dev/null 2>&1 hg glog --template '{rev}: {desc}\n' echo '% Rebasing D onto E detaching from C' -hg rebase --detach -s 3 -d 4 2>&1 | sed 's/\(saving bundle to \).*/\1/' +hg rebase --detach -s 3 -d 4 | hidebackup hg glog --template '{rev}: {desc}\n' echo "Expected A, D, E" hg manifest @@ -42,7 +44,7 @@ createrepo > /dev/null 2>&1 hg glog --template '{rev}: {desc}\n' echo '% Rebasing C onto E detaching from B' -hg rebase --detach -s 2 -d 4 2>&1 | sed 's/\(saving bundle to \).*/\1/' +hg rebase --detach -s 2 -d 4 | hidebackup hg glog --template '{rev}: {desc}\n' echo "Expected A, C, D, E" hg manifest @@ -51,7 +53,7 @@ createrepo > /dev/null 2>&1 hg glog --template '{rev}: {desc}\n' echo '% Rebasing B onto E using detach (same as not using it)' -hg rebase --detach -s 1 -d 4 2>&1 | sed 's/\(saving bundle to \).*/\1/' +hg rebase --detach -s 1 -d 4 | hidebackup hg glog --template '{rev}: {desc}\n' echo "Expected A, B, C, D, E" hg manifest @@ -60,7 +62,7 @@ createrepo > /dev/null 2>&1 hg glog --template '{rev}: {desc}\n' echo '% Rebasing C onto E detaching from B and collapsing' -hg rebase --detach --collapse -s 2 -d 4 2>&1 | sed 's/\(saving bundle to \).*/\1/' +hg rebase --detach --collapse -s 2 -d 4 | hidebackup hg glog --template '{rev}: {desc}\n' echo "Expected A, C, D, E" hg manifest diff -r 0bb67503ad4b -r ac6fec2af8c8 tests/test-rebase-detach.out --- a/tests/test-rebase-detach.out Mon May 31 21:37:01 2010 +0200 +++ b/tests/test-rebase-detach.out Mon May 31 21:43:03 2010 +0200 @@ -9,13 +9,7 @@ o 0: A % Rebasing D onto E detaching from C -saving bundle to -adding branch -adding changesets -adding manifests -adding file changes -added 2 changesets with 2 changes to 2 files (+1 heads) -rebase completed +saved backup bundle to @ 4: D | o 3: E @@ -42,13 +36,7 @@ o 0: A % Rebasing C onto E detaching from B -saving bundle to -adding branch -adding changesets -adding manifests -adding file changes -added 3 changesets with 3 changes to 3 files (+1 heads) -rebase completed +saved backup bundle to @ 4: D | o 3: C @@ -76,13 +64,7 @@ o 0: A % Rebasing B onto E using detach (same as not using it) -saving bundle to -adding branch -adding changesets -adding manifests -adding file changes -added 4 changesets with 4 changes to 4 files -rebase completed +saved backup bundle to @ 4: D | o 3: C @@ -111,13 +93,7 @@ o 0: A % Rebasing C onto E detaching from B and collapsing -saving bundle to -adding branch -adding changesets -adding manifests -adding file changes -added 2 changesets with 3 changes to 3 files (+1 heads) -rebase completed +saved backup bundle to @ 3: Collapsed revision | * C | * D diff -r 0bb67503ad4b -r ac6fec2af8c8 tests/test-rebase-interruptions --- a/tests/test-rebase-interruptions Mon May 31 21:37:01 2010 +0200 +++ b/tests/test-rebase-interruptions Mon May 31 21:43:03 2010 +0200 @@ -1,5 +1,6 @@ #!/bin/sh +. $TESTDIR/helpers.sh echo "[extensions]" >> $HGRCPATH echo "graphlog=" >> $HGRCPATH echo "rebase=" >> $HGRCPATH @@ -38,7 +39,7 @@ echo echo "% - Rebasing B onto E" -hg rebase -s 1 -d 4 2>&1 | sed 's/\(saving bundle to \).*/\1/' +hg rebase -s 1 -d 4 | hidebackup echo echo "% - Force a commit on C during the interruption" @@ -66,7 +67,7 @@ echo echo "% Rebasing B onto E" -hg rebase -s 1 -d 4 2>&1 | sed 's/\(saving bundle to \).*/\1/' +hg rebase -s 1 -d 4 | hidebackup echo echo "% Force a commit on B' during the interruption" diff -r 0bb67503ad4b -r ac6fec2af8c8 tests/test-rebase-interruptions.out --- a/tests/test-rebase-interruptions.out Mon May 31 21:37:01 2010 +0200 +++ b/tests/test-rebase-interruptions.out Mon May 31 21:43:03 2010 +0200 @@ -11,10 +11,10 @@ % - Rebasing B onto E -merging A warning: conflicts during merge. merging A failed! abort: fix unresolved conflicts with hg resolve then run hg rebase --continue +merging A % - Force a commit on C during the interruption 1 files updated, 0 files merged, 1 files removed, 0 files unresolved @@ -41,7 +41,6 @@ % - Solve the conflict and go on warning: new changesets detected on source branch, not stripping -rebase completed @ 7: C | | o 6: Extra @@ -73,10 +72,10 @@ % Rebasing B onto E -merging A warning: conflicts during merge. merging A failed! abort: fix unresolved conflicts with hg resolve then run hg rebase --continue +merging A % Force a commit on B' during the interruption 1 files updated, 0 files merged, 0 files removed, 0 files unresolved @@ -96,7 +95,7 @@ % Abort the rebasing -warning: new changesets detected on target branch, not stripping +warning: new changesets detected on target branch, can't abort @ 6: Extra | o 5: B diff -r 0bb67503ad4b -r ac6fec2af8c8 tests/test-rebase-issue-noparam-single-rev --- a/tests/test-rebase-issue-noparam-single-rev Mon May 31 21:37:01 2010 +0200 +++ b/tests/test-rebase-issue-noparam-single-rev Mon May 31 21:43:03 2010 +0200 @@ -1,5 +1,7 @@ #!/bin/sh +. $TESTDIR/helpers.sh + echo "[extensions]" >> $HGRCPATH echo "graphlog=" >> $HGRCPATH echo "rebase=" >> $HGRCPATH @@ -25,7 +27,7 @@ echo echo '% Rebase with no arguments - single revision in source branch' hg update -C 2 -hg rebase 2>&1 | sed 's/\(saving bundle to \).*/\1/' +hg rebase | hidebackup hg glog --template '{rev}:{desc}\n' cd .. @@ -45,5 +47,5 @@ echo echo '% Rebase with no arguments - single revision in target branch' hg update -C 3 -hg rebase 2>&1 | sed 's/\(saving bundle to \).*/\1/' +hg rebase 2>&1 | hidebackup hg glog --template '{rev}:{desc}\n' diff -r 0bb67503ad4b -r ac6fec2af8c8 tests/test-rebase-issue-noparam-single-rev.out --- a/tests/test-rebase-issue-noparam-single-rev.out Mon May 31 21:37:01 2010 +0200 +++ b/tests/test-rebase-issue-noparam-single-rev.out Mon May 31 21:43:03 2010 +0200 @@ -13,13 +13,7 @@ % Rebase with no arguments - single revision in source branch 1 files updated, 0 files merged, 2 files removed, 0 files unresolved -saving bundle to -adding branch -adding changesets -adding manifests -adding file changes -added 3 changesets with 3 changes to 3 files -rebase completed +saved backup bundle to @ 4:l1 | o 3:r2 @@ -45,13 +39,7 @@ % Rebase with no arguments - single revision in target branch 2 files updated, 0 files merged, 1 files removed, 0 files unresolved -saving bundle to -adding branch -adding changesets -adding manifests -adding file changes -added 3 changesets with 3 changes to 3 files -rebase completed +saved backup bundle to @ 4:l2 | o 3:l1 diff -r 0bb67503ad4b -r ac6fec2af8c8 tests/test-rebase-keep-branch --- a/tests/test-rebase-keep-branch Mon May 31 21:37:01 2010 +0200 +++ b/tests/test-rebase-keep-branch Mon May 31 21:43:03 2010 +0200 @@ -26,7 +26,7 @@ echo echo '% Rebase a branch while preserving the branch name' hg update -C 3 -hg rebase -b 4 -d 3 --keepbranches 2>&1 | sed 's/\(saving bundle to \).*/\1/' +hg rebase -b 4 -d 3 --keepbranches -q hg glog --template '{rev}:{desc}:{branches}\n' echo '% dirstate branch should be "notdefault"' hg branch diff -r 0bb67503ad4b -r ac6fec2af8c8 tests/test-rebase-keep-branch.out --- a/tests/test-rebase-keep-branch.out Mon May 31 21:37:01 2010 +0200 +++ b/tests/test-rebase-keep-branch.out Mon May 31 21:43:03 2010 +0200 @@ -1,6 +1,5 @@ 0 files updated, 0 files merged, 2 files removed, 0 files unresolved marked working directory as branch notdefault -created new head @ 4:r1:notdefault | | o 3:l2: @@ -14,13 +13,6 @@ % Rebase a branch while preserving the branch name 2 files updated, 0 files merged, 1 files removed, 0 files unresolved -saving bundle to -adding branch -adding changesets -adding manifests -adding file changes -added 1 changesets with 1 changes to 1 files -rebase completed @ 4:r1:notdefault | o 3:l2: diff -r 0bb67503ad4b -r ac6fec2af8c8 tests/test-rebase-mq --- a/tests/test-rebase-mq Mon May 31 21:37:01 2010 +0200 +++ b/tests/test-rebase-mq Mon May 31 21:43:03 2010 +0200 @@ -1,5 +1,7 @@ #!/bin/sh +. $TESTDIR/helpers.sh + echo "[extensions]" >> $HGRCPATH echo "graphlog=" >> $HGRCPATH echo "rebase=" >> $HGRCPATH @@ -58,13 +60,13 @@ echo '% Fix the 1st conflict' echo 'mq1r1' > f hg resolve -m f -hg rebase -c 2>&1 | sed -e 's/\(saving bundle to \).*/\1/' +hg rebase -c | hidebackup echo echo '% Fix the 2nd conflict' echo 'mq1r1mq2' > f hg resolve -m f -hg rebase -c 2>&1 | sed -e 's/\(saving bundle to \).*/\1/' +hg rebase -c | hidebackup hg glog --template '{rev} {desc} tags: {tags}\n' @@ -109,7 +111,7 @@ echo echo '% Rebase the applied mq patches' -hg rebase -s 2 -d 1 --quiet 2>&1 | sed -e 's/\(saving bundle to \).*/\1/' +hg rebase -s 2 -d 1 --quiet echo '% And the patches are correct' echo '% Git patch' diff -r 0bb67503ad4b -r ac6fec2af8c8 tests/test-rebase-mq-skip --- a/tests/test-rebase-mq-skip Mon May 31 21:37:01 2010 +0200 +++ b/tests/test-rebase-mq-skip Mon May 31 21:43:03 2010 +0200 @@ -2,6 +2,8 @@ # This emulates the effects of an hg pull --rebase in which the remote repo # already has one local mq patch +. $TESTDIR/helpers.sh + echo "[extensions]" >> $HGRCPATH echo "graphlog=" >> $HGRCPATH echo "rebase=" >> $HGRCPATH @@ -40,5 +42,5 @@ echo echo '% Rebase' hg up -C qtip -hg rebase 2>&1 | sed -e 's/\(saving bundle to \).*/\1/' +hg rebase | hidebackup hg glog --template '{rev} {desc} tags: {tags}\n' diff -r 0bb67503ad4b -r ac6fec2af8c8 tests/test-rebase-mq-skip.out --- a/tests/test-rebase-mq-skip.out Mon May 31 21:37:01 2010 +0200 +++ b/tests/test-rebase-mq-skip.out Mon May 31 21:43:03 2010 +0200 @@ -6,14 +6,8 @@ % Rebase 1 files updated, 0 files merged, 1 files removed, 0 files unresolved -saving bundle to -adding branch -adding changesets -adding manifests -adding file changes -added 2 changesets with 2 changes to 2 files -rebase completed -@ 3 P0 tags: p0.patch qtip tip qbase +saved backup bundle to +@ 3 P0 tags: p0.patch qbase qtip tip | o 2 P1 tags: qparent | diff -r 0bb67503ad4b -r ac6fec2af8c8 tests/test-rebase-mq.out --- a/tests/test-rebase-mq.out Mon May 31 21:37:01 2010 +0200 +++ b/tests/test-rebase-mq.out Mon May 31 21:43:03 2010 +0200 @@ -1,5 +1,5 @@ 1 files updated, 0 files merged, 0 files removed, 0 files unresolved -@ 3 P1 tags: qtip tip f2.patch +@ 3 P1 tags: f2.patch qtip tip | o 2 P0 tags: f.patch qbase | @@ -21,20 +21,14 @@ abort: fix unresolved conflicts with hg resolve then run hg rebase --continue % Fix the 1st conflict -merging f warning: conflicts during merge. merging f failed! abort: fix unresolved conflicts with hg resolve then run hg rebase --continue +merging f % Fix the 2nd conflict -saving bundle to -adding branch -adding changesets -adding manifests -adding file changes -added 2 changesets with 2 changes to 1 files -rebase completed -@ 3 P1 tags: qtip tip f2.patch +saved backup bundle to +@ 3 P1 tags: f2.patch qtip tip | o 2 P0 tags: f.patch qbase | @@ -107,7 +101,6 @@ +mq2 % Rebase the applied mq patches -saving bundle to % And the patches are correct % Git patch # HG changeset patch diff -r 0bb67503ad4b -r ac6fec2af8c8 tests/test-rebase-newancestor --- a/tests/test-rebase-newancestor Mon May 31 21:37:01 2010 +0200 +++ b/tests/test-rebase-newancestor Mon May 31 21:43:03 2010 +0200 @@ -1,5 +1,7 @@ #!/bin/sh +. $TESTDIR/helpers.sh + echo "[extensions]" >> $HGRCPATH echo "graphlog=" >> $HGRCPATH echo "rebase=" >> $HGRCPATH @@ -25,7 +27,7 @@ hg commit -AmAD -u test -d '3 0' hg glog --template '{rev}:{desc} {node|short}\n' -hg rebase -q -s 1 -d 3 2>&1 | grep -v 'saving bundle' +hg rebase -q -s 1 -d 3 | hidebackup hg glog --template '{rev}:{desc} {node|short}\n' exit 0 diff -r 0bb67503ad4b -r ac6fec2af8c8 tests/test-rebase-parameters --- a/tests/test-rebase-parameters Mon May 31 21:37:01 2010 +0200 +++ b/tests/test-rebase-parameters Mon May 31 21:43:03 2010 +0200 @@ -1,5 +1,7 @@ #!/bin/sh +. $TESTDIR/helpers.sh + echo "[extensions]" >> $HGRCPATH echo "rebase=" >> $HGRCPATH @@ -62,7 +64,7 @@ echo echo "% Rebase with no arguments (from 3 onto 7)" hg update -C 5 -hg rebase 2>&1 | sed 's/\(saving bundle to \).*/\1/' +hg rebase 2>&1 | hidebackup echo "% Try to rollback after a rebase (fail)" hg rollback @@ -71,38 +73,38 @@ echo echo "% Rebase with base == '.' => same as no arguments (from 3 onto 7)" hg update -C 5 -hg rebase --base . 2>&1 | sed 's/\(saving bundle to \).*/\1/' +hg rebase --base . | hidebackup createrepo > /dev/null 2>&1 echo echo "% Rebase with dest == `hg branch` => same as no arguments (from 3 onto 7)" hg update -C 5 -hg rebase --dest `hg branch` 2>&1 | sed 's/\(saving bundle to \).*/\1/' +hg rebase --dest `hg branch` | hidebackup createrepo > /dev/null 2>&1 echo echo "% Specify only source (from 4 onto 7)" -hg rebase --source 4 2>&1 | sed 's/\(saving bundle to \).*/\1/' +hg rebase --source 4 | hidebackup createrepo > /dev/null 2>&1 echo echo "% Specify only dest (from 3 onto 6)" hg update -C 5 -hg rebase --dest 6 2>&1 | sed 's/\(saving bundle to \).*/\1/' +hg rebase --dest 6 | hidebackup createrepo > /dev/null 2>&1 echo echo "% Specify only base (from 3 onto 7)" -hg rebase --base 5 2>&1 | sed 's/\(saving bundle to \).*/\1/' +hg rebase --base 5 | hidebackup createrepo > /dev/null 2>&1 echo echo "% Specify source and dest (from 4 onto 6)" -hg rebase --source 4 --dest 6 2>&1 | sed 's/\(saving bundle to \).*/\1/' +hg rebase --source 4 --dest 6 | hidebackup createrepo > /dev/null 2>&1 echo echo "% Specify base and dest (from 3 onto 6)" -hg rebase --base 4 --dest 6 2>&1 | sed 's/\(saving bundle to \).*/\1/' +hg rebase --base 4 --dest 6 | hidebackup exit 0 diff -r 0bb67503ad4b -r ac6fec2af8c8 tests/test-rebase-parameters.out --- a/tests/test-rebase-parameters.out Mon May 31 21:37:01 2010 +0200 +++ b/tests/test-rebase-parameters.out Mon May 31 21:43:03 2010 +0200 @@ -47,6 +47,8 @@ If a rebase is interrupted to manually resolve a merge, it can be continued with --continue/-c or aborted with --abort/-a. + Returns 0 on success, 1 if nothing to rebase. + options: -s --source rebase from the specified changeset @@ -111,6 +113,8 @@ If a rebase is interrupted to manually resolve a merge, it can be continued with --continue/-c or aborted with --abort/-a. + Returns 0 on success, 1 if nothing to rebase. + options: -s --source rebase from the specified changeset @@ -175,6 +179,8 @@ If a rebase is interrupted to manually resolve a merge, it can be continued with --continue/-c or aborted with --abort/-a. + Returns 0 on success, 1 if nothing to rebase. + options: -s --source rebase from the specified changeset @@ -239,6 +245,8 @@ If a rebase is interrupted to manually resolve a merge, it can be continued with --continue/-c or aborted with --abort/-a. + Returns 0 on success, 1 if nothing to rebase. + options: -s --source rebase from the specified changeset @@ -267,78 +275,30 @@ % Rebase with no arguments (from 3 onto 7) 3 files updated, 0 files merged, 2 files removed, 0 files unresolved -saving bundle to -adding branch -adding changesets -adding manifests -adding file changes -added 5 changesets with 5 changes to 5 files -rebase completed +saved backup bundle to % Try to rollback after a rebase (fail) no rollback information available % Rebase with base == '.' => same as no arguments (from 3 onto 7) 3 files updated, 0 files merged, 3 files removed, 0 files unresolved -saving bundle to -adding branch -adding changesets -adding manifests -adding file changes -added 5 changesets with 5 changes to 5 files -rebase completed +saved backup bundle to % Rebase with dest == default => same as no arguments (from 3 onto 7) 3 files updated, 0 files merged, 3 files removed, 0 files unresolved -saving bundle to -adding branch -adding changesets -adding manifests -adding file changes -added 5 changesets with 5 changes to 5 files -rebase completed +saved backup bundle to % Specify only source (from 4 onto 7) -saving bundle to -adding branch -adding changesets -adding manifests -adding file changes -added 4 changesets with 4 changes to 4 files (-1 heads) -rebase completed +saved backup bundle to % Specify only dest (from 3 onto 6) 3 files updated, 0 files merged, 3 files removed, 0 files unresolved -saving bundle to -adding branch -adding changesets -adding manifests -adding file changes -added 5 changesets with 5 changes to 5 files (+1 heads) -rebase completed +saved backup bundle to % Specify only base (from 3 onto 7) -saving bundle to -adding branch -adding changesets -adding manifests -adding file changes -added 5 changesets with 5 changes to 5 files -rebase completed +saved backup bundle to % Specify source and dest (from 4 onto 6) -saving bundle to -adding branch -adding changesets -adding manifests -adding file changes -added 4 changesets with 4 changes to 4 files -rebase completed +saved backup bundle to % Specify base and dest (from 3 onto 6) -saving bundle to -adding branch -adding changesets -adding manifests -adding file changes -added 5 changesets with 5 changes to 5 files (+1 heads) -rebase completed +saved backup bundle to diff -r 0bb67503ad4b -r ac6fec2af8c8 tests/test-rebase-pull --- a/tests/test-rebase-pull Mon May 31 21:37:01 2010 +0200 +++ b/tests/test-rebase-pull Mon May 31 21:43:03 2010 +0200 @@ -1,5 +1,7 @@ #!/bin/sh +. $TESTDIR/helpers.sh + echo "[extensions]" >> $HGRCPATH echo "graphlog=" >> $HGRCPATH echo "rebase=" >> $HGRCPATH @@ -34,14 +36,13 @@ cd ../b echo echo "% Now b has one revision to be pulled from a" -hg pull --rebase 2>&1 | sed -e 's/\(saving bundle to \).*/\1/' \ - -e 's/\(pulling from \).*/\1/' +hg pull --rebase | hidebackup | sed -e 's/\(pulling from \).*/\1/' hg glog --template '{rev}:{desc}\n' echo echo "% Re-run pull --rebase" -hg pull --rebase 2>&1 | sed 's/\(pulling from \).*/\1/' +hg pull --rebase | sed 's/\(pulling from \).*/\1/' echo echo "% Invoke pull --rebase and nothing to rebase" diff -r 0bb67503ad4b -r ac6fec2af8c8 tests/test-rebase-pull.out --- a/tests/test-rebase-pull.out Mon May 31 21:37:01 2010 +0200 +++ b/tests/test-rebase-pull.out Mon May 31 21:43:03 2010 +0200 @@ -10,14 +10,7 @@ adding manifests adding file changes added 1 changesets with 1 changes to 1 files (+1 heads) -(run 'hg heads' to see heads, 'hg merge' to merge) -saving bundle to -adding branch -adding changesets -adding manifests -adding file changes -added 2 changesets with 2 changes to 2 files -rebase completed +saved backup bundle to @ 3:L1 | o 2:R1 @@ -39,8 +32,8 @@ adding manifests adding file changes added 1 changesets with 1 changes to 1 files -(run 'hg update' to get a working copy) nothing to rebase +1 files updated, 0 files merged, 0 files removed, 0 files unresolved @ 2 | diff -r 0bb67503ad4b -r ac6fec2af8c8 tests/test-rebase-rename --- a/tests/test-rebase-rename Mon May 31 21:37:01 2010 +0200 +++ b/tests/test-rebase-rename Mon May 31 21:43:03 2010 +0200 @@ -1,5 +1,7 @@ #!/bin/sh +. $TESTDIR/helpers.sh + echo "[extensions]" >> $HGRCPATH echo "rebase=" >> $HGRCPATH echo "[diff]" >> $HGRCPATH @@ -7,12 +9,6 @@ BASE=`pwd` -cleanoutput () { - sed -e 's/\(Rebase status stored to\).*/\1/' \ - -e 's/\(Rebase status restored from\).*/\1/' \ - -e 's/\(saving bundle to \).*/\1/' -} - hg init repo1 cd repo1 echo "a">a @@ -28,7 +24,7 @@ hg log -p -r tip --template '{rev}:{desc}\n' echo '% Rebase the revision containing the rename' -hg rebase -s 2 -d 1 --quiet 2>&1 | cleanoutput +hg rebase -s 2 -d 1 --quiet | cleanrebase echo echo '% Rename is not lost' @@ -51,7 +47,7 @@ hg log -p -r tip --template '{rev}:{desc}\n' echo '% Rebase the revision containing the copy' -hg rebase -s 2 -d 1 --quiet 2>&1 | cleanoutput +hg rebase -s 2 -d 1 --quiet | cleanrebase echo echo '% Copy is not lost' diff -r 0bb67503ad4b -r ac6fec2af8c8 tests/test-rebase-rename.out --- a/tests/test-rebase-rename.out Mon May 31 21:37:01 2010 +0200 +++ b/tests/test-rebase-rename.out Mon May 31 21:43:03 2010 +0200 @@ -10,7 +10,6 @@ rename to a-renamed % Rebase the revision containing the rename -saving bundle to % Rename is not lost 2:rename A @@ -30,7 +29,6 @@ copy to a-copied % Rebase the revision containing the copy -saving bundle to % Copy is not lost 2:copy A diff -r 0bb67503ad4b -r ac6fec2af8c8 tests/test-rebase-scenario-global --- a/tests/test-rebase-scenario-global Mon May 31 21:37:01 2010 +0200 +++ b/tests/test-rebase-scenario-global Mon May 31 21:43:03 2010 +0200 @@ -1,5 +1,7 @@ #!/bin/sh +. $TESTDIR/helpers.sh + echo "[extensions]" >> $HGRCPATH echo "graphlog=" >> $HGRCPATH echo "rebase=" >> $HGRCPATH @@ -42,32 +44,32 @@ echo '% Rebasing' echo '% B onto F - simple rebase' -hg rebase -s 1 -d 5 2>&1 | sed 's/\(saving bundle to \).*/\1/' +hg rebase -s 1 -d 5 | hidebackup hg glog --template '{rev}: {desc}\n' createrepo > /dev/null 2>&1 echo '% B onto D - intermediate point' -hg rebase -s 1 -d 3 2>&1 | sed 's/\(saving bundle to \).*/\1/' +hg rebase -s 1 -d 3 | hidebackup hg glog --template '{rev}: {desc}\n' createrepo > /dev/null 2>&1 echo '% C onto F - skip of E' -hg rebase -s 2 -d 5 2>&1 | sed 's/\(saving bundle to \).*/\1/' +hg rebase -s 2 -d 5 | hidebackup hg glog --template '{rev}: {desc}\n' createrepo > /dev/null 2>&1 echo '% D onto C - rebase of a branching point (skip E)' -hg rebase -s 3 -d 2 2>&1 | sed 's/\(saving bundle to \).*/\1/' +hg rebase -s 3 -d 2 | hidebackup hg glog --template '{rev}: {desc}\n' createrepo > /dev/null 2>&1 echo '% E onto F - merged revision having a parent in ancestors of target' -hg rebase -s 4 -d 5 2>&1 | sed 's/\(saving bundle to \).*/\1/' +hg rebase -s 4 -d 5 | hidebackup hg glog --template '{rev}: {desc}\n' createrepo > /dev/null 2>&1 echo '% D onto B - E maintains C as parent' -hg rebase -s 3 -d 1 2>&1 | sed 's/\(saving bundle to \).*/\1/' +hg rebase -s 3 -d 1 | hidebackup hg glog --template '{rev}: {desc}\n' echo '% These will fail (using --source)' diff -r 0bb67503ad4b -r ac6fec2af8c8 tests/test-rebase-scenario-global.out --- a/tests/test-rebase-scenario-global.out Mon May 31 21:37:01 2010 +0200 +++ b/tests/test-rebase-scenario-global.out Mon May 31 21:43:03 2010 +0200 @@ -12,13 +12,7 @@ % Rebasing % B onto F - simple rebase -saving bundle to -adding branch -adding changesets -adding manifests -adding file changes -added 5 changesets with 4 changes to 4 files (+1 heads) -rebase completed +saved backup bundle to @ 5: B | o 4: F @@ -32,13 +26,7 @@ o 0: A % B onto D - intermediate point -saving bundle to -adding branch -adding changesets -adding manifests -adding file changes -added 5 changesets with 4 changes to 4 files (+2 heads) -rebase completed +saved backup bundle to @ 5: B | | o 4: F @@ -52,13 +40,7 @@ o 0: A % C onto F - skip of E -saving bundle to -adding branch -adding changesets -adding manifests -adding file changes -added 3 changesets with 3 changes to 3 files (+1 heads) -rebase completed +saved backup bundle to @ 4: C | o 3: F @@ -70,13 +52,7 @@ o 0: A % D onto C - rebase of a branching point (skip E) -saving bundle to -adding branch -adding changesets -adding manifests -adding file changes -added 2 changesets with 2 changes to 2 files -rebase completed +saved backup bundle to @ 4: F | o 3: D @@ -88,13 +64,7 @@ o 0: A % E onto F - merged revision having a parent in ancestors of target -saving bundle to -adding branch -adding changesets -adding manifests -adding file changes -added 2 changesets with 1 changes to 1 files (-1 heads) -rebase completed +saved backup bundle to @ 5: E |\ | o 4: F @@ -108,13 +78,7 @@ o 0: A % D onto B - E maintains C as parent -saving bundle to -adding branch -adding changesets -adding manifests -adding file changes -added 3 changesets with 2 changes to 2 files -rebase completed +saved backup bundle to @ 5: F | | o 4: E diff -r 0bb67503ad4b -r ac6fec2af8c8 tests/test-record --- a/tests/test-record Mon May 31 21:37:01 2010 +0200 +++ b/tests/test-record Mon May 31 21:43:03 2010 +0200 @@ -37,6 +37,10 @@ EOF echo; hg tip -p +echo % summary shows we updated to the new cset +hg summary +echo + echo % rename empty file hg mv empty-rw empty-rename @@ -326,4 +330,3 @@ y EOF echo; hg tip -p - diff -r 0bb67503ad4b -r ac6fec2af8c8 tests/test-record.out --- a/tests/test-record.out Mon May 31 21:37:01 2010 +0200 +++ b/tests/test-record.out Mon May 31 21:43:03 2010 +0200 @@ -6,7 +6,7 @@ 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. + 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 @@ -41,7 +41,8 @@ % select no files diff --git a/empty-rw b/empty-rw new file mode 100644 -examine changes to 'empty-rw'? [Ynsfdaq?] no changes to record +examine changes to 'empty-rw'? [Ynsfdaq?] +no changes to record changeset: -1:000000000000 tag: tip @@ -52,7 +53,8 @@ % select files but no hunks diff --git a/empty-rw b/empty-rw new file mode 100644 -examine changes to 'empty-rw'? [Ynsfdaq?] abort: empty commit message +examine changes to 'empty-rw'? [Ynsfdaq?] +abort: empty commit message changeset: -1:000000000000 tag: tip @@ -64,6 +66,7 @@ diff --git a/empty-rw b/empty-rw new file mode 100644 examine changes to 'empty-rw'? [Ynsfdaq?] + changeset: 0:c0708cf4e46e tag: tip user: test @@ -71,11 +74,19 @@ summary: empty +% summary shows we updated to the new cset +parent: 0:c0708cf4e46e tip + empty +branch: default +commit: (clean) +update: (current) + % rename empty file diff --git a/empty-rw b/empty-rename rename from empty-rw rename to empty-rename examine changes to 'empty-rw' and 'empty-rename'? [Ynsfdaq?] + changeset: 1:d695e8dcb197 tag: tip user: test @@ -88,6 +99,7 @@ copy from empty-rename copy to empty-copy examine changes to 'empty-rename' and 'empty-copy'? [Ynsfdaq?] + changeset: 2:1d4b90bea524 tag: tip user: test @@ -99,6 +111,7 @@ diff --git a/empty-copy b/empty-copy deleted file mode 100644 examine changes to 'empty-copy'? [Ynsfdaq?] + changeset: 3:b39a238f01a1 tag: tip user: test @@ -112,6 +125,7 @@ new file mode 100644 this is a binary file examine changes to 'tip.bundle'? [Ynsfdaq?] + changeset: 4:ad816da3711e tag: tip user: test @@ -126,6 +140,7 @@ diff --git a/tip.bundle b/tip.bundle this modifies a binary file (all or nothing) examine changes to 'tip.bundle'? [Ynsfdaq?] + changeset: 5:dccd6f3eb485 tag: tip user: test @@ -142,6 +157,7 @@ rename to top.bundle this modifies a binary file (all or nothing) examine changes to 'tip.bundle' and 'top.bundle'? [Ynsfdaq?] + changeset: 6:7fa44105f5b3 tag: tip user: test @@ -157,6 +173,7 @@ diff --git a/plain b/plain new file mode 100644 examine changes to 'plain'? [Ynsfdaq?] + changeset: 7:11fb457c1be4 tag: tip user: test @@ -181,46 +198,55 @@ % 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 @@ +examine changes to 'plain'? [Ynsfdaq?] +@@ -8,3 +8,4 @@ 8 9 10 +11 -record this change to 'plain'? [Ynsfdaq?] % modify end of plain file, no EOL +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 @@ +examine changes to 'plain'? [Ynsfdaq?] +@@ -9,3 +9,4 @@ 9 10 11 +7264f99c5f5ff3261504828afa4fb4d406c3af54 \ No newline at end of file -record this change to 'plain'? [Ynsfdaq?] % modify end of plain file, add EOL +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 @@ +examine changes to 'plain'? [Ynsfdaq?] +@@ -9,4 +9,4 @@ 9 10 11 -7264f99c5f5ff3261504828afa4fb4d406c3af54 \ No newline at end of file +7264f99c5f5ff3261504828afa4fb4d406c3af54 -record this change to 'plain'? [Ynsfdaq?] % modify beginning, trim end, record both +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 @@ +examine changes to 'plain'? [Ynsfdaq?] +@@ -1,4 +1,4 @@ -1 +2 2 3 4 -record change 1/2 to 'plain'? [Ynsfdaq?] @@ -8,5 +8,3 @@ +record change 1/2 to 'plain'? [Ynsfdaq?] +@@ -8,5 +8,3 @@ 8 9 10 -11 -7264f99c5f5ff3261504828afa4fb4d406c3af54 record change 2/2 to 'plain'? [Ynsfdaq?] + changeset: 11:efca65c9b09e tag: tip user: test @@ -247,7 +273,8 @@ % record end diff --git a/plain b/plain 2 hunks, 5 lines changed -examine changes to 'plain'? [Ynsfdaq?] @@ -1,9 +1,6 @@ +examine changes to 'plain'? [Ynsfdaq?] +@@ -1,9 +1,6 @@ -2 -2 -3 @@ -257,7 +284,8 @@ 7 8 9 -record change 1/2 to 'plain'? [Ynsfdaq?] @@ -4,7 +1,7 @@ +record change 1/2 to 'plain'? [Ynsfdaq?] +@@ -4,7 +1,7 @@ 4 5 6 @@ -267,6 +295,7 @@ -10 +10.new record change 2/2 to 'plain'? [Ynsfdaq?] + changeset: 12:7d1e66983c15 tag: tip user: test @@ -286,7 +315,8 @@ % record beginning diff --git a/plain b/plain 1 hunks, 3 lines changed -examine changes to 'plain'? [Ynsfdaq?] @@ -1,6 +1,3 @@ +examine changes to 'plain'? [Ynsfdaq?] +@@ -1,6 +1,3 @@ -2 -2 -3 @@ -294,6 +324,7 @@ 5 6 record this change to 'plain'? [Ynsfdaq?] + changeset: 13:a09fc62a0e61 tag: tip user: test @@ -315,7 +346,8 @@ % record end diff --git a/plain b/plain 2 hunks, 4 lines changed -examine changes to 'plain'? [Ynsfdaq?] @@ -1,6 +1,9 @@ +examine changes to 'plain'? [Ynsfdaq?] +@@ -1,6 +1,9 @@ +1 +2 +3 @@ -325,7 +357,8 @@ 7 8 9 -record change 1/2 to 'plain'? [Ynsfdaq?] @@ -1,7 +4,6 @@ +record change 1/2 to 'plain'? [Ynsfdaq?] +@@ -1,7 +4,6 @@ 4 5 6 @@ -333,17 +366,20 @@ 8 9 -10.new -record change 2/2 to 'plain'? [Ynsfdaq?] % add to beginning, middle, end +record change 2/2 to 'plain'? [Ynsfdaq?] +% add to beginning, middle, end % record beginning, middle diff --git a/plain b/plain 3 hunks, 7 lines changed -examine changes to 'plain'? [Ynsfdaq?] @@ -1,2 +1,5 @@ +examine changes to 'plain'? [Ynsfdaq?] +@@ -1,2 +1,5 @@ +1 +2 +3 4 5 -record change 1/3 to 'plain'? [Ynsfdaq?] @@ -1,6 +4,8 @@ +record change 1/3 to 'plain'? [Ynsfdaq?] +@@ -1,6 +4,8 @@ 4 5 +5.new @@ -352,7 +388,8 @@ 7 8 9 -record change 2/3 to 'plain'? [Ynsfdaq?] @@ -3,4 +8,6 @@ +record change 2/3 to 'plain'? [Ynsfdaq?] +@@ -3,4 +8,6 @@ 6 7 8 @@ -360,6 +397,7 @@ +10 +11 record change 3/3 to 'plain'? [Ynsfdaq?] + changeset: 15:7d137997f3a6 tag: tip user: test @@ -384,13 +422,15 @@ % record end diff --git a/plain b/plain 1 hunks, 2 lines changed -examine changes to 'plain'? [Ynsfdaq?] @@ -9,3 +9,5 @@ +examine changes to 'plain'? [Ynsfdaq?] +@@ -9,3 +9,5 @@ 7 8 9 +10 +11 record this change to 'plain'? [Ynsfdaq?] + changeset: 16:4959e3ff13eb tag: tip user: test @@ -410,10 +450,12 @@ 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 @@ +examine changes to 'subdir/a'? [Ynsfdaq?] +@@ -1,1 +1,2 @@ a +a record this change to 'subdir/a'? [Ynsfdaq?] + changeset: 18:40698cd490b2 tag: tip user: test @@ -430,7 +472,8 @@ % help, quit diff --git a/subdir/f1 b/subdir/f1 1 hunks, 1 lines changed -examine changes to 'subdir/f1'? [Ynsfdaq?] y - record this change +examine changes to 'subdir/f1'? [Ynsfdaq?] +y - record this change n - skip this change s - skip remaining changes to this file f - record remaining changes to this file @@ -438,31 +481,38 @@ a - record all changes to all remaining files q - quit, recording no changes ? - display help -examine changes to 'subdir/f1'? [Ynsfdaq?] abort: user quit +examine changes to 'subdir/f1'? [Ynsfdaq?] +abort: user quit % skip diff --git a/subdir/f1 b/subdir/f1 1 hunks, 1 lines changed -examine changes to 'subdir/f1'? [Ynsfdaq?] diff --git a/subdir/f2 b/subdir/f2 +examine changes to 'subdir/f1'? [Ynsfdaq?] +diff --git a/subdir/f2 b/subdir/f2 1 hunks, 1 lines changed examine changes to 'subdir/f2'? [Ynsfdaq?] abort: response expected % no diff --git a/subdir/f1 b/subdir/f1 1 hunks, 1 lines changed -examine changes to 'subdir/f1'? [Ynsfdaq?] diff --git a/subdir/f2 b/subdir/f2 +examine changes to 'subdir/f1'? [Ynsfdaq?] +diff --git a/subdir/f2 b/subdir/f2 1 hunks, 1 lines changed examine changes to 'subdir/f2'? [Ynsfdaq?] abort: response expected % f, quit diff --git a/subdir/f1 b/subdir/f1 1 hunks, 1 lines changed -examine changes to 'subdir/f1'? [Ynsfdaq?] diff --git a/subdir/f2 b/subdir/f2 +examine changes to 'subdir/f1'? [Ynsfdaq?] +diff --git a/subdir/f2 b/subdir/f2 1 hunks, 1 lines changed -examine changes to 'subdir/f2'? [Ynsfdaq?] abort: user quit +examine changes to 'subdir/f2'? [Ynsfdaq?] +abort: user quit % s, all diff --git a/subdir/f1 b/subdir/f1 1 hunks, 1 lines changed -examine changes to 'subdir/f1'? [Ynsfdaq?] diff --git a/subdir/f2 b/subdir/f2 +examine changes to 'subdir/f1'? [Ynsfdaq?] +diff --git a/subdir/f2 b/subdir/f2 1 hunks, 1 lines changed examine changes to 'subdir/f2'? [Ynsfdaq?] + changeset: 20:d2d8c25276a8 tag: tip user: test @@ -480,6 +530,7 @@ diff --git a/subdir/f1 b/subdir/f1 1 hunks, 1 lines changed examine changes to 'subdir/f1'? [Ynsfdaq?] + changeset: 21:1013f51ce32f tag: tip user: test @@ -498,11 +549,13 @@ old mode 100644 new mode 100755 1 hunks, 1 lines changed -examine changes to 'subdir/f1'? [Ynsfdaq?] @@ -1,2 +1,3 @@ +examine changes to 'subdir/f1'? [Ynsfdaq?] +@@ -1,2 +1,3 @@ a a +a record this change to 'subdir/f1'? [Ynsfdaq?] + changeset: 22:5df857735621 tag: tip user: test @@ -522,12 +575,14 @@ % preserve execute permission on original diff --git a/subdir/f1 b/subdir/f1 1 hunks, 1 lines changed -examine changes to 'subdir/f1'? [Ynsfdaq?] @@ -1,3 +1,4 @@ +examine changes to 'subdir/f1'? [Ynsfdaq?] +@@ -1,3 +1,4 @@ a a a +b record this change to 'subdir/f1'? [Ynsfdaq?] + changeset: 23:a4ae36a78715 tag: tip user: test @@ -548,12 +603,14 @@ old mode 100755 new mode 100644 1 hunks, 1 lines changed -examine changes to 'subdir/f1'? [Ynsfdaq?] @@ -2,3 +2,4 @@ +examine changes to 'subdir/f1'? [Ynsfdaq?] +@@ -2,3 +2,4 @@ a a b +c record this change to 'subdir/f1'? [Ynsfdaq?] + changeset: 24:1460f6e47966 tag: tip user: test @@ -574,7 +631,6 @@ % abort early when a merge is in progress 1 files updated, 0 files merged, 5 files removed, 0 files unresolved marked working directory as branch thatbranch -created new head 5 files updated, 0 files merged, 2 files removed, 0 files unresolved 1 files updated, 0 files merged, 0 files removed, 0 files unresolved (branch merge, don't forget to commit) @@ -584,12 +640,14 @@ % with win32ext diff --git a/subdir/f1 b/subdir/f1 1 hunks, 1 lines changed -examine changes to 'subdir/f1'? [Ynsfdaq?] @@ -3,3 +3,4 @@ +examine changes to 'subdir/f1'? [Ynsfdaq?] +@@ -3,3 +3,4 @@ a b c +d record this change to 'subdir/f1'? [Ynsfdaq?] + changeset: 26:5bacc1f6e9cf tag: tip parent: 24:1460f6e47966 diff -r 0bb67503ad4b -r ac6fec2af8c8 tests/test-rename --- a/tests/test-rename Mon May 31 21:37:01 2010 +0200 +++ b/tests/test-rename Mon May 31 21:43:03 2010 +0200 @@ -23,6 +23,17 @@ hg update -C rm d2/c +echo '# rename --after a single file when src and tgt already tracked' +mv d1/d11/a1 d2/c +hg addrem +hg rename --after d1/d11/a1 d2/c +hg status -C +hg update -C +rm d2/c + +echo "# rename --after a single file to a nonexistant target filename" +hg rename --after d1/a dummy + echo "# move a single file to an existing directory" hg rename d1/d11/a1 d2 hg status -C diff -r 0bb67503ad4b -r ac6fec2af8c8 tests/test-rename-after-merge.out --- a/tests/test-rename-after-merge.out Mon May 31 21:37:01 2010 +0200 +++ b/tests/test-rename-after-merge.out Mon May 31 21:43:03 2010 +0200 @@ -34,7 +34,7 @@ (branch merge, don't forget to commit) % commit issue 1476 copies: c2 (c1) -rolling back last transaction +rolling back to revision 2 (undo commit) 2 files updated, 0 files merged, 2 files removed, 0 files unresolved % merge heads again 2 files updated, 0 files merged, 0 files removed, 0 files unresolved diff -r 0bb67503ad4b -r ac6fec2af8c8 tests/test-rename.out --- a/tests/test-rename.out Mon May 31 21:37:01 2010 +0200 +++ b/tests/test-rename.out Mon May 31 21:43:03 2010 +0200 @@ -8,6 +8,15 @@ d1/d11/a1 R d1/d11/a1 1 files updated, 0 files merged, 0 files removed, 0 files unresolved +# rename --after a single file when src and tgt already tracked +removing d1/d11/a1 +adding d2/c +A d2/c + d1/d11/a1 +R d1/d11/a1 +1 files updated, 0 files merged, 0 files removed, 0 files unresolved +# rename --after a single file to a nonexistant target filename +d1/a: not recording move - dummy does not exist # move a single file to an existing directory A d2/a1 d1/d11/a1 @@ -170,6 +179,8 @@ 4 files updated, 0 files merged, 0 files removed, 0 files unresolved # move --after some files under d1 to d2/d21 (glob) moving d1/a to d2/d21/a +d1/b: not recording move - d2/d21/b does not exist +d1/ba: not recording move - d2/d21/ba does not exist moving d1/d11/a1 to d2/d21/a1 A d2/d21/a d1/a diff -r 0bb67503ad4b -r ac6fec2af8c8 tests/test-repair-strip --- a/tests/test-repair-strip Mon May 31 21:37:01 2010 +0200 +++ b/tests/test-repair-strip Mon May 31 21:43:03 2010 +0200 @@ -8,7 +8,7 @@ echo % before update $1, strip $2 hg parents chmod -$3 $4 - hg strip $2 2>&1 | sed 's/\(saving bundle to \).*/\1/' | sed 's/Permission denied.*\.hg\/store\/\(.*\)/Permission denied \.hg\/store\/\1/' + hg strip $2 2>&1 | sed 's/\(bundle\).*/\1/' | sed 's/Permission denied.*\.hg\/store\/\(.*\)/Permission denied \.hg\/store\/\1/' echo % after update $1, strip $2 chmod +$3 $4 hg verify diff -r 0bb67503ad4b -r ac6fec2af8c8 tests/test-repair-strip.out --- a/tests/test-repair-strip.out Mon May 31 21:37:01 2010 +0200 +++ b/tests/test-repair-strip.out Mon May 31 21:43:03 2010 +0200 @@ -4,10 +4,11 @@ date: Thu Jan 01 00:00:00 1970 +0000 summary: a -saving bundle to +saved backup bundle transaction abort! failed to truncate data/b.i rollback failed - please run hg recover +strip failed, full bundle abort: Permission denied .hg/store/data/b.i % after update 0, strip 2 abandoned transaction found - run hg recover @@ -54,10 +55,11 @@ date: Thu Jan 01 00:00:00 1970 +0000 summary: a -saving bundle to +saved backup bundle transaction abort! failed to truncate 00manifest.i rollback failed - please run hg recover +strip failed, full bundle abort: Permission denied .hg/store/00manifest.i % after update 0, strip 2 abandoned transaction found - run hg recover diff -r 0bb67503ad4b -r ac6fec2af8c8 tests/test-rollback.out --- a/tests/test-rollback.out Mon May 31 21:37:01 2010 +0200 +++ b/tests/test-rollback.out Mon May 31 21:43:03 2010 +0200 @@ -9,7 +9,7 @@ date: Mon Jan 12 13:46:40 1970 +0000 summary: test -rolling back last transaction +rolling back to revision -1 (undo commit) checking changesets checking manifests crosschecking files in changesets and manifests @@ -18,14 +18,14 @@ A a % Test issue 902 marked working directory as branch test -rolling back last transaction +rolling back to revision -1 (undo commit) default % Test issue 1635 (commit message saved) .hg/last-message.txt: test2 % Test rollback of hg before issue 902 was fixed marked working directory as branch test -rolling back last transaction +rolling back to revision -1 (undo commit) Named branch could not be reset, current branch still is: test test % rollback by pretxncommit saves commit message (issue 1635) diff -r 0bb67503ad4b -r ac6fec2af8c8 tests/test-serve --- a/tests/test-serve Mon May 31 21:37:01 2010 +0200 +++ b/tests/test-serve Mon May 31 21:43:03 2010 +0200 @@ -2,8 +2,10 @@ 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\//' + hg serve -a localhost -d --pid-file=hg.pid -E errors.log -v $@ \ + | sed -e "s/:$HGPORT1\\([^0-9]\\)/:HGPORT1\1/g" \ + -e "s/:$HGPORT2\\([^0-9]\\)/:HGPORT2\1/g" \ + -e 's/http:\/\/[^/]*\//http:\/\/localhost\//' cat hg.pid >> "$DAEMON_PIDS" echo % errors cat errors.log @@ -17,6 +19,7 @@ echo '[web]' > .hg/hgrc echo 'accesslog = access.log' >> .hg/hgrc +echo "port = $HGPORT1" >> .hg/hgrc echo % Without -v hg serve -a localhost -p $HGPORT -d --pid-file=hg.pid -E errors.log @@ -30,6 +33,9 @@ echo % With -v hgserve +echo % With -v and -p HGPORT2 +hgserve -p "$HGPORT2" + echo % With --prefix foo hgserve --prefix foo diff -r 0bb67503ad4b -r ac6fec2af8c8 tests/test-serve.out --- a/tests/test-serve.out Mon May 31 21:37:01 2010 +0200 +++ b/tests/test-serve.out Mon May 31 21:43:03 2010 +0200 @@ -2,17 +2,20 @@ access log created - .hg/hgrc respected % errors % With -v -listening at http://localhost/ (bound to 127.0.0.1) +listening at http://localhost/ (bound to 127.0.0.1:HGPORT1) +% errors +% With -v and -p HGPORT2 +listening at http://localhost/ (bound to 127.0.0.1:HGPORT2) % errors % With --prefix foo -listening at http://localhost/foo/ (bound to 127.0.0.1) +listening at http://localhost/foo/ (bound to 127.0.0.1:HGPORT1) % errors % With --prefix /foo -listening at http://localhost/foo/ (bound to 127.0.0.1) +listening at http://localhost/foo/ (bound to 127.0.0.1:HGPORT1) % errors % With --prefix foo/ -listening at http://localhost/foo/ (bound to 127.0.0.1) +listening at http://localhost/foo/ (bound to 127.0.0.1:HGPORT1) % errors % With --prefix /foo/ -listening at http://localhost/foo/ (bound to 127.0.0.1) +listening at http://localhost/foo/ (bound to 127.0.0.1:HGPORT1) % errors diff -r 0bb67503ad4b -r ac6fec2af8c8 tests/test-status-color --- a/tests/test-status-color Mon May 31 21:37:01 2010 +0200 +++ b/tests/test-status-color Mon May 31 21:43:03 2010 +0200 @@ -2,6 +2,8 @@ echo "[extensions]" >> $HGRCPATH echo "color=" >> $HGRCPATH +echo "[color]" >> $HGRCPATH +echo "mode=ansi" >> $HGRCPATH hg init repo1 cd repo1 diff -r 0bb67503ad4b -r ac6fec2af8c8 tests/test-status-inprocess.py --- a/tests/test-status-inprocess.py Mon May 31 21:37:01 2010 +0200 +++ b/tests/test-status-inprocess.py Mon May 31 21:43:03 2010 +0200 @@ -1,5 +1,4 @@ #!/usr/bin/python -import os from mercurial.ui import ui from mercurial.localrepo import localrepository from mercurial.commands import add, commit, status diff -r 0bb67503ad4b -r ac6fec2af8c8 tests/test-strict.out --- a/tests/test-strict.out Mon May 31 21:37:01 2010 +0200 +++ b/tests/test-strict.out Mon May 31 21:43:03 2010 +0200 @@ -18,10 +18,10 @@ 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 + serve start stand-alone webserver status show changed files in the working directory summary summarize working directory state - update update working directory + update update working directory (or switch revisions) use "hg help" for the full list of commands or "hg -v" for details 0: a diff -r 0bb67503ad4b -r ac6fec2af8c8 tests/test-strip-cross --- a/tests/test-strip-cross Mon May 31 21:37:01 2010 +0200 +++ b/tests/test-strip-cross Mon May 31 21:43:03 2010 +0200 @@ -2,17 +2,13 @@ # test stripping of filelogs where the linkrev doesn't always increase +. $TESTDIR/helpers.sh 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 @@ -60,7 +56,7 @@ 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 + hg --cwd $i strip $i | hidebackup echo "% Verifying" hg --cwd $i verify echo diff -r 0bb67503ad4b -r ac6fec2af8c8 tests/test-strip-cross.out --- a/tests/test-strip-cross.out Mon May 31 21:37:01 2010 +0200 +++ b/tests/test-strip-cross.out Mon May 31 21:43:03 2010 +0200 @@ -48,13 +48,7 @@ 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) +saved backup bundle to % Verifying checking changesets checking manifests @@ -63,13 +57,7 @@ 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) +saved backup bundle to % Verifying checking changesets checking manifests @@ -78,13 +66,7 @@ 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) +saved backup bundle to % Verifying checking changesets checking manifests @@ -93,13 +75,7 @@ 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) +saved backup bundle to % Verifying checking changesets checking manifests @@ -108,7 +84,7 @@ 7 files, 4 changesets, 19 total revisions % Trying to strip revision 4 -saving bundle to strip-backup/9147ea23c156-backup +saved backup bundle to % Verifying checking changesets checking manifests diff -r 0bb67503ad4b -r ac6fec2af8c8 tests/test-subrepo --- a/tests/test-subrepo Mon May 31 21:37:01 2010 +0200 +++ b/tests/test-subrepo Mon May 31 21:43:03 2010 +0200 @@ -16,6 +16,7 @@ hg init s echo a > s/a hg -R s ci -Ams0 +hg sum hg ci -m1 echo % add sub sub @@ -24,7 +25,9 @@ echo a > s/ss/a hg -R s add s/.hgsub hg -R s/ss add s/ss/a +hg sum hg ci -m2 +hg sum echo % bump sub rev echo b > s/a @@ -78,7 +81,7 @@ echo % clone cd .. -hg clone t tc +hg clone t tc | sed 's|from .*/sub|from .../sub|g' cd tc hg debugsub @@ -102,7 +105,8 @@ echo % pull cd ../tc hg pull | sed 's/ .*sub/ ...sub/g' -hg up # should pull t +# should pull t +hg up | sed 's|from .*/sub|from .../sub|g' cat t/t echo % bogus subrepo path aborts @@ -214,7 +218,7 @@ hg -R repo/s ci -Am0 echo s = s > repo/.hgsub hg -R repo ci -Am1 -hg clone repo repo2 +hg clone repo repo2 | sed 's|from .*/sub|from .../sub|g' hg -q -R repo2 pull -u echo 1 > repo2/s/a hg -R repo2/s ci -m2 diff -r 0bb67503ad4b -r ac6fec2af8c8 tests/test-subrepo-deep-nested-change --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tests/test-subrepo-deep-nested-change Mon May 31 21:43:03 2010 +0200 @@ -0,0 +1,57 @@ +#!/bin/sh + +echo % Preparing the subrepository 'sub2' +hg init sub2 +echo sub2 > sub2/sub2 +hg add -R sub2 +hg commit -R sub2 -m "sub2 import" + +echo % Preparing the 'sub1' repo which depends on the subrepo 'sub2' +hg init sub1 +echo sub1 > sub1/sub1 +echo "sub2 = ../sub2" > sub1/.hgsub +hg clone sub2 sub1/sub2 | sed 's/ .*sub/ ...sub/g' +hg add -R sub1 +hg commit -R sub1 -m "sub1 import" + +echo % Preparing the 'main' repo which depends on the subrepo 'sub1' +hg init main +echo main > main/main +echo "sub1 = ../sub1" > main/.hgsub +hg clone sub1 main/sub1 | sed 's/ .*sub/ ...sub/g' +hg add -R main +hg commit -R main -m "main import" + +echo % Cleaning both repositories, just as a clone -U +hg up -C -R sub2 null +hg up -C -R sub1 null +hg up -C -R main null +rm -rf main/sub1 +rm -rf sub1/sub2 + +echo % Clone main +hg clone main cloned | sed 's/ .*sub/ ...sub/g' + +echo % Checking cloned repo ids +printf "cloned " ; hg id -R cloned +printf "cloned/sub1 " ; hg id -R cloned/sub1 +printf "cloned/sub1/sub2 " ; hg id -R cloned/sub1/sub2 + +echo % debugsub output for main and sub1 +hg debugsub -R cloned +hg debugsub -R cloned/sub1 + +echo % Modifying deeply nested 'sub2' +echo modified > cloned/sub1/sub2/sub2 +hg commit -m "deep nested modif should trigger a commit" -R cloned + +echo % Checking modified node ids +printf "cloned " ; hg id -R cloned +printf "cloned/sub1 " ; hg id -R cloned/sub1 +printf "cloned/sub1/sub2 " ; hg id -R cloned/sub1/sub2 + +echo % debugsub output for main and sub1 +hg debugsub -R cloned +hg debugsub -R cloned/sub1 + +exit 0 diff -r 0bb67503ad4b -r ac6fec2af8c8 tests/test-subrepo-deep-nested-change.out --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tests/test-subrepo-deep-nested-change.out Mon May 31 21:43:03 2010 +0200 @@ -0,0 +1,64 @@ +% Preparing the subrepository sub2 +adding sub2/sub2 +% Preparing the sub1 repo which depends on the subrepo sub2 +updating to branch default +1 files updated, 0 files merged, 0 files removed, 0 files unresolved +adding sub1/.hgsub +adding sub1/sub1 +committing subrepository sub2 +% Preparing the main repo which depends on the subrepo sub1 +updating to branch default +pulling ...sub2 +requesting all changes +adding changesets +adding manifests +adding file changes +added 1 changesets with 1 changes to 1 files +3 files updated, 0 files merged, 0 files removed, 0 files unresolved +adding main/.hgsub +adding main/main +committing subrepository sub1 +% Cleaning both repositories, just as a clone -U +0 files updated, 0 files merged, 1 files removed, 0 files unresolved +0 files updated, 0 files merged, 3 files removed, 0 files unresolved +0 files updated, 0 files merged, 3 files removed, 0 files unresolved +% Clone main +updating to branch default +pulling ...sub1 +requesting all changes +adding changesets +adding manifests +adding file changes +added 1 changesets with 3 changes to 3 files +pulling ...sub2 +requesting all changes +adding changesets +adding manifests +adding file changes +added 1 changesets with 1 changes to 1 files +3 files updated, 0 files merged, 0 files removed, 0 files unresolved +% Checking cloned repo ids +cloned 7f491f53a367 tip +cloned/sub1 fc3b4ce2696f tip +cloned/sub1/sub2 c57a0840e3ba tip +% debugsub output for main and sub1 +path sub1 + source ../sub1 + revision fc3b4ce2696f7741438c79207583768f2ce6b0dd +path sub2 + source ../sub2 + revision c57a0840e3badd667ef3c3ef65471609acb2ba3c +% Modifying deeply nested sub2 +committing subrepository sub1 +committing subrepository sub1/sub2 +% Checking modified node ids +cloned ffe6649062fe tip +cloned/sub1 2ecb03bf44a9 tip +cloned/sub1/sub2 53dd3430bcaf tip +% debugsub output for main and sub1 +path sub1 + source ../sub1 + revision 2ecb03bf44a94e749e8669481dd9069526ce7cb9 +path sub2 + source ../sub2 + revision 53dd3430bcaf5ab4a7c48262bcad6d441f510487 diff -r 0bb67503ad4b -r ac6fec2af8c8 tests/test-subrepo-relative-path --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tests/test-subrepo-relative-path Mon May 31 21:43:03 2010 +0200 @@ -0,0 +1,40 @@ +#!/bin/sh + +echo % Preparing the subrepository 'sub' +hg init sub +echo sub > sub/sub +hg add -R sub +hg commit -R sub -m "sub import" + +echo % Preparing the 'main' repo which depends on the subrepo 'sub' +hg init main +echo main > main/main +echo "sub = ../sub" > main/.hgsub +hg clone sub main/sub | sed 's/ .*sub/ ...sub/g' +hg add -R main +hg commit -R main -m "main import" + +echo % Cleaning both repositories, just as a clone -U +hg up -C -R sub null +hg up -C -R main null +rm -rf main/sub + +echo % Serving them both using hgweb +printf '[paths]\n/main = main\nsub = sub\n' > webdir.conf +hg serve --webdir-conf webdir.conf -a localhost -p $HGPORT \ + -A /dev/null -E /dev/null --pid-file hg.pid -d +cat hg.pid >> $DAEMON_PIDS + +echo % Clone main from hgweb +hg clone "http://localhost:$HGPORT/main" cloned | sed 's/ .*sub/ ...sub/g' + +echo % Checking cloned repo ids +hg id -R cloned +hg id -R cloned/sub + +echo % subrepo debug for 'main' clone +hg debugsub -R cloned + +"$TESTDIR/killdaemons.py" + +exit 0 diff -r 0bb67503ad4b -r ac6fec2af8c8 tests/test-subrepo-relative-path.out --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tests/test-subrepo-relative-path.out Mon May 31 21:43:03 2010 +0200 @@ -0,0 +1,33 @@ +% Preparing the subrepository sub +adding sub/sub +% Preparing the main repo which depends on the subrepo sub +updating to branch default +1 files updated, 0 files merged, 0 files removed, 0 files unresolved +adding main/.hgsub +adding main/main +committing subrepository sub +% Cleaning both repositories, just as a clone -U +0 files updated, 0 files merged, 1 files removed, 0 files unresolved +0 files updated, 0 files merged, 3 files removed, 0 files unresolved +% Serving them both using hgweb +% Clone main from hgweb +requesting all changes +adding changesets +adding manifests +adding file changes +added 1 changesets with 3 changes to 3 files +updating to branch default +pulling ...sub +requesting all changes +adding changesets +adding manifests +adding file changes +added 1 changesets with 1 changes to 1 files +3 files updated, 0 files merged, 0 files removed, 0 files unresolved +% Checking cloned repo ids +fdfeeb3e979e tip +863c1745b441 tip +% subrepo debug for main clone +path sub + source ../sub + revision 863c1745b441bd97a8c4a096e87793073f4fb215 diff -r 0bb67503ad4b -r ac6fec2af8c8 tests/test-subrepo-svn --- a/tests/test-subrepo-svn Mon May 31 21:37:01 2010 +0200 +++ b/tests/test-subrepo-svn Mon May 31 21:43:03 2010 +0200 @@ -63,7 +63,8 @@ echo % change file in svn and hg, commit echo a >> a echo alpha >> s/alpha -hg commit -m 'Message!' | sed "$filterexternal" +hg commit -m 'Message!' | sed "$filterexternal" \ + | sed 's:Sending.*s/alpha:Sending s/alpha:g' hg debugsub | sed "$filterpath" echo diff -r 0bb67503ad4b -r ac6fec2af8c8 tests/test-subrepo.out --- a/tests/test-subrepo.out Mon May 31 21:37:01 2010 +0200 +++ b/tests/test-subrepo.out Mon May 31 21:43:03 2010 +0200 @@ -2,10 +2,25 @@ adding a % add first sub adding a +parent: 0:f7b1eb17ad24 tip + 0 +branch: default +commit: 1 added, 1 subrepos +update: (current) committing subrepository s % add sub sub +parent: 1:7cf8cfea66e4 tip + 1 +branch: default +commit: 1 subrepos +update: (current) committing subrepository s -committing subrepository ss +committing subrepository s/ss +parent: 2:df30734270ae tip + 2 +branch: default +commit: (clean) +update: (current) % bump sub rev committing subrepository s % leave sub dirty @@ -75,6 +90,7 @@ source t revision 6747d179aa9a688023c4b0cad32e4c92bb7f34ad committing subrepository t +created new head searching for copies back to rev 2 resolving manifests overwrite None partial False @@ -108,19 +124,19 @@ >>>>>>> other % clone updating to branch default -pulling subrepo s +pulling subrepo s from .../sub/t/s requesting all changes adding changesets adding manifests adding file changes added 4 changesets with 5 changes to 3 files -pulling subrepo ss +pulling subrepo s/ss from .../sub/t/s/ss requesting all changes adding changesets adding manifests adding file changes added 1 changesets with 1 changes to 1 files -pulling subrepo t +pulling subrepo t from .../sub/t/t requesting all changes adding changesets adding manifests @@ -136,13 +152,13 @@ % push committing subrepository t pushing ...sub/t -pushing ...subrepo ss +pushing ...sub/t/s/ss searching for changes no changes found -pushing ...subrepo s +pushing ...sub/t/s searching for changes no changes found -pushing ...subrepo t +pushing ...sub/t/t searching for changes adding changesets adding manifests @@ -157,23 +173,23 @@ committing subrepository s abort: push creates new remote heads on branch 'default'! pushing ...sub/t -pushing ...subrepo ss +pushing ...sub/t/s/ss searching for changes no changes found -pushing ...subrepo s +pushing ...sub/t/s searching for changes (did you forget to merge? use push -f to force) pushing ...sub/t -pushing ...subrepo ss +pushing ...sub/t/s/ss searching for changes no changes found -pushing ...subrepo s +pushing ...sub/t/s searching for changes adding changesets adding manifests adding file changes added 1 changesets with 1 changes to 1 files (+1 heads) -pushing ...subrepo t +pushing ...sub/t/t searching for changes no changes found searching for changes @@ -192,7 +208,7 @@ adding file changes added 1 changesets with 1 changes to 1 files (run 'hg update' to get a working copy) -pulling subrepo t +pulling subrepo t from .../sub/t/t searching for changes adding changesets adding manifests @@ -207,13 +223,11 @@ marked working directory as branch br 1 files updated, 0 files merged, 0 files removed, 0 files unresolved adding b -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) 1 files updated, 0 files merged, 0 files removed, 0 files unresolved adding c -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) @@ -226,7 +240,6 @@ committing subrepository s 1 files updated, 0 files merged, 1 files removed, 0 files unresolved adding c -created new head 2 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) @@ -234,7 +247,6 @@ 1 files updated, 0 files merged, 0 files removed, 0 files unresolved adding d committing subrepository s -created new head 2 files updated, 0 files merged, 1 files removed, 0 files unresolved 1 files updated, 0 files merged, 0 files removed, 0 files unresolved adding e @@ -260,13 +272,13 @@ [paths] default = $HGTMP/test-subrepo/sub/mercurial/nested_absolute [paths] -default = $HGTMP/test-subrepo/sub/mercurial/main/../nested_relative +default = $HGTMP/test-subrepo/sub/mercurial/nested_relative % issue 1977 adding a adding .hgsub committing subrepository s updating to branch default -pulling subrepo s +pulling subrepo s from .../sub/repo/s requesting all changes adding changesets adding manifests diff -r 0bb67503ad4b -r ac6fec2af8c8 tests/test-tag --- a/tests/test-tag Mon May 31 21:37:01 2010 +0200 +++ b/tests/test-tag Mon May 31 21:43:03 2010 +0200 @@ -68,3 +68,18 @@ cat .hgtags hg tag -d '1000000 0' newline cat .hgtags + +echo % tag and branch using same name +hg branch tag-and-branch-same-name +hg ci -m"discouraged" +hg tag tag-and-branch-same-name + +echo '% test custom commit messages' +cat > $HGTMP/editor <<'__EOF__' +#!/bin/sh +echo "custom tag message" > "$1" +echo "second line" >> "$1" +__EOF__ +chmod +x "$HGTMP"/editor +HGEDITOR="'$HGTMP'"/editor hg tag custom-tag -e +hg log -l1 --template "{desc}\n" diff -r 0bb67503ad4b -r ac6fec2af8c8 tests/test-tag.out --- a/tests/test-tag.out Mon May 31 21:37:01 2010 +0200 +++ b/tests/test-tag.out Mon May 31 21:43:03 2010 +0200 @@ -49,9 +49,9 @@ abort: ':' cannot be used in a tag name % cloning local tags changeset: 0:0acdaf898367 -tag: foobar tag: bleah tag: bleah0 +tag: foobar user: test date: Mon Jan 12 13:46:40 1970 +0000 summary: test @@ -94,3 +94,9 @@ f68b039e72eacbb2e68b0543e1f6e50990aa2bb5 localnewline 0acdaf8983679e0aac16e811534eb49d7ee1f2b4 foobar0acdaf8983679e0aac16e811534eb49d7ee1f2b4 foobar 6ae703d793c8b1f097116869275ecd97b2977a2b newline +% tag and branch using same name +marked working directory as branch tag-and-branch-same-name +warning: tag tag-and-branch-same-name conflicts with existing branch name +% test custom commit messages +custom tag message +second line diff -r 0bb67503ad4b -r ac6fec2af8c8 tests/test-tags.out --- a/tests/test-tags.out Mon May 31 21:37:01 2010 +0200 +++ b/tests/test-tags.out Mon May 31 21:43:03 2010 +0200 @@ -111,7 +111,7 @@ summary: Removed tag bar % rollback undoes tag operation -rolling back last transaction +rolling back to revision 4 (undo commit) tip 4:0c192d7d5e6b bar 1:78391a272241 tip 4:0c192d7d5e6b diff -r 0bb67503ad4b -r ac6fec2af8c8 tests/test-ui-config.py --- a/tests/test-ui-config.py Mon May 31 21:37:01 2010 +0200 +++ b/tests/test-ui-config.py Mon May 31 21:43:03 2010 +0200 @@ -11,6 +11,19 @@ 'lists.list2=foo bar baz', 'lists.list3=alice, bob', 'lists.list4=foo bar baz alice, bob', + 'lists.list5=abc d"ef"g "hij def"', + 'lists.list6="hello world", "how are you?"', + 'lists.list7=Do"Not"Separate', + 'lists.list8="Do"Separate', + 'lists.list9="Do\\"NotSeparate"', + 'lists.list10=string "with extraneous" quotation mark"', + 'lists.list11=x, y', + 'lists.list12="x", "y"', + 'lists.list13=""" key = "x", "y" """', + 'lists.list14=,,,, ', + 'lists.list15=" just with starting quotation', + 'lists.list16="longer quotation" with "no ending quotation', + 'lists.list17=this is \\" "not a quotation mark"', ]) print repr(testui.configitems('values')) @@ -36,6 +49,19 @@ print repr(testui.configlist('lists', 'list3')) print repr(testui.configlist('lists', 'list4')) print repr(testui.configlist('lists', 'list4', ['foo'])) +print repr(testui.configlist('lists', 'list5')) +print repr(testui.configlist('lists', 'list6')) +print repr(testui.configlist('lists', 'list7')) +print repr(testui.configlist('lists', 'list8')) +print repr(testui.configlist('lists', 'list9')) +print repr(testui.configlist('lists', 'list10')) +print repr(testui.configlist('lists', 'list11')) +print repr(testui.configlist('lists', 'list12')) +print repr(testui.configlist('lists', 'list13')) +print repr(testui.configlist('lists', 'list14')) +print repr(testui.configlist('lists', 'list15')) +print repr(testui.configlist('lists', 'list16')) +print repr(testui.configlist('lists', 'list17')) print repr(testui.configlist('lists', 'unknown')) print repr(testui.configlist('lists', 'unknown', '')) print repr(testui.configlist('lists', 'unknown', 'foo')) diff -r 0bb67503ad4b -r ac6fec2af8c8 tests/test-ui-config.py.out --- a/tests/test-ui-config.py.out Mon May 31 21:37:01 2010 +0200 +++ b/tests/test-ui-config.py.out Mon May 31 21:43:03 2010 +0200 @@ -1,5 +1,5 @@ [('string', 'string value'), ('bool1', 'true'), ('bool2', 'false')] -[('list1', 'foo'), ('list2', 'foo bar baz'), ('list3', 'alice, bob'), ('list4', 'foo bar baz alice, bob')] +[('list1', 'foo'), ('list2', 'foo bar baz'), ('list3', 'alice, bob'), ('list4', 'foo bar baz alice, bob'), ('list5', 'abc d"ef"g "hij def"'), ('list6', '"hello world", "how are you?"'), ('list7', 'Do"Not"Separate'), ('list8', '"Do"Separate'), ('list9', '"Do\\"NotSeparate"'), ('list10', 'string "with extraneous" quotation mark"'), ('list11', 'x, y'), ('list12', '"x", "y"'), ('list13', '""" key = "x", "y" """'), ('list14', ',,,, '), ('list15', '" just with starting quotation'), ('list16', '"longer quotation" with "no ending quotation'), ('list17', 'this is \\" "not a quotation mark"')] --- 'string value' 'true' @@ -18,6 +18,19 @@ ['alice', 'bob'] ['foo', 'bar', 'baz', 'alice', 'bob'] ['foo', 'bar', 'baz', 'alice', 'bob'] +['abc', 'd"ef"g', 'hij def'] +['hello world', 'how are you?'] +['Do"Not"Separate'] +['Do', 'Separate'] +['Do"NotSeparate'] +['string', 'with extraneous', 'quotation', 'mark"'] +['x', 'y'] +['x', 'y'] +['', ' key = ', 'x"', 'y', '', '"'] +[] +['"', 'just', 'with', 'starting', 'quotation'] +['longer quotation', 'with', '"no', 'ending', 'quotation'] +['this', 'is', '"', 'not a quotation mark'] [] [] ['foo'] diff -r 0bb67503ad4b -r ac6fec2af8c8 tests/test-url-rev.out --- a/tests/test-url-rev.out Mon May 31 21:37:01 2010 +0200 +++ b/tests/test-url-rev.out Mon May 31 21:43:03 2010 +0200 @@ -75,7 +75,7 @@ % rolling back -rolling back last transaction +rolling back to revision 1 (undo push) % incoming 2:faba9097cad4 % pull @@ -104,7 +104,7 @@ % no new revs, no update 0:1f0dee641bb7 % rollback -rolling back last transaction +rolling back to revision 1 (undo pull) 1 files updated, 0 files merged, 0 files removed, 0 files unresolved 0:1f0dee641bb7 % pull -u takes us back to branch foo @@ -116,7 +116,7 @@ summary: new head of branch foo % rollback -rolling back last transaction +rolling back to revision 1 (undo pull) 1 files updated, 0 files merged, 0 files removed, 0 files unresolved % parents 0:1f0dee641bb7