# HG changeset patch # User Matt Mackall # Date 1272744935 18000 # Node ID 37d1b20168d17f695265d0bc100b0406940c1914 # Parent 132d783d6a87506fd02b2009e8da3a5b5536c649# Parent 2e9f735a6179a913e661ccbda4d9d530c1888f35 Merge with stable diff -r 2e9f735a6179 -r 37d1b20168d1 Makefile --- a/Makefile Sat May 01 15:08:30 2010 -0500 +++ b/Makefile Sat May 01 15:15:35 2010 -0500 @@ -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 2e9f735a6179 -r 37d1b20168d1 contrib/check-code.py --- a/contrib/check-code.py Sat May 01 15:08:30 2010 -0500 +++ b/contrib/check-code.py Sat May 01 15:15:35 2010 -0500 @@ -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)) @@ -84,6 +91,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 +100,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 +131,7 @@ cfilters = [ (r'(/\*)(((\*(?!/))|[^*])*)\*/', repccomment), - (r'''(?(?([^"]|\\")+)"(?!")''', repquote), (r'''(#\s*include\s+<)([^>]+)>''', repinclude), (r'(\()([^)]+\))', repcallspaces), ] @@ -134,12 +142,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 +192,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 2e9f735a6179 -r 37d1b20168d1 contrib/git-viz/git-cat-file --- a/contrib/git-viz/git-cat-file Sat May 01 15:08:30 2010 -0500 +++ /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 2e9f735a6179 -r 37d1b20168d1 contrib/git-viz/git-diff-tree --- a/contrib/git-viz/git-diff-tree Sat May 01 15:08:30 2010 -0500 +++ /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 2e9f735a6179 -r 37d1b20168d1 contrib/git-viz/git-rev-list --- a/contrib/git-viz/git-rev-list Sat May 01 15:08:30 2010 -0500 +++ /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 2e9f735a6179 -r 37d1b20168d1 contrib/git-viz/git-rev-tree --- a/contrib/git-viz/git-rev-tree Sat May 01 15:08:30 2010 -0500 +++ /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 2e9f735a6179 -r 37d1b20168d1 contrib/git-viz/hg-viz --- a/contrib/git-viz/hg-viz Sat May 01 15:08:30 2010 -0500 +++ /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 2e9f735a6179 -r 37d1b20168d1 contrib/hgdiff --- a/contrib/hgdiff Sat May 01 15:08:30 2010 -0500 +++ /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 2e9f735a6179 -r 37d1b20168d1 contrib/hgweb.fcgi --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/contrib/hgweb.fcgi Sat May 01 15:15:35 2010 -0500 @@ -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 2e9f735a6179 -r 37d1b20168d1 contrib/hgweb.wsgi --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/contrib/hgweb.wsgi Sat May 01 15:15:35 2010 -0500 @@ -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 2e9f735a6179 -r 37d1b20168d1 contrib/hgwebdir.fcgi --- a/contrib/hgwebdir.fcgi Sat May 01 15:08:30 2010 -0500 +++ /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 2e9f735a6179 -r 37d1b20168d1 contrib/hgwebdir.wsgi --- a/contrib/hgwebdir.wsgi Sat May 01 15:08:30 2010 -0500 +++ /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 2e9f735a6179 -r 37d1b20168d1 contrib/logo-droplets.svg --- a/contrib/logo-droplets.svg Sat May 01 15:08:30 2010 -0500 +++ b/contrib/logo-droplets.svg Sat May 01 15:15:35 2010 -0500 @@ -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 2e9f735a6179 -r 37d1b20168d1 contrib/mercurial.spec --- a/contrib/mercurial.spec Sat May 01 15:08:30 2010 -0500 +++ b/contrib/mercurial.spec Sat May 01 15:15:35 2010 -0500 @@ -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 2e9f735a6179 -r 37d1b20168d1 contrib/rewrite-log --- a/contrib/rewrite-log Sat May 01 15:08:30 2010 -0500 +++ /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 2e9f735a6179 -r 37d1b20168d1 contrib/shrink-revlog.py --- a/contrib/shrink-revlog.py Sat May 01 15:08:30 2010 -0500 +++ b/contrib/shrink-revlog.py Sat May 01 15:15:35 2010 -0500 @@ -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 2e9f735a6179 -r 37d1b20168d1 contrib/win32/win32-build.txt --- a/contrib/win32/win32-build.txt Sat May 01 15:08:30 2010 -0500 +++ b/contrib/win32/win32-build.txt Sat May 01 15:15:35 2010 -0500 @@ -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 2e9f735a6179 -r 37d1b20168d1 contrib/wix/contrib.wxs --- a/contrib/wix/contrib.wxs Sat May 01 15:08:30 2010 -0500 +++ b/contrib/wix/contrib.wxs Sat May 01 15:15:35 2010 -0500 @@ -1,218 +1,42 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff -r 2e9f735a6179 -r 37d1b20168d1 contrib/wix/doc.wxs --- a/contrib/wix/doc.wxs Sat May 01 15:08:30 2010 -0500 +++ b/contrib/wix/doc.wxs Sat May 01 15:15:35 2010 -0500 @@ -1,33 +1,49 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff -r 2e9f735a6179 -r 37d1b20168d1 contrib/wix/guids.wxi --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/contrib/wix/guids.wxi Sat May 01 15:15:35 2010 -0500 @@ -0,0 +1,46 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff -r 2e9f735a6179 -r 37d1b20168d1 contrib/wix/help.wxs --- a/contrib/wix/help.wxs Sat May 01 15:08:30 2010 -0500 +++ b/contrib/wix/help.wxs Sat May 01 15:15:35 2010 -0500 @@ -1,72 +1,25 @@ - + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + diff -r 2e9f735a6179 -r 37d1b20168d1 contrib/wix/i18n.wxs --- a/contrib/wix/i18n.wxs Sat May 01 15:08:30 2010 -0500 +++ b/contrib/wix/i18n.wxs Sat May 01 15:15:35 2010 -0500 @@ -1,78 +1,25 @@ - + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + diff -r 2e9f735a6179 -r 37d1b20168d1 contrib/wix/locale.wxs --- a/contrib/wix/locale.wxs Sat May 01 15:08:30 2010 -0500 +++ b/contrib/wix/locale.wxs Sat May 01 15:15:35 2010 -0500 @@ -1,172 +1,32 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + diff -r 2e9f735a6179 -r 37d1b20168d1 contrib/wix/mercurial.wxs --- a/contrib/wix/mercurial.wxs Sat May 01 15:08:30 2010 -0500 +++ b/contrib/wix/mercurial.wxs Sat May 01 15:15:35 2010 -0500 @@ -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 2e9f735a6179 -r 37d1b20168d1 contrib/wix/templates.wxs --- a/contrib/wix/templates.wxs Sat May 01 15:08:30 2010 -0500 +++ b/contrib/wix/templates.wxs Sat May 01 15:15:35 2010 -0500 @@ -1,801 +1,206 @@ - + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff -r 2e9f735a6179 -r 37d1b20168d1 doc/Makefile --- a/doc/Makefile Sat May 01 15:08:30 2010 -0500 +++ b/doc/Makefile Sat May 01 15:15:35 2010 -0500 @@ -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 2e9f735a6179 -r 37d1b20168d1 doc/hgrc.5.txt --- a/doc/hgrc.5.txt Sat May 01 15:08:30 2010 -0500 +++ b/doc/hgrc.5.txt Sat May 01 15:15:35 2010 -0500 @@ -111,10 +111,18 @@ 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 -------- @@ -268,8 +276,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 @@ -757,7 +765,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 @@ -867,20 +875,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. @@ -915,17 +921,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 @@ -941,7 +945,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 2e9f735a6179 -r 37d1b20168d1 doc/manpage.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/doc/manpage.py Sat May 01 15:15:35 2010 -0500 @@ -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 2e9f735a6179 -r 37d1b20168d1 doc/rst2man.py --- a/doc/rst2man.py Sat May 01 15:08:30 2010 -0500 +++ /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 2e9f735a6179 -r 37d1b20168d1 doc/runrst --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/doc/runrst Sat May 01 15:15:35 2010 -0500 @@ -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 2e9f735a6179 -r 37d1b20168d1 hgext/acl.py --- a/hgext/acl.py Sat May 01 15:08:30 2010 -0500 +++ b/hgext/acl.py Sat May 01 15:15:35 2010 -0500 @@ -8,7 +8,8 @@ '''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. +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 @@ -21,40 +22,92 @@ Nor is it safe if remote users share an account, because then there is no way to distinguish them. +The deny list is checked before the allow list. + +The allow and deny sections take key-value pairs, having a subtree pattern +as key (with a glob syntax by default). The corresponding value can be either: + +1) an asterisk, to match everyone; +2) a comma-separated list containing users and 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. + To use this hook, configure the acl extension in your hgrc like this:: [extensions] acl = [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") + # Check whether the source of incoming changes is in this list where + # "serve" == ssh or http, and "push", "pull" and "bundle" are the + # corresponding hg commands. 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] + # This list is checked first. If a match is found, 'acl.allow' will not be + # 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 not present, all users 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 +import getpass, urllib, grp + +def _getusers(group): + return grp.getgrnam(group).gr_mem + +def _usermatch(user, usersorgroups): + + if usersorgroups == '*': + return True + + for ug in usersorgroups.replace(',', ' ').split(): + if user == ug or ug.find('@') == 0 and user in _getusers(ug[1:]): + return True + + return False def buildmatch(ui, repo, user, key): '''return tuple of (match function, list enabled).''' @@ -63,7 +116,7 @@ return None pats = [pat for pat, users in ui.configitems(key) - if users == '*' or user in users.replace(',', ' ').split()] + if _usermatch(user, users)] ui.debug('acl: %s enabled, %d entries for user %s\n' % (key, len(pats), user)) if pats: @@ -72,10 +125,11 @@ 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 diff -r 2e9f735a6179 -r 37d1b20168d1 hgext/bookmarks.py --- a/hgext/bookmarks.py Sat May 01 15:08:30 2010 -0500 +++ b/hgext/bookmarks.py Sat May 01 15:15:35 2010 -0500 @@ -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 2e9f735a6179 -r 37d1b20168d1 hgext/churn.py --- a/hgext/churn.py Sat May 01 15:08:30 2010 -0500 +++ b/hgext/churn.py Sat May 01 15:15:35 2010 -0500 @@ -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 2e9f735a6179 -r 37d1b20168d1 hgext/color.py --- a/hgext/color.py Sat May 01 15:08:30 2010 -0500 +++ b/hgext/color.py Sat May 01 15:15:35 2010 -0500 @@ -61,314 +61,224 @@ 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''' - 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 sys.__stdout__.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 + def colorcmd(orig, ui_, opts, cmd, cmdfunc): + if (opts['color'] == 'always' or + (opts['color'] == 'auto' and (os.environ.get('TERM') != 'dumb' + and sys.__stdout__.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, + 'blue_background': BACKGROUND_BLUE, + 'cyan_background': BACKGROUND_BLUE | BACKGROUND_GREEN, + 'bold_background': FOREGROUND_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 2e9f735a6179 -r 37d1b20168d1 hgext/fetch.py --- a/hgext/fetch.py Sat May 01 15:08:30 2010 -0500 +++ b/hgext/fetch.py Sat May 01 15:15:35 2010 -0500 @@ -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 2e9f735a6179 -r 37d1b20168d1 hgext/hgcia.py --- a/hgext/hgcia.py Sat May 01 15:08:30 2010 -0500 +++ b/hgext/hgcia.py Sat May 01 15:15:35 2010 -0500 @@ -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 2e9f735a6179 -r 37d1b20168d1 hgext/highlight/highlight.py --- a/hgext/highlight/highlight.py Sat May 01 15:08:30 2010 -0500 +++ b/hgext/highlight/highlight.py Sat May 01 15:15:35 2010 -0500 @@ -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 2e9f735a6179 -r 37d1b20168d1 hgext/inotify/__init__.py --- a/hgext/inotify/__init__.py Sat May 01 15:08:30 2010 -0500 +++ b/hgext/inotify/__init__.py Sat May 01 15:15:35 2010 -0500 @@ -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 2e9f735a6179 -r 37d1b20168d1 hgext/keyword.py --- a/hgext/keyword.py Sat May 01 15:08:30 2010 -0500 +++ b/hgext/keyword.py Sat May 01 15:15:35 2010 -0500 @@ -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. @@ -49,27 +49,22 @@ 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". -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 +74,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,8 +87,10 @@ # 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' # provide cvs-like UTC date filter utcdate = lambda x: util.datestr((x[0], 0), '%Y/%m/%d %H:%M:%S') @@ -126,6 +122,7 @@ 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 @@ -136,16 +133,16 @@ self.re_kw = re.compile(kwpat) templatefilters.filters['utcdate'] = utcdate - self.ct = cmdutil.changeset_templater(self.ui, self.repo, - False, None, '', False) 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 +160,25 @@ 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, recctx=None): '''Overwrites selected files expanding/shrinking keywords.''' - ctx = self.repo[node] + if recctx is None: + ctx = self.repo[node] + else: + ctx = recctx 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 recctx is None: + data = self.repo.file(f).read(mf[f]) + else: + data = self.repo.wread(f) if util.binary(data): continue if expand: @@ -190,7 +189,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 +250,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 +261,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 @@ -295,7 +290,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) @@ -331,35 +325,23 @@ 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 +364,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 +380,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 +401,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 +467,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 +497,21 @@ 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) + recctx = repo['.'] + if ctx != recctx: + kwt.overwrite('.', True, None, recctx) + return ret + finally: + wlock.release() + repo.__class__ = kwrepo extensions.wrapfunction(patch.patchfile, '__init__', kwpatchfile_init) @@ -526,6 +519,11 @@ extensions.wrapfunction(patch, 'diff', kw_diff) for c in 'annotate changeset rev filediff diff'.split(): extensions.wrapfunction(webcommands, c, kwweb_skip) + try: + record = extensions.find('record') + extensions.wrapfunction(record, 'dorecord', kw_dorecord) + except KeyError: + pass cmdtable = { 'kwdemo': @@ -540,9 +538,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 2e9f735a6179 -r 37d1b20168d1 hgext/mq.py --- a/hgext/mq.py Sat May 01 15:08:30 2010 -0500 +++ b/hgext/mq.py Sat May 01 15:15:35 2010 -0500 @@ -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 @@ -53,18 +53,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 +72,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 @@ -265,8 +257,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 +324,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 +477,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 +525,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 +544,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 +562,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 +588,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) @@ -638,7 +613,7 @@ 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 +646,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 +677,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 +709,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 +717,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) @@ -789,8 +765,8 @@ self._cleanup(realpatches, 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 +846,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 +903,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 +926,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 +978,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 +1034,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 +1053,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 +1063,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 +1089,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 +1100,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 +1119,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 +1138,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 +1148,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 +1156,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 +1167,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 +1197,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 +1209,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 +1366,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 +1406,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 +1418,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 +1434,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 +1458,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 +1469,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 +1485,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 +1523,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 +1539,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 +1565,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 +1610,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 +1620,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 +1654,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 +1665,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 +1698,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 @@ -1897,7 +1874,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))) @@ -2056,9 +2033,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 +2135,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 +2231,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): @@ -2555,8 +2542,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, @@ -2581,7 +2568,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 +2594,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)) @@ -2736,7 +2723,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 +2736,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 +2761,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 +2806,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')), @@ -2837,3 +2825,11 @@ [('a', 'applied', None, _('finish all applied changesets'))], _('hg qfinish [-a] [REV]...')), } + +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 2e9f735a6179 -r 37d1b20168d1 hgext/pager.py --- a/hgext/pager.py Sat May 01 15:08:30 2010 -0500 +++ b/hgext/pager.py Sat May 01 15:15:35 2010 -0500 @@ -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 diff -r 2e9f735a6179 -r 37d1b20168d1 hgext/patchbomb.py --- a/hgext/patchbomb.py Sat May 01 15:08:30 2010 -0500 +++ b/hgext/patchbomb.py Sat May 01 15:15:35 2010 -0500 @@ -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 @@ -38,11 +38,11 @@ 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 +105,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 +174,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)) @@ -192,7 +196,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 +253,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 +333,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')) diff -r 2e9f735a6179 -r 37d1b20168d1 hgext/progress.py --- a/hgext/progress.py Sat May 01 15:08:30 2010 -0500 +++ b/hgext/progress.py Sat May 01 15:15:35 2010 -0500 @@ -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 2e9f735a6179 -r 37d1b20168d1 hgext/purge.py --- a/hgext/purge.py Sat May 01 15:08:30 2010 -0500 +++ b/hgext/purge.py Sat May 01 15:15:35 2010 -0500 @@ -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 2e9f735a6179 -r 37d1b20168d1 hgext/rebase.py --- a/hgext/rebase.py Sat May 01 15:08:30 2010 -0500 +++ b/hgext/rebase.py Sat May 01 15:15:35 2010 -0500 @@ -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 @@ -87,6 +87,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: @@ -178,7 +181,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' @@ -344,10 +347,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()) @@ -444,8 +447,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: @@ -501,7 +504,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) @@ -509,7 +519,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 2e9f735a6179 -r 37d1b20168d1 hgext/record.py --- a/hgext/record.py Sat May 01 15:08:30 2010 -0500 +++ b/hgext/record.py Sat May 01 15:15:35 2010 -0500 @@ -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(_('y - record this change')) @@ -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 @@ -387,7 +388,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. ''' @@ -519,7 +520,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": @@ -543,7 +555,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 2e9f735a6179 -r 37d1b20168d1 hgext/relink.py --- a/hgext/relink.py Sat May 01 15:08:30 2010 -0500 +++ b/hgext/relink.py Sat May 01 15:15:35 2010 -0500 @@ -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 2e9f735a6179 -r 37d1b20168d1 hgext/zeroconf/__init__.py --- a/hgext/zeroconf/__init__.py Sat May 01 15:08:30 2010 -0500 +++ b/hgext/zeroconf/__init__.py Sat May 01 15:15:35 2010 -0500 @@ -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 2e9f735a6179 -r 37d1b20168d1 hgweb.cgi --- a/hgweb.cgi Sat May 01 15:08:30 2010 -0500 +++ b/hgweb.cgi Sat May 01 15:15:35 2010 -0500 @@ -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 2e9f735a6179 -r 37d1b20168d1 hgwebdir.cgi --- a/hgwebdir.cgi Sat May 01 15:08:30 2010 -0500 +++ /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 2e9f735a6179 -r 37d1b20168d1 i18n/da.po --- a/i18n/da.po Sat May 01 15:08:30 2010 -0500 +++ b/i18n/da.po Sat May 01 15:15:35 2010 -0500 @@ -17,8 +17,8 @@ msgstr "" "Project-Id-Version: Mercurial\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2010-04-04 23:48+0200\n" -"PO-Revision-Date: 2010-04-05 01:09+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" @@ -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" @@ -2168,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" @@ -2259,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]..." @@ -4776,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" @@ -5103,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" @@ -6781,6 +6773,10 @@ " 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" @@ -6790,6 +6786,10 @@ " 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.\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 @@ -7739,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)" @@ -9156,9 +9156,6 @@ msgid "queries" msgstr "" -msgid "searching" -msgstr "søger" - msgid "already have changeset " msgstr "har allerede ændringen " @@ -9493,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" @@ -9640,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 2e9f735a6179 -r 37d1b20168d1 i18n/fr.po --- a/i18n/fr.po Sat May 01 15:08:30 2010 -0500 +++ b/i18n/fr.po Sat May 01 15:15:35 2010 -0500 @@ -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 2e9f735a6179 -r 37d1b20168d1 i18n/sv.po --- a/i18n/sv.po Sat May 01 15:08:30 2010 -0500 +++ b/i18n/sv.po Sat May 01 15:15:35 2010 -0500 @@ -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 2e9f735a6179 -r 37d1b20168d1 mercurial/byterange.py --- a/mercurial/byterange.py Sat May 01 15:08:30 2010 -0500 +++ b/mercurial/byterange.py Sat May 01 15:15:35 2010 -0500 @@ -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 2e9f735a6179 -r 37d1b20168d1 mercurial/cmdutil.py --- a/mercurial/cmdutil.py Sat May 01 15:08:30 2010 -0500 +++ b/mercurial/cmdutil.py Sat May 01 15:15:35 2010 -0500 @@ -10,6 +10,7 @@ import os, sys, errno, re, glob, tempfile import mdiff, bdiff, 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 @@ -647,6 +616,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 +715,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 +725,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 +737,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 +924,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 +939,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 +1092,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 2e9f735a6179 -r 37d1b20168d1 mercurial/commands.py --- a/mercurial/commands.py Sat May 01 15:08:30 2010 -0500 +++ b/mercurial/commands.py Sat May 01 15:15:35 2010 -0500 @@ -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 @@ -32,7 +31,7 @@ .. container:: verbose An example showing how new (unknown) files are added - automatically by ``hg add``:: + automatically by :hg:`add`:: $ ls foo.c @@ -158,8 +157,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,7 +170,7 @@ :``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 @@ -184,16 +185,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 +229,7 @@ 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. ''' if rev and node: raise util.Abort(_("please specify just one revision")) @@ -438,8 +455,8 @@ 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. """ if opts.get('clean'): @@ -467,7 +484,7 @@ 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. """ hexfunc = ui.debugflag and hex or short @@ -619,11 +636,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. @@ -687,7 +704,7 @@ 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" + 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,7 +713,7 @@ 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. """ extra = {} if opts.get('close_branch'): @@ -784,6 +801,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]) @@ -854,6 +873,8 @@ for each config item. """ + 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: @@ -1131,7 +1152,7 @@ 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`. """ revs = opts.get('rev') @@ -1151,21 +1172,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,7 +1206,7 @@ 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. @@ -1210,7 +1219,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)) @@ -1337,6 +1346,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 +1361,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 @@ -1848,7 +1865,7 @@ 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. """ patches = (patch1,) + patches @@ -2051,7 +2068,7 @@ 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. """ hg.repository(cmdutil.remoteui(ui, opts), dest, create=1) @@ -2106,7 +2123,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 @@ -2131,6 +2148,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 +2160,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 @@ -2346,8 +2366,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 +2377,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"): @@ -2402,7 +2423,7 @@ 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. """ source, branches = hg.parseurl(ui.expandpath(source), opts.get('branch')) other = hg.repository(cmdutil.remoteui(repo, opts), source) @@ -2438,7 +2459,7 @@ If -r/--rev is used, the named 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. """ dest = ui.expandpath(dest or 'default-push', dest or 'default') @@ -2592,7 +2613,7 @@ 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) for f in ms: @@ -2601,7 +2622,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: @@ -2635,8 +2658,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 @@ -2838,8 +2861,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 @@ -2865,7 +2888,7 @@ repository; for example an in-progress pull from the repository may fail if a rollback is performed. """ - repo.rollback() + repo.rollback(opts.get('dry_run')) def root(ui, repo): """print the root (top) of the current working directory @@ -2875,13 +2898,17 @@ 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. 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. """ if opts["stdio"]: @@ -2891,25 +2918,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: @@ -2930,8 +2968,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() @@ -3022,9 +3064,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 @@ -3039,33 +3083,41 @@ 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')] + 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')] t = [] for s, l in zip(st, labels): if s: @@ -3157,7 +3209,10 @@ 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. """ rev_ = "." @@ -3272,7 +3327,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. @@ -3299,12 +3354,12 @@ 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. """ if rev and node: raise util.Abort(_("please specify just one revision")) @@ -3411,6 +3466,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 = [ @@ -3690,8 +3746,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, @@ -3777,7 +3835,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, @@ -3785,15 +3843,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')), diff -r 2e9f735a6179 -r 37d1b20168d1 mercurial/context.py --- a/mercurial/context.py Sat May 01 15:08:30 2010 -0500 +++ b/mercurial/context.py Sat May 01 15:15:35 2010 -0500 @@ -539,15 +539,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,8 +555,6 @@ 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) diff -r 2e9f735a6179 -r 37d1b20168d1 mercurial/dirstate.py --- a/mercurial/dirstate.py Sat May 01 15:08:30 2010 -0500 +++ b/mercurial/dirstate.py Sat May 01 15:15:35 2010 -0500 @@ -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 2e9f735a6179 -r 37d1b20168d1 mercurial/dispatch.py --- a/mercurial/dispatch.py Sat May 01 15:08:30 2010 -0500 +++ b/mercurial/dispatch.py Sat May 01 15:15:35 2010 -0500 @@ -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 2e9f735a6179 -r 37d1b20168d1 mercurial/filelog.py --- a/mercurial/filelog.py Sat May 01 15:08:30 2010 -0500 +++ b/mercurial/filelog.py Sat May 01 15:15:35 2010 -0500 @@ -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 2e9f735a6179 -r 37d1b20168d1 mercurial/graphmod.py --- a/mercurial/graphmod.py Sat May 01 15:08:30 2010 -0500 +++ b/mercurial/graphmod.py Sat May 01 15:15:35 2010 -0500 @@ -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 2e9f735a6179 -r 37d1b20168d1 mercurial/help.py --- a/mercurial/help.py Sat May 01 15:08:30 2010 -0500 +++ b/mercurial/help.py Sat May 01 15:15:35 2010 -0500 @@ -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 2e9f735a6179 -r 37d1b20168d1 mercurial/help/config.txt --- a/mercurial/help/config.txt Sat May 01 15:08:30 2010 -0500 +++ b/mercurial/help/config.txt Sat May 01 15:15:35 2010 -0500 @@ -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 2e9f735a6179 -r 37d1b20168d1 mercurial/help/diffs.txt --- a/mercurial/help/diffs.txt Sat May 01 15:08:30 2010 -0500 +++ b/mercurial/help/diffs.txt Sat May 01 15:15:35 2010 -0500 @@ -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 2e9f735a6179 -r 37d1b20168d1 mercurial/help/hgweb.txt --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/mercurial/help/hgweb.txt Sat May 01 15:15:35 2010 -0500 @@ -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 2e9f735a6179 -r 37d1b20168d1 mercurial/help/urls.txt --- a/mercurial/help/urls.txt Sat May 01 15:08:30 2010 -0500 +++ b/mercurial/help/urls.txt Sat May 01 15:15:35 2010 -0500 @@ -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 2e9f735a6179 -r 37d1b20168d1 mercurial/hg.py --- a/mercurial/hg.py Sat May 01 15:08:30 2010 -0500 +++ b/mercurial/hg.py Sat May 01 15:15:35 2010 -0500 @@ -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): @@ -359,7 +359,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 +370,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 +388,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 2e9f735a6179 -r 37d1b20168d1 mercurial/hgweb/__init__.py --- a/mercurial/hgweb/__init__.py Sat May 01 15:08:30 2010 -0500 +++ b/mercurial/hgweb/__init__.py Sat May 01 15:15:35 2010 -0500 @@ -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 2e9f735a6179 -r 37d1b20168d1 mercurial/hgweb/hgweb_mod.py --- a/mercurial/hgweb/hgweb_mod.py Sat May 01 15:08:30 2010 -0500 +++ b/mercurial/hgweb/hgweb_mod.py Sat May 01 15:15:35 2010 -0500 @@ -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 2e9f735a6179 -r 37d1b20168d1 mercurial/hgweb/hgwebdir_mod.py --- a/mercurial/hgweb/hgwebdir_mod.py Sat May 01 15:08:30 2010 -0500 +++ b/mercurial/hgweb/hgwebdir_mod.py Sat May 01 15:15:35 2010 -0500 @@ -195,11 +195,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: @@ -251,19 +248,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 2e9f735a6179 -r 37d1b20168d1 mercurial/hgweb/server.py --- a/mercurial/hgweb/server.py Sat May 01 15:08:30 2010 -0500 +++ b/mercurial/hgweb/server.py Sat May 01 15:15:35 2010 -0500 @@ -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 2e9f735a6179 -r 37d1b20168d1 mercurial/localrepo.py --- a/mercurial/localrepo.py Sat May 01 15:08:30 2010 -0500 +++ b/mercurial/localrepo.py Sat May 01 15:15:35 2010 -0500 @@ -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") % ( + args[0], args[1], args[2]) + elif len(args) >= 2: + desc = _("rolling back to revision %s (undo %s)\n") % ( + args[0], 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")) @@ -767,7 +795,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 +804,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 +847,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) @@ -852,8 +879,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 +918,7 @@ lock = self.lock() try: - tr = self.transaction() + tr = self.transaction("commit") trp = weakref.proxy(tr) # check in files @@ -996,7 +1024,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 @@ -1396,7 +1424,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 @@ -1829,7 +1857,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 @@ -1857,7 +1885,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. @@ -1906,7 +1934,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) @@ -1958,7 +1986,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) @@ -1967,7 +1995,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): @@ -1985,7 +2013,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() @@ -2016,6 +2044,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 @@ -2023,7 +2052,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 @@ -2033,8 +2062,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) @@ -2042,12 +2073,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, @@ -2070,14 +2105,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 diff -r 2e9f735a6179 -r 37d1b20168d1 mercurial/mdiff.py --- a/mercurial/mdiff.py Sat May 01 15:08:30 2010 -0500 +++ b/mercurial/mdiff.py Sat May 01 15:15:35 2010 -0500 @@ -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 2e9f735a6179 -r 37d1b20168d1 mercurial/merge.py --- a/mercurial/merge.py Sat May 01 15:08:30 2010 -0500 +++ b/mercurial/merge.py Sat May 01 15:15:35 2010 -0500 @@ -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 @@ -507,8 +507,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 2e9f735a6179 -r 37d1b20168d1 mercurial/minirst.py --- a/mercurial/minirst.py Sat May 01 15:08:30 2010 -0500 +++ b/mercurial/minirst.py Sat May 01 15:15:35 2010 -0500 @@ -225,6 +225,8 @@ return blocks, pruned +_sectionre = re.compile(r"""^([-=`:.'"~^_*+#])\1+$""") + def findsections(blocks): """Finds sections. @@ -240,18 +242,30 @@ # +------------------------------+ 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 +_hgrolere = re.compile(r':hg:`([^`]+)`') + +def hgrole(blocks): + for b in blocks: + if b['type'] in ('paragraph', 'section'): + b['lines'] = [_hgrolere.sub(r'"hg \1"', l) for l in b['lines']] + return blocks + + def addmargins(blocks): """Adds empty blocks for vertical spacing. @@ -261,7 +275,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 +294,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 +304,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 +336,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 +347,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 2e9f735a6179 -r 37d1b20168d1 mercurial/patch.py --- a/mercurial/patch.py Sat May 01 15:08:30 2010 -0500 +++ b/mercurial/patch.py Sat May 01 15:15:35 2010 -0500 @@ -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 2e9f735a6179 -r 37d1b20168d1 mercurial/posix.py --- a/mercurial/posix.py Sat May 01 15:08:30 2010 -0500 +++ b/mercurial/posix.py Sat May 01 15:15:35 2010 -0500 @@ -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 diff -r 2e9f735a6179 -r 37d1b20168d1 mercurial/pure/osutil.py --- a/mercurial/pure/osutil.py Sat May 01 15:08:30 2010 -0500 +++ b/mercurial/pure/osutil.py Sat May 01 15:15:35 2010 -0500 @@ -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 2e9f735a6179 -r 37d1b20168d1 mercurial/repair.py --- a/mercurial/repair.py Sat May 01 15:08:30 2010 -0500 +++ b/mercurial/repair.py Sat May 01 15:15:35 2010 -0500 @@ -114,7 +114,7 @@ mfst = repo.manifest - tr = repo.transaction() + tr = repo.transaction("strip") offset = len(tr.entries) tr.startgroup() diff -r 2e9f735a6179 -r 37d1b20168d1 mercurial/revlog.py --- a/mercurial/revlog.py Sat May 01 15:08:30 2010 -0500 +++ b/mercurial/revlog.py Sat May 01 15:15:35 2010 -0500 @@ -848,6 +848,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 +1148,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 2e9f735a6179 -r 37d1b20168d1 mercurial/similar.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/mercurial/similar.py Sat May 01 15:15:35 2010 -0500 @@ -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 2e9f735a6179 -r 37d1b20168d1 mercurial/streamclone.py --- a/mercurial/streamclone.py Sat May 01 15:08:30 2010 -0500 +++ b/mercurial/streamclone.py Sat May 01 15:15:35 2010 -0500 @@ -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 2e9f735a6179 -r 37d1b20168d1 mercurial/subrepo.py --- a/mercurial/subrepo.py Sat May 01 15:08:30 2010 -0500 +++ b/mercurial/subrepo.py Sat May 01 15:15:35 2010 -0500 @@ -228,8 +228,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') + % (self._path, srcurl)) other = hg.repository(self._repo.ui, srcurl) self._repo.pull(other) diff -r 2e9f735a6179 -r 37d1b20168d1 mercurial/tags.py --- a/mercurial/tags.py Sat May 01 15:08:30 2010 -0500 +++ b/mercurial/tags.py Sat May 01 15:15:35 2010 -0500 @@ -339,4 +339,3 @@ cachefile.write("%s %s\n" % (hex(node), name)) cachefile.rename() - cachefile.close() diff -r 2e9f735a6179 -r 37d1b20168d1 mercurial/templatefilters.py --- a/mercurial/templatefilters.py Sat May 01 15:08:30 2010 -0500 +++ b/mercurial/templatefilters.py Sat May 01 15:15:35 2010 -0500 @@ -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 2e9f735a6179 -r 37d1b20168d1 mercurial/templater.py --- a/mercurial/templater.py Sat May 01 15:08:30 2010 -0500 +++ b/mercurial/templater.py Sat May 01 15:15:35 2010 -0500 @@ -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 2e9f735a6179 -r 37d1b20168d1 mercurial/templates/gitweb/graph.tmpl --- a/mercurial/templates/gitweb/graph.tmpl Sat May 01 15:08:30 2010 -0500 +++ b/mercurial/templates/gitweb/graph.tmpl Sat May 01 15:15:35 2010 -0500 @@ -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 2e9f735a6179 -r 37d1b20168d1 mercurial/templates/monoblue/graph.tmpl --- a/mercurial/templates/monoblue/graph.tmpl Sat May 01 15:08:30 2010 -0500 +++ b/mercurial/templates/monoblue/graph.tmpl Sat May 01 15:15:35 2010 -0500 @@ -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 2e9f735a6179 -r 37d1b20168d1 mercurial/templates/paper/graph.tmpl --- a/mercurial/templates/paper/graph.tmpl Sat May 01 15:08:30 2010 -0500 +++ b/mercurial/templates/paper/graph.tmpl Sat May 01 15:15:35 2010 -0500 @@ -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 2e9f735a6179 -r 37d1b20168d1 mercurial/templates/spartan/graph.tmpl --- a/mercurial/templates/spartan/graph.tmpl Sat May 01 15:08:30 2010 -0500 +++ b/mercurial/templates/spartan/graph.tmpl Sat May 01 15:15:35 2010 -0500 @@ -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 2e9f735a6179 -r 37d1b20168d1 mercurial/ui.py --- a/mercurial/ui.py Sat May 01 15:08:30 2010 -0500 +++ b/mercurial/ui.py Sat May 01 15:15:35 2010 -0500 @@ -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): @@ -239,17 +311,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 +432,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 +534,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 2e9f735a6179 -r 37d1b20168d1 mercurial/util.py --- a/mercurial/util.py Sat May 01 15:08:30 2010 -0500 +++ b/mercurial/util.py Sat May 01 15:15:35 2010 -0500 @@ -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' diff -r 2e9f735a6179 -r 37d1b20168d1 mercurial/verify.py --- a/mercurial/verify.py Sat May 01 15:08:30 2010 -0500 +++ b/mercurial/verify.py Sat May 01 15:15:35 2010 -0500 @@ -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 2e9f735a6179 -r 37d1b20168d1 mercurial/win32.py --- a/mercurial/win32.py Sat May 01 15:08:30 2010 -0500 +++ b/mercurial/win32.py Sat May 01 15:15:35 2010 -0500 @@ -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 2e9f735a6179 -r 37d1b20168d1 setup.py --- a/setup.py Sat May 01 15:08:30 2010 -0500 +++ b/setup.py Sat May 01 15:15:35 2010 -0500 @@ -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 2e9f735a6179 -r 37d1b20168d1 tests/get-with-headers.py --- a/tests/get-with-headers.py Sat May 01 15:08:30 2010 -0500 +++ b/tests/get-with-headers.py Sat May 01 15:15:35 2010 -0500 @@ -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 2e9f735a6179 -r 37d1b20168d1 tests/helpers.sh --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tests/helpers.sh Sat May 01 15:15:35 2010 -0500 @@ -0,0 +1,9 @@ +#/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/"; } \ No newline at end of file diff -r 2e9f735a6179 -r 37d1b20168d1 tests/hghave --- a/tests/hghave Sat May 01 15:08:30 2010 -0500 +++ b/tests/hghave Sat May 01 15:15:35 2010 -0500 @@ -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 2e9f735a6179 -r 37d1b20168d1 tests/killdaemons.py --- a/tests/killdaemons.py Sat May 01 15:08:30 2010 -0500 +++ b/tests/killdaemons.py Sat May 01 15:15:35 2010 -0500 @@ -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 2e9f735a6179 -r 37d1b20168d1 tests/run-tests.py --- a/tests/run-tests.py Sat May 01 15:08:30 2010 -0500 +++ b/tests/run-tests.py Sat May 01 15:15:35 2010 -0500 @@ -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 2e9f735a6179 -r 37d1b20168d1 tests/test-acl --- a/tests/test-acl Sat May 01 15:08:30 2010 -0500 +++ b/tests/test-acl Sat May 01 15:15:35 2010 -0500 @@ -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 "$HGTMP" echo '"""' if test -f acl.config; then echo 'acl.config = """' @@ -24,6 +24,24 @@ 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 + + diff -r 2e9f735a6179 -r 37d1b20168d1 tests/test-acl.out --- a/tests/test-acl.out Sat May 01 15:08:30 2010 -0500 +++ b/tests/test-acl.out Sat May 01 15:15:35 2010 -0500 @@ -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 1 (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 1 (undo push) 0:6675d58eff77 No [acl.allow]/[acl.deny] @@ -203,16 +203,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 acl: acl.allow not enabled @@ -221,7 +221,7 @@ acl: allowing changeset f9cafe1212c8 acl: allowing changeset 911600dab2ae updating the branch cache -rolling back last transaction +rolling back to revision 1 (undo push) 0:6675d58eff77 Empty [acl.allow] @@ -282,16 +282,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 acl: acl.allow enabled, 0 entries for user fred @@ -362,16 +362,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 acl: acl.allow enabled, 1 entries for user fred @@ -445,16 +445,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 acl: acl.allow enabled, 0 entries for user barney @@ -527,16 +527,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 acl: acl.allow enabled, 1 entries for user fred @@ -612,16 +612,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 acl: acl.allow enabled, 1 entries for user fred @@ -696,16 +696,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 acl: acl.allow enabled, 0 entries for user barney @@ -781,16 +781,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 acl: acl.allow enabled, 1 entries for user barney @@ -799,7 +799,7 @@ acl: allowing changeset f9cafe1212c8 acl: allowing changeset 911600dab2ae updating the branch cache -rolling back last transaction +rolling back to revision 1 (undo push) 0:6675d58eff77 wilma can change files with a .txt extension @@ -867,16 +867,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 acl: acl.allow enabled, 1 entries for user wilma @@ -957,16 +957,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,16 +1046,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 acl: acl.allow enabled, 1 entries for user betty @@ -1142,16 +1142,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 acl: acl.allow enabled, 1 entries for user barney @@ -1160,6 +1160,331 @@ acl: allowing changeset f9cafe1212c8 acl: allowing changeset 911600dab2ae updating the branch cache -rolling back last transaction +rolling back to revision 1 (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 enabled, 1 entries for user fred +acl: acl.deny not enabled +acl: allowing changeset ef1ea85a6374 +acl: allowing changeset f9cafe1212c8 +acl: allowing changeset 911600dab2ae +updating the branch cache +rolling back to revision 1 (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/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 fred +acl: acl.deny enabled, 1 entries for user fred +acl: allowing changeset ef1ea85a6374 +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 fred +acl: acl.deny not enabled +acl: allowing changeset ef1ea85a6374 +acl: allowing changeset f9cafe1212c8 +acl: allowing changeset 911600dab2ae +updating the branch cache +rolling back to revision 1 (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 enabled, 1 entries for user fred +acl: acl.deny enabled, 1 entries for user fred +acl: allowing changeset ef1ea85a6374 +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 + diff -r 2e9f735a6179 -r 37d1b20168d1 tests/test-archive --- a/tests/test-archive Sat May 01 15:08:30 2010 -0500 +++ b/tests/test-archive Sat May 01 15:15:35 2010 -0500 @@ -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 2e9f735a6179 -r 37d1b20168d1 tests/test-bookmarks-current --- a/tests/test-bookmarks-current Sat May 01 15:08:30 2010 -0500 +++ b/tests/test-bookmarks-current Sat May 01 15:15:35 2010 -0500 @@ -17,6 +17,9 @@ echo % list bookmarks hg bookmark +echo % list bookmarks with color +hg --config extensions.color= bookmark --color=always + echo % update to bookmark X hg update X diff -r 2e9f735a6179 -r 37d1b20168d1 tests/test-bookmarks-current.out --- a/tests/test-bookmarks-current.out Sat May 01 15:08:30 2010 -0500 +++ b/tests/test-bookmarks-current.out Sat May 01 15:15:35 2010 -0500 @@ -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 2e9f735a6179 -r 37d1b20168d1 tests/test-bookmarks-rebase.out --- a/tests/test-bookmarks-rebase.out Sat May 01 15:08:30 2010 -0500 +++ b/tests/test-bookmarks-rebase.out Sat May 01 15:15:35 2010 -0500 @@ -17,9 +17,9 @@ added 1 changesets with 1 changes to 1 files (-1 heads) rebase completed changeset: 3:9163974d1cb5 +tag: one tag: tip tag: two -tag: one parent: 1:925d80f479bb parent: 2:db815d6d32e6 user: test diff -r 2e9f735a6179 -r 37d1b20168d1 tests/test-bookmarks.out --- a/tests/test-bookmarks.out Sat May 01 15:08:30 2010 -0500 +++ b/tests/test-bookmarks.out Sat May 01 15:15:35 2010 -0500 @@ -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 2e9f735a6179 -r 37d1b20168d1 tests/test-bundle-r.out --- a/tests/test-bundle-r.out Sat May 01 15:08:30 2010 -0500 +++ b/tests/test-bundle-r.out Sat May 01 15:15:35 2010 -0500 @@ -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 5 (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 3 (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 3 (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 3 (undo unbundle) adding changesets adding manifests adding file changes diff -r 2e9f735a6179 -r 37d1b20168d1 tests/test-bundle.out --- a/tests/test-bundle.out Sat May 01 15:08:30 2010 -0500 +++ b/tests/test-bundle.out Sat May 01 15:15:35 2010 -0500 @@ -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 0 (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 0 (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 0 (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 2e9f735a6179 -r 37d1b20168d1 tests/test-check-code --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tests/test-check-code Sat May 01 15:15:35 2010 -0500 @@ -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 2e9f735a6179 -r 37d1b20168d1 tests/test-churn --- a/tests/test-churn Sat May 01 15:08:30 2010 -0500 +++ b/tests/test-churn Sat May 01 15:15:35 2010 -0500 @@ -52,6 +52,8 @@ 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 --diffstat --color=always echo % changeset number churn hg churn -c diff -r 2e9f735a6179 -r 37d1b20168d1 tests/test-churn.out --- a/tests/test-churn.out Sat May 01 15:08:30 2010 -0500 +++ b/tests/test-churn.out Sat May 01 15:15:35 2010 -0500 @@ -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 2e9f735a6179 -r 37d1b20168d1 tests/test-convert-cvs.out --- a/tests/test-convert-cvs.out Sat May 01 15:08:30 2010 -0500 +++ b/tests/test-convert-cvs.out Sat May 01 15:15:35 2010 -0500 @@ -45,7 +45,7 @@ 2 Initial revision 1 import filtering out empty revision -rolling back last transaction +rolling back to revision 1 (undo commit) 0 ci0 updating tags c diff -r 2e9f735a6179 -r 37d1b20168d1 tests/test-convert.out --- a/tests/test-convert.out Sat May 01 15:08:30 2010 -0500 +++ b/tests/test-convert.out Sat May 01 15:15:35 2010 -0500 @@ -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 2e9f735a6179 -r 37d1b20168d1 tests/test-debugcomplete.out --- a/tests/test-debugcomplete.out Sat May 01 15:08:30 2010 -0500 +++ b/tests/test-debugcomplete.out Sat May 01 15:15:35 2010 -0500 @@ -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 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,17 +210,17 @@ 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 diff -r 2e9f735a6179 -r 37d1b20168d1 tests/test-diff-color.out --- a/tests/test-diff-color.out Sat May 01 15:08:30 2010 -0500 +++ b/tests/test-diff-color.out Sat May 01 15:15:35 2010 -0500 @@ -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 1 (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 2e9f735a6179 -r 37d1b20168d1 tests/test-eolfilename.out --- a/tests/test-eolfilename.out Sat May 01 15:08:30 2010 -0500 +++ b/tests/test-eolfilename.out Sat May 01 15:15:35 2010 -0500 @@ -15,6 +15,6 @@ o % test issue2039 ? foo -bar +bar ? foo -bar.baz +bar.baz diff -r 2e9f735a6179 -r 37d1b20168d1 tests/test-fncache.out --- a/tests/test-fncache.out Sat May 01 15:08:30 2010 -0500 +++ b/tests/test-fncache.out Sat May 01 15:15:35 2010 -0500 @@ -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 2e9f735a6179 -r 37d1b20168d1 tests/test-gendoc --- a/tests/test-gendoc Sat May 01 15:08:30 2010 -0500 +++ b/tests/test-gendoc Sat May 01 15:15:35 2010 -0500 @@ -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 2e9f735a6179 -r 37d1b20168d1 tests/test-gendoc.out --- a/tests/test-gendoc.out Sat May 01 15:08:30 2010 -0500 +++ b/tests/test-gendoc.out Sat May 01 15:15:35 2010 -0500 @@ -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 2e9f735a6179 -r 37d1b20168d1 tests/test-globalopts.out --- a/tests/test-globalopts.out Sat May 01 15:08:30 2010 -0500 +++ b/tests/test-globalopts.out Sat May 01 15:15:35 2010 -0500 @@ -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 2e9f735a6179 -r 37d1b20168d1 tests/test-grep --- a/tests/test-grep Sat May 01 15:08:30 2010 -0500 +++ b/tests/test-grep Sat May 01 15:15:35 2010 -0500 @@ -21,6 +21,8 @@ hg grep '**test**' echo % simple hg grep port port +echo % simple with color +hg --config extensions.color= grep --color=always port port echo % all hg grep --traceback --all -nu port port echo % other diff -r 2e9f735a6179 -r 37d1b20168d1 tests/test-grep.out --- a/tests/test-grep.out Sat May 01 15:08:30 2010 -0500 +++ b/tests/test-grep.out Sat May 01 15:15:35 2010 -0500 @@ -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 2e9f735a6179 -r 37d1b20168d1 tests/test-help.out --- a/tests/test-help.out Sat May 01 15:08:30 2010 -0500 +++ b/tests/test-help.out Sat May 01 15:15:35 2010 -0500 @@ -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 @@ -391,7 +393,7 @@ 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". options: @@ -487,10 +489,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 +513,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 +567,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 +577,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 +597,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 2e9f735a6179 -r 37d1b20168d1 tests/test-hgrc.out --- a/tests/test-hgrc.out Sat May 01 15:08:30 2010 -0500 +++ b/tests/test-hgrc.out Sat May 01 15:15:35 2010 -0500 @@ -11,6 +11,7 @@ foo.baz=bif cb hg: config error at $HGRCPATH:1: cannot include /no-such-file (No such file or directory) % customized hgrc +read config from: .../.hgrc .../.hgrc:13: alias.log=log -g .../.hgrc:11: defaults.identify=-n .../.hgrc:2: ui.debug=true @@ -22,6 +23,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 2e9f735a6179 -r 37d1b20168d1 tests/test-hook.out --- a/tests/test-hook.out Sat May 01 15:08:30 2010 -0500 +++ b/tests/test-hook.out Sat May 01 15:15:35 2010 -0500 @@ -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 4 (undo pull) preoutgoing hook: HG_SOURCE=pull preoutgoing.forbid hook: HG_SOURCE=pull pulling from ../a diff -r 2e9f735a6179 -r 37d1b20168d1 tests/test-hup.out --- a/tests/test-hup.out Sat May 01 15:08:30 2010 -0500 +++ b/tests/test-hup.out Sat May 01 15:15:35 2010 -0500 @@ -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 2e9f735a6179 -r 37d1b20168d1 tests/test-import.out --- a/tests/test-import.out Sat May 01 15:08:30 2010 -0500 +++ b/tests/test-import.out Sat May 01 15:15:35 2010 -0500 @@ -182,7 +182,7 @@ applying ../patch1 applying ../patch2 applied 1d4bd90af0e4 -rolling back last transaction +rolling back to revision 2 (undo commit) parent: 1 % hg import in a subdirectory requesting all changes diff -r 2e9f735a6179 -r 37d1b20168d1 tests/test-inherit-mode.out --- a/tests/test-inherit-mode.out Sat May 01 15:08:30 2010 -0500 +++ b/tests/test-inherit-mode.out Sat May 01 15:15:35 2010 -0500 @@ -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 2e9f735a6179 -r 37d1b20168d1 tests/test-keyword --- a/tests/test-keyword Sat May 01 15:08:30 2010 -0500 +++ b/tests/test-keyword Sat May 01 15:15:35 2010 -0500 @@ -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 2 (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 3 (undo commit) % status R a ? c @@ -308,7 +347,7 @@ ignore $Id$ % rollback -rolling back last transaction +rolling back to revision 3 (undo commit) % clean update 1 files updated, 0 files merged, 0 files removed, 0 files unresolved % kwexpand/kwshrink on selected files diff -r 2e9f735a6179 -r 37d1b20168d1 tests/test-log --- a/tests/test-log Sat May 01 15:08:30 2010 -0500 +++ b/tests/test-log Sat May 01 15:15:35 2010 -0500 @@ -118,6 +118,12 @@ echo '% log -d -1' hg log -d -1 +echo '% log -p -l2 --color=always' +hg --config extensions.color= log -p -l2 --color=always + +echo '% log -r tip --stat' +hg log -r tip --stat + cd .. hg init usertest @@ -132,4 +138,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 2e9f735a6179 -r 37d1b20168d1 tests/test-log.out --- a/tests/test-log.out Sat May 01 15:08:30 2010 -0500 +++ b/tests/test-log.out Sat May 01 15:15:35 2010 -0500 @@ -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,134 @@ 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 +created new head +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 2e9f735a6179 -r 37d1b20168d1 tests/test-minirst.py --- a/tests/test-minirst.py Sat May 01 15:08:30 2010 -0500 +++ b/tests/test-minirst.py Sat May 01 15:15:35 2010 -0500 @@ -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 2e9f735a6179 -r 37d1b20168d1 tests/test-minirst.py.out --- a/tests/test-minirst.py.out Sat May 01 15:08:30 2010 -0500 +++ b/tests/test-minirst.py.out Sat May 01 15:15:35 2010 -0500 @@ -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 2e9f735a6179 -r 37d1b20168d1 tests/test-mq --- a/tests/test-mq Sat May 01 15:08:30 2010 -0500 +++ b/tests/test-mq Sat May 01 15:15:35 2010 -0500 @@ -219,6 +219,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 diff -r 2e9f735a6179 -r 37d1b20168d1 tests/test-mq-guards --- a/tests/test-mq-guards Sat May 01 15:08:30 2010 -0500 +++ b/tests/test-mq-guards Sat May 01 15:15:35 2010 -0500 @@ -99,6 +99,8 @@ 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 -l --color=always echo % list series hg qseries -v echo % list guards @@ -125,6 +127,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 +163,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 2e9f735a6179 -r 37d1b20168d1 tests/test-mq-guards.out --- a/tests/test-mq-guards.out Sat May 01 15:08:30 2010 -0500 +++ b/tests/test-mq-guards.out Sat May 01 15:15:35 2010 -0500 @@ -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 2e9f735a6179 -r 37d1b20168d1 tests/test-mq-qdiff --- a/tests/test-mq-qdiff Sat May 01 15:08:30 2010 -0500 +++ b/tests/test-mq-qdiff Sat May 01 15:15:35 2010 -0500 @@ -68,3 +68,5 @@ echo a >> lines hg qdiff +echo % qdiff --stat +hg qdiff --stat diff -r 2e9f735a6179 -r 37d1b20168d1 tests/test-mq-qdiff.out --- a/tests/test-mq-qdiff.out Sat May 01 15:08:30 2010 -0500 +++ b/tests/test-mq-qdiff.out Sat May 01 15:15:35 2010 -0500 @@ -122,3 +122,6 @@ 8 9 +a +% qdiff --stat + lines | 7 +++++-- + 1 files changed, 5 insertions(+), 2 deletions(-) diff -r 2e9f735a6179 -r 37d1b20168d1 tests/test-mq-qimport.out --- a/tests/test-mq-qimport.out Sat May 01 15:08:30 2010 -0500 +++ b/tests/test-mq-qimport.out Sat May 01 15:15:35 2010 -0500 @@ -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 2e9f735a6179 -r 37d1b20168d1 tests/test-mq.out --- a/tests/test-mq.out Sat May 01 15:08:30 2010 -0500 +++ b/tests/test-mq.out Sat May 01 15:15:35 2010 -0500 @@ -193,6 +193,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 @@ -357,8 +383,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 @@ -577,10 +603,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 2e9f735a6179 -r 37d1b20168d1 tests/test-newbranch.out --- a/tests/test-newbranch.out Sat May 01 15:08:30 2010 -0500 +++ b/tests/test-newbranch.out Sat May 01 15:15:35 2010 -0500 @@ -62,7 +62,7 @@ default bar % test for invalid branch cache -rolling back last transaction +rolling back to revision 5 (undo commit) changeset: 4:4909a3732169 branch: foo tag: tip diff -r 2e9f735a6179 -r 37d1b20168d1 tests/test-notify.out --- a/tests/test-notify.out Sat May 01 15:08:30 2010 -0500 +++ b/tests/test-notify.out Sat May 01 15:15:35 2010 -0500 @@ -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 1 (undo pull) pull failed % pull -rolling back last transaction +rolling back to revision 1 (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 1 (undo pull) pulling from ../a searching for changes adding changesets diff -r 2e9f735a6179 -r 37d1b20168d1 tests/test-patchbomb --- a/tests/test-patchbomb Sat May 01 15:08:30 2010 -0500 +++ b/tests/test-patchbomb Sat May 01 15:15:35 2010 -0500 @@ -126,6 +126,11 @@ 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 diff -r 2e9f735a6179 -r 37d1b20168d1 tests/test-patchbomb.out --- a/tests/test-patchbomb.out Sat May 01 15:08:30 2010 -0500 +++ b/tests/test-patchbomb.out Sat May 01 15:15:35 2010 -0500 @@ -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 2e9f735a6179 -r 37d1b20168d1 tests/test-progress.out --- a/tests/test-progress.out Sat May 01 15:08:30 2010 -0500 +++ b/tests/test-progress.out Sat May 01 15:15:35 2010 -0500 @@ -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 2e9f735a6179 -r 37d1b20168d1 tests/test-pull --- a/tests/test-pull Sat May 01 15:08:30 2010 -0500 +++ b/tests/test-pull Sat May 01 15:15:35 2010 -0500 @@ -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 2e9f735a6179 -r 37d1b20168d1 tests/test-pull-r.out --- a/tests/test-pull-r.out Sat May 01 15:08:30 2010 -0500 +++ b/tests/test-pull-r.out Sat May 01 15:15:35 2010 -0500 @@ -19,7 +19,7 @@ abort: unknown revision 'missing'! % pull multiple revisions with update 0:bbd179dfa0a7 -rolling back last transaction +rolling back to revision 0 (undo pull) % pull -r 0 changeset: 0:bbd179dfa0a7 tag: tip diff -r 2e9f735a6179 -r 37d1b20168d1 tests/test-pull.out --- a/tests/test-pull.out Sat May 01 15:08:30 2010 -0500 +++ b/tests/test-pull.out Sat May 01 15:15:35 2010 -0500 @@ -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 0 (undo pull: http://foo:***@localhost:$HGPORT/) % issue 622 pulling from ../test requesting all changes diff -r 2e9f735a6179 -r 37d1b20168d1 tests/test-push-http.out --- a/tests/test-push-http.out Sat May 01 15:08:30 2010 -0500 +++ b/tests/test-push-http.out Sat May 01 15:15:35 2010 -0500 @@ -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 1 (undo serve) % expect authorization error: all users denied abort: authorization failed pushing to http://localhost:$HGPORT/ diff -r 2e9f735a6179 -r 37d1b20168d1 tests/test-qrecord.out --- a/tests/test-qrecord.out Sat May 01 15:08:30 2010 -0500 +++ b/tests/test-qrecord.out Sat May 01 15:15:35 2010 -0500 @@ -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 2e9f735a6179 -r 37d1b20168d1 tests/test-rebase-mq-skip.out --- a/tests/test-rebase-mq-skip.out Sat May 01 15:08:30 2010 -0500 +++ b/tests/test-rebase-mq-skip.out Sat May 01 15:15:35 2010 -0500 @@ -13,7 +13,7 @@ adding file changes added 2 changesets with 2 changes to 2 files rebase completed -@ 3 P0 tags: p0.patch qtip tip qbase +@ 3 P0 tags: p0.patch qbase qtip tip | o 2 P1 tags: qparent | diff -r 2e9f735a6179 -r 37d1b20168d1 tests/test-rebase-mq.out --- a/tests/test-rebase-mq.out Sat May 01 15:08:30 2010 -0500 +++ b/tests/test-rebase-mq.out Sat May 01 15:15:35 2010 -0500 @@ -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 | @@ -34,7 +34,7 @@ adding file changes added 2 changesets with 2 changes to 1 files rebase completed -@ 3 P1 tags: qtip tip f2.patch +@ 3 P1 tags: f2.patch qtip tip | o 2 P0 tags: f.patch qbase | diff -r 2e9f735a6179 -r 37d1b20168d1 tests/test-rebase-pull.out --- a/tests/test-rebase-pull.out Sat May 01 15:08:30 2010 -0500 +++ b/tests/test-rebase-pull.out Sat May 01 15:15:35 2010 -0500 @@ -10,7 +10,6 @@ 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 @@ -39,8 +38,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 2e9f735a6179 -r 37d1b20168d1 tests/test-record --- a/tests/test-record Sat May 01 15:08:30 2010 -0500 +++ b/tests/test-record Sat May 01 15:15:35 2010 -0500 @@ -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 @@ -315,4 +319,3 @@ y EOF echo; hg tip -p - diff -r 2e9f735a6179 -r 37d1b20168d1 tests/test-record.out --- a/tests/test-record.out Sat May 01 15:08:30 2010 -0500 +++ b/tests/test-record.out Sat May 01 15:15:35 2010 -0500 @@ -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 @@ -39,7 +39,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 @@ -50,7 +51,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 @@ -62,6 +64,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 @@ -69,11 +72,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 @@ -86,6 +97,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 @@ -97,6 +109,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 @@ -110,6 +123,7 @@ new file mode 100644 this is a binary file examine changes to 'tip.bundle'? [Ynsfdaq?] + changeset: 4:ad816da3711e tag: tip user: test @@ -124,6 +138,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 @@ -140,6 +155,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 @@ -155,6 +171,7 @@ diff --git a/plain b/plain new file mode 100644 examine changes to 'plain'? [Ynsfdaq?] + changeset: 7:11fb457c1be4 tag: tip user: test @@ -179,46 +196,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 @@ -245,7 +271,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 @@ -255,7 +282,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 @@ -265,6 +293,7 @@ -10 +10.new record change 2/2 to 'plain'? [Ynsfdaq?] + changeset: 12:7d1e66983c15 tag: tip user: test @@ -284,7 +313,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 @@ -292,6 +322,7 @@ 5 6 record this change to 'plain'? [Ynsfdaq?] + changeset: 13:a09fc62a0e61 tag: tip user: test @@ -313,7 +344,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 @@ -323,7 +355,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 @@ -331,17 +364,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 @@ -350,7 +386,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 @@ -358,6 +395,7 @@ +10 +11 record change 3/3 to 'plain'? [Ynsfdaq?] + changeset: 15:7d137997f3a6 tag: tip user: test @@ -382,13 +420,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 @@ -408,10 +448,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 @@ -428,7 +470,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 @@ -436,31 +479,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 @@ -478,6 +528,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 @@ -496,11 +547,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 @@ -520,12 +573,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 @@ -546,12 +601,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 @@ -572,12 +629,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: 25:5bacc1f6e9cf tag: tip user: test diff -r 2e9f735a6179 -r 37d1b20168d1 tests/test-rename-after-merge.out --- a/tests/test-rename-after-merge.out Sat May 01 15:08:30 2010 -0500 +++ b/tests/test-rename-after-merge.out Sat May 01 15:15:35 2010 -0500 @@ -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 3 (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 2e9f735a6179 -r 37d1b20168d1 tests/test-rollback.out --- a/tests/test-rollback.out Sat May 01 15:08:30 2010 -0500 +++ b/tests/test-rollback.out Sat May 01 15:15:35 2010 -0500 @@ -9,7 +9,7 @@ date: Mon Jan 12 13:46:40 1970 +0000 summary: test -rolling back last transaction +rolling back to revision 0 (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 0 (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 0 (undo commit) Named branch could not be reset, current branch still is: test test % rollback by pretxncommit saves commit message (issue 1635) diff -r 2e9f735a6179 -r 37d1b20168d1 tests/test-serve --- a/tests/test-serve Sat May 01 15:08:30 2010 -0500 +++ b/tests/test-serve Sat May 01 15:15:35 2010 -0500 @@ -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 2e9f735a6179 -r 37d1b20168d1 tests/test-serve.out --- a/tests/test-serve.out Sat May 01 15:08:30 2010 -0500 +++ b/tests/test-serve.out Sat May 01 15:15:35 2010 -0500 @@ -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 2e9f735a6179 -r 37d1b20168d1 tests/test-status-inprocess.py --- a/tests/test-status-inprocess.py Sat May 01 15:08:30 2010 -0500 +++ b/tests/test-status-inprocess.py Sat May 01 15:15:35 2010 -0500 @@ -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 2e9f735a6179 -r 37d1b20168d1 tests/test-strict.out --- a/tests/test-strict.out Sat May 01 15:08:30 2010 -0500 +++ b/tests/test-strict.out Sat May 01 15:15:35 2010 -0500 @@ -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 2e9f735a6179 -r 37d1b20168d1 tests/test-subrepo --- a/tests/test-subrepo Sat May 01 15:08:30 2010 -0500 +++ b/tests/test-subrepo Sat May 01 15:15:35 2010 -0500 @@ -78,7 +78,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 +102,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 diff -r 2e9f735a6179 -r 37d1b20168d1 tests/test-subrepo.out --- a/tests/test-subrepo.out Sat May 01 15:08:30 2010 -0500 +++ b/tests/test-subrepo.out Sat May 01 15:15:35 2010 -0500 @@ -108,19 +108,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 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 @@ -192,7 +192,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 diff -r 2e9f735a6179 -r 37d1b20168d1 tests/test-tag --- a/tests/test-tag Sat May 01 15:08:30 2010 -0500 +++ b/tests/test-tag Sat May 01 15:15:35 2010 -0500 @@ -68,3 +68,8 @@ 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 diff -r 2e9f735a6179 -r 37d1b20168d1 tests/test-tag.out --- a/tests/test-tag.out Sat May 01 15:08:30 2010 -0500 +++ b/tests/test-tag.out Sat May 01 15:15:35 2010 -0500 @@ -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,6 @@ 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 diff -r 2e9f735a6179 -r 37d1b20168d1 tests/test-tags.out --- a/tests/test-tags.out Sat May 01 15:08:30 2010 -0500 +++ b/tests/test-tags.out Sat May 01 15:15:35 2010 -0500 @@ -111,7 +111,7 @@ summary: Removed tag bar % rollback undoes tag operation -rolling back last transaction +rolling back to revision 5 (undo commit) tip 4:0c192d7d5e6b bar 1:78391a272241 tip 4:0c192d7d5e6b diff -r 2e9f735a6179 -r 37d1b20168d1 tests/test-ui-config.py --- a/tests/test-ui-config.py Sat May 01 15:08:30 2010 -0500 +++ b/tests/test-ui-config.py Sat May 01 15:15:35 2010 -0500 @@ -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 2e9f735a6179 -r 37d1b20168d1 tests/test-ui-config.py.out --- a/tests/test-ui-config.py.out Sat May 01 15:08:30 2010 -0500 +++ b/tests/test-ui-config.py.out Sat May 01 15:15:35 2010 -0500 @@ -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 2e9f735a6179 -r 37d1b20168d1 tests/test-url-rev.out --- a/tests/test-url-rev.out Sat May 01 15:08:30 2010 -0500 +++ b/tests/test-url-rev.out Sat May 01 15:15:35 2010 -0500 @@ -75,7 +75,7 @@ % rolling back -rolling back last transaction +rolling back to revision 2 (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 2 (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 2 (undo pull) 1 files updated, 0 files merged, 0 files removed, 0 files unresolved % parents 0:1f0dee641bb7