--- a/doc/Makefile Wed Sep 16 16:11:44 2009 +0200
+++ b/doc/Makefile Wed Sep 16 23:46:06 2009 +0200
@@ -5,8 +5,7 @@
MANDIR=$(PREFIX)/share/man
INSTALL=install -c -m 644
PYTHON=python
-RST2HTML=rst2html
-RST2MAN=rst2man
+RST2HTML=$(shell which rst2html 2> /dev/null || which rst2html.py)
all: man html
@@ -18,20 +17,18 @@
touch hg.1.txt
hg.1.gendoc.txt: gendoc.py ../mercurial/commands.py ../mercurial/help.py
- ${PYTHON} gendoc.py > $@
+ ${PYTHON} gendoc.py > $@.tmp
+ mv $@.tmp $@
%: %.txt common.txt
- # add newline after all literal blocks and fix backslash escape
- $(RST2MAN) $*.txt \
- | sed -e 's/^\.fi$$/.fi\n/' \
- | sed -e 's/\\fB\\\\fP/\\fB\\e\\fP/' \
- > $*
+ $(PYTHON) rst2man.py --halt warning \
+ --strip-elements-with-class htmlonly $*.txt $*
%.html: %.txt common.txt
- $(RST2HTML) $*.txt > $*.html
+ $(RST2HTML) --halt warning $*.txt $*.html
MANIFEST: man html
- # tracked files are already in the main MANIFEST
+# tracked files are already in the main MANIFEST
$(RM) $@
for i in $(MAN) $(HTML) hg.1.gendoc.txt; do \
echo "doc/$$i" >> $@ ; \
--- a/doc/README Wed Sep 16 16:11:44 2009 +0200
+++ b/doc/README Wed Sep 16 23:46:06 2009 +0200
@@ -4,14 +4,8 @@
http://docutils.sourceforge.net/rst.html
It's also convertible to a variety of other formats including standard
-UNIX man page format and HTML.
-
-To do this, you'll need to install the rst2html and rst2man tools,
-which are part of Docutils:
+UNIX man page format and HTML. You'll need to install Docutils:
http://docutils.sourceforge.net/
-The rst2man tool is still in their so-called "sandbox". The above page
-has links to tarballs of both Docutils and their sandbox.
-
Use the Makefile in this directory to generate the man and HTML pages.
--- a/doc/hg.1.txt Wed Sep 16 16:11:44 2009 +0200
+++ b/doc/hg.1.txt Wed Sep 16 23:46:06 2009 +0200
@@ -11,6 +11,10 @@
:Manual section: 1
:Manual group: Mercurial Manual
+.. contents::
+ :backlinks: top
+ :class: htmlonly
+
SYNOPSIS
--------
--- a/doc/hgrc.5.txt Wed Sep 16 16:11:44 2009 +0200
+++ b/doc/hgrc.5.txt Wed Sep 16 23:46:06 2009 +0200
@@ -11,6 +11,10 @@
:Manual section: 5
:Manual group: Mercurial Manual
+.. contents::
+ :backlinks: top
+ :class: htmlonly
+
SYNOPSIS
--------
@@ -100,12 +104,12 @@
Lines beginning with "``#``" or "``;``" are ignored and may be used to provide
comments.
-A line of the form "`%include file`" will include `file` into the
+A line of the form "``%include file``" will include ``file`` into the
current configuration file. The inclusion is recursive, which means
that included files can include other files. Filenames are relative to
-the configuration file in which the `%include` directive is found.
+the configuration file in which the ``%include`` directive is found.
-A line with "`%unset name`" will remove `name` from the current
+A line with "``%unset name``" will remove ``name`` from the current
section, if it has been set previously.
@@ -882,6 +886,10 @@
the list of repositories. The contents of the deny_read list have
priority over (are examined before) the contents of the allow_read
list.
+``descend``
+ hgwebdir indexes will not descend into subdirectories. Only repositories
+ directly in the current path will be shown (other repositories are still
+ available from the index corresponding to their containing path).
``description``
Textual description of the repository's purpose or contents.
Default is "unknown".
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/doc/rst2man.py Wed Sep 16 23:46:06 2009 +0200
@@ -0,0 +1,1112 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+# $Id: manpage.py 6110 2009-08-31 14:40:33Z grubert $
+# Author: Engelbert Gruber <grubert@users.sourceforge.net>
+# 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' : ('\\fC', '\\fP'),
+ 'literal_block' : ('.sp\n.nf\n.ft C\n', '\n.ft P\n.fi\n'),
+
+ 'option_list_item' : ('.TP\n', ''),
+
+ 'reference' : (r'\fI\%', r'\fP'),
+ '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 dont 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('.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 ')
+ 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 :
--- a/hgext/churn.py Wed Sep 16 16:11:44 2009 +0200
+++ b/hgext/churn.py Wed Sep 16 23:46:06 2009 +0200
@@ -53,15 +53,17 @@
if opts.get('date'):
df = util.matchdate(opts['date'])
- get = util.cachefunc(lambda r: repo[r].changeset())
+ get = util.cachefunc(lambda r: repo[r])
changeiter, matchfn = cmdutil.walkchangerevs(ui, repo, pats, get, opts)
for st, rev, fns in changeiter:
+
if not st == 'add':
continue
- if df and not df(get(rev)[2][0]): # doesn't match date format
+
+ ctx = get(rev)
+ if df and not df(ctx.date()[0]): # doesn't match date format
continue
- ctx = repo[rev]
key = getkey(ctx)
key = amap.get(key, key) # alias remap
if opts.get('changesets'):
@@ -147,8 +149,8 @@
rate.sort(key=sortkey)
# Be careful not to have a zero maxcount (issue833)
- maxcount = float(max([v for k, v in rate])) or 1.0
- maxname = max([len(k) for k, v in rate])
+ maxcount = float(max(v for k, v in rate)) or 1.0
+ maxname = max(len(k) for k, v in rate)
ttywidth = util.termwidth()
ui.debug(_("assuming %i character terminal\n") % ttywidth)
--- a/hgext/color.py Wed Sep 16 16:11:44 2009 +0200
+++ b/hgext/color.py Wed Sep 16 23:46:06 2009 +0200
@@ -59,7 +59,6 @@
'''
import os, sys
-import itertools
from mercurial import cmdutil, commands, extensions, error
from mercurial.i18n import _
@@ -146,7 +145,7 @@
patchlines = ui.popbuffer().splitlines()
patchnames = repo.mq.series
- for patch, patchname in itertools.izip(patchlines, patchnames):
+ for patch, patchname in zip(patchlines, patchnames):
if opts['missing']:
effects = _patch_effects['missing']
# Determine if patch is applied.
@@ -219,12 +218,8 @@
'changed': ['white'],
'trailingwhitespace': ['bold', 'red_background']}
-_ui = None
-
def uisetup(ui):
'''Initialize the extension.'''
- global _ui
- _ui = ui
_setupcmd(ui, 'diff', commands.table, colordiff, _diff_effects)
_setupcmd(ui, 'incoming', commands.table, None, _diff_effects)
_setupcmd(ui, 'log', commands.table, None, _diff_effects)
@@ -232,17 +227,10 @@
_setupcmd(ui, 'tip', commands.table, None, _diff_effects)
_setupcmd(ui, 'status', commands.table, colorstatus, _status_effects)
-def extsetup():
try:
mq = extensions.find('mq')
- try:
- # If we are loaded after mq, we must wrap commands.table
- _setupcmd(_ui, 'qdiff', commands.table, colordiff, _diff_effects)
- _setupcmd(_ui, 'qseries', commands.table, colorqseries, _patch_effects)
- except error.UnknownCommand:
- # Otherwise we wrap mq.cmdtable
- _setupcmd(_ui, 'qdiff', mq.cmdtable, colordiff, _diff_effects)
- _setupcmd(_ui, 'qseries', mq.cmdtable, colorqseries, _patch_effects)
+ _setupcmd(ui, 'qdiff', mq.cmdtable, colordiff, _diff_effects)
+ _setupcmd(ui, 'qseries', mq.cmdtable, colorqseries, _patch_effects)
except KeyError:
# The mq extension is not enabled
pass
--- a/hgext/convert/common.py Wed Sep 16 16:11:44 2009 +0200
+++ b/hgext/convert/common.py Wed Sep 16 23:46:06 2009 +0200
@@ -203,6 +203,8 @@
"""Put tags into sink.
tags: {tagname: sink_rev_id, ...} where tagname is an UTF-8 string.
+ Return a pair (tag_revision, tag_parent_revision), or (None, None)
+ if nothing was changed.
"""
raise NotImplementedError()
--- a/hgext/convert/convcmd.py Wed Sep 16 16:11:44 2009 +0200
+++ b/hgext/convert/convcmd.py Wed Sep 16 23:46:06 2009 +0200
@@ -336,11 +336,14 @@
ctags[k] = self.map[v]
if c and ctags:
- nrev = self.dest.puttags(ctags)
- # write another hash correspondence to override the previous
- # one so we don't end up with extra tag heads
- if nrev:
- self.map[c] = nrev
+ nrev, tagsparent = self.dest.puttags(ctags)
+ if nrev and tagsparent:
+ # write another hash correspondence to override the previous
+ # one so we don't end up with extra tag heads
+ tagsparents = [e for e in self.map.iteritems()
+ if e[1] == tagsparent]
+ if tagsparents:
+ self.map[tagsparents[0][0]] = nrev
self.writeauthormap()
finally:
--- a/hgext/convert/gnuarch.py Wed Sep 16 16:11:44 2009 +0200
+++ b/hgext/convert/gnuarch.py Wed Sep 16 23:46:06 2009 +0200
@@ -284,7 +284,7 @@
self.changes[rev].summary = self.recode(self.changes[rev].summary)
# Commit revision origin when dealing with a branch or tag
- if catlog.has_key('Continuation-of'):
+ if 'Continuation-of' in catlog:
self.changes[rev].continuationof = self.recode(catlog['Continuation-of'])
except Exception:
raise util.Abort(_('could not parse cat-log of %s') % rev)
--- a/hgext/convert/hg.py Wed Sep 16 16:11:44 2009 +0200
+++ b/hgext/convert/hg.py Wed Sep 16 23:46:06 2009 +0200
@@ -189,7 +189,7 @@
newlines = sorted([("%s %s\n" % (tags[tag], tag)) for tag in tags])
if newlines == oldlines:
- return None
+ return None, None
data = "".join(newlines)
def getfilectx(repo, memctx, f):
return context.memfilectx(f, data, False, False, None)
@@ -201,7 +201,7 @@
[".hgtags"], getfilectx, "convert-repo", date,
extra)
self.repo.commitctx(ctx)
- return hex(self.repo.changelog.tip())
+ return hex(self.repo.changelog.tip()), hex(tagparent)
def setfilemapmode(self, active):
self.filemapmode = active
--- a/hgext/graphlog.py Wed Sep 16 16:11:44 2009 +0200
+++ b/hgext/graphlog.py Wed Sep 16 23:46:06 2009 +0200
@@ -22,48 +22,31 @@
ASCIIDATA = 'ASC'
-def asciiformat(ui, repo, revdag, opts, parentrepo=None):
- """formats a changelog DAG walk for ASCII output"""
- if parentrepo is None:
- parentrepo = repo
- showparents = [ctx.node() for ctx in parentrepo[None].parents()]
- displayer = show_changeset(ui, repo, opts, buffered=True)
- for (id, type, ctx, parentids) in revdag:
- if type != graphmod.CHANGESET:
- continue
- displayer.show(ctx)
- lines = displayer.hunk.pop(ctx.rev()).split('\n')[:-1]
- char = ctx.node() in showparents and '@' or 'o'
- yield (id, ASCIIDATA, (char, lines), parentids)
-
-def asciiedges(nodes):
+def asciiedges(seen, rev, parents):
"""adds edge info to changelog DAG walk suitable for ascii()"""
- seen = []
- for node, type, data, parents in nodes:
- if node not in seen:
- seen.append(node)
- nodeidx = seen.index(node)
+ if rev not in seen:
+ seen.append(rev)
+ nodeidx = seen.index(rev)
+
+ knownparents = []
+ newparents = []
+ for parent in parents:
+ if parent in seen:
+ knownparents.append(parent)
+ else:
+ newparents.append(parent)
- knownparents = []
- newparents = []
- for parent in parents:
- if parent in seen:
- knownparents.append(parent)
- else:
- newparents.append(parent)
+ ncols = len(seen)
+ seen[nodeidx:nodeidx + 1] = newparents
+ edges = [(nodeidx, seen.index(p)) for p in knownparents]
- ncols = len(seen)
- nextseen = seen[:]
- nextseen[nodeidx:nodeidx + 1] = newparents
- edges = [(nodeidx, nextseen.index(p)) for p in knownparents]
+ if len(newparents) > 0:
+ edges.append((nodeidx, nodeidx))
+ if len(newparents) > 1:
+ edges.append((nodeidx, nodeidx + 1))
- if len(newparents) > 0:
- edges.append((nodeidx, nodeidx))
- if len(newparents) > 1:
- edges.append((nodeidx, nodeidx + 1))
- nmorecols = len(nextseen) - ncols
- seen = nextseen
- yield (nodeidx, type, data, edges, ncols, nmorecols)
+ nmorecols = len(seen) - ncols
+ return nodeidx, edges, ncols, nmorecols
def fix_long_right_edges(edges):
for (i, (start, end)) in enumerate(edges):
@@ -117,11 +100,13 @@
line.extend(["|", " "] * (n_columns - ni - 1))
return line
-def ascii(ui, dag):
+def ascii(ui, base, type, char, text, coldata):
"""prints an ASCII graph of the DAG
- dag is a generator that emits tuples with the following elements:
+ takes the following arguments (one call per node in the graph):
+ - ui to write to
+ - A list we can keep the needed state in
- Column of the current node in the set of ongoing edges.
- Type indicator of node data == ASCIIDATA.
- Payload: (char, lines):
@@ -135,91 +120,87 @@
in the current revision. That is: -1 means one column removed;
0 means no columns added or removed; 1 means one column added.
"""
- prev_n_columns_diff = 0
- prev_node_index = 0
- for (node_index, type, (node_ch, node_lines), edges, n_columns, n_columns_diff) in dag:
- assert -2 < n_columns_diff < 2
- if n_columns_diff == -1:
- # Transform
- #
- # | | | | | |
- # o | | into o---+
- # |X / |/ /
- # | | | |
- fix_long_right_edges(edges)
-
- # add_padding_line says whether to rewrite
+ idx, edges, ncols, coldiff = coldata
+ assert -2 < coldiff < 2
+ if coldiff == -1:
+ # Transform
#
- # | | | | | | | |
- # | o---+ into | o---+
- # | / / | | | # <--- padding line
- # o | | | / /
- # o | |
- add_padding_line = (len(node_lines) > 2 and
- n_columns_diff == -1 and
- [x for (x, y) in edges if x + 1 < y])
+ # | | | | | |
+ # o | | into o---+
+ # |X / |/ /
+ # | | | |
+ fix_long_right_edges(edges)
+
+ # add_padding_line says whether to rewrite
+ #
+ # | | | | | | | |
+ # | o---+ into | o---+
+ # | / / | | | # <--- padding line
+ # o | | | / /
+ # o | |
+ add_padding_line = (len(text) > 2 and coldiff == -1 and
+ [x for (x, y) in edges if x + 1 < y])
- # fix_nodeline_tail says whether to rewrite
- #
- # | | o | | | | o | |
- # | | |/ / | | |/ /
- # | o | | into | o / / # <--- fixed nodeline tail
- # | |/ / | |/ /
- # o | | o | |
- fix_nodeline_tail = len(node_lines) <= 2 and not add_padding_line
+ # fix_nodeline_tail says whether to rewrite
+ #
+ # | | o | | | | o | |
+ # | | |/ / | | |/ /
+ # | o | | into | o / / # <--- fixed nodeline tail
+ # | |/ / | |/ /
+ # o | | o | |
+ fix_nodeline_tail = len(text) <= 2 and not add_padding_line
- # nodeline is the line containing the node character (typically o)
- nodeline = ["|", " "] * node_index
- nodeline.extend([node_ch, " "])
+ # nodeline is the line containing the node character (typically o)
+ nodeline = ["|", " "] * idx
+ nodeline.extend([char, " "])
- nodeline.extend(
- get_nodeline_edges_tail(
- node_index, prev_node_index, n_columns, n_columns_diff,
- prev_n_columns_diff, fix_nodeline_tail))
+ nodeline.extend(
+ get_nodeline_edges_tail(idx, base[1], ncols, coldiff,
+ base[0], fix_nodeline_tail))
- # shift_interline is the line containing the non-vertical
- # edges between this entry and the next
- shift_interline = ["|", " "] * node_index
- if n_columns_diff == -1:
- n_spaces = 1
- edge_ch = "/"
- elif n_columns_diff == 0:
- n_spaces = 2
- edge_ch = "|"
- else:
- n_spaces = 3
- edge_ch = "\\"
- shift_interline.extend(n_spaces * [" "])
- shift_interline.extend([edge_ch, " "] * (n_columns - node_index - 1))
+ # shift_interline is the line containing the non-vertical
+ # edges between this entry and the next
+ shift_interline = ["|", " "] * idx
+ if coldiff == -1:
+ n_spaces = 1
+ edge_ch = "/"
+ elif coldiff == 0:
+ n_spaces = 2
+ edge_ch = "|"
+ else:
+ n_spaces = 3
+ edge_ch = "\\"
+ shift_interline.extend(n_spaces * [" "])
+ shift_interline.extend([edge_ch, " "] * (ncols - idx - 1))
- # draw edges from the current node to its parents
- draw_edges(edges, nodeline, shift_interline)
+ # draw edges from the current node to its parents
+ draw_edges(edges, nodeline, shift_interline)
- # lines is the list of all graph lines to print
- lines = [nodeline]
- if add_padding_line:
- lines.append(get_padding_line(node_index, n_columns, edges))
- lines.append(shift_interline)
+ # lines is the list of all graph lines to print
+ lines = [nodeline]
+ if add_padding_line:
+ lines.append(get_padding_line(idx, ncols, edges))
+ lines.append(shift_interline)
- # make sure that there are as many graph lines as there are
- # log strings
- while len(node_lines) < len(lines):
- node_lines.append("")
- if len(lines) < len(node_lines):
- extra_interline = ["|", " "] * (n_columns + n_columns_diff)
- while len(lines) < len(node_lines):
- lines.append(extra_interline)
+ # make sure that there are as many graph lines as there are
+ # log strings
+ while len(text) < len(lines):
+ text.append("")
+ if len(lines) < len(text):
+ extra_interline = ["|", " "] * (ncols + coldiff)
+ while len(lines) < len(text):
+ lines.append(extra_interline)
- # print lines
- indentation_level = max(n_columns, n_columns + n_columns_diff)
- for (line, logstr) in zip(lines, node_lines):
- ln = "%-*s %s" % (2 * indentation_level, "".join(line), logstr)
- ui.write(ln.rstrip() + '\n')
+ # print lines
+ indentation_level = max(ncols, ncols + coldiff)
+ for (line, logstr) in zip(lines, text):
+ ln = "%-*s %s" % (2 * indentation_level, "".join(line), logstr)
+ ui.write(ln.rstrip() + '\n')
- # ... and start over
- prev_node_index = node_index
- prev_n_columns_diff = n_columns_diff
+ # ... and start over
+ base[0] = coldiff
+ base[1] = idx
def get_revs(repo, rev_opt):
if rev_opt:
@@ -235,6 +216,14 @@
if op in opts and opts[op]:
raise util.Abort(_("--graph option is incompatible with --%s") % op)
+def generate(ui, dag, displayer, showparents, edgefn):
+ seen, base = [], [0, 0]
+ for rev, type, ctx, parents in dag:
+ char = ctx.node() in showparents and '@' or 'o'
+ displayer.show(ctx)
+ lines = displayer.hunk.pop(rev).split('\n')[:-1]
+ ascii(ui, base, type, char, lines, edgefn(seen, rev, parents))
+
def graphlog(ui, repo, path=None, **opts):
"""show revision history alongside an ASCII revision graph
@@ -259,8 +248,9 @@
else:
revdag = graphmod.revisions(repo, start, stop)
- fmtdag = asciiformat(ui, repo, revdag, opts)
- ascii(ui, asciiedges(fmtdag))
+ displayer = show_changeset(ui, repo, opts, buffered=True)
+ showparents = [ctx.node() for ctx in repo[None].parents()]
+ generate(ui, revdag, displayer, showparents, asciiedges)
def graphrevs(repo, nodes, opts):
limit = cmdutil.loglimit(opts)
@@ -294,8 +284,9 @@
o = repo.changelog.nodesbetween(o, revs)[0]
revdag = graphrevs(repo, o, opts)
- fmtdag = asciiformat(ui, repo, revdag, opts)
- ascii(ui, asciiedges(fmtdag))
+ displayer = show_changeset(ui, repo, opts, buffered=True)
+ showparents = [ctx.node() for ctx in repo[None].parents()]
+ generate(ui, revdag, displayer, showparents, asciiedges)
def gincoming(ui, repo, source="default", **opts):
"""show the incoming changesets alongside an ASCII revision graph
@@ -343,8 +334,9 @@
chlist = other.changelog.nodesbetween(incoming, revs)[0]
revdag = graphrevs(other, chlist, opts)
- fmtdag = asciiformat(ui, other, revdag, opts, parentrepo=repo)
- ascii(ui, asciiedges(fmtdag))
+ displayer = show_changeset(ui, other, opts, buffered=True)
+ showparents = [ctx.node() for ctx in repo[None].parents()]
+ generate(ui, revdag, displayer, showparents, asciiedges)
finally:
if hasattr(other, 'close'):
--- a/hgext/highlight/__init__.py Wed Sep 16 16:11:44 2009 +0200
+++ b/hgext/highlight/__init__.py Wed Sep 16 23:46:06 2009 +0200
@@ -53,8 +53,9 @@
req.respond(common.HTTP_OK, 'text/css')
return ['/* pygments_style = %s */\n\n' % pg_style, fmter.get_style_defs('')]
-# monkeypatch in the new version
-extensions.wrapfunction(webcommands, '_filerevision', filerevision_highlight)
-extensions.wrapfunction(webcommands, 'annotate', annotate_highlight)
-webcommands.highlightcss = generate_css
-webcommands.__all__.append('highlightcss')
+def extsetup():
+ # monkeypatch in the new version
+ extensions.wrapfunction(webcommands, '_filerevision', filerevision_highlight)
+ extensions.wrapfunction(webcommands, 'annotate', annotate_highlight)
+ webcommands.highlightcss = generate_css
+ webcommands.__all__.append('highlightcss')
--- a/hgext/highlight/highlight.py Wed Sep 16 16:11:44 2009 +0200
+++ b/hgext/highlight/highlight.py Wed Sep 16 23:46:06 2009 +0200
@@ -32,26 +32,27 @@
if util.binary(text):
return
- # avoid UnicodeDecodeError in pygments
- text = encoding.tolocal(text)
+ # Pygments is best used with Unicode strings:
+ # <http://pygments.org/docs/unicode/>
+ text = text.decode(encoding.encoding, 'replace')
# To get multi-line strings right, we can't format line-by-line
try:
- lexer = guess_lexer_for_filename(fctx.path(), text[:1024],
- encoding=encoding.encoding)
+ lexer = guess_lexer_for_filename(fctx.path(), text[:1024])
except (ClassNotFound, ValueError):
try:
- lexer = guess_lexer(text[:1024], encoding=encoding.encoding)
+ lexer = guess_lexer(text[:1024])
except (ClassNotFound, ValueError):
- lexer = TextLexer(encoding=encoding.encoding)
+ lexer = TextLexer()
- formatter = HtmlFormatter(style=style, encoding=encoding.encoding)
+ formatter = HtmlFormatter(style=style)
colorized = highlight(text, lexer, formatter)
# strip wrapping div
colorized = colorized[:colorized.find('\n</pre>')]
colorized = colorized[colorized.find('<pre>')+5:]
- coloriter = iter(colorized.splitlines())
+ coloriter = (s.encode(encoding.encoding, 'replace')
+ for s in colorized.splitlines())
tmpl.filters['colorize'] = lambda x: coloriter.next()
--- a/hgext/inotify/__init__.py Wed Sep 16 16:11:44 2009 +0200
+++ b/hgext/inotify/__init__.py Wed Sep 16 23:46:06 2009 +0200
@@ -13,7 +13,6 @@
from mercurial.i18n import _
from mercurial import cmdutil, util
import server
-from weakref import proxy
from client import client, QueryFailed
def serve(ui, repo, **opts):
@@ -25,7 +24,8 @@
class service(object):
def init(self):
try:
- self.master = server.master(ui, repo, timeout)
+ self.master = server.master(ui, repo.dirstate,
+ repo.root, timeout)
except server.AlreadyStartedException, inst:
raise util.Abort(str(inst))
@@ -56,9 +56,6 @@
if not hasattr(repo, 'dirstate'):
return
- # XXX: weakref until hg stops relying on __del__
- repo = proxy(repo)
-
class inotifydirstate(repo.dirstate.__class__):
# We'll set this to false after an unsuccessful attempt so that
--- a/hgext/inotify/client.py Wed Sep 16 16:11:44 2009 +0200
+++ b/hgext/inotify/client.py Wed Sep 16 23:46:06 2009 +0200
@@ -29,12 +29,12 @@
if err[0] == errno.ECONNREFUSED:
self.ui.warn(_('(found dead inotify server socket; '
'removing it)\n'))
- os.unlink(self.repo.join('inotify.sock'))
+ os.unlink(os.path.join(self.root, '.hg', 'inotify.sock'))
if err[0] in (errno.ECONNREFUSED, errno.ENOENT) and autostart:
self.ui.debug(_('(starting inotify server)\n'))
try:
try:
- server.start(self.ui, self.repo)
+ server.start(self.ui, self.dirstate, self.root)
except server.AlreadyStartedException, inst:
# another process may have started its own
# inotify server while this one was starting.
@@ -64,11 +64,12 @@
class client(object):
def __init__(self, ui, repo):
self.ui = ui
- self.repo = repo
+ self.dirstate = repo.dirstate
+ self.root = repo.root
self.sock = socket.socket(socket.AF_UNIX)
def _connect(self):
- sockpath = self.repo.join('inotify.sock')
+ sockpath = os.path.join(self.root, '.hg', 'inotify.sock')
try:
self.sock.connect(sockpath)
except socket.error, err:
--- a/hgext/inotify/linux/_inotify.c Wed Sep 16 16:11:44 2009 +0200
+++ b/hgext/inotify/linux/_inotify.c Wed Sep 16 23:46:06 2009 +0200
@@ -106,13 +106,12 @@
static PyObject *remove_watch(PyObject *self, PyObject *args)
{
- PyObject *ret = NULL;
uint32_t wd;
int fd;
int r;
if (!PyArg_ParseTuple(args, "iI:remove_watch", &fd, &wd))
- goto bail;
+ return NULL;
Py_BEGIN_ALLOW_THREADS
r = inotify_rm_watch(fd, wd);
@@ -120,18 +119,11 @@
if (r == -1) {
PyErr_SetFromErrno(PyExc_OSError);
- goto bail;
+ return NULL;
}
Py_INCREF(Py_None);
-
- goto done;
-
-bail:
- Py_CLEAR(ret);
-
-done:
- return ret;
+ return Py_None;
}
PyDoc_STRVAR(
--- a/hgext/inotify/server.py Wed Sep 16 16:11:44 2009 +0200
+++ b/hgext/inotify/server.py Wed Sep 16 23:46:06 2009 +0200
@@ -34,13 +34,11 @@
walk_ignored_errors = (errno.ENOENT, errno.ENAMETOOLONG)
-def walkrepodirs(repo):
+def walkrepodirs(dirstate, absroot):
'''Iterate over all subdirectories of this repo.
Exclude the .hg directory, any nested repos, and ignored dirs.'''
- rootslash = repo.root + os.sep
-
def walkit(dirname, top):
- fullpath = rootslash + dirname
+ fullpath = join(absroot, dirname)
try:
for name, kind in osutil.listdir(fullpath):
if kind == stat.S_IFDIR:
@@ -49,7 +47,7 @@
return
else:
d = join(dirname, name)
- if repo.dirstate._ignore(d):
+ if dirstate._ignore(d):
continue
for subdir in walkit(d, False):
yield subdir
@@ -60,18 +58,16 @@
return walkit('', True)
-def walk(repo, root):
+def walk(dirstate, absroot, root):
'''Like os.walk, but only yields regular files.'''
# This function is critical to performance during startup.
- rootslash = repo.root + os.sep
-
def walkit(root, reporoot):
files, dirs = [], []
try:
- fullpath = rootslash + root
+ fullpath = join(absroot, root)
for name, kind in osutil.listdir(fullpath):
if kind == stat.S_IFDIR:
if name == '.hg':
@@ -80,7 +76,7 @@
else:
dirs.append(name)
path = join(root, name)
- if repo.dirstate._ignore(path):
+ if dirstate._ignore(path):
continue
for result in walkit(path, False):
yield result
@@ -98,7 +94,7 @@
return walkit(root, root == '')
-def _explain_watch_limit(ui, repo):
+def _explain_watch_limit(ui, dirstate, rootabs):
path = '/proc/sys/fs/inotify/max_user_watches'
try:
limit = int(file(path).read())
@@ -112,7 +108,7 @@
ui.warn(_('*** this limit is too low to watch every '
'directory in this repository\n'))
ui.warn(_('*** counting directories: '))
- ndirs = len(list(walkrepodirs(repo)))
+ ndirs = len(list(walkrepodirs(dirstate, rootabs)))
ui.warn(_('found %d\n') % ndirs)
newlimit = min(limit, 1024)
while newlimit < ((limit + ndirs) * 1.1):
@@ -121,7 +117,7 @@
(limit, newlimit))
ui.warn(_('*** echo %d > %s\n') % (newlimit, path))
raise util.Abort(_('cannot watch %s until inotify watch limit is raised')
- % repo.root)
+ % rootabs)
class pollable(object):
"""
@@ -309,10 +305,12 @@
inotify.IN_UNMOUNT |
0)
- def __init__(self, ui, repo):
+ def __init__(self, ui, dirstate, root):
self.ui = ui
- self.repo = repo
- self.wprefix = self.repo.wjoin('')
+ self.dirstate = dirstate
+
+ self.wprefix = join(root, '')
+ self.prefixlen = len(self.wprefix)
try:
self.watcher = watcher.watcher()
except OSError, err:
@@ -351,7 +349,7 @@
def dirstate_info(self):
try:
- st = os.lstat(self.repo.join('dirstate'))
+ st = os.lstat(self.wprefix + '.hg/dirstate')
return st.st_mtime, st.st_ino
except OSError, err:
if err.errno != errno.ENOENT:
@@ -363,7 +361,7 @@
return
if self.watcher.path(path) is None:
if self.ui.debugflag:
- self.ui.note(_('watching %r\n') % path[len(self.wprefix):])
+ self.ui.note(_('watching %r\n') % path[self.prefixlen:])
try:
self.watcher.add(path, mask)
except OSError, err:
@@ -371,16 +369,16 @@
return
if err.errno != errno.ENOSPC:
raise
- _explain_watch_limit(self.ui, self.repo)
+ _explain_watch_limit(self.ui, self.dirstate, self.wprefix)
def setup(self):
- self.ui.note(_('watching directories under %r\n') % self.repo.root)
- self.add_watch(self.repo.path, inotify.IN_DELETE)
+ self.ui.note(_('watching directories under %r\n') % self.wprefix)
+ self.add_watch(self.wprefix + '.hg', inotify.IN_DELETE)
self.check_dirstate()
def filestatus(self, fn, st):
try:
- type_, mode, size, time = self.repo.dirstate._map[fn][:4]
+ type_, mode, size, time = self.dirstate._map[fn][:4]
except KeyError:
type_ = '?'
if type_ == 'n':
@@ -392,7 +390,7 @@
if time != int(st_mtime):
return 'l'
return 'n'
- if type_ == '?' and self.repo.dirstate._ignore(fn):
+ if type_ == '?' and self.dirstate._ignore(fn):
return 'i'
return type_
@@ -443,14 +441,13 @@
if oldstatus and oldstatus in self.statuskeys \
and oldstatus != newstatus:
del self.statustrees[oldstatus].dir(root).files[fn]
- if newstatus and newstatus != 'i':
+
+ if newstatus in (None, 'i'):
+ d.files.pop(fn, None)
+ elif oldstatus != newstatus:
d.files[fn] = newstatus
- if newstatus in self.statuskeys:
- dd = self.statustrees[newstatus].dir(root)
- if oldstatus != newstatus or fn not in dd.files:
- dd.files[fn] = newstatus
- else:
- d.files.pop(fn, None)
+ if newstatus != 'n':
+ self.statustrees[newstatus].dir(root).files[fn] = newstatus
def check_deleted(self, key):
@@ -458,7 +455,7 @@
# may have vanished from the dirstate; we must clean them up.
nuke = []
for wfn, ignore in self.statustrees[key].walk(key):
- if wfn not in self.repo.dirstate:
+ if wfn not in self.dirstate:
nuke.append(wfn)
for wfn in nuke:
root, fn = split(wfn)
@@ -466,12 +463,12 @@
del self.tree.dir(root).files[fn]
def scan(self, topdir=''):
- ds = self.repo.dirstate._map.copy()
- self.add_watch(join(self.repo.root, topdir), self.mask)
- for root, dirs, files in walk(self.repo, topdir):
+ ds = self.dirstate._map.copy()
+ self.add_watch(join(self.wprefix, topdir), self.mask)
+ for root, dirs, files in walk(self.dirstate, self.wprefix, topdir):
for d in dirs:
self.add_watch(join(root, d), self.mask)
- wroot = root[len(self.wprefix):]
+ wroot = root[self.prefixlen:]
for fn in files:
wfn = join(wroot, fn)
self.updatefile(wfn, self.getstat(wfn))
@@ -500,7 +497,7 @@
if not self.ui.debugflag:
self.last_event = None
self.ui.note(_('%s dirstate reload\n') % self.event_time())
- self.repo.dirstate.invalidate()
+ self.dirstate.invalidate()
self.handle_timeout()
self.scan()
self.ui.note(_('%s end dirstate reload\n') % self.event_time())
@@ -516,8 +513,8 @@
# But it's easier to do nothing than to open that can of
# worms.
- if '_ignore' in self.repo.dirstate.__dict__:
- delattr(self.repo.dirstate, '_ignore')
+ if '_ignore' in self.dirstate.__dict__:
+ delattr(self.dirstate, '_ignore')
self.ui.note(_('rescanning due to .hgignore change\n'))
self.handle_timeout()
self.scan()
@@ -560,7 +557,7 @@
try:
st = self.stat(wpath)
if stat.S_ISREG(st[0]):
- if self.repo.dirstate[wpath] in 'lmn':
+ if self.dirstate[wpath] in 'lmn':
self.updatefile(wpath, st)
except OSError:
pass
@@ -574,7 +571,7 @@
self.check_dirstate()
return
- self.deletefile(wpath, self.repo.dirstate[wpath])
+ self.deletefile(wpath, self.dirstate[wpath])
def process_create(self, wpath, evt):
if self.ui.debugflag:
@@ -634,7 +631,7 @@
(self.event_time(), len(events)))
for evt in events:
assert evt.fullpath.startswith(self.wprefix)
- wpath = evt.fullpath[len(self.wprefix):]
+ wpath = evt.fullpath[self.prefixlen:]
# paths have been normalized, wpath never ends with a '/'
@@ -672,18 +669,17 @@
Returns a sorted list of relatives paths currently watched,
for debugging purposes.
"""
- return sorted(tuple[0][len(self.wprefix):] for tuple in self.watcher)
+ return sorted(tuple[0][self.prefixlen:] for tuple in self.watcher)
class server(pollable):
"""
Listens for client queries on unix socket inotify.sock
"""
- def __init__(self, ui, repo, repowatcher, timeout):
+ def __init__(self, ui, root, repowatcher, timeout):
self.ui = ui
- self.repo = repo
self.repowatcher = repowatcher
self.sock = socket.socket(socket.AF_UNIX)
- self.sockpath = self.repo.join('inotify.sock')
+ self.sockpath = join(root, '.hg/inotify.sock')
self.realsockpath = None
try:
self.sock.bind(self.sockpath)
@@ -811,11 +807,10 @@
raise
class master(object):
- def __init__(self, ui, repo, timeout=None):
+ def __init__(self, ui, dirstate, root, timeout=None):
self.ui = ui
- self.repo = repo
- self.repowatcher = repowatcher(ui, repo)
- self.server = server(ui, repo, self.repowatcher, timeout)
+ self.repowatcher = repowatcher(ui, dirstate, root)
+ self.server = server(ui, root, self.repowatcher, timeout)
def shutdown(self):
for obj in pollable.instances.itervalues():
@@ -828,7 +823,7 @@
sys.exit(0)
pollable.run()
-def start(ui, repo):
+def start(ui, dirstate, root):
def closefds(ignore):
# (from python bug #1177468)
# close all inherited file descriptors
@@ -849,7 +844,7 @@
except OSError:
pass
- m = master(ui, repo)
+ m = master(ui, dirstate, root)
sys.stdout.flush()
sys.stderr.flush()
--- a/hgext/mq.py Wed Sep 16 16:11:44 2009 +0200
+++ b/hgext/mq.py Wed Sep 16 23:46:06 2009 +0200
@@ -22,7 +22,6 @@
print patch series qseries
print applied patches qapplied
- print name of top applied patch qtop
add known patch to applied stack qpush
remove patch from applied stack qpop
@@ -1682,17 +1681,35 @@
def applied(ui, repo, patch=None, **opts):
"""print the patches already applied"""
+
q = repo.mq
+ l = len(q.applied)
+
if patch:
if patch not in q.series:
raise util.Abort(_("patch %s is not in series file") % patch)
end = q.series.index(patch) + 1
else:
end = q.series_end(True)
- return q.qseries(repo, length=end, status='A', summary=opts.get('summary'))
+
+ if opts.get('last') and not end:
+ ui.write(_("no patches applied\n"))
+ return 1
+ elif opts.get('last') and end == 1:
+ ui.write(_("only one patch applied\n"))
+ return 1
+ elif opts.get('last'):
+ start = end - 2
+ end = 1
+ else:
+ start = 0
+
+ return q.qseries(repo, length=end, start=start, status='A',
+ summary=opts.get('summary'))
def unapplied(ui, repo, patch=None, **opts):
"""print the patches not yet applied"""
+
q = repo.mq
if patch:
if patch not in q.series:
@@ -1700,7 +1717,14 @@
start = q.series.index(patch) + 1
else:
start = q.series_end(True)
- q.qseries(repo, start=start, status='U', summary=opts.get('summary'))
+
+ if start == len(q.series) and opts.get('first'):
+ ui.write(_("all patches applied\n"))
+ return 1
+
+ length = opts.get('first') and 1 or None
+ return q.qseries(repo, start=start, length=length, status='U',
+ summary=opts.get('summary'))
def qimport(ui, repo, *filename, **opts):
"""import a patch
@@ -2522,7 +2546,10 @@
seriesopts = [('s', 'summary', None, _('print first line of patch header'))]
cmdtable = {
- "qapplied": (applied, [] + seriesopts, _('hg qapplied [-s] [PATCH]')),
+ "qapplied":
+ (applied,
+ [('1', 'last', None, _('show only the last patch'))] + seriesopts,
+ _('hg qapplied [-1] [-s] [PATCH]')),
"qclone":
(clone,
[('', 'pull', None, _('use pull protocol to copy metadata')),
@@ -2645,7 +2672,10 @@
('n', 'nobackup', None, _('no backups'))],
_('hg strip [-f] [-b] [-n] REV')),
"qtop": (top, [] + seriesopts, _('hg qtop [-s]')),
- "qunapplied": (unapplied, [] + seriesopts, _('hg qunapplied [-s] [PATCH]')),
+ "qunapplied":
+ (unapplied,
+ [('1', 'first', None, _('show only the first patch'))] + seriesopts,
+ _('hg qunapplied [-1] [-s] [PATCH]')),
"qfinish":
(finish,
[('a', 'applied', None, _('finish all applied changesets'))],
--- a/hgext/patchbomb.py Wed Sep 16 16:11:44 2009 +0200
+++ b/hgext/patchbomb.py Wed Sep 16 23:46:06 2009 +0200
@@ -162,12 +162,16 @@
body += '\n'.join(patch)
msg = mail.mimetextpatch(body, display=opts.get('test'))
+ flag = ' '.join(opts.get('flag'))
+ if flag:
+ flag = ' ' + flag
+
subj = desc[0].strip().rstrip('. ')
if total == 1 and not opts.get('intro'):
- subj = '[PATCH] ' + (opts.get('subject') or subj)
+ subj = '[PATCH%s] %s' % (flag, opts.get('subject') or subj)
else:
tlen = len(str(total))
- subj = '[PATCH %0*d of %d] %s' % (tlen, idx, total, subj)
+ subj = '[PATCH %0*d of %d%s] %s' % (tlen, idx, total, flag, subj)
msg['Subject'] = mail.headencode(ui, subj, _charsets, opts.get('test'))
msg['X-Mercurial-Node'] = node
return msg, subj
@@ -322,11 +326,13 @@
if len(patches) > 1 or opts.get('intro'):
tlen = len(str(len(patches)))
- subj = '[PATCH %0*d of %d] %s' % (
- tlen, 0, len(patches),
- opts.get('subject') or
- prompt(ui, 'Subject:',
- rest=' [PATCH %0*d of %d] ' % (tlen, 0, len(patches))))
+ flag = ' '.join(opts.get('flag'))
+ if flag:
+ subj = '[PATCH %0*d of %d %s] ' % (tlen, 0, len(patches), flag)
+ else:
+ subj = '[PATCH %0*d of %d] ' % (tlen, 0, len(patches))
+ subj += opts.get('subject') or prompt(ui, 'Subject:', rest=subj,
+ default='None')
body = ''
if opts.get('diffstat'):
@@ -477,6 +483,7 @@
_('subject of first message (intro or single patch)')),
('', 'in-reply-to', '',
_('message identifier to reply to')),
+ ('', 'flag', [], _('flags to add in subject prefixes')),
('t', 'to', [], _('email addresses of recipients')),
]
--- a/hgext/transplant.py Wed Sep 16 16:11:44 2009 +0200
+++ b/hgext/transplant.py Wed Sep 16 23:46:06 2009 +0200
@@ -182,7 +182,7 @@
fp.write("# HG changeset patch\n")
fp.write("# User %s\n" % user)
fp.write("# Date %d %d\n" % date)
- fp.write(changelog[4])
+ fp.write(msg + '\n')
fp.close()
try:
--- a/hgext/win32mbcs.py Wed Sep 16 16:11:44 2009 +0200
+++ b/hgext/win32mbcs.py Wed Sep 16 23:46:06 2009 +0200
@@ -101,7 +101,7 @@
if args:
args = list(args)
args[0] = appendsep(args[0])
- if kwds.has_key('path'):
+ if 'path' in kwds:
kwds['path'] = appendsep(kwds['path'])
return func(*args, **kwds)
@@ -123,7 +123,7 @@
funcs = '''os.path.join os.path.split os.path.splitext
os.path.splitunc os.path.normpath os.path.normcase os.makedirs
mercurial.util.endswithsep mercurial.util.splitpath mercurial.util.checkcase
- mercurial.util.fspath mercurial.windows.pconvert'''
+ mercurial.util.fspath mercurial.util.pconvert'''
# codec and alias names of sjis and big5 to be faked.
problematic_encodings = '''big5 big5-tw csbig5 big5hkscs big5-hkscs
--- a/hgext/zeroconf/__init__.py Wed Sep 16 16:11:44 2009 +0200
+++ b/hgext/zeroconf/__init__.py Wed Sep 16 23:46:06 2009 +0200
@@ -101,17 +101,20 @@
def __init__(self, repo, name=None):
super(hgwebzc, self).__init__(repo, name)
name = self.reponame or os.path.basename(repo.root)
+ path = self.repo.ui.config("web", "prefix", "").strip('/')
desc = self.repo.ui.config("web", "description", name)
- publish(name, desc, name, int(repo.ui.config("web", "port", 8000)))
+ publish(name, desc, path, int(repo.ui.config("web", "port", 8000)))
class hgwebdirzc(hgwebdir_mod.hgwebdir):
- def run(self):
+ def __init__(self, conf, baseui=None):
+ super(hgwebdirzc, self).__init__(conf, baseui)
+ prefix = self.ui.config("web", "prefix", "").strip('/') + '/'
for r, p in self.repos:
u = self.ui.copy()
u.readconfig(os.path.join(p, '.hg', 'hgrc'))
n = os.path.basename(r)
- publish(n, "hgweb", p, int(u.config("web", "port", 8000)))
- return super(hgwebdirzc, self).run()
+ path = (prefix + r).strip('/')
+ publish(n, "hgweb", path, int(u.config("web", "port", 8000)))
# listen
--- a/mercurial/changegroup.py Wed Sep 16 16:11:44 2009 +0200
+++ b/mercurial/changegroup.py Wed Sep 16 23:46:06 2009 +0200
@@ -10,7 +10,7 @@
import struct, os, bz2, zlib, tempfile
def getchunk(source):
- """get a chunk from a changegroup"""
+ """return the next chunk from changegroup 'source' as a string"""
d = source.read(4)
if not d:
return ""
@@ -25,7 +25,8 @@
return d
def chunkiter(source):
- """iterate through the chunks in source"""
+ """iterate through the chunks in source, yielding a sequence of chunks
+ (strings)"""
while 1:
c = getchunk(source)
if not c:
@@ -33,10 +34,11 @@
yield c
def chunkheader(length):
- """build a changegroup chunk header"""
+ """return a changegroup chunk header (string)"""
return struct.pack(">l", length + 4)
def closechunk():
+ """return a changegroup chunk header (string) for a zero-length chunk"""
return struct.pack(">l", 0)
class nocompress(object):
--- a/mercurial/cmdutil.py Wed Sep 16 16:11:44 2009 +0200
+++ b/mercurial/cmdutil.py Wed Sep 16 23:46:06 2009 +0200
@@ -987,12 +987,12 @@
def finddate(ui, repo, date):
"""Find the tipmost changeset that matches the given date spec"""
df = util.matchdate(date)
- get = util.cachefunc(lambda r: repo[r].changeset())
+ get = util.cachefunc(lambda r: repo[r])
changeiter, matchfn = walkchangerevs(ui, repo, [], get, {'rev':None})
results = {}
for st, rev, fns in changeiter:
if st == 'add':
- d = get(rev)[2]
+ d = get(rev).date()
if df(d[0]):
results[rev] = d
elif st == 'iter':
@@ -1118,13 +1118,13 @@
def changerevgen():
for i, window in increasing_windows(len(repo) - 1, nullrev):
for j in xrange(i - window, i + 1):
- yield j, change(j)[3]
+ yield change(j)
- for rev, changefiles in changerevgen():
- matches = filter(m, changefiles)
+ for ctx in changerevgen():
+ matches = filter(m, ctx.files())
if matches:
- fncache[rev] = matches
- wanted.add(rev)
+ fncache[ctx.rev()] = matches
+ wanted.add(ctx.rev())
class followfilter(object):
def __init__(self, onlyfirst=False):
@@ -1189,7 +1189,7 @@
fns = fncache.get(rev)
if not fns:
def fns_generator():
- for f in change(rev)[3]:
+ for f in change(rev).files():
if m(f):
yield f
fns = fns_generator()
--- a/mercurial/commands.py Wed Sep 16 16:11:44 2009 +0200
+++ b/mercurial/commands.py Wed Sep 16 23:46:06 2009 +0200
@@ -1275,9 +1275,9 @@
if opts.get('all'):
cols.append(change)
if opts.get('user'):
- cols.append(ui.shortuser(get(r)[1]))
+ cols.append(ui.shortuser(get(r).user()))
if opts.get('date'):
- cols.append(datefunc(get(r)[2]))
+ cols.append(datefunc(get(r).date()))
if opts.get('files_with_matches'):
c = (fn, r)
if c in filerevmatches:
@@ -1291,7 +1291,7 @@
skip = {}
revfiles = {}
- get = util.cachefunc(lambda r: repo[r].changeset())
+ get = util.cachefunc(lambda r: repo[r])
changeiter, matchfn = cmdutil.walkchangerevs(ui, repo, pats, get, opts)
found = False
follow = opts.get('follow')
@@ -1300,7 +1300,7 @@
matches.clear()
revfiles.clear()
elif st == 'add':
- ctx = repo[rev]
+ ctx = get(rev)
pctx = ctx.parents()[0]
parent = pctx.rev()
matches.setdefault(rev, {})
@@ -1323,18 +1323,18 @@
continue
files.append(fn)
- if not matches[rev].has_key(fn):
+ if fn not in matches[rev]:
grepbody(fn, rev, flog.read(fnode))
pfn = copy or fn
- if not matches[parent].has_key(pfn):
+ if pfn not in matches[parent]:
try:
fnode = pctx.filenode(pfn)
grepbody(pfn, parent, flog.read(fnode))
except error.LookupError:
pass
elif st == 'iter':
- parent = repo[rev].parents()[0].rev()
+ parent = get(rev).parents()[0].rev()
for fn in sorted(revfiles.get(rev, [])):
states = matches[rev][fn]
copy = copies.get(rev, {}).get(fn)
@@ -1982,7 +1982,7 @@
will appear in files:.
"""
- get = util.cachefunc(lambda r: repo[r].changeset())
+ get = util.cachefunc(lambda r: repo[r])
changeiter, matchfn = cmdutil.walkchangerevs(ui, repo, pats, get, opts)
limit = cmdutil.loglimit(opts)
@@ -2040,40 +2040,37 @@
if opts.get('only_merges') and len(parents) != 2:
continue
- if only_branches:
- revbranch = get(rev)[5]['branch']
- if revbranch not in only_branches:
- continue
-
- if df:
- changes = get(rev)
- if not df(changes[2][0]):
- continue
+ ctx = get(rev)
+ if only_branches and ctx.branch() not in only_branches:
+ continue
+
+ if df and not df(ctx.date()[0]):
+ continue
if opts.get('keyword'):
- changes = get(rev)
miss = 0
for k in [kw.lower() for kw in opts['keyword']]:
- if not (k in changes[1].lower() or
- k in changes[4].lower() or
- k in " ".join(changes[3]).lower()):
+ if not (k in ctx.user().lower() or
+ k in ctx.description().lower() or
+ k in " ".join(ctx.files()).lower()):
miss = 1
break
if miss:
continue
if opts['user']:
- changes = get(rev)
- if not [k for k in opts['user'] if k in changes[1]]:
+ if not [k for k in opts['user'] if k in ctx.user()]:
continue
copies = []
if opts.get('copies') and rev:
- for fn in get(rev)[3]:
+ for fn in ctx.files():
rename = getrenamed(fn, rev)
if rename:
copies.append((fn, rename[0]))
- displayer.show(context.changectx(repo, rev), copies=copies)
+
+ displayer.show(ctx, copies=copies)
+
elif st == 'iter':
if count == limit: break
if displayer.flush(rev):
@@ -2158,7 +2155,8 @@
roots, heads = [common.node()], [p2.node()]
displayer = cmdutil.show_changeset(ui, repo, opts)
for node in repo.changelog.nodesbetween(roots=roots, heads=heads)[0]:
- displayer.show(repo[node])
+ if node not in roots:
+ displayer.show(repo[node])
return 0
return hg.merge(repo, node, force=opts.get('force'))
--- a/mercurial/dispatch.py Wed Sep 16 16:11:44 2009 +0200
+++ b/mercurial/dispatch.py Wed Sep 16 23:46:06 2009 +0200
@@ -335,7 +335,7 @@
path = _findrepo(os.getcwd()) or ""
if not path:
lui = ui
- if path:
+ else:
try:
lui = ui.copy()
lui.readconfig(os.path.join(path, ".hg", "hgrc"))
@@ -349,19 +349,25 @@
lui = ui.copy()
lui.readconfig(os.path.join(path, ".hg", "hgrc"))
+ # Configure extensions in phases: uisetup, extsetup, cmdtable, and
+ # reposetup. Programs like TortoiseHg will call _dispatch several
+ # times so we keep track of configured extensions in _loaded.
extensions.loadall(lui)
- for name, module in extensions.extensions():
- if name in _loaded:
- continue
+ exts = [ext for ext in extensions.extensions() if ext[0] not in _loaded]
- # setup extensions
- # TODO this should be generalized to scheme, where extensions can
- # redepend on other extensions. then we should toposort them, and
- # do initialization in correct order
+ # (uisetup is handled in extensions.loadall)
+
+ for name, module in exts:
extsetup = getattr(module, 'extsetup', None)
if extsetup:
- extsetup()
+ try:
+ extsetup(ui)
+ except TypeError:
+ if extsetup.func_code.co_argcount != 0:
+ raise
+ extsetup() # old extsetup with no ui argument
+ for name, module in exts:
cmdtable = getattr(module, 'cmdtable', {})
overrides = [cmd for cmd in cmdtable if cmd in commands.table]
if overrides:
@@ -370,6 +376,8 @@
commands.table.update(cmdtable)
_loaded.add(name)
+ # (reposetup is handled in hg.repository)
+
addaliases(lui, commands.table)
# check for fallback encoding
--- a/mercurial/extensions.py Wed Sep 16 16:11:44 2009 +0200
+++ b/mercurial/extensions.py Wed Sep 16 23:46:06 2009 +0200
@@ -40,6 +40,7 @@
return imp.load_source(module_name, path)
def load(ui, name, path):
+ # unused ui argument kept for backwards compatibility
if name.startswith('hgext.') or name.startswith('hgext/'):
shortname = name[6:]
else:
@@ -66,12 +67,9 @@
_extensions[shortname] = mod
_order.append(shortname)
- uisetup = getattr(mod, 'uisetup', None)
- if uisetup:
- uisetup(ui)
-
def loadall(ui):
result = ui.configitems("extensions")
+ newindex = len(_order)
for (name, path) in result:
if path:
if path[0] == '!':
@@ -90,6 +88,11 @@
if ui.traceback():
return 1
+ for name in _order[newindex:]:
+ uisetup = getattr(_extensions[name], 'uisetup', None)
+ if uisetup:
+ uisetup(ui)
+
def wrapcommand(table, command, wrapper):
aliases, entry = cmdutil.findcmd(command, table)
for alias, e in table.iteritems():
--- a/mercurial/help.py Wed Sep 16 16:11:44 2009 +0200
+++ b/mercurial/help.py Wed Sep 16 23:46:06 2009 +0200
@@ -405,59 +405,59 @@
List of filters:
- :addbreaks: Any text. Add an XHTML "<br />" tag before the end of
- every line except the last.
- :age: Date. Returns a human-readable date/time difference
- between the given date/time and the current
- date/time.
- :basename: Any text. Treats the text as a path, and returns the
- last component of the path after splitting by the
- path separator (ignoring trailing separators). For
- example, "foo/bar/baz" becomes "baz" and "foo/bar//"
- becomes "bar".
- :stripdir: Treat the text as path and strip a directory level,
- if possible. For example, "foo" and "foo/bar" becomes
- "foo".
- :date: Date. Returns a date in a Unix date format, including
- the timezone: "Mon Sep 04 15:13:13 2006 0700".
- :domain: Any text. Finds the first string that looks like an
- email address, and extracts just the domain
- component. Example: 'User <user@example.com>' becomes
- 'example.com'.
- :email: Any text. Extracts the first string that looks like
- an email address. Example: 'User <user@example.com>'
- becomes 'user@example.com'.
- :escape: Any text. Replaces the special XML/XHTML characters
- "&", "<" and ">" with XML entities.
- :fill68: Any text. Wraps the text to fit in 68 columns.
- :fill76: Any text. Wraps the text to fit in 76 columns.
- :firstline: Any text. Returns the first line of text.
- :nonempty: Any text. Returns '(none)' if the string is empty.
- :hgdate: Date. Returns the date as a pair of numbers:
- "1157407993 25200" (Unix timestamp, timezone offset).
- :isodate: Date. Returns the date in ISO 8601 format:
- "2009-08-18 13:00 +0200".
- :isodatesec: Date. Returns the date in ISO 8601 format, including
- seconds: "2009-08-18 13:00:13 +0200". See also the
- rfc3339date filter.
- :localdate: Date. Converts a date to local date.
- :obfuscate: Any text. Returns the input text rendered as a
- sequence of XML entities.
- :person: Any text. Returns the text before an email address.
- :rfc822date: Date. Returns a date using the same format used in
- email headers: "Tue, 18 Aug 2009 13:00:13 +0200".
+ :addbreaks: Any text. Add an XHTML "<br />" tag before the end of
+ every line except the last.
+ :age: Date. Returns a human-readable date/time difference
+ between the given date/time and the current
+ date/time.
+ :basename: Any text. Treats the text as a path, and returns the
+ last component of the path after splitting by the
+ path separator (ignoring trailing separators). For
+ example, "foo/bar/baz" becomes "baz" and "foo/bar//"
+ becomes "bar".
+ :stripdir: Treat the text as path and strip a directory level,
+ if possible. For example, "foo" and "foo/bar" becomes
+ "foo".
+ :date: Date. Returns a date in a Unix date format, including
+ the timezone: "Mon Sep 04 15:13:13 2006 0700".
+ :domain: Any text. Finds the first string that looks like an
+ email address, and extracts just the domain
+ component. Example: 'User <user@example.com>' becomes
+ 'example.com'.
+ :email: Any text. Extracts the first string that looks like
+ an email address. Example: 'User <user@example.com>'
+ becomes 'user@example.com'.
+ :escape: Any text. Replaces the special XML/XHTML characters
+ "&", "<" and ">" with XML entities.
+ :fill68: Any text. Wraps the text to fit in 68 columns.
+ :fill76: Any text. Wraps the text to fit in 76 columns.
+ :firstline: Any text. Returns the first line of text.
+ :nonempty: Any text. Returns '(none)' if the string is empty.
+ :hgdate: Date. Returns the date as a pair of numbers:
+ "1157407993 25200" (Unix timestamp, timezone offset).
+ :isodate: Date. Returns the date in ISO 8601 format:
+ "2009-08-18 13:00 +0200".
+ :isodatesec: Date. Returns the date in ISO 8601 format, including
+ seconds: "2009-08-18 13:00:13 +0200". See also the
+ rfc3339date filter.
+ :localdate: Date. Converts a date to local date.
+ :obfuscate: Any text. Returns the input text rendered as a
+ sequence of XML entities.
+ :person: Any text. Returns the text before an email address.
+ :rfc822date: Date. Returns a date using the same format used in
+ email headers: "Tue, 18 Aug 2009 13:00:13 +0200".
:rfc3339date: Date. Returns a date using the Internet date format
specified in RFC 3339: "2009-08-18T13:00:13+02:00".
- :short: Changeset hash. Returns the short form of a changeset
- hash, i.e. a 12-byte hexadecimal string.
- :shortdate: Date. Returns a date like "2006-09-18".
- :strip: Any text. Strips all leading and trailing whitespace.
- :tabindent: Any text. Returns the text, with every line except
- the first starting with a tab character.
- :urlescape: Any text. Escapes all "special" characters. For
- example, "foo bar" becomes "foo%20bar".
- :user: Any text. Returns the user portion of an email
- address.
+ :short: Changeset hash. Returns the short form of a changeset
+ hash, i.e. a 12-byte hexadecimal string.
+ :shortdate: Date. Returns a date like "2006-09-18".
+ :strip: Any text. Strips all leading and trailing whitespace.
+ :tabindent: Any text. Returns the text, with every line except
+ the first starting with a tab character.
+ :urlescape: Any text. Escapes all "special" characters. For
+ example, "foo bar" becomes "foo%20bar".
+ :user: Any text. Returns the user portion of an email
+ address.
''')),
(['urls'], _('URL Paths'),
--- a/mercurial/hgweb/hgwebdir_mod.py Wed Sep 16 16:11:44 2009 +0200
+++ b/mercurial/hgweb/hgwebdir_mod.py Wed Sep 16 23:46:06 2009 +0200
@@ -198,12 +198,17 @@
sortdefault = 'name', False
def entries(sortcolumn="", descending=False, subdir="", **map):
+
rows = []
parity = paritygen(self.stripecount)
+ descend = self.ui.configbool('web', 'descend', True)
for name, path in self.repos:
+
if not name.startswith(subdir):
continue
name = name[len(subdir):]
+ if not descend and '/' in name:
+ continue
u = self.ui.copy()
try:
--- a/mercurial/localrepo.py Wed Sep 16 16:11:44 2009 +0200
+++ b/mercurial/localrepo.py Wed Sep 16 23:46:06 2009 +0200
@@ -1457,6 +1457,12 @@
return self.push_addchangegroup(remote, force, revs)
def prepush(self, remote, force, revs):
+ '''Analyze the local and remote repositories and determine which
+ changesets need to be pushed to the remote. Return a tuple
+ (changegroup, remoteheads). changegroup is a readable file-like
+ object whose read() returns successive changegroup chunks ready to
+ be sent over the wire. remoteheads is the list of remote heads.
+ '''
common = {}
remote_heads = remote.heads()
inc = self.findincoming(remote, common, remote_heads, force=force)
@@ -1601,9 +1607,10 @@
self.ui.debug("%s\n" % hex(node))
def changegroupsubset(self, bases, heads, source, extranodes=None):
- """This function generates a changegroup consisting of all the nodes
- that are descendents of any of the bases, and ancestors of any of
- the heads.
+ """Compute a changegroup consisting of all the nodes that are
+ descendents of any of the bases and ancestors of any of the heads.
+ Return a chunkbuffer object whose read() method will return
+ successive changegroup chunks.
It is fairly complex as determining which filenodes and which
manifest nodes need to be included for the changeset to be complete
@@ -1902,8 +1909,9 @@
return self.changegroupsubset(basenodes, self.heads(), source)
def _changegroup(self, common, source):
- """Generate a changegroup of all nodes that we have that a recipient
- doesn't.
+ """Compute the changegroup of all nodes that we have that a recipient
+ doesn't. Return a chunkbuffer object whose read() method will return
+ successive changegroup chunks.
This is much easier than the previous function as we can assume that
the recipient has any changenode we aren't sending them.
@@ -1937,6 +1945,7 @@
return lookuprevlink
def gengroup():
+ '''yield a sequence of changegroup chunks (strings)'''
# construct a list of all changed files
changedfiles = set()
--- a/mercurial/manifest.py Wed Sep 16 16:11:44 2009 +0200
+++ b/mercurial/manifest.py Wed Sep 16 23:46:06 2009 +0200
@@ -20,12 +20,11 @@
def set(self, f, flags):
self._flags[f] = flags
def copy(self):
- return manifestdict(dict.copy(self), dict.copy(self._flags))
+ return manifestdict(self, dict.copy(self._flags))
class manifest(revlog.revlog):
def __init__(self, opener):
- self.mapcache = None
- self.listcache = None
+ self._mancache = None
revlog.revlog.__init__(self, opener, "00manifest.i")
def parse(self, lines):
@@ -40,12 +39,12 @@
def read(self, node):
if node == revlog.nullid:
return manifestdict() # don't upset local cache
- if self.mapcache and self.mapcache[0] == node:
- return self.mapcache[1]
+ if self._mancache and self._mancache[0] == node:
+ return self._mancache[1]
text = self.revision(node)
- self.listcache = array.array('c', text)
+ arraytext = array.array('c', text)
mapping = self.parse(text)
- self.mapcache = (node, mapping)
+ self._mancache = (node, mapping, arraytext)
return mapping
def _search(self, m, s, lo=0, hi=None):
@@ -93,8 +92,8 @@
def find(self, node, f):
'''look up entry for a single file efficiently.
return (node, flags) pair if found, (None, None) if not.'''
- if self.mapcache and node == self.mapcache[0]:
- return self.mapcache[1].get(f), self.mapcache[1].flags(f)
+ if self._mancache and self._mancache[0] == node:
+ return self._mancache[1].get(f), self._mancache[1].flags(f)
text = self.revision(node)
start, end = self._search(text, f)
if start == end:
@@ -110,17 +109,13 @@
def addlistdelta(addlist, x):
# start from the bottom up
# so changes to the offsets don't mess things up.
- i = len(x)
- while i > 0:
- i -= 1
- start = x[i][0]
- end = x[i][1]
- if x[i][2]:
- addlist[start:end] = array.array('c', x[i][2])
+ for start, end, content in reversed(x):
+ if content:
+ addlist[start:end] = array.array('c', content)
else:
del addlist[start:end]
- return "".join([struct.pack(">lll", d[0], d[1], len(d[2])) + d[2]
- for d in x ])
+ return "".join(struct.pack(">lll", start, end, len(content)) + content
+ for start, end, content in x)
def checkforbidden(l):
for f in l:
@@ -128,26 +123,29 @@
raise error.RevlogError(
_("'\\n' and '\\r' disallowed in filenames: %r") % f)
- # if we're using the listcache, make sure it is valid and
+ # if we're using the cache, make sure it is valid and
# parented by the same node we're diffing against
- if not (changed and self.listcache and p1 and self.mapcache[0] == p1):
+ if not (changed and self._mancache and p1 and self._mancache[0] == p1):
files = sorted(map)
checkforbidden(files)
# if this is changed to support newlines in filenames,
# be sure to check the templates/ dir again (especially *-raw.tmpl)
hex, flags = revlog.hex, map.flags
- text = ["%s\000%s%s\n" % (f, hex(map[f]), flags(f))
- for f in files]
- self.listcache = array.array('c', "".join(text))
+ text = ''.join("%s\000%s%s\n" % (f, hex(map[f]), flags(f))
+ for f in files)
+ arraytext = array.array('c', text)
cachedelta = None
else:
- addlist = self.listcache
+ added, removed = changed
+ addlist = self._mancache[2]
- checkforbidden(changed[0])
+ checkforbidden(added)
# combine the changed lists into one list for sorting
- work = [[x, 0] for x in changed[0]]
- work[len(work):] = [[x, 1] for x in changed[1]]
+ work = [(x, False) for x in added]
+ work.extend((x, True) for x in removed)
+ # this could use heapq.merge() (from python2.6+) or equivalent
+ # since the lists are already sorted
work.sort()
delta = []
@@ -160,18 +158,17 @@
# start with a readonly loop that finds the offset of
# each line and creates the deltas
- for w in work:
- f = w[0]
+ for f, todelete in work:
# bs will either be the index of the item or the insert point
start, end = self._search(addbuf, f, start)
- if w[1] == 0:
+ if not todelete:
l = "%s\000%s%s\n" % (f, revlog.hex(map[f]), map.flags(f))
else:
+ if start == end:
+ # item we want to delete was not found, error out
+ raise AssertionError(
+ _("failed to remove %s from manifest") % f)
l = ""
- if start == end and w[1] == 1:
- # item we want to delete was not found, error out
- raise AssertionError(
- _("failed to remove %s from manifest") % f)
if dstart != None and dstart <= start and dend >= start:
if dend < end:
dend = end
@@ -190,12 +187,12 @@
cachedelta = addlistdelta(addlist, delta)
# the delta is only valid if we've been processing the tip revision
- if self.mapcache[0] != self.tip():
+ if p1 != self.tip():
cachedelta = None
- self.listcache = addlist
+ arraytext = addlist
+ text = buffer(arraytext)
- n = self.addrevision(buffer(self.listcache), transaction, link,
- p1, p2, cachedelta)
- self.mapcache = (n, map)
+ n = self.addrevision(text, transaction, link, p1, p2, cachedelta)
+ self._mancache = (n, map, arraytext)
return n
--- a/mercurial/minirst.py Wed Sep 16 16:11:44 2009 +0200
+++ b/mercurial/minirst.py Wed Sep 16 23:46:06 2009 +0200
@@ -279,6 +279,8 @@
def formatblock(block, width):
"""Format a block according to width."""
+ if width <= 0:
+ width = 78
indent = ' ' * block['indent']
if block['type'] == 'margin':
return ''
--- a/mercurial/parsers.c Wed Sep 16 16:11:44 2009 +0200
+++ b/mercurial/parsers.c Wed Sep 16 23:46:06 2009 +0200
@@ -92,8 +92,6 @@
goto bail;
if (nlen > 40) {
- PyObject *flags;
-
flags = PyString_FromStringAndSize(zero + 41,
nlen - 40);
if (!flags)
--- a/mercurial/patch.py Wed Sep 16 16:11:44 2009 +0200
+++ b/mercurial/patch.py Wed Sep 16 23:46:06 2009 +0200
@@ -188,7 +188,7 @@
if m:
if gp:
gitpatches.append(gp)
- src, dst = m.group(1, 2)
+ dst = m.group(2)
gp = patchmeta(dst)
gp.lineno = lineno
elif gp:
@@ -378,15 +378,13 @@
self.write()
self.write_rej()
- def apply(self, h, reverse):
+ def apply(self, h):
if not h.complete():
raise PatchError(_("bad hunk #%d %s (%d %d %d %d)") %
(h.number, h.desc, len(h.a), h.lena, len(h.b),
h.lenb))
self.hunks += 1
- if reverse:
- h.reverse()
if self.missing:
self.rej.append(h)
@@ -600,31 +598,6 @@
self.startb, self.lenb)
self.hunk[0] = self.desc
- def reverse(self):
- self.create, self.remove = self.remove, self.create
- origlena = self.lena
- origstarta = self.starta
- self.lena = self.lenb
- self.starta = self.startb
- self.lenb = origlena
- self.startb = origstarta
- self.a = []
- self.b = []
- # self.hunk[0] is the @@ description
- for x in xrange(1, len(self.hunk)):
- o = self.hunk[x]
- if o.startswith('-'):
- n = '+' + o[1:]
- self.b.append(o[1:])
- elif o.startswith('+'):
- n = '-' + o[1:]
- self.a.append(n)
- else:
- n = o
- self.b.append(o[1:])
- self.a.append(o)
- self.hunk[x] = o
-
def fix_newline(self):
diffhelpers.fix_newline(self.hunk, self.a, self.b)
@@ -762,7 +735,7 @@
return s
return s[:i]
-def selectfile(afile_orig, bfile_orig, hunk, strip, reverse):
+def selectfile(afile_orig, bfile_orig, hunk, strip):
def pathstrip(path, count=1):
pathlen = len(path)
i = 0
@@ -790,8 +763,6 @@
else:
goodb = not nullb and os.path.exists(bfile)
createfunc = hunk.createfile
- if reverse:
- createfunc = hunk.rmfile
missing = not goodb and not gooda and not createfunc()
# some diff programs apparently produce create patches where the
@@ -977,8 +948,7 @@
if hunknum == 0 and dopatch and not gitworkdone:
raise NoHunks
-def applydiff(ui, fp, changed, strip=1, sourcefile=None, reverse=False,
- eol=None):
+def applydiff(ui, fp, changed, strip=1, sourcefile=None, eol=None):
"""
Reads a patch from fp and tries to apply it.
@@ -1008,7 +978,7 @@
if not current_file:
continue
current_hunk = values
- ret = current_file.apply(current_hunk, reverse)
+ ret = current_file.apply(current_hunk)
if ret >= 0:
changed.setdefault(current_file.fname, None)
if ret > 0:
@@ -1021,7 +991,7 @@
current_file = patchfile(ui, sourcefile, opener, eol=eol)
else:
current_file, missing = selectfile(afile, bfile, first_hunk,
- strip, reverse)
+ strip)
current_file = patchfile(ui, current_file, opener, missing, eol)
except PatchError, err:
ui.warn(str(err) + '\n')
--- a/mercurial/repo.py Wed Sep 16 16:11:44 2009 +0200
+++ b/mercurial/repo.py Wed Sep 16 23:46:06 2009 +0200
@@ -40,4 +40,5 @@
url = self.url()
if url.endswith('/'):
return url + path
- return url + '/' + path
+ else:
+ return url + '/' + path
--- a/mercurial/revlog.py Wed Sep 16 16:11:44 2009 +0200
+++ b/mercurial/revlog.py Wed Sep 16 23:46:06 2009 +0200
@@ -973,7 +973,7 @@
if node == nullid:
return ""
if self._cache and self._cache[0] == node:
- return str(self._cache[2])
+ return self._cache[2]
# look up what we need to read
text = None
@@ -988,7 +988,7 @@
# do we have useful data cached?
if self._cache and self._cache[1] >= base and self._cache[1] < rev:
base = self._cache[1]
- text = str(self._cache[2])
+ text = self._cache[2]
self._loadindex(base, rev + 1)
self._chunkraw(base, rev)
@@ -1111,7 +1111,8 @@
ifh.write(data[1])
self.checkinlinesize(transaction, ifh)
- self._cache = (node, curr, text)
+ if type(text) == str: # only accept immutable objects
+ self._cache = (node, curr, text)
return node
def ancestor(self, a, b):
@@ -1127,7 +1128,8 @@
return self.node(c)
def group(self, nodelist, lookup, infocollect=None):
- """calculate a delta group
+ """Calculate a delta group, yielding a sequence of changegroup chunks
+ (strings).
Given a list of changeset revs, return a set of deltas and
metadata corresponding to nodes. the first delta is
--- a/mercurial/tags.py Wed Sep 16 16:11:44 2009 +0200
+++ b/mercurial/tags.py Wed Sep 16 23:46:06 2009 +0200
@@ -301,7 +301,10 @@
def _writetagcache(ui, repo, heads, tagfnode, cachetags):
- cachefile = repo.opener('tags.cache', 'w', atomictemp=True)
+ try:
+ cachefile = repo.opener('tags.cache', 'w', atomictemp=True)
+ except (OSError, IOError):
+ return
_debug(ui, 'writing cache file %s\n' % cachefile.name)
realheads = repo.heads() # for sanity checks below
--- a/mercurial/templatefilters.py Wed Sep 16 16:11:44 2009 +0200
+++ b/mercurial/templatefilters.py Wed Sep 16 23:46:06 2009 +0200
@@ -105,13 +105,14 @@
'''indent each non-empty line of text after first with prefix.'''
lines = text.splitlines()
num_lines = len(lines)
+ endswithnewline = text[-1:] == '\n'
def indenter():
for i in xrange(num_lines):
l = lines[i]
if i and l.strip():
yield prefix
yield l
- if i < num_lines - 1 or text.endswith('\n'):
+ if i < num_lines - 1 or endswithnewline:
yield '\n'
return "".join(indenter())
--- a/mercurial/templater.py Wed Sep 16 16:11:44 2009 +0200
+++ b/mercurial/templater.py Wed Sep 16 23:46:06 2009 +0200
@@ -42,7 +42,7 @@
filter uses function to transform value. syntax is
{key|filter1|filter2|...}.'''
- template_re = re.compile(r'{([\w\|%]+)}|#([\w\|%]+)#')
+ template_re = re.compile(r'{([\w\|%]+)}')
def __init__(self, loader, filters={}, defaults={}):
self.loader = loader
--- a/mercurial/ui.py Wed Sep 16 16:11:44 2009 +0200
+++ b/mercurial/ui.py Wed Sep 16 23:46:06 2009 +0200
@@ -199,7 +199,7 @@
def _path(self, loc):
p = self.config('paths', loc)
if p and '%%' in p:
- self.warn('(deprecated \'%%\' in path %s=%s from %s)\n' %
+ self.warn("(deprecated '%%' in path %s=%s from %s)\n" %
(loc, p, self.configsource('paths', loc)))
p = p.replace('%%', '%')
return p
@@ -358,7 +358,7 @@
visible. 'topic' is the current operation, 'item' is a
non-numeric marker of the current position (ie the currently
in-process file), 'pos' is the current numeric position (ie
- revision, bytes, etc.), units is a corresponding unit label,
+ revision, bytes, etc.), unit is a corresponding unit label,
and total is the highest expected pos.
Multiple nested topics may be active at a time. All topics
@@ -368,14 +368,14 @@
if pos == None or not self.debugflag:
return
- if units:
- units = ' ' + units
+ if unit:
+ unit = ' ' + unit
if item:
item = ' ' + item
if total:
pct = 100.0 * pos / total
ui.debug('%s:%s %s/%s%s (%4.2g%%)\n'
- % (topic, item, pos, total, units, pct))
+ % (topic, item, pos, total, unit, pct))
else:
- ui.debug('%s:%s %s%s\n' % (topic, item, pos, units))
+ ui.debug('%s:%s %s%s\n' % (topic, item, pos, unit))
--- a/mercurial/url.py Wed Sep 16 16:11:44 2009 +0200
+++ b/mercurial/url.py Wed Sep 16 23:46:06 2009 +0200
@@ -487,6 +487,8 @@
authinfo = None
return url, authinfo
+handlerfuncs = []
+
def opener(ui, authinfo=None):
'''
construct an opener suitable for urllib2
@@ -507,6 +509,7 @@
handlers.extend((urllib2.HTTPBasicAuthHandler(passmgr),
httpdigestauthhandler(passmgr)))
+ handlers.extend([h(ui, passmgr) for h in handlerfuncs])
opener = urllib2.build_opener(*handlers)
# 1.0 here is the _protocol_ version
--- a/mercurial/util.py Wed Sep 16 16:11:44 2009 +0200
+++ b/mercurial/util.py Wed Sep 16 23:46:06 2009 +0200
@@ -266,9 +266,7 @@
def canonpath(root, cwd, myname):
"""return the canonical path of myname, given cwd and root"""
- if root == os.sep:
- rootsep = os.sep
- elif endswithsep(root):
+ if endswithsep(root):
rootsep = root
else:
rootsep = root + os.sep
@@ -663,8 +661,9 @@
contents = _fspathcache[dir]
lpart = part.lower()
+ lenp = len(part)
for n in contents:
- if n.lower() == lpart:
+ if lenp == len(n) and n.lower() == lpart:
result.append(n)
break
else:
@@ -1275,6 +1274,9 @@
def wrap(line, hangindent, width=None):
if width is None:
width = termwidth() - 2
+ if width <= hangindent:
+ # adjust for weird terminal size
+ width = max(78, hangindent + 1)
padding = '\n' + ' ' * hangindent
return padding.join(textwrap.wrap(line, width=width - hangindent))
--- a/mercurial/windows.py Wed Sep 16 16:11:44 2009 +0200
+++ b/mercurial/windows.py Wed Sep 16 23:46:06 2009 +0200
@@ -17,7 +17,7 @@
try:
return osutil.posixfile(name, mode, buffering)
except WindowsError, err:
- raise IOError(err.errno, err.strerror)
+ raise IOError(err.errno, '%s: %s' % (name, err.strerror))
posixfile.__doc__ = osutil.posixfile.__doc__
class winstdout(object):
--- a/tests/hghave Wed Sep 16 16:11:44 2009 +0200
+++ b/tests/hghave Wed Sep 16 23:46:06 2009 +0200
@@ -78,7 +78,7 @@
def has_icasefs():
# Stolen from mercurial.util
- fd, path = tempfile.mkstemp(prefix=tempprefix)
+ fd, path = tempfile.mkstemp(prefix=tempprefix, dir='.')
os.close(fd)
try:
s1 = os.stat(path)
@@ -123,6 +123,10 @@
def has_git():
return matchoutput('git --version 2>&1', r'^git version')
+def has_rst2html():
+ return matchoutput('rst2html --version', r'^rst2html \(Docutils') or \
+ matchoutput('rst2html.py --version', r'^rst2html.py \(Docutils')
+
def has_svn():
return matchoutput('svn --version 2>&1', r'^svn, version') and \
matchoutput('svnadmin --version 2>&1', r'^svnadmin, version')
@@ -195,6 +199,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"),
"svn": (has_svn, "subversion client and admin tools"),
"svn-bindings": (has_svn_bindings, "subversion python bindings"),
"symlink": (has_symlink, "symbolic links"),
--- a/tests/printenv.py Wed Sep 16 16:11:44 2009 +0200
+++ b/tests/printenv.py Wed Sep 16 23:46:06 2009 +0200
@@ -46,12 +46,9 @@
elif url.startswith("remote:http"):
os.environ["HG_URL"] = "remote:http"
-if "HG_PENDING" in os.environ:
- os.environ["HG_PENDING"] = os.environ["HG_PENDING"] and "true"
-
out.write("%s hook: " % name)
for v in env:
- out.write("%s=%s " % (v, os.environ[v]))
+ out.write("%s=%s " % (v, os.environ[v].replace(os.environ["HGTMP"], '$HGTMP')))
out.write("\n")
out.close()
--- a/tests/run-tests.py Wed Sep 16 16:11:44 2009 +0200
+++ b/tests/run-tests.py Wed Sep 16 23:46:06 2009 +0200
@@ -167,16 +167,22 @@
else:
vlog = lambda *msg: None
+ if options.tmpdir:
+ options.tmpdir = os.path.expanduser(options.tmpdir)
+ try:
+ os.makedirs(options.tmpdir)
+ except OSError, err:
+ if err.errno != errno.EEXIST:
+ raise
+
if options.jobs < 1:
- print >> sys.stderr, 'ERROR: -j/--jobs must be positive'
- sys.exit(1)
+ parser.error('--jobs must be positive')
if options.interactive and options.jobs > 1:
print '(--interactive overrides --jobs)'
options.jobs = 1
if options.py3k_warnings:
if sys.version_info[:2] < (2, 6) or sys.version_info[:2] >= (3, 0):
- print 'ERROR: Py3k warnings switch can only be used on Python 2.6+'
- sys.exit(1)
+ parser.error('--py3k-warnings can only be used on Python 2.6+')
return (options, args)
--- a/tests/test-casefolding Wed Sep 16 16:11:44 2009 +0200
+++ b/tests/test-casefolding Wed Sep 16 23:46:06 2009 +0200
@@ -31,6 +31,9 @@
hg rm a
hg ci -Am removea
echo A > A
+# on linux hfs keeps the old case stored, force it
+mv a aa
+mv aa A
hg ci -Am addA
# Used to fail under case insensitive fs
hg up -C 0
--- a/tests/test-convert-clonebranches.out Wed Sep 16 16:11:44 2009 +0200
+++ b/tests/test-convert-clonebranches.out Wed Sep 16 23:46:06 2009 +0200
@@ -18,12 +18,12 @@
% incremental conversion
2 c1
pulling from branch0 into branch1
-2 changesets found
+4 changesets found
1 c2
pulling from branch0 into branch2
-2 changesets found
+4 changesets found
0 c3
pulling from branch2 into branch3
-3 changesets found
+5 changesets found
pulling from branch1 into branch3
1 changesets found
--- a/tests/test-convert-git Wed Sep 16 16:11:44 2009 +0200
+++ b/tests/test-convert-git Wed Sep 16 23:46:06 2009 +0200
@@ -42,7 +42,7 @@
echo b >> a
commit -a -m t4.1
-git checkout -b other HEAD^ >/dev/null 2>/dev/null
+git checkout -b other HEAD~ >/dev/null 2>/dev/null
echo c > a
echo a >> a
commit -a -m t4.2
@@ -68,7 +68,7 @@
echo >> foo
commit -a -m 'change foo'
-git checkout -b Bar HEAD^ >/dev/null 2>/dev/null
+git checkout -b Bar HEAD~ >/dev/null 2>/dev/null
echo quux >> quux
git add quux
commit -a -m 'add quux'
@@ -77,7 +77,7 @@
git add bar
commit -a -m 'add bar'
-git checkout -b Baz HEAD^ >/dev/null 2>/dev/null
+git checkout -b Baz HEAD~ >/dev/null 2>/dev/null
echo baz > baz
git add baz
commit -a -m 'add baz'
@@ -89,7 +89,7 @@
echo bar >> bar
commit -a -m 'change bar'
-git checkout -b Foo HEAD^ >/dev/null 2>/dev/null
+git checkout -b Foo HEAD~ >/dev/null 2>/dev/null
echo >> foo
commit -a -m 'change foo'
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/tests/test-convert-tagsbranch-topology Wed Sep 16 23:46:06 2009 +0200
@@ -0,0 +1,64 @@
+#!/bin/sh
+
+"$TESTDIR/hghave" git || exit 80
+
+echo "[extensions]" >> $HGRCPATH
+echo "convert=" >> $HGRCPATH
+echo 'hgext.graphlog =' >> $HGRCPATH
+echo '[convert]' >> $HGRCPATH
+echo 'hg.usebranchnames = True' >> $HGRCPATH
+echo 'hg.tagsbranch = tags-update' >> $HGRCPATH
+
+GIT_AUTHOR_NAME='test'; export GIT_AUTHOR_NAME
+GIT_AUTHOR_EMAIL='test@example.org'; export GIT_AUTHOR_EMAIL
+GIT_AUTHOR_DATE="2007-01-01 00:00:00 +0000"; export GIT_AUTHOR_DATE
+GIT_COMMITTER_NAME="$GIT_AUTHOR_NAME"; export GIT_COMMITTER_NAME
+GIT_COMMITTER_EMAIL="$GIT_AUTHOR_EMAIL"; export GIT_COMMITTER_EMAIL
+GIT_COMMITTER_DATE="$GIT_AUTHOR_DATE"; export GIT_COMMITTER_DATE
+
+count=10
+action()
+{
+ GIT_AUTHOR_DATE="2007-01-01 00:00:$count +0000"
+ GIT_COMMITTER_DATE="$GIT_AUTHOR_DATE"
+ git "$@" >/dev/null 2>/dev/null || echo "git command error"
+ count=`expr $count + 1`
+}
+
+glog()
+{
+ hg glog --template '{rev} "{desc|firstline}" files: {files}\n' "$@"
+}
+
+convertrepo()
+{
+ hg convert --datesort git-repo hg-repo
+}
+
+# Build a GIT repo with at least 1 tag
+mkdir git-repo
+cd git-repo
+git init >/dev/null 2>&1
+echo a > a
+git add a
+action commit -m "rev1"
+action tag -m "tag1" tag1
+cd ..
+
+# Do a first conversion
+convertrepo
+
+# Simulate upstream updates after first conversion
+cd git-repo
+echo b > a
+git add a
+action commit -m "rev2"
+action tag -m "tag2" tag2
+cd ..
+
+# Perform an incremental conversion
+convertrepo
+
+# Print the log
+cd hg-repo
+glog
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/tests/test-convert-tagsbranch-topology.out Wed Sep 16 23:46:06 2009 +0200
@@ -0,0 +1,19 @@
+initializing destination hg-repo repository
+scanning source...
+sorting...
+converting...
+0 rev1
+updating tags
+scanning source...
+sorting...
+converting...
+0 rev2
+updating tags
+o 3 "update tags" files: .hgtags
+|
+| o 2 "rev2" files: a
+| |
+o | 1 "update tags" files: .hgtags
+ /
+o 0 "rev1" files: a
+
--- a/tests/test-double-merge.out Wed Sep 16 16:11:44 2009 +0200
+++ b/tests/test-double-merge.out Wed Sep 16 23:46:06 2009 +0200
@@ -1,9 +1,4 @@
created new head
-changeset: 0:310fd17130da
-user: test
-date: Mon Jan 12 13:46:40 1970 +0000
-summary: add foo
-
changeset: 1:7731dad1c2b9
user: test
date: Mon Jan 12 13:46:40 1970 +0000
--- a/tests/test-extension Wed Sep 16 16:11:44 2009 +0200
+++ b/tests/test-extension Wed Sep 16 23:46:06 2009 +0200
@@ -55,6 +55,29 @@
hg foo
echo 'barfoo = !' >> $HGRCPATH
+# check that extensions are loaded in phases
+cat > foo.py <<EOF
+import os
+name = os.path.basename(__file__).rsplit('.', 1)[0]
+print "1) %s imported" % name
+def uisetup(ui):
+ print "2) %s uisetup" % name
+def extsetup():
+ print "3) %s extsetup" % name
+def reposetup(ui, repo):
+ print "4) %s reposetup" % name
+EOF
+
+cp foo.py bar.py
+echo 'foo = foo.py' >> $HGRCPATH
+echo 'bar = bar.py' >> $HGRCPATH
+
+# command with no output, we just want to see the extensions loaded
+hg paths
+
+echo 'foo = !' >> $HGRCPATH
+echo 'bar = !' >> $HGRCPATH
+
cd ..
cat > empty.py <<EOF
'''empty cmdtable
--- a/tests/test-extension.out Wed Sep 16 16:11:44 2009 +0200
+++ b/tests/test-extension.out Wed Sep 16 23:46:06 2009 +0200
@@ -16,6 +16,14 @@
reposetup called for a
ui == repo.ui
Foo
+1) foo imported
+1) bar imported
+2) foo uisetup
+2) bar uisetup
+3) foo extsetup
+3) bar extsetup
+4) foo reposetup
+4) bar reposetup
empty extension - empty cmdtable
no commands defined
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/tests/test-gendoc Wed Sep 16 23:46:06 2009 +0200
@@ -0,0 +1,12 @@
+#!/bin/sh
+
+"$TESTDIR/hghave" rst2html || exit 80
+RST2HTML=$(which rst2html 2> /dev/null || which rst2html.py)
+
+echo "checking for syntax errors in gendoc.py"
+python $TESTDIR/../doc/gendoc.py > gendoc.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.txt /dev/null
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/tests/test-gendoc.out Wed Sep 16 23:46:06 2009 +0200
@@ -0,0 +1,2 @@
+checking for syntax errors in gendoc.py
+checking for parse errors with rst2html
--- a/tests/test-grep Wed Sep 16 16:11:44 2009 +0200
+++ b/tests/test-grep Wed Sep 16 23:46:06 2009 +0200
@@ -22,14 +22,14 @@
echo % simple
hg grep port port
echo % all
-hg grep --all -nu port port
+hg grep --traceback --all -nu port port
echo % other
hg grep import port
hg cp port port2
hg commit -m 4 -u spam -d '5 0'
-echo '% follow'
-hg grep -f 'import$' port2
+echo % follow
+hg grep --traceback -f 'import$' port2
echo deport >> port2
hg commit -m 5 -u eggs -d '6 0'
hg grep -f --all -nu port port2
--- a/tests/test-hgwebdir Wed Sep 16 16:11:44 2009 +0200
+++ b/tests/test-hgwebdir Wed Sep 16 23:46:06 2009 +0200
@@ -29,6 +29,7 @@
root=`pwd`
cd ..
+
cat > paths.conf <<EOF
[paths]
a=$root/a
@@ -50,6 +51,7 @@
echo % should give a 404 - repo is not published
"$TESTDIR/get-with-headers.py" localhost:$HGPORT '/c/file/tip/c?style=raw'
+
cat > paths.conf <<EOF
[paths]
t/a/=$root/a
@@ -83,6 +85,24 @@
"$TESTDIR/get-with-headers.py" localhost:$HGPORT1 '/rcoll/b/d/file/tip/d?style=raw'
+"$TESTDIR/killdaemons.py"
+cat > paths.conf <<EOF
+[paths]
+t/a = $root/a
+t/b = $root/b
+c = $root/c
+[web]
+descend=false
+EOF
+
+hg serve -p $HGPORT1 -d --pid-file=hg.pid --webdir-conf paths.conf \
+ -A access-paths.log -E error-paths-3.log
+cat hg.pid >> $DAEMON_PIDS
+echo % test descend = False
+"$TESTDIR/get-with-headers.py" localhost:$HGPORT1 '/?style=raw'
+"$TESTDIR/get-with-headers.py" localhost:$HGPORT1 '/t/?style=raw'
+
+
cat > collections.conf <<EOF
[collections]
$root=$root
@@ -99,9 +119,12 @@
"$TESTDIR/get-with-headers.py" localhost:$HGPORT2 '/b/file/tip/b?style=raw'
"$TESTDIR/get-with-headers.py" localhost:$HGPORT2 '/c/file/tip/c?style=raw'
+
echo % paths errors 1
cat error-paths-1.log
echo % paths errors 2
cat error-paths-2.log
+echo % paths errors 3
+cat error-paths-3.log
echo % collections errors
cat error-collections.log
--- a/tests/test-hgwebdir.out Wed Sep 16 16:11:44 2009 +0200
+++ b/tests/test-hgwebdir.out Wed Sep 16 23:46:06 2009 +0200
@@ -307,6 +307,18 @@
200 Script output follows
d
+% test descend = False
+200 Script output follows
+
+
+/c/
+
+200 Script output follows
+
+
+/t/a/
+/t/b/
+
% collections: should succeed
200 Script output follows
@@ -327,4 +339,5 @@
c
% paths errors 1
% paths errors 2
+% paths errors 3
% collections errors
--- a/tests/test-highlight Wed Sep 16 16:11:44 2009 +0200
+++ b/tests/test-highlight Wed Sep 16 23:46:06 2009 +0200
@@ -47,9 +47,6 @@
print "The first %d primes: %s" % (n, list(islice(p, n)))
EOF
-# check for UnicodeDecodeError with iso-8859-1 file contents
-python -c 'fp = open("isolatin.txt", "w"); fp.write("h\xFCbsch\n"); fp.close();'
-
hg ci -Ama
echo % hg serve
@@ -60,10 +57,6 @@
("$TESTDIR/get-with-headers.py" localhost:$HGPORT '/file/tip/primes.py') \
| sed "s/class=\"k\"/class=\"kn\"/g" | sed "s/class=\"mf\"/class=\"mi\"/g"
-echo % hgweb filerevision, html
-("$TESTDIR/get-with-headers.py" localhost:$HGPORT '/file/tip/isolatin.txt') \
- | sed "s/class=\"k\"/class=\"kn\"/g"
-
echo % hgweb fileannotate, html
("$TESTDIR/get-with-headers.py" localhost:$HGPORT '/annotate/tip/primes.py') \
| sed "s/class=\"k\"/class=\"kn\"/g" | sed "s/class=\"mi\"/class=\"mf\"/g"
@@ -121,3 +114,28 @@
echo % errors encountered
cat errors.log
+
+cd ..
+hg init eucjp
+cd eucjp
+
+printf '\265\376\n' >> eucjp.txt # Japanese kanji "Kyo"
+
+hg ci -Ama
+
+hgserveget () {
+ "$TESTDIR/killdaemons.py"
+ echo % HGENCODING="$1" hg serve
+ HGENCODING="$1" hg serve -p $HGPORT -d -n test --pid-file=hg.pid -E errors.log
+ cat hg.pid >> $DAEMON_PIDS
+
+ echo % hgweb filerevision, html
+ "$TESTDIR/get-with-headers.py" localhost:$HGPORT "/file/tip/$2" \
+ | grep '<div class="parity0 source">' | $TESTDIR/printrepr.py
+ echo % errors encountered
+ cat errors.log
+}
+
+hgserveget euc-jp eucjp.txt
+hgserveget utf-8 eucjp.txt
+hgserveget us-ascii eucjp.txt
--- a/tests/test-highlight.out Wed Sep 16 16:11:44 2009 +0200
+++ b/tests/test-highlight.out Wed Sep 16 23:46:06 2009 +0200
@@ -1,4 +1,3 @@
-adding isolatin.txt
adding primes.py
% hg serve
% hgweb filerevision, html
@@ -12,7 +11,7 @@
<link rel="stylesheet" href="/static/style-paper.css" type="text/css" />
<link rel="stylesheet" href="/highlightcss" type="text/css" />
-<title>test: 3e1445510fe7 primes.py</title>
+<title>test: 853dcd4de2a6 primes.py</title>
</head>
<body>
@@ -23,27 +22,27 @@
<img src="/static/hglogo.png" alt="mercurial" /></a>
</div>
<ul>
-<li><a href="/shortlog/3e1445510fe7">log</a></li>
-<li><a href="/graph/3e1445510fe7">graph</a></li>
+<li><a href="/shortlog/853dcd4de2a6">log</a></li>
+<li><a href="/graph/853dcd4de2a6">graph</a></li>
<li><a href="/tags">tags</a></li>
<li><a href="/branches">branches</a></li>
</ul>
<ul>
-<li><a href="/rev/3e1445510fe7">changeset</a></li>
-<li><a href="/file/3e1445510fe7/">browse</a></li>
+<li><a href="/rev/853dcd4de2a6">changeset</a></li>
+<li><a href="/file/853dcd4de2a6/">browse</a></li>
</ul>
<ul>
<li class="active">file</li>
-<li><a href="/diff/3e1445510fe7/primes.py">diff</a></li>
-<li><a href="/annotate/3e1445510fe7/primes.py">annotate</a></li>
-<li><a href="/log/3e1445510fe7/primes.py">file log</a></li>
-<li><a href="/raw-file/3e1445510fe7/primes.py">raw</a></li>
+<li><a href="/diff/853dcd4de2a6/primes.py">diff</a></li>
+<li><a href="/annotate/853dcd4de2a6/primes.py">annotate</a></li>
+<li><a href="/log/853dcd4de2a6/primes.py">file log</a></li>
+<li><a href="/raw-file/853dcd4de2a6/primes.py">raw</a></li>
</ul>
</div>
<div class="main">
<h2><a href="/">test</a></h2>
-<h3>view primes.py @ 0:3e1445510fe7</h3>
+<h3>view primes.py @ 0:853dcd4de2a6</h3>
<form class="search" action="/log">
@@ -119,93 +118,6 @@
</body>
</html>
-% hgweb filerevision, html
-200 Script output follows
-
-<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
-<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en-US">
-<head>
-<link rel="icon" href="/static/hgicon.png" type="image/png" />
-<meta name="robots" content="index, nofollow" />
-<link rel="stylesheet" href="/static/style-paper.css" type="text/css" />
-
-<link rel="stylesheet" href="/highlightcss" type="text/css" />
-<title>test: 3e1445510fe7 isolatin.txt</title>
-</head>
-<body>
-
-<div class="container">
-<div class="menu">
-<div class="logo">
-<a href="http://mercurial.selenic.com/">
-<img src="/static/hglogo.png" alt="mercurial" /></a>
-</div>
-<ul>
-<li><a href="/shortlog/3e1445510fe7">log</a></li>
-<li><a href="/graph/3e1445510fe7">graph</a></li>
-<li><a href="/tags">tags</a></li>
-<li><a href="/branches">branches</a></li>
-</ul>
-<ul>
-<li><a href="/rev/3e1445510fe7">changeset</a></li>
-<li><a href="/file/3e1445510fe7/">browse</a></li>
-</ul>
-<ul>
-<li class="active">file</li>
-<li><a href="/diff/3e1445510fe7/isolatin.txt">diff</a></li>
-<li><a href="/annotate/3e1445510fe7/isolatin.txt">annotate</a></li>
-<li><a href="/log/3e1445510fe7/isolatin.txt">file log</a></li>
-<li><a href="/raw-file/3e1445510fe7/isolatin.txt">raw</a></li>
-</ul>
-</div>
-
-<div class="main">
-<h2><a href="/">test</a></h2>
-<h3>view isolatin.txt @ 0:3e1445510fe7</h3>
-
-<form class="search" action="/log">
-
-<p><input name="rev" id="search1" type="text" size="30" /></p>
-<div id="hint">find changesets by author, revision,
-files, or words in the commit message</div>
-</form>
-
-<div class="description">a</div>
-
-<table id="changesetEntry">
-<tr>
- <th class="author">author</th>
- <td class="author">test</td>
-</tr>
-<tr>
- <th class="date">date</th>
- <td class="date">Thu Jan 01 00:00:00 1970 +0000 (many years ago)</td>
-</tr>
-<tr>
- <th class="author">parents</th>
- <td class="author"></td>
-</tr>
-<tr>
- <th class="author">children</th>
- <td class="author"></td>
-</tr>
-
-</table>
-
-<div class="overflow">
-<div class="sourcefirst"> line source</div>
-
-<div class="parity0 source"><a href="#l1" id="l1"> 1</a> h?bsch</div>
-<div class="sourcelast"></div>
-</div>
-</div>
-</div>
-
-
-
-</body>
-</html>
-
% hgweb fileannotate, html
200 Script output follows
@@ -228,28 +140,28 @@
<img src="/static/hglogo.png" alt="mercurial" /></a>
</div>
<ul>
-<li><a href="/shortlog/3e1445510fe7">log</a></li>
-<li><a href="/graph/3e1445510fe7">graph</a></li>
+<li><a href="/shortlog/853dcd4de2a6">log</a></li>
+<li><a href="/graph/853dcd4de2a6">graph</a></li>
<li><a href="/tags">tags</a></li>
<li><a href="/branches">branches</a></li>
</ul>
<ul>
-<li><a href="/rev/3e1445510fe7">changeset</a></li>
-<li><a href="/file/3e1445510fe7/">browse</a></li>
+<li><a href="/rev/853dcd4de2a6">changeset</a></li>
+<li><a href="/file/853dcd4de2a6/">browse</a></li>
</ul>
<ul>
-<li><a href="/file/3e1445510fe7/primes.py">file</a></li>
-<li><a href="/diff/3e1445510fe7/primes.py">diff</a></li>
+<li><a href="/file/853dcd4de2a6/primes.py">file</a></li>
+<li><a href="/diff/853dcd4de2a6/primes.py">diff</a></li>
<li class="active">annotate</li>
-<li><a href="/log/3e1445510fe7/primes.py">file log</a></li>
-<li><a href="/raw-annotate/3e1445510fe7/primes.py">raw</a></li>
+<li><a href="/log/853dcd4de2a6/primes.py">file log</a></li>
+<li><a href="/raw-annotate/853dcd4de2a6/primes.py">raw</a></li>
</ul>
</div>
<div class="main">
<h2><a href="/">test</a></h2>
-<h3>annotate primes.py @ 0:3e1445510fe7</h3>
+<h3>annotate primes.py @ 0:853dcd4de2a6</h3>
<form class="search" action="/log">
@@ -289,225 +201,225 @@
<tr class="parity0">
<td class="annotate">
-<a href="/annotate/3e1445510fe7/primes.py#1"
-title="3e1445510fe7: a">test@0</a>
+<a href="/annotate/853dcd4de2a6/primes.py#1"
+title="853dcd4de2a6: a">test@0</a>
</td>
<td class="source"><a href="#l1" id="l1"> 1</a> <span class="c">#!/usr/bin/env python</span></td>
</tr>
<tr class="parity1">
<td class="annotate">
-<a href="/annotate/3e1445510fe7/primes.py#2"
-title="3e1445510fe7: a">test@0</a>
+<a href="/annotate/853dcd4de2a6/primes.py#2"
+title="853dcd4de2a6: a">test@0</a>
</td>
<td class="source"><a href="#l2" id="l2"> 2</a> </td>
</tr>
<tr class="parity0">
<td class="annotate">
-<a href="/annotate/3e1445510fe7/primes.py#3"
-title="3e1445510fe7: a">test@0</a>
+<a href="/annotate/853dcd4de2a6/primes.py#3"
+title="853dcd4de2a6: a">test@0</a>
</td>
<td class="source"><a href="#l3" id="l3"> 3</a> <span class="sd">"""Fun with generators. Corresponding Haskell implementation:</span></td>
</tr>
<tr class="parity1">
<td class="annotate">
-<a href="/annotate/3e1445510fe7/primes.py#4"
-title="3e1445510fe7: a">test@0</a>
+<a href="/annotate/853dcd4de2a6/primes.py#4"
+title="853dcd4de2a6: a">test@0</a>
</td>
<td class="source"><a href="#l4" id="l4"> 4</a> </td>
</tr>
<tr class="parity0">
<td class="annotate">
-<a href="/annotate/3e1445510fe7/primes.py#5"
-title="3e1445510fe7: a">test@0</a>
+<a href="/annotate/853dcd4de2a6/primes.py#5"
+title="853dcd4de2a6: a">test@0</a>
</td>
<td class="source"><a href="#l5" id="l5"> 5</a> <span class="sd">primes = 2 : sieve [3, 5..]</span></td>
</tr>
<tr class="parity1">
<td class="annotate">
-<a href="/annotate/3e1445510fe7/primes.py#6"
-title="3e1445510fe7: a">test@0</a>
+<a href="/annotate/853dcd4de2a6/primes.py#6"
+title="853dcd4de2a6: a">test@0</a>
</td>
<td class="source"><a href="#l6" id="l6"> 6</a> <span class="sd"> where sieve (p:ns) = p : sieve [n | n <- ns, mod n p /= 0]</span></td>
</tr>
<tr class="parity0">
<td class="annotate">
-<a href="/annotate/3e1445510fe7/primes.py#7"
-title="3e1445510fe7: a">test@0</a>
+<a href="/annotate/853dcd4de2a6/primes.py#7"
+title="853dcd4de2a6: a">test@0</a>
</td>
<td class="source"><a href="#l7" id="l7"> 7</a> <span class="sd">"""</span></td>
</tr>
<tr class="parity1">
<td class="annotate">
-<a href="/annotate/3e1445510fe7/primes.py#8"
-title="3e1445510fe7: a">test@0</a>
+<a href="/annotate/853dcd4de2a6/primes.py#8"
+title="853dcd4de2a6: a">test@0</a>
</td>
<td class="source"><a href="#l8" id="l8"> 8</a> </td>
</tr>
<tr class="parity0">
<td class="annotate">
-<a href="/annotate/3e1445510fe7/primes.py#9"
-title="3e1445510fe7: a">test@0</a>
+<a href="/annotate/853dcd4de2a6/primes.py#9"
+title="853dcd4de2a6: a">test@0</a>
</td>
<td class="source"><a href="#l9" id="l9"> 9</a> <span class="kn">from</span> <span class="nn">itertools</span> <span class="kn">import</span> <span class="n">dropwhile</span><span class="p">,</span> <span class="n">ifilter</span><span class="p">,</span> <span class="n">islice</span><span class="p">,</span> <span class="n">count</span><span class="p">,</span> <span class="n">chain</span></td>
</tr>
<tr class="parity1">
<td class="annotate">
-<a href="/annotate/3e1445510fe7/primes.py#10"
-title="3e1445510fe7: a">test@0</a>
+<a href="/annotate/853dcd4de2a6/primes.py#10"
+title="853dcd4de2a6: a">test@0</a>
</td>
<td class="source"><a href="#l10" id="l10"> 10</a> </td>
</tr>
<tr class="parity0">
<td class="annotate">
-<a href="/annotate/3e1445510fe7/primes.py#11"
-title="3e1445510fe7: a">test@0</a>
+<a href="/annotate/853dcd4de2a6/primes.py#11"
+title="853dcd4de2a6: a">test@0</a>
</td>
<td class="source"><a href="#l11" id="l11"> 11</a> <span class="kn">def</span> <span class="nf">primes</span><span class="p">():</span></td>
</tr>
<tr class="parity1">
<td class="annotate">
-<a href="/annotate/3e1445510fe7/primes.py#12"
-title="3e1445510fe7: a">test@0</a>
+<a href="/annotate/853dcd4de2a6/primes.py#12"
+title="853dcd4de2a6: a">test@0</a>
</td>
<td class="source"><a href="#l12" id="l12"> 12</a> <span class="sd">"""Generate all primes."""</span></td>
</tr>
<tr class="parity0">
<td class="annotate">
-<a href="/annotate/3e1445510fe7/primes.py#13"
-title="3e1445510fe7: a">test@0</a>
+<a href="/annotate/853dcd4de2a6/primes.py#13"
+title="853dcd4de2a6: a">test@0</a>
</td>
<td class="source"><a href="#l13" id="l13"> 13</a> <span class="kn">def</span> <span class="nf">sieve</span><span class="p">(</span><span class="n">ns</span><span class="p">):</span></td>
</tr>
<tr class="parity1">
<td class="annotate">
-<a href="/annotate/3e1445510fe7/primes.py#14"
-title="3e1445510fe7: a">test@0</a>
+<a href="/annotate/853dcd4de2a6/primes.py#14"
+title="853dcd4de2a6: a">test@0</a>
</td>
<td class="source"><a href="#l14" id="l14"> 14</a> <span class="n">p</span> <span class="o">=</span> <span class="n">ns</span><span class="o">.</span><span class="n">next</span><span class="p">()</span></td>
</tr>
<tr class="parity0">
<td class="annotate">
-<a href="/annotate/3e1445510fe7/primes.py#15"
-title="3e1445510fe7: a">test@0</a>
+<a href="/annotate/853dcd4de2a6/primes.py#15"
+title="853dcd4de2a6: a">test@0</a>
</td>
<td class="source"><a href="#l15" id="l15"> 15</a> <span class="c"># It is important to yield *here* in order to stop the</span></td>
</tr>
<tr class="parity1">
<td class="annotate">
-<a href="/annotate/3e1445510fe7/primes.py#16"
-title="3e1445510fe7: a">test@0</a>
+<a href="/annotate/853dcd4de2a6/primes.py#16"
+title="853dcd4de2a6: a">test@0</a>
</td>
<td class="source"><a href="#l16" id="l16"> 16</a> <span class="c"># infinite recursion.</span></td>
</tr>
<tr class="parity0">
<td class="annotate">
-<a href="/annotate/3e1445510fe7/primes.py#17"
-title="3e1445510fe7: a">test@0</a>
+<a href="/annotate/853dcd4de2a6/primes.py#17"
+title="853dcd4de2a6: a">test@0</a>
</td>
<td class="source"><a href="#l17" id="l17"> 17</a> <span class="kn">yield</span> <span class="n">p</span></td>
</tr>
<tr class="parity1">
<td class="annotate">
-<a href="/annotate/3e1445510fe7/primes.py#18"
-title="3e1445510fe7: a">test@0</a>
+<a href="/annotate/853dcd4de2a6/primes.py#18"
+title="853dcd4de2a6: a">test@0</a>
</td>
<td class="source"><a href="#l18" id="l18"> 18</a> <span class="n">ns</span> <span class="o">=</span> <span class="n">ifilter</span><span class="p">(</span><span class="kn">lambda</span> <span class="n">n</span><span class="p">:</span> <span class="n">n</span> <span class="o">%</span> <span class="n">p</span> <span class="o">!=</span> <span class="mf">0</span><span class="p">,</span> <span class="n">ns</span><span class="p">)</span></td>
</tr>
<tr class="parity0">
<td class="annotate">
-<a href="/annotate/3e1445510fe7/primes.py#19"
-title="3e1445510fe7: a">test@0</a>
+<a href="/annotate/853dcd4de2a6/primes.py#19"
+title="853dcd4de2a6: a">test@0</a>
</td>
<td class="source"><a href="#l19" id="l19"> 19</a> <span class="kn">for</span> <span class="n">n</span> <span class="ow">in</span> <span class="n">sieve</span><span class="p">(</span><span class="n">ns</span><span class="p">):</span></td>
</tr>
<tr class="parity1">
<td class="annotate">
-<a href="/annotate/3e1445510fe7/primes.py#20"
-title="3e1445510fe7: a">test@0</a>
+<a href="/annotate/853dcd4de2a6/primes.py#20"
+title="853dcd4de2a6: a">test@0</a>
</td>
<td class="source"><a href="#l20" id="l20"> 20</a> <span class="kn">yield</span> <span class="n">n</span></td>
</tr>
<tr class="parity0">
<td class="annotate">
-<a href="/annotate/3e1445510fe7/primes.py#21"
-title="3e1445510fe7: a">test@0</a>
+<a href="/annotate/853dcd4de2a6/primes.py#21"
+title="853dcd4de2a6: a">test@0</a>
</td>
<td class="source"><a href="#l21" id="l21"> 21</a> </td>
</tr>
<tr class="parity1">
<td class="annotate">
-<a href="/annotate/3e1445510fe7/primes.py#22"
-title="3e1445510fe7: a">test@0</a>
+<a href="/annotate/853dcd4de2a6/primes.py#22"
+title="853dcd4de2a6: a">test@0</a>
</td>
<td class="source"><a href="#l22" id="l22"> 22</a> <span class="n">odds</span> <span class="o">=</span> <span class="n">ifilter</span><span class="p">(</span><span class="kn">lambda</span> <span class="n">i</span><span class="p">:</span> <span class="n">i</span> <span class="o">%</span> <span class="mf">2</span> <span class="o">==</span> <span class="mf">1</span><span class="p">,</span> <span class="n">count</span><span class="p">())</span></td>
</tr>
<tr class="parity0">
<td class="annotate">
-<a href="/annotate/3e1445510fe7/primes.py#23"
-title="3e1445510fe7: a">test@0</a>
+<a href="/annotate/853dcd4de2a6/primes.py#23"
+title="853dcd4de2a6: a">test@0</a>
</td>
<td class="source"><a href="#l23" id="l23"> 23</a> <span class="kn">return</span> <span class="n">chain</span><span class="p">([</span><span class="mf">2</span><span class="p">],</span> <span class="n">sieve</span><span class="p">(</span><span class="n">dropwhile</span><span class="p">(</span><span class="kn">lambda</span> <span class="n">n</span><span class="p">:</span> <span class="n">n</span> <span class="o"><</span> <span class="mf">3</span><span class="p">,</span> <span class="n">odds</span><span class="p">)))</span></td>
</tr>
<tr class="parity1">
<td class="annotate">
-<a href="/annotate/3e1445510fe7/primes.py#24"
-title="3e1445510fe7: a">test@0</a>
+<a href="/annotate/853dcd4de2a6/primes.py#24"
+title="853dcd4de2a6: a">test@0</a>
</td>
<td class="source"><a href="#l24" id="l24"> 24</a> </td>
</tr>
<tr class="parity0">
<td class="annotate">
-<a href="/annotate/3e1445510fe7/primes.py#25"
-title="3e1445510fe7: a">test@0</a>
+<a href="/annotate/853dcd4de2a6/primes.py#25"
+title="853dcd4de2a6: a">test@0</a>
</td>
<td class="source"><a href="#l25" id="l25"> 25</a> <span class="kn">if</span> <span class="n">__name__</span> <span class="o">==</span> <span class="s">"__main__"</span><span class="p">:</span></td>
</tr>
<tr class="parity1">
<td class="annotate">
-<a href="/annotate/3e1445510fe7/primes.py#26"
-title="3e1445510fe7: a">test@0</a>
+<a href="/annotate/853dcd4de2a6/primes.py#26"
+title="853dcd4de2a6: a">test@0</a>
</td>
<td class="source"><a href="#l26" id="l26"> 26</a> <span class="kn">import</span> <span class="nn">sys</span></td>
</tr>
<tr class="parity0">
<td class="annotate">
-<a href="/annotate/3e1445510fe7/primes.py#27"
-title="3e1445510fe7: a">test@0</a>
+<a href="/annotate/853dcd4de2a6/primes.py#27"
+title="853dcd4de2a6: a">test@0</a>
</td>
<td class="source"><a href="#l27" id="l27"> 27</a> <span class="kn">try</span><span class="p">:</span></td>
</tr>
<tr class="parity1">
<td class="annotate">
-<a href="/annotate/3e1445510fe7/primes.py#28"
-title="3e1445510fe7: a">test@0</a>
+<a href="/annotate/853dcd4de2a6/primes.py#28"
+title="853dcd4de2a6: a">test@0</a>
</td>
<td class="source"><a href="#l28" id="l28"> 28</a> <span class="n">n</span> <span class="o">=</span> <span class="nb">int</span><span class="p">(</span><span class="n">sys</span><span class="o">.</span><span class="n">argv</span><span class="p">[</span><span class="mf">1</span><span class="p">])</span></td>
</tr>
<tr class="parity0">
<td class="annotate">
-<a href="/annotate/3e1445510fe7/primes.py#29"
-title="3e1445510fe7: a">test@0</a>
+<a href="/annotate/853dcd4de2a6/primes.py#29"
+title="853dcd4de2a6: a">test@0</a>
</td>
<td class="source"><a href="#l29" id="l29"> 29</a> <span class="kn">except</span> <span class="p">(</span><span class="ne">ValueError</span><span class="p">,</span> <span class="ne">IndexError</span><span class="p">):</span></td>
</tr>
<tr class="parity1">
<td class="annotate">
-<a href="/annotate/3e1445510fe7/primes.py#30"
-title="3e1445510fe7: a">test@0</a>
+<a href="/annotate/853dcd4de2a6/primes.py#30"
+title="853dcd4de2a6: a">test@0</a>
</td>
<td class="source"><a href="#l30" id="l30"> 30</a> <span class="n">n</span> <span class="o">=</span> <span class="mf">10</span></td>
</tr>
<tr class="parity0">
<td class="annotate">
-<a href="/annotate/3e1445510fe7/primes.py#31"
-title="3e1445510fe7: a">test@0</a>
+<a href="/annotate/853dcd4de2a6/primes.py#31"
+title="853dcd4de2a6: a">test@0</a>
</td>
<td class="source"><a href="#l31" id="l31"> 31</a> <span class="n">p</span> <span class="o">=</span> <span class="n">primes</span><span class="p">()</span></td>
</tr>
<tr class="parity1">
<td class="annotate">
-<a href="/annotate/3e1445510fe7/primes.py#32"
-title="3e1445510fe7: a">test@0</a>
+<a href="/annotate/853dcd4de2a6/primes.py#32"
+title="853dcd4de2a6: a">test@0</a>
</td>
<td class="source"><a href="#l32" id="l32"> 32</a> <span class="kn">print</span> <span class="s">"The first </span><span class="si">%d</span><span class="s"> primes: </span><span class="si">%s</span><span class="s">"</span> <span class="o">%</span> <span class="p">(</span><span class="n">n</span><span class="p">,</span> <span class="nb">list</span><span class="p">(</span><span class="n">islice</span><span class="p">(</span><span class="n">p</span><span class="p">,</span> <span class="n">n</span><span class="p">)))</span></td>
</tr>
@@ -538,3 +450,16 @@
/* pygments_style = fruity */
% errors encountered
+adding eucjp.txt
+% HGENCODING=euc-jp hg serve
+% hgweb filerevision, html
+<div class="parity0 source"><a href="#l1" id="l1"> 1</a> \xb5\xfe</div>
+% errors encountered
+% HGENCODING=utf-8 hg serve
+% hgweb filerevision, html
+<div class="parity0 source"><a href="#l1" id="l1"> 1</a> \xef\xbf\xbd\xef\xbf\xbd</div>
+% errors encountered
+% HGENCODING=us-ascii hg serve
+% hgweb filerevision, html
+<div class="parity0 source"><a href="#l1" id="l1"> 1</a> ??</div>
+% errors encountered
--- a/tests/test-hook.out Wed Sep 16 16:11:44 2009 +0200
+++ b/tests/test-hook.out Wed Sep 16 23:46:06 2009 +0200
@@ -1,18 +1,18 @@
precommit hook: HG_PARENT1=0000000000000000000000000000000000000000
-pretxncommit hook: HG_NODE=29b62aeb769fdf78d8d9c5f28b017f76d7ef824b HG_PARENT1=0000000000000000000000000000000000000000 HG_PENDING=true
+pretxncommit hook: HG_NODE=29b62aeb769fdf78d8d9c5f28b017f76d7ef824b HG_PARENT1=0000000000000000000000000000000000000000 HG_PENDING=$HGTMP/test-hook/a
0:29b62aeb769f
commit hook: HG_NODE=29b62aeb769fdf78d8d9c5f28b017f76d7ef824b HG_PARENT1=0000000000000000000000000000000000000000
commit.b hook: HG_NODE=29b62aeb769fdf78d8d9c5f28b017f76d7ef824b HG_PARENT1=0000000000000000000000000000000000000000
updating working directory
1 files updated, 0 files merged, 0 files removed, 0 files unresolved
precommit hook: HG_PARENT1=29b62aeb769fdf78d8d9c5f28b017f76d7ef824b
-pretxncommit hook: HG_NODE=b702efe9688826e3a91283852b328b84dbf37bc2 HG_PARENT1=29b62aeb769fdf78d8d9c5f28b017f76d7ef824b HG_PENDING=true
+pretxncommit hook: HG_NODE=b702efe9688826e3a91283852b328b84dbf37bc2 HG_PARENT1=29b62aeb769fdf78d8d9c5f28b017f76d7ef824b HG_PENDING=$HGTMP/test-hook/a
1:b702efe96888
commit hook: HG_NODE=b702efe9688826e3a91283852b328b84dbf37bc2 HG_PARENT1=29b62aeb769fdf78d8d9c5f28b017f76d7ef824b
commit.b hook: HG_NODE=b702efe9688826e3a91283852b328b84dbf37bc2 HG_PARENT1=29b62aeb769fdf78d8d9c5f28b017f76d7ef824b
1 files updated, 0 files merged, 0 files removed, 0 files unresolved
precommit hook: HG_PARENT1=29b62aeb769fdf78d8d9c5f28b017f76d7ef824b
-pretxncommit hook: HG_NODE=1324a5531bac09b329c3845d35ae6a7526874edb HG_PARENT1=29b62aeb769fdf78d8d9c5f28b017f76d7ef824b HG_PENDING=true
+pretxncommit hook: HG_NODE=1324a5531bac09b329c3845d35ae6a7526874edb HG_PARENT1=29b62aeb769fdf78d8d9c5f28b017f76d7ef824b HG_PENDING=$HGTMP/test-hook/a
2:1324a5531bac
commit hook: HG_NODE=1324a5531bac09b329c3845d35ae6a7526874edb HG_PARENT1=29b62aeb769fdf78d8d9c5f28b017f76d7ef824b
commit.b hook: HG_NODE=1324a5531bac09b329c3845d35ae6a7526874edb HG_PARENT1=29b62aeb769fdf78d8d9c5f28b017f76d7ef824b
@@ -20,7 +20,7 @@
1 files updated, 0 files merged, 0 files removed, 0 files unresolved
(branch merge, don't forget to commit)
precommit hook: HG_PARENT1=1324a5531bac09b329c3845d35ae6a7526874edb HG_PARENT2=b702efe9688826e3a91283852b328b84dbf37bc2
-pretxncommit hook: HG_NODE=4c52fb2e402287dd5dc052090682536c8406c321 HG_PARENT1=1324a5531bac09b329c3845d35ae6a7526874edb HG_PARENT2=b702efe9688826e3a91283852b328b84dbf37bc2 HG_PENDING=true
+pretxncommit hook: HG_NODE=4c52fb2e402287dd5dc052090682536c8406c321 HG_PARENT1=1324a5531bac09b329c3845d35ae6a7526874edb HG_PARENT2=b702efe9688826e3a91283852b328b84dbf37bc2 HG_PENDING=$HGTMP/test-hook/a
3:4c52fb2e4022
commit hook: HG_NODE=4c52fb2e402287dd5dc052090682536c8406c321 HG_PARENT1=1324a5531bac09b329c3845d35ae6a7526874edb HG_PARENT2=b702efe9688826e3a91283852b328b84dbf37bc2
commit.b hook: HG_NODE=4c52fb2e402287dd5dc052090682536c8406c321 HG_PARENT1=1324a5531bac09b329c3845d35ae6a7526874edb HG_PARENT2=b702efe9688826e3a91283852b328b84dbf37bc2
@@ -43,7 +43,7 @@
(run 'hg update' to get a working copy)
pretag hook: HG_LOCAL=0 HG_NODE=4c52fb2e402287dd5dc052090682536c8406c321 HG_TAG=a
precommit hook: HG_PARENT1=4c52fb2e402287dd5dc052090682536c8406c321
-pretxncommit hook: HG_NODE=8ea2ef7ad3e8cac946c72f1e0c79d6aebc301198 HG_PARENT1=4c52fb2e402287dd5dc052090682536c8406c321 HG_PENDING=true
+pretxncommit hook: HG_NODE=8ea2ef7ad3e8cac946c72f1e0c79d6aebc301198 HG_PARENT1=4c52fb2e402287dd5dc052090682536c8406c321 HG_PENDING=$HGTMP/test-hook/a
4:8ea2ef7ad3e8
commit hook: HG_NODE=8ea2ef7ad3e8cac946c72f1e0c79d6aebc301198 HG_PARENT1=4c52fb2e402287dd5dc052090682536c8406c321
commit.b hook: HG_NODE=8ea2ef7ad3e8cac946c72f1e0c79d6aebc301198 HG_PARENT1=4c52fb2e402287dd5dc052090682536c8406c321
@@ -58,10 +58,10 @@
abort: pretag.forbid hook exited with status 1
4:8ea2ef7ad3e8
precommit hook: HG_PARENT1=8ea2ef7ad3e8cac946c72f1e0c79d6aebc301198
-pretxncommit hook: HG_NODE=fad284daf8c032148abaffcd745dafeceefceb61 HG_PARENT1=8ea2ef7ad3e8cac946c72f1e0c79d6aebc301198 HG_PENDING=true
+pretxncommit hook: HG_NODE=fad284daf8c032148abaffcd745dafeceefceb61 HG_PARENT1=8ea2ef7ad3e8cac946c72f1e0c79d6aebc301198 HG_PENDING=$HGTMP/test-hook/a
5:fad284daf8c0
5:fad284daf8c0
-pretxncommit.forbid hook: HG_NODE=fad284daf8c032148abaffcd745dafeceefceb61 HG_PARENT1=8ea2ef7ad3e8cac946c72f1e0c79d6aebc301198 HG_PENDING=true
+pretxncommit.forbid hook: HG_NODE=fad284daf8c032148abaffcd745dafeceefceb61 HG_PARENT1=8ea2ef7ad3e8cac946c72f1e0c79d6aebc301198 HG_PENDING=$HGTMP/test-hook/a
transaction abort!
rollback completed
abort: pretxncommit.forbid1 hook exited with status 1
@@ -81,7 +81,7 @@
searching for changes
abort: prechangegroup.forbid hook exited with status 1
4:8ea2ef7ad3e8
-pretxnchangegroup.forbid hook: HG_NODE=8ea2ef7ad3e8cac946c72f1e0c79d6aebc301198 HG_PENDING=true HG_SOURCE=pull HG_URL=file:
+pretxnchangegroup.forbid hook: HG_NODE=8ea2ef7ad3e8cac946c72f1e0c79d6aebc301198 HG_PENDING=$HGTMP/test-hook/b HG_SOURCE=pull HG_URL=file:
pulling from ../a
searching for changes
adding changesets
--- a/tests/test-log Wed Sep 16 16:11:44 2009 +0200
+++ b/tests/test-log Wed Sep 16 23:46:06 2009 +0200
@@ -104,6 +104,12 @@
echo '% log -r <some unknown node id>'
hg log -r 1000000000000000000000000000000000000000
+echo '% log -k r1'
+hg log -k r1
+
+echo '% log -d -1'
+hg log -d -1
+
cd ..
hg init usertest
--- a/tests/test-log.out Wed Sep 16 16:11:44 2009 +0200
+++ b/tests/test-log.out Wed Sep 16 23:46:06 2009 +0200
@@ -239,6 +239,13 @@
abort: 00changelog.i@: ambiguous identifier!
% log -r <some unknown node id>
abort: unknown revision '1000000000000000000000000000000000000000'!
+% log -k r1
+changeset: 1:3d5bf5654eda
+user: test
+date: Thu Jan 01 00:00:01 1970 +0000
+summary: r1
+
+% log -d -1
adding a
adding b
changeset: 0:29a4c94f1924
--- a/tests/test-merge-default.out Wed Sep 16 16:11:44 2009 +0200
+++ b/tests/test-merge-default.out Wed Sep 16 23:46:06 2009 +0200
@@ -13,11 +13,6 @@
0 files updated, 0 files merged, 0 files removed, 0 files unresolved
(branch merge, don't forget to commit)
% should succeed - 2 heads
-changeset: 1:ba677d0156c1
-user: test
-date: Thu Jan 01 00:00:00 1970 +0000
-summary: b
-
changeset: 3:903c264cdf57
parent: 1:ba677d0156c1
user: test
--- a/tests/test-merge1.out Wed Sep 16 16:11:44 2009 +0200
+++ b/tests/test-merge1.out Wed Sep 16 23:46:06 2009 +0200
@@ -1,11 +1,6 @@
0 files updated, 0 files merged, 1 files removed, 0 files unresolved
created new head
%% no merges expected
-changeset: 0:98e00378acd0
-user: test
-date: Mon Jan 12 13:46:40 1970 +0000
-summary: commit #0
-
changeset: 1:4ee19afe4659
user: test
date: Mon Jan 12 13:46:40 1970 +0000
--- a/tests/test-mq Wed Sep 16 16:11:44 2009 +0200
+++ b/tests/test-mq Wed Sep 16 23:46:06 2009 +0200
@@ -155,18 +155,13 @@
echo % qtop
hg qtop
-echo % qprev
-hg qprev
-
-echo % qnext
-hg qnext
+echo % prev
+hg qapp -1
-echo % pop, qnext, qprev, qapplied
+echo % next
+hg qunapp -1
+
hg qpop
-hg qnext
-hg qprev
-hg qapplied
-
echo % commit should fail
hg commit
@@ -197,6 +192,42 @@
hg qpop test2.patch-2
hg qpush test1b.patch+1
+echo % pop, qapplied, qunapplied
+hg qseries -v
+echo % qapplied -1 test.patch
+hg qapplied -1 test.patch
+echo % qapplied -1 test1b.patch
+hg qapplied -1 test1b.patch
+echo % qapplied -1 test2.patch
+hg qapplied -1 test2.patch
+echo % qapplied -1
+hg qapplied -1
+echo % qapplied
+hg qapplied
+echo % qapplied test1b.patch
+hg qapplied test1b.patch
+echo % qunapplied -1
+hg qunapplied -1
+echo % qunapplied
+hg qunapplied
+echo % popping
+hg qpop
+echo % qunapplied -1
+hg qunapplied -1
+echo % qunapplied
+hg qunapplied
+echo % qunapplied test2.patch
+hg qunapplied test2.patch
+echo % qunapplied -1 test2.patch
+hg qunapplied -1 test2.patch
+echo % popping -a
+hg qpop -a
+echo % qapplied
+hg qapplied
+echo % qapplied -1
+hg qapplied -1
+hg qpush
+
echo % push should succeed
hg qpop -a
hg push ../../k
--- a/tests/test-mq.out Wed Sep 16 16:11:44 2009 +0200
+++ b/tests/test-mq.out Wed Sep 16 23:46:06 2009 +0200
@@ -16,7 +16,6 @@
print patch series qseries
print applied patches qapplied
- print name of top applied patch qtop
add known patch to applied stack qpush
remove patch from applied stack qpop
@@ -146,16 +145,12 @@
test2.patch
% qtop
test2.patch
-% qprev
+% prev
test.patch
-% qnext
+% next
all patches applied
-% pop, qnext, qprev, qapplied
popping test2.patch
now at: test.patch
-test2.patch
-only one patch applied
-test.patch
% commit should fail
abort: cannot commit over an applied mq patch
% push should fail
@@ -182,11 +177,50 @@
applying test1b.patch
applying test2.patch
now at: test2.patch
-% push should succeed
+% pop, qapplied, qunapplied
+0 A test.patch
+1 A test1b.patch
+2 A test2.patch
+% qapplied -1 test.patch
+only one patch applied
+% qapplied -1 test1b.patch
+test.patch
+% qapplied -1 test2.patch
+test1b.patch
+% qapplied -1
+test1b.patch
+% qapplied
+test.patch
+test1b.patch
+test2.patch
+% qapplied test1b.patch
+test.patch
+test1b.patch
+% qunapplied -1
+all patches applied
+% qunapplied
+% popping
popping test2.patch
+now at: test1b.patch
+% qunapplied -1
+test2.patch
+% qunapplied
+test2.patch
+% qunapplied test2.patch
+% qunapplied -1 test2.patch
+all patches applied
+% popping -a
popping test1b.patch
popping test.patch
patch queue now empty
+% qapplied
+% qapplied -1
+no patches applied
+applying test.patch
+now at: test.patch
+% push should succeed
+popping test.patch
+patch queue now empty
pushing to ../../k
searching for changes
adding changesets
--- a/tests/test-patchbomb Wed Sep 16 16:11:44 2009 +0200
+++ b/tests/test-patchbomb Wed Sep 16 23:46:06 2009 +0200
@@ -151,3 +151,19 @@
hg email --date '1970-1-1 0:1' -n -f quux -t foo -c bar --in-reply-to baz \
-r 0:1 | fixheaders
+
+echo "% test single flag for single patch"
+hg email --date '1970-1-1 0:1' -n --flag fooFlag -f quux -t foo -c bar -s test \
+ -r 2 | fixheaders
+
+echo "% test single flag for multiple patches"
+hg email --date '1970-1-1 0:1' -n --flag fooFlag -f quux -t foo -c bar -s test \
+ -r 0:1 | fixheaders
+
+echo "% test mutiple flags for single patch"
+hg email --date '1970-1-1 0:1' -n --flag fooFlag --flag barFlag -f quux -t foo \
+ -c bar -s test -r 2 | fixheaders
+
+echo "% test multiple flags for multiple patches"
+hg email --date '1970-1-1 0:1' -n --flag fooFlag --flag barFlag -f quux -t foo \
+ -c bar -s test -r 0:1 | fixheaders
--- a/tests/test-patchbomb.out Wed Sep 16 16:11:44 2009 +0200
+++ b/tests/test-patchbomb.out Wed Sep 16 23:46:06 2009 +0200
@@ -1254,3 +1254,215 @@
@@ -0,0 +1,1 @@
+b
+% test single flag for single patch
+This patch series consists of 1 patches.
+
+
+Displaying [PATCH fooFlag] test ...
+Content-Type: text/plain; charset="us-ascii"
+MIME-Version: 1.0
+Content-Transfer-Encoding: 7bit
+Subject: [PATCH fooFlag] test
+X-Mercurial-Node: ff2c9fa2018b15fa74b33363bda9527323e2a99f
+Message-Id: <ff2c9fa2018b15fa74b3.60@
+User-Agent: Mercurial-patchbomb
+Date: Thu, 01 Jan 1970 00:01:00 +0000
+From: quux
+To: foo
+Cc: bar
+
+# HG changeset patch
+# User test
+# Date 3 0
+# Node ID ff2c9fa2018b15fa74b33363bda9527323e2a99f
+# Parent 97d72e5f12c7e84f85064aa72e5a297142c36ed9
+c
+
+diff -r 97d72e5f12c7 -r ff2c9fa2018b c
+--- /dev/null Thu Jan 01 00:00:00 1970 +0000
++++ b/c Thu Jan 01 00:00:03 1970 +0000
+@@ -0,0 +1,1 @@
++c
+
+% test single flag for multiple patches
+This patch series consists of 2 patches.
+
+
+Write the introductory message for the patch series.
+
+
+Displaying [PATCH 0 of 2 fooFlag] test ...
+Content-Type: text/plain; charset="us-ascii"
+MIME-Version: 1.0
+Content-Transfer-Encoding: 7bit
+Subject: [PATCH 0 of 2 fooFlag] test
+Message-Id: <patchbomb.60@
+User-Agent: Mercurial-patchbomb
+Date: Thu, 01 Jan 1970 00:01:00 +0000
+From: quux
+To: foo
+Cc: bar
+
+
+Displaying [PATCH 1 of 2 fooFlag] a ...
+Content-Type: text/plain; charset="us-ascii"
+MIME-Version: 1.0
+Content-Transfer-Encoding: 7bit
+Subject: [PATCH 1 of 2 fooFlag] a
+X-Mercurial-Node: 8580ff50825a50c8f716709acdf8de0deddcd6ab
+Message-Id: <8580ff50825a50c8f716.61@
+In-Reply-To: <patchbomb.60@
+References: <patchbomb.60@
+User-Agent: Mercurial-patchbomb
+Date: Thu, 01 Jan 1970 00:01:01 +0000
+From: quux
+To: foo
+Cc: bar
+
+# HG changeset patch
+# User test
+# Date 1 0
+# Node ID 8580ff50825a50c8f716709acdf8de0deddcd6ab
+# Parent 0000000000000000000000000000000000000000
+a
+
+diff -r 000000000000 -r 8580ff50825a a
+--- /dev/null Thu Jan 01 00:00:00 1970 +0000
++++ b/a Thu Jan 01 00:00:01 1970 +0000
+@@ -0,0 +1,1 @@
++a
+
+Displaying [PATCH 2 of 2 fooFlag] b ...
+Content-Type: text/plain; charset="us-ascii"
+MIME-Version: 1.0
+Content-Transfer-Encoding: 7bit
+Subject: [PATCH 2 of 2 fooFlag] b
+X-Mercurial-Node: 97d72e5f12c7e84f85064aa72e5a297142c36ed9
+Message-Id: <97d72e5f12c7e84f8506.62@
+In-Reply-To: <patchbomb.60@
+References: <patchbomb.60@
+User-Agent: Mercurial-patchbomb
+Date: Thu, 01 Jan 1970 00:01:02 +0000
+From: quux
+To: foo
+Cc: bar
+
+# HG changeset patch
+# User test
+# Date 2 0
+# Node ID 97d72e5f12c7e84f85064aa72e5a297142c36ed9
+# Parent 8580ff50825a50c8f716709acdf8de0deddcd6ab
+b
+
+diff -r 8580ff50825a -r 97d72e5f12c7 b
+--- /dev/null Thu Jan 01 00:00:00 1970 +0000
++++ b/b Thu Jan 01 00:00:02 1970 +0000
+@@ -0,0 +1,1 @@
++b
+
+% test mutiple flags for single patch
+This patch series consists of 1 patches.
+
+
+Displaying [PATCH fooFlag barFlag] test ...
+Content-Type: text/plain; charset="us-ascii"
+MIME-Version: 1.0
+Content-Transfer-Encoding: 7bit
+Subject: [PATCH fooFlag barFlag] test
+X-Mercurial-Node: ff2c9fa2018b15fa74b33363bda9527323e2a99f
+Message-Id: <ff2c9fa2018b15fa74b3.60@
+User-Agent: Mercurial-patchbomb
+Date: Thu, 01 Jan 1970 00:01:00 +0000
+From: quux
+To: foo
+Cc: bar
+
+# HG changeset patch
+# User test
+# Date 3 0
+# Node ID ff2c9fa2018b15fa74b33363bda9527323e2a99f
+# Parent 97d72e5f12c7e84f85064aa72e5a297142c36ed9
+c
+
+diff -r 97d72e5f12c7 -r ff2c9fa2018b c
+--- /dev/null Thu Jan 01 00:00:00 1970 +0000
++++ b/c Thu Jan 01 00:00:03 1970 +0000
+@@ -0,0 +1,1 @@
++c
+
+% test multiple flags for multiple patches
+This patch series consists of 2 patches.
+
+
+Write the introductory message for the patch series.
+
+
+Displaying [PATCH 0 of 2 fooFlag barFlag] test ...
+Content-Type: text/plain; charset="us-ascii"
+MIME-Version: 1.0
+Content-Transfer-Encoding: 7bit
+Subject: [PATCH 0 of 2 fooFlag barFlag] test
+Message-Id: <patchbomb.60@
+User-Agent: Mercurial-patchbomb
+Date: Thu, 01 Jan 1970 00:01:00 +0000
+From: quux
+To: foo
+Cc: bar
+
+
+Displaying [PATCH 1 of 2 fooFlag barFlag] a ...
+Content-Type: text/plain; charset="us-ascii"
+MIME-Version: 1.0
+Content-Transfer-Encoding: 7bit
+Subject: [PATCH 1 of 2 fooFlag barFlag] a
+X-Mercurial-Node: 8580ff50825a50c8f716709acdf8de0deddcd6ab
+Message-Id: <8580ff50825a50c8f716.61@
+In-Reply-To: <patchbomb.60@
+References: <patchbomb.60@
+User-Agent: Mercurial-patchbomb
+Date: Thu, 01 Jan 1970 00:01:01 +0000
+From: quux
+To: foo
+Cc: bar
+
+# HG changeset patch
+# User test
+# Date 1 0
+# Node ID 8580ff50825a50c8f716709acdf8de0deddcd6ab
+# Parent 0000000000000000000000000000000000000000
+a
+
+diff -r 000000000000 -r 8580ff50825a a
+--- /dev/null Thu Jan 01 00:00:00 1970 +0000
++++ b/a Thu Jan 01 00:00:01 1970 +0000
+@@ -0,0 +1,1 @@
++a
+
+Displaying [PATCH 2 of 2 fooFlag barFlag] b ...
+Content-Type: text/plain; charset="us-ascii"
+MIME-Version: 1.0
+Content-Transfer-Encoding: 7bit
+Subject: [PATCH 2 of 2 fooFlag barFlag] b
+X-Mercurial-Node: 97d72e5f12c7e84f85064aa72e5a297142c36ed9
+Message-Id: <97d72e5f12c7e84f8506.62@
+In-Reply-To: <patchbomb.60@
+References: <patchbomb.60@
+User-Agent: Mercurial-patchbomb
+Date: Thu, 01 Jan 1970 00:01:02 +0000
+From: quux
+To: foo
+Cc: bar
+
+# HG changeset patch
+# User test
+# Date 2 0
+# Node ID 97d72e5f12c7e84f85064aa72e5a297142c36ed9
+# Parent 8580ff50825a50c8f716709acdf8de0deddcd6ab
+b
+
+diff -r 8580ff50825a -r 97d72e5f12c7 b
+--- /dev/null Thu Jan 01 00:00:00 1970 +0000
++++ b/b Thu Jan 01 00:00:02 1970 +0000
+@@ -0,0 +1,1 @@
++b
+
--- a/tests/test-tags Wed Sep 16 16:11:44 2009 +0200
+++ b/tests/test-tags Wed Sep 16 23:46:06 2009 +0200
@@ -37,9 +37,17 @@
hg identify
# repeat with cold tag cache
+echo "% identify with cold cache"
rm -f .hg/tags.cache
hg identify
+# and again, but now unable to write tag cache
+echo "% identify with unwritable cache"
+rm -f .hg/tags.cache
+chmod 555 .hg
+hg identify
+chmod 755 .hg
+
echo "% create a branch"
echo bb > a
hg status
--- a/tests/test-tags.out Wed Sep 16 16:11:44 2009 +0200
+++ b/tests/test-tags.out Wed Sep 16 23:46:06 2009 +0200
@@ -13,6 +13,9 @@
tip 1:b9154636be93
first 0:acb14030fe0a
b9154636be93 tip
+% identify with cold cache
+b9154636be93 tip
+% identify with unwritable cache
b9154636be93 tip
% create a branch
M a