##// END OF EJS Templates
merge with stable
Matt Mackall -
r16621:8c3c9031 merge default
parent child Browse files
Show More
@@ -1,57 +1,57 b''
1 #!/usr/bin/env python
1 #!/usr/bin/env python
2 #
2 #
3 # Copyright 2005-2007 by Intevation GmbH <intevation@intevation.de>
3 # Copyright 2005-2007 by Intevation GmbH <intevation@intevation.de>
4 #
4 #
5 # Author(s):
5 # Author(s):
6 # Thomas Arendsen Hein <thomas@intevation.de>
6 # Thomas Arendsen Hein <thomas@intevation.de>
7 #
7 #
8 # This software may be used and distributed according to the terms of the
8 # This software may be used and distributed according to the terms of the
9 # GNU General Public License version 2 or any later version.
9 # GNU General Public License version 2 or any later version.
10
10
11 """
11 """
12 hg-ssh - a wrapper for ssh access to a limited set of mercurial repos
12 hg-ssh - a wrapper for ssh access to a limited set of mercurial repos
13
13
14 To be used in ~/.ssh/authorized_keys with the "command" option, see sshd(8):
14 To be used in ~/.ssh/authorized_keys with the "command" option, see sshd(8):
15 command="hg-ssh path/to/repo1 /path/to/repo2 ~/repo3 ~user/repo4" ssh-dss ...
15 command="hg-ssh path/to/repo1 /path/to/repo2 ~/repo3 ~user/repo4" ssh-dss ...
16 (probably together with these other useful options:
16 (probably together with these other useful options:
17 no-port-forwarding,no-X11-forwarding,no-agent-forwarding)
17 no-port-forwarding,no-X11-forwarding,no-agent-forwarding)
18
18
19 This allows pull/push over ssh from/to the repositories given as arguments.
19 This allows pull/push over ssh from/to the repositories given as arguments.
20
20
21 If all your repositories are subdirectories of a common directory, you can
21 If all your repositories are subdirectories of a common directory, you can
22 allow shorter paths with:
22 allow shorter paths with:
23 command="cd path/to/my/repositories && hg-ssh repo1 subdir/repo2"
23 command="cd path/to/my/repositories && hg-ssh repo1 subdir/repo2"
24
24
25 You can use pattern matching of your normal shell, e.g.:
25 You can use pattern matching of your normal shell, e.g.:
26 command="cd repos && hg-ssh user/thomas/* projects/{mercurial,foo}"
26 command="cd repos && hg-ssh user/thomas/* projects/{mercurial,foo}"
27 """
27 """
28
28
29 # enable importing on demand to reduce startup time
29 # enable importing on demand to reduce startup time
30 from mercurial import demandimport; demandimport.enable()
30 from mercurial import demandimport; demandimport.enable()
31
31
32 from mercurial import dispatch
32 from mercurial import dispatch
33
33
34 import sys, os, shlex
34 import sys, os, shlex
35
35
36 cwd = os.getcwd()
36 cwd = os.getcwd()
37 allowed_paths = [os.path.normpath(os.path.join(cwd, os.path.expanduser(path)))
37 allowed_paths = [os.path.normpath(os.path.join(cwd, os.path.expanduser(path)))
38 for path in sys.argv[1:]]
38 for path in sys.argv[1:]]
39 orig_cmd = os.getenv('SSH_ORIGINAL_COMMAND', '?')
39 orig_cmd = os.getenv('SSH_ORIGINAL_COMMAND', '?')
40 try:
40 try:
41 cmdargv = shlex.split(orig_cmd)
41 cmdargv = shlex.split(orig_cmd)
42 except ValueError, e:
42 except ValueError, e:
43 sys.stderr.write("Illegal command %r: %s\n" % (orig_cmd, e))
43 sys.stderr.write('Illegal command "%s": %s\n' % (orig_cmd, e))
44 sys.exit(-1)
44 sys.exit(255)
45
45
46 if cmdargv[:2] == ['hg', '-R'] and cmdargv[3:] == ['serve', '--stdio']:
46 if cmdargv[:2] == ['hg', '-R'] and cmdargv[3:] == ['serve', '--stdio']:
47 path = cmdargv[2]
47 path = cmdargv[2]
48 repo = os.path.normpath(os.path.join(cwd, os.path.expanduser(path)))
48 repo = os.path.normpath(os.path.join(cwd, os.path.expanduser(path)))
49 if repo in allowed_paths:
49 if repo in allowed_paths:
50 dispatch.dispatch(dispatch.request(['-R', repo, 'serve', '--stdio']))
50 dispatch.dispatch(dispatch.request(['-R', repo, 'serve', '--stdio']))
51 else:
51 else:
52 sys.stderr.write("Illegal repository %r\n" % repo)
52 sys.stderr.write('Illegal repository "%s"\n' % repo)
53 sys.exit(-1)
53 sys.exit(255)
54 else:
54 else:
55 sys.stderr.write("Illegal command %r\n" % orig_cmd)
55 sys.stderr.write('Illegal command "%s"\n' % orig_cmd)
56 sys.exit(-1)
56 sys.exit(255)
57
57
@@ -1,1107 +1,1110 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2 # $Id: manpage.py 6110 2009-08-31 14:40:33Z grubert $
2 # $Id: manpage.py 6110 2009-08-31 14:40:33Z grubert $
3 # Author: Engelbert Gruber <grubert@users.sourceforge.net>
3 # Author: Engelbert Gruber <grubert@users.sourceforge.net>
4 # Copyright: This module is put into the public domain.
4 # Copyright: This module is put into the public domain.
5
5
6 """
6 """
7 Simple man page writer for reStructuredText.
7 Simple man page writer for reStructuredText.
8
8
9 Man pages (short for "manual pages") contain system documentation on unix-like
9 Man pages (short for "manual pages") contain system documentation on unix-like
10 systems. The pages are grouped in numbered sections:
10 systems. The pages are grouped in numbered sections:
11
11
12 1 executable programs and shell commands
12 1 executable programs and shell commands
13 2 system calls
13 2 system calls
14 3 library functions
14 3 library functions
15 4 special files
15 4 special files
16 5 file formats
16 5 file formats
17 6 games
17 6 games
18 7 miscellaneous
18 7 miscellaneous
19 8 system administration
19 8 system administration
20
20
21 Man pages are written *troff*, a text file formatting system.
21 Man pages are written *troff*, a text file formatting system.
22
22
23 See http://www.tldp.org/HOWTO/Man-Page for a start.
23 See http://www.tldp.org/HOWTO/Man-Page for a start.
24
24
25 Man pages have no subsection only parts.
25 Man pages have no subsection only parts.
26 Standard parts
26 Standard parts
27
27
28 NAME ,
28 NAME ,
29 SYNOPSIS ,
29 SYNOPSIS ,
30 DESCRIPTION ,
30 DESCRIPTION ,
31 OPTIONS ,
31 OPTIONS ,
32 FILES ,
32 FILES ,
33 SEE ALSO ,
33 SEE ALSO ,
34 BUGS ,
34 BUGS ,
35
35
36 and
36 and
37
37
38 AUTHOR .
38 AUTHOR .
39
39
40 A unix-like system keeps an index of the DESCRIPTIONs, which is accesable
40 A unix-like system keeps an index of the DESCRIPTIONs, which is accesable
41 by the command whatis or apropos.
41 by the command whatis or apropos.
42
42
43 """
43 """
44
44
45 __docformat__ = 'reStructuredText'
45 __docformat__ = 'reStructuredText'
46
46
47 import re
47 import re
48
48
49 from docutils import nodes, writers, languages
49 from docutils import nodes, writers, languages
50 import roman
50 try:
51 import roman
52 except ImportError:
53 from docutils.utils import roman
51 import inspect
54 import inspect
52
55
53 FIELD_LIST_INDENT = 7
56 FIELD_LIST_INDENT = 7
54 DEFINITION_LIST_INDENT = 7
57 DEFINITION_LIST_INDENT = 7
55 OPTION_LIST_INDENT = 7
58 OPTION_LIST_INDENT = 7
56 BLOCKQOUTE_INDENT = 3.5
59 BLOCKQOUTE_INDENT = 3.5
57
60
58 # Define two macros so man/roff can calculate the
61 # Define two macros so man/roff can calculate the
59 # indent/unindent margins by itself
62 # indent/unindent margins by itself
60 MACRO_DEF = (r""".
63 MACRO_DEF = (r""".
61 .nr rst2man-indent-level 0
64 .nr rst2man-indent-level 0
62 .
65 .
63 .de1 rstReportMargin
66 .de1 rstReportMargin
64 \\$1 \\n[an-margin]
67 \\$1 \\n[an-margin]
65 level \\n[rst2man-indent-level]
68 level \\n[rst2man-indent-level]
66 level margin: \\n[rst2man-indent\\n[rst2man-indent-level]]
69 level margin: \\n[rst2man-indent\\n[rst2man-indent-level]]
67 -
70 -
68 \\n[rst2man-indent0]
71 \\n[rst2man-indent0]
69 \\n[rst2man-indent1]
72 \\n[rst2man-indent1]
70 \\n[rst2man-indent2]
73 \\n[rst2man-indent2]
71 ..
74 ..
72 .de1 INDENT
75 .de1 INDENT
73 .\" .rstReportMargin pre:
76 .\" .rstReportMargin pre:
74 . RS \\$1
77 . RS \\$1
75 . nr rst2man-indent\\n[rst2man-indent-level] \\n[an-margin]
78 . nr rst2man-indent\\n[rst2man-indent-level] \\n[an-margin]
76 . nr rst2man-indent-level +1
79 . nr rst2man-indent-level +1
77 .\" .rstReportMargin post:
80 .\" .rstReportMargin post:
78 ..
81 ..
79 .de UNINDENT
82 .de UNINDENT
80 . RE
83 . RE
81 .\" indent \\n[an-margin]
84 .\" indent \\n[an-margin]
82 .\" old: \\n[rst2man-indent\\n[rst2man-indent-level]]
85 .\" old: \\n[rst2man-indent\\n[rst2man-indent-level]]
83 .nr rst2man-indent-level -1
86 .nr rst2man-indent-level -1
84 .\" new: \\n[rst2man-indent\\n[rst2man-indent-level]]
87 .\" new: \\n[rst2man-indent\\n[rst2man-indent-level]]
85 .in \\n[rst2man-indent\\n[rst2man-indent-level]]u
88 .in \\n[rst2man-indent\\n[rst2man-indent-level]]u
86 ..
89 ..
87 """)
90 """)
88
91
89 class Writer(writers.Writer):
92 class Writer(writers.Writer):
90
93
91 supported = ('manpage')
94 supported = ('manpage')
92 """Formats this writer supports."""
95 """Formats this writer supports."""
93
96
94 output = None
97 output = None
95 """Final translated form of `document`."""
98 """Final translated form of `document`."""
96
99
97 def __init__(self):
100 def __init__(self):
98 writers.Writer.__init__(self)
101 writers.Writer.__init__(self)
99 self.translator_class = Translator
102 self.translator_class = Translator
100
103
101 def translate(self):
104 def translate(self):
102 visitor = self.translator_class(self.document)
105 visitor = self.translator_class(self.document)
103 self.document.walkabout(visitor)
106 self.document.walkabout(visitor)
104 self.output = visitor.astext()
107 self.output = visitor.astext()
105
108
106
109
107 class Table(object):
110 class Table(object):
108 def __init__(self):
111 def __init__(self):
109 self._rows = []
112 self._rows = []
110 self._options = ['center']
113 self._options = ['center']
111 self._tab_char = '\t'
114 self._tab_char = '\t'
112 self._coldefs = []
115 self._coldefs = []
113 def new_row(self):
116 def new_row(self):
114 self._rows.append([])
117 self._rows.append([])
115 def append_separator(self, separator):
118 def append_separator(self, separator):
116 """Append the separator for table head."""
119 """Append the separator for table head."""
117 self._rows.append([separator])
120 self._rows.append([separator])
118 def append_cell(self, cell_lines):
121 def append_cell(self, cell_lines):
119 """cell_lines is an array of lines"""
122 """cell_lines is an array of lines"""
120 start = 0
123 start = 0
121 if len(cell_lines) > 0 and cell_lines[0] == '.sp\n':
124 if len(cell_lines) > 0 and cell_lines[0] == '.sp\n':
122 start = 1
125 start = 1
123 self._rows[-1].append(cell_lines[start:])
126 self._rows[-1].append(cell_lines[start:])
124 if len(self._coldefs) < len(self._rows[-1]):
127 if len(self._coldefs) < len(self._rows[-1]):
125 self._coldefs.append('l')
128 self._coldefs.append('l')
126 def _minimize_cell(self, cell_lines):
129 def _minimize_cell(self, cell_lines):
127 """Remove leading and trailing blank and ``.sp`` lines"""
130 """Remove leading and trailing blank and ``.sp`` lines"""
128 while (cell_lines and cell_lines[0] in ('\n', '.sp\n')):
131 while (cell_lines and cell_lines[0] in ('\n', '.sp\n')):
129 del cell_lines[0]
132 del cell_lines[0]
130 while (cell_lines and cell_lines[-1] in ('\n', '.sp\n')):
133 while (cell_lines and cell_lines[-1] in ('\n', '.sp\n')):
131 del cell_lines[-1]
134 del cell_lines[-1]
132 def as_list(self):
135 def as_list(self):
133 text = ['.TS\n']
136 text = ['.TS\n']
134 text.append(' '.join(self._options) + ';\n')
137 text.append(' '.join(self._options) + ';\n')
135 text.append('|%s|.\n' % ('|'.join(self._coldefs)))
138 text.append('|%s|.\n' % ('|'.join(self._coldefs)))
136 for row in self._rows:
139 for row in self._rows:
137 # row = array of cells. cell = array of lines.
140 # row = array of cells. cell = array of lines.
138 text.append('_\n') # line above
141 text.append('_\n') # line above
139 text.append('T{\n')
142 text.append('T{\n')
140 for i in range(len(row)):
143 for i in range(len(row)):
141 cell = row[i]
144 cell = row[i]
142 self._minimize_cell(cell)
145 self._minimize_cell(cell)
143 text.extend(cell)
146 text.extend(cell)
144 if not text[-1].endswith('\n'):
147 if not text[-1].endswith('\n'):
145 text[-1] += '\n'
148 text[-1] += '\n'
146 if i < len(row)-1:
149 if i < len(row)-1:
147 text.append('T}'+self._tab_char+'T{\n')
150 text.append('T}'+self._tab_char+'T{\n')
148 else:
151 else:
149 text.append('T}\n')
152 text.append('T}\n')
150 text.append('_\n')
153 text.append('_\n')
151 text.append('.TE\n')
154 text.append('.TE\n')
152 return text
155 return text
153
156
154 class Translator(nodes.NodeVisitor):
157 class Translator(nodes.NodeVisitor):
155 """"""
158 """"""
156
159
157 words_and_spaces = re.compile(r'\S+| +|\n')
160 words_and_spaces = re.compile(r'\S+| +|\n')
158 document_start = """Man page generated from reStructeredText."""
161 document_start = """Man page generated from reStructeredText."""
159
162
160 def __init__(self, document):
163 def __init__(self, document):
161 nodes.NodeVisitor.__init__(self, document)
164 nodes.NodeVisitor.__init__(self, document)
162 self.settings = settings = document.settings
165 self.settings = settings = document.settings
163 lcode = settings.language_code
166 lcode = settings.language_code
164 arglen = len(inspect.getargspec(languages.get_language)[0])
167 arglen = len(inspect.getargspec(languages.get_language)[0])
165 if arglen == 2:
168 if arglen == 2:
166 self.language = languages.get_language(lcode,
169 self.language = languages.get_language(lcode,
167 self.document.reporter)
170 self.document.reporter)
168 else:
171 else:
169 self.language = languages.get_language(lcode)
172 self.language = languages.get_language(lcode)
170 self.head = []
173 self.head = []
171 self.body = []
174 self.body = []
172 self.foot = []
175 self.foot = []
173 self.section_level = 0
176 self.section_level = 0
174 self.context = []
177 self.context = []
175 self.topic_class = ''
178 self.topic_class = ''
176 self.colspecs = []
179 self.colspecs = []
177 self.compact_p = 1
180 self.compact_p = 1
178 self.compact_simple = None
181 self.compact_simple = None
179 # the list style "*" bullet or "#" numbered
182 # the list style "*" bullet or "#" numbered
180 self._list_char = []
183 self._list_char = []
181 # writing the header .TH and .SH NAME is postboned after
184 # writing the header .TH and .SH NAME is postboned after
182 # docinfo.
185 # docinfo.
183 self._docinfo = {
186 self._docinfo = {
184 "title" : "", "title_upper": "",
187 "title" : "", "title_upper": "",
185 "subtitle" : "",
188 "subtitle" : "",
186 "manual_section" : "", "manual_group" : "",
189 "manual_section" : "", "manual_group" : "",
187 "author" : [],
190 "author" : [],
188 "date" : "",
191 "date" : "",
189 "copyright" : "",
192 "copyright" : "",
190 "version" : "",
193 "version" : "",
191 }
194 }
192 self._docinfo_keys = [] # a list to keep the sequence as in source.
195 self._docinfo_keys = [] # a list to keep the sequence as in source.
193 self._docinfo_names = {} # to get name from text not normalized.
196 self._docinfo_names = {} # to get name from text not normalized.
194 self._in_docinfo = None
197 self._in_docinfo = None
195 self._active_table = None
198 self._active_table = None
196 self._in_literal = False
199 self._in_literal = False
197 self.header_written = 0
200 self.header_written = 0
198 self._line_block = 0
201 self._line_block = 0
199 self.authors = []
202 self.authors = []
200 self.section_level = 0
203 self.section_level = 0
201 self._indent = [0]
204 self._indent = [0]
202 # central definition of simple processing rules
205 # central definition of simple processing rules
203 # what to output on : visit, depart
206 # what to output on : visit, depart
204 # Do not use paragraph requests ``.PP`` because these set indentation.
207 # Do not use paragraph requests ``.PP`` because these set indentation.
205 # use ``.sp``. Remove superfluous ``.sp`` in ``astext``.
208 # use ``.sp``. Remove superfluous ``.sp`` in ``astext``.
206 #
209 #
207 # Fonts are put on a stack, the top one is used.
210 # Fonts are put on a stack, the top one is used.
208 # ``.ft P`` or ``\\fP`` pop from stack.
211 # ``.ft P`` or ``\\fP`` pop from stack.
209 # ``B`` bold, ``I`` italic, ``R`` roman should be available.
212 # ``B`` bold, ``I`` italic, ``R`` roman should be available.
210 # Hopefully ``C`` courier too.
213 # Hopefully ``C`` courier too.
211 self.defs = {
214 self.defs = {
212 'indent' : ('.INDENT %.1f\n', '.UNINDENT\n'),
215 'indent' : ('.INDENT %.1f\n', '.UNINDENT\n'),
213 'definition_list_item' : ('.TP', ''),
216 'definition_list_item' : ('.TP', ''),
214 'field_name' : ('.TP\n.B ', '\n'),
217 'field_name' : ('.TP\n.B ', '\n'),
215 'literal' : ('\\fB', '\\fP'),
218 'literal' : ('\\fB', '\\fP'),
216 'literal_block' : ('.sp\n.nf\n.ft C\n', '\n.ft P\n.fi\n'),
219 'literal_block' : ('.sp\n.nf\n.ft C\n', '\n.ft P\n.fi\n'),
217
220
218 'option_list_item' : ('.TP\n', ''),
221 'option_list_item' : ('.TP\n', ''),
219
222
220 'reference' : (r'\%', r'\:'),
223 'reference' : (r'\%', r'\:'),
221 'emphasis': ('\\fI', '\\fP'),
224 'emphasis': ('\\fI', '\\fP'),
222 'strong' : ('\\fB', '\\fP'),
225 'strong' : ('\\fB', '\\fP'),
223 'term' : ('\n.B ', '\n'),
226 'term' : ('\n.B ', '\n'),
224 'title_reference' : ('\\fI', '\\fP'),
227 'title_reference' : ('\\fI', '\\fP'),
225
228
226 'topic-title' : ('.SS ',),
229 'topic-title' : ('.SS ',),
227 'sidebar-title' : ('.SS ',),
230 'sidebar-title' : ('.SS ',),
228
231
229 'problematic' : ('\n.nf\n', '\n.fi\n'),
232 'problematic' : ('\n.nf\n', '\n.fi\n'),
230 }
233 }
231 # NOTE don't specify the newline before a dot-command, but ensure
234 # NOTE don't specify the newline before a dot-command, but ensure
232 # it is there.
235 # it is there.
233
236
234 def comment_begin(self, text):
237 def comment_begin(self, text):
235 """Return commented version of the passed text WITHOUT end of
238 """Return commented version of the passed text WITHOUT end of
236 line/comment."""
239 line/comment."""
237 prefix = '.\\" '
240 prefix = '.\\" '
238 out_text = ''.join(
241 out_text = ''.join(
239 [(prefix + in_line + '\n')
242 [(prefix + in_line + '\n')
240 for in_line in text.split('\n')])
243 for in_line in text.split('\n')])
241 return out_text
244 return out_text
242
245
243 def comment(self, text):
246 def comment(self, text):
244 """Return commented version of the passed text."""
247 """Return commented version of the passed text."""
245 return self.comment_begin(text)+'.\n'
248 return self.comment_begin(text)+'.\n'
246
249
247 def ensure_eol(self):
250 def ensure_eol(self):
248 """Ensure the last line in body is terminated by new line."""
251 """Ensure the last line in body is terminated by new line."""
249 if self.body[-1][-1] != '\n':
252 if self.body[-1][-1] != '\n':
250 self.body.append('\n')
253 self.body.append('\n')
251
254
252 def astext(self):
255 def astext(self):
253 """Return the final formatted document as a string."""
256 """Return the final formatted document as a string."""
254 if not self.header_written:
257 if not self.header_written:
255 # ensure we get a ".TH" as viewers require it.
258 # ensure we get a ".TH" as viewers require it.
256 self.head.append(self.header())
259 self.head.append(self.header())
257 # filter body
260 # filter body
258 for i in xrange(len(self.body)-1, 0, -1):
261 for i in xrange(len(self.body)-1, 0, -1):
259 # remove superfluous vertical gaps.
262 # remove superfluous vertical gaps.
260 if self.body[i] == '.sp\n':
263 if self.body[i] == '.sp\n':
261 if self.body[i - 1][:4] in ('.BI ','.IP '):
264 if self.body[i - 1][:4] in ('.BI ','.IP '):
262 self.body[i] = '.\n'
265 self.body[i] = '.\n'
263 elif (self.body[i - 1][:3] == '.B ' and
266 elif (self.body[i - 1][:3] == '.B ' and
264 self.body[i - 2][:4] == '.TP\n'):
267 self.body[i - 2][:4] == '.TP\n'):
265 self.body[i] = '.\n'
268 self.body[i] = '.\n'
266 elif (self.body[i - 1] == '\n' and
269 elif (self.body[i - 1] == '\n' and
267 self.body[i - 2][0] != '.' and
270 self.body[i - 2][0] != '.' and
268 (self.body[i - 3][:7] == '.TP\n.B '
271 (self.body[i - 3][:7] == '.TP\n.B '
269 or self.body[i - 3][:4] == '\n.B ')
272 or self.body[i - 3][:4] == '\n.B ')
270 ):
273 ):
271 self.body[i] = '.\n'
274 self.body[i] = '.\n'
272 return ''.join(self.head + self.body + self.foot)
275 return ''.join(self.head + self.body + self.foot)
273
276
274 def deunicode(self, text):
277 def deunicode(self, text):
275 text = text.replace(u'\xa0', '\\ ')
278 text = text.replace(u'\xa0', '\\ ')
276 text = text.replace(u'\u2020', '\\(dg')
279 text = text.replace(u'\u2020', '\\(dg')
277 return text
280 return text
278
281
279 def visit_Text(self, node):
282 def visit_Text(self, node):
280 text = node.astext()
283 text = node.astext()
281 text = text.replace('\\','\\e')
284 text = text.replace('\\','\\e')
282 replace_pairs = [
285 replace_pairs = [
283 (u'-', ur'\-'),
286 (u'-', ur'\-'),
284 (u'\'', ur'\(aq'),
287 (u'\'', ur'\(aq'),
285 (u'Β΄', ur'\''),
288 (u'Β΄', ur'\''),
286 (u'`', ur'\(ga'),
289 (u'`', ur'\(ga'),
287 ]
290 ]
288 for (in_char, out_markup) in replace_pairs:
291 for (in_char, out_markup) in replace_pairs:
289 text = text.replace(in_char, out_markup)
292 text = text.replace(in_char, out_markup)
290 # unicode
293 # unicode
291 text = self.deunicode(text)
294 text = self.deunicode(text)
292 if self._in_literal:
295 if self._in_literal:
293 # prevent interpretation of "." at line start
296 # prevent interpretation of "." at line start
294 if text[0] == '.':
297 if text[0] == '.':
295 text = '\\&' + text
298 text = '\\&' + text
296 text = text.replace('\n.', '\n\\&.')
299 text = text.replace('\n.', '\n\\&.')
297 self.body.append(text)
300 self.body.append(text)
298
301
299 def depart_Text(self, node):
302 def depart_Text(self, node):
300 pass
303 pass
301
304
302 def list_start(self, node):
305 def list_start(self, node):
303 class enum_char(object):
306 class enum_char(object):
304 enum_style = {
307 enum_style = {
305 'bullet' : '\\(bu',
308 'bullet' : '\\(bu',
306 'emdash' : '\\(em',
309 'emdash' : '\\(em',
307 }
310 }
308
311
309 def __init__(self, style):
312 def __init__(self, style):
310 self._style = style
313 self._style = style
311 if 'start' in node:
314 if 'start' in node:
312 self._cnt = node['start'] - 1
315 self._cnt = node['start'] - 1
313 else:
316 else:
314 self._cnt = 0
317 self._cnt = 0
315 self._indent = 2
318 self._indent = 2
316 if style == 'arabic':
319 if style == 'arabic':
317 # indentation depends on number of childrens
320 # indentation depends on number of childrens
318 # and start value.
321 # and start value.
319 self._indent = len(str(len(node.children)))
322 self._indent = len(str(len(node.children)))
320 self._indent += len(str(self._cnt)) + 1
323 self._indent += len(str(self._cnt)) + 1
321 elif style == 'loweralpha':
324 elif style == 'loweralpha':
322 self._cnt += ord('a') - 1
325 self._cnt += ord('a') - 1
323 self._indent = 3
326 self._indent = 3
324 elif style == 'upperalpha':
327 elif style == 'upperalpha':
325 self._cnt += ord('A') - 1
328 self._cnt += ord('A') - 1
326 self._indent = 3
329 self._indent = 3
327 elif style.endswith('roman'):
330 elif style.endswith('roman'):
328 self._indent = 5
331 self._indent = 5
329
332
330 def next(self):
333 def next(self):
331 if self._style == 'bullet':
334 if self._style == 'bullet':
332 return self.enum_style[self._style]
335 return self.enum_style[self._style]
333 elif self._style == 'emdash':
336 elif self._style == 'emdash':
334 return self.enum_style[self._style]
337 return self.enum_style[self._style]
335 self._cnt += 1
338 self._cnt += 1
336 # TODO add prefix postfix
339 # TODO add prefix postfix
337 if self._style == 'arabic':
340 if self._style == 'arabic':
338 return "%d." % self._cnt
341 return "%d." % self._cnt
339 elif self._style in ('loweralpha', 'upperalpha'):
342 elif self._style in ('loweralpha', 'upperalpha'):
340 return "%c." % self._cnt
343 return "%c." % self._cnt
341 elif self._style.endswith('roman'):
344 elif self._style.endswith('roman'):
342 res = roman.toRoman(self._cnt) + '.'
345 res = roman.toRoman(self._cnt) + '.'
343 if self._style.startswith('upper'):
346 if self._style.startswith('upper'):
344 return res.upper()
347 return res.upper()
345 return res.lower()
348 return res.lower()
346 else:
349 else:
347 return "%d." % self._cnt
350 return "%d." % self._cnt
348 def get_width(self):
351 def get_width(self):
349 return self._indent
352 return self._indent
350 def __repr__(self):
353 def __repr__(self):
351 return 'enum_style-%s' % list(self._style)
354 return 'enum_style-%s' % list(self._style)
352
355
353 if 'enumtype' in node:
356 if 'enumtype' in node:
354 self._list_char.append(enum_char(node['enumtype']))
357 self._list_char.append(enum_char(node['enumtype']))
355 else:
358 else:
356 self._list_char.append(enum_char('bullet'))
359 self._list_char.append(enum_char('bullet'))
357 if len(self._list_char) > 1:
360 if len(self._list_char) > 1:
358 # indent nested lists
361 # indent nested lists
359 self.indent(self._list_char[-2].get_width())
362 self.indent(self._list_char[-2].get_width())
360 else:
363 else:
361 self.indent(self._list_char[-1].get_width())
364 self.indent(self._list_char[-1].get_width())
362
365
363 def list_end(self):
366 def list_end(self):
364 self.dedent()
367 self.dedent()
365 self._list_char.pop()
368 self._list_char.pop()
366
369
367 def header(self):
370 def header(self):
368 tmpl = (".TH %(title_upper)s %(manual_section)s"
371 tmpl = (".TH %(title_upper)s %(manual_section)s"
369 " \"%(date)s\" \"%(version)s\" \"%(manual_group)s\"\n"
372 " \"%(date)s\" \"%(version)s\" \"%(manual_group)s\"\n"
370 ".SH NAME\n"
373 ".SH NAME\n"
371 "%(title)s \- %(subtitle)s\n")
374 "%(title)s \- %(subtitle)s\n")
372 return tmpl % self._docinfo
375 return tmpl % self._docinfo
373
376
374 def append_header(self):
377 def append_header(self):
375 """append header with .TH and .SH NAME"""
378 """append header with .TH and .SH NAME"""
376 # NOTE before everything
379 # NOTE before everything
377 # .TH title_upper section date source manual
380 # .TH title_upper section date source manual
378 if self.header_written:
381 if self.header_written:
379 return
382 return
380 self.body.append(self.header())
383 self.body.append(self.header())
381 self.body.append(MACRO_DEF)
384 self.body.append(MACRO_DEF)
382 self.header_written = 1
385 self.header_written = 1
383
386
384 def visit_address(self, node):
387 def visit_address(self, node):
385 self.visit_docinfo_item(node, 'address')
388 self.visit_docinfo_item(node, 'address')
386
389
387 def depart_address(self, node):
390 def depart_address(self, node):
388 pass
391 pass
389
392
390 def visit_admonition(self, node, name=None):
393 def visit_admonition(self, node, name=None):
391 if name:
394 if name:
392 self.body.append('.IP %s\n' %
395 self.body.append('.IP %s\n' %
393 self.language.labels.get(name, name))
396 self.language.labels.get(name, name))
394
397
395 def depart_admonition(self, node):
398 def depart_admonition(self, node):
396 self.body.append('.RE\n')
399 self.body.append('.RE\n')
397
400
398 def visit_attention(self, node):
401 def visit_attention(self, node):
399 self.visit_admonition(node, 'attention')
402 self.visit_admonition(node, 'attention')
400
403
401 depart_attention = depart_admonition
404 depart_attention = depart_admonition
402
405
403 def visit_docinfo_item(self, node, name):
406 def visit_docinfo_item(self, node, name):
404 if name == 'author':
407 if name == 'author':
405 self._docinfo[name].append(node.astext())
408 self._docinfo[name].append(node.astext())
406 else:
409 else:
407 self._docinfo[name] = node.astext()
410 self._docinfo[name] = node.astext()
408 self._docinfo_keys.append(name)
411 self._docinfo_keys.append(name)
409 raise nodes.SkipNode
412 raise nodes.SkipNode
410
413
411 def depart_docinfo_item(self, node):
414 def depart_docinfo_item(self, node):
412 pass
415 pass
413
416
414 def visit_author(self, node):
417 def visit_author(self, node):
415 self.visit_docinfo_item(node, 'author')
418 self.visit_docinfo_item(node, 'author')
416
419
417 depart_author = depart_docinfo_item
420 depart_author = depart_docinfo_item
418
421
419 def visit_authors(self, node):
422 def visit_authors(self, node):
420 # _author is called anyway.
423 # _author is called anyway.
421 pass
424 pass
422
425
423 def depart_authors(self, node):
426 def depart_authors(self, node):
424 pass
427 pass
425
428
426 def visit_block_quote(self, node):
429 def visit_block_quote(self, node):
427 # BUG/HACK: indent alway uses the _last_ indention,
430 # BUG/HACK: indent alway uses the _last_ indention,
428 # thus we need two of them.
431 # thus we need two of them.
429 self.indent(BLOCKQOUTE_INDENT)
432 self.indent(BLOCKQOUTE_INDENT)
430 self.indent(0)
433 self.indent(0)
431
434
432 def depart_block_quote(self, node):
435 def depart_block_quote(self, node):
433 self.dedent()
436 self.dedent()
434 self.dedent()
437 self.dedent()
435
438
436 def visit_bullet_list(self, node):
439 def visit_bullet_list(self, node):
437 self.list_start(node)
440 self.list_start(node)
438
441
439 def depart_bullet_list(self, node):
442 def depart_bullet_list(self, node):
440 self.list_end()
443 self.list_end()
441
444
442 def visit_caption(self, node):
445 def visit_caption(self, node):
443 pass
446 pass
444
447
445 def depart_caption(self, node):
448 def depart_caption(self, node):
446 pass
449 pass
447
450
448 def visit_caution(self, node):
451 def visit_caution(self, node):
449 self.visit_admonition(node, 'caution')
452 self.visit_admonition(node, 'caution')
450
453
451 depart_caution = depart_admonition
454 depart_caution = depart_admonition
452
455
453 def visit_citation(self, node):
456 def visit_citation(self, node):
454 num, text = node.astext().split(None, 1)
457 num, text = node.astext().split(None, 1)
455 num = num.strip()
458 num = num.strip()
456 self.body.append('.IP [%s] 5\n' % num)
459 self.body.append('.IP [%s] 5\n' % num)
457
460
458 def depart_citation(self, node):
461 def depart_citation(self, node):
459 pass
462 pass
460
463
461 def visit_citation_reference(self, node):
464 def visit_citation_reference(self, node):
462 self.body.append('['+node.astext()+']')
465 self.body.append('['+node.astext()+']')
463 raise nodes.SkipNode
466 raise nodes.SkipNode
464
467
465 def visit_classifier(self, node):
468 def visit_classifier(self, node):
466 pass
469 pass
467
470
468 def depart_classifier(self, node):
471 def depart_classifier(self, node):
469 pass
472 pass
470
473
471 def visit_colspec(self, node):
474 def visit_colspec(self, node):
472 self.colspecs.append(node)
475 self.colspecs.append(node)
473
476
474 def depart_colspec(self, node):
477 def depart_colspec(self, node):
475 pass
478 pass
476
479
477 def write_colspecs(self):
480 def write_colspecs(self):
478 self.body.append("%s.\n" % ('L '*len(self.colspecs)))
481 self.body.append("%s.\n" % ('L '*len(self.colspecs)))
479
482
480 def visit_comment(self, node,
483 def visit_comment(self, node,
481 sub=re.compile('-(?=-)').sub):
484 sub=re.compile('-(?=-)').sub):
482 self.body.append(self.comment(node.astext()))
485 self.body.append(self.comment(node.astext()))
483 raise nodes.SkipNode
486 raise nodes.SkipNode
484
487
485 def visit_contact(self, node):
488 def visit_contact(self, node):
486 self.visit_docinfo_item(node, 'contact')
489 self.visit_docinfo_item(node, 'contact')
487
490
488 depart_contact = depart_docinfo_item
491 depart_contact = depart_docinfo_item
489
492
490 def visit_container(self, node):
493 def visit_container(self, node):
491 pass
494 pass
492
495
493 def depart_container(self, node):
496 def depart_container(self, node):
494 pass
497 pass
495
498
496 def visit_compound(self, node):
499 def visit_compound(self, node):
497 pass
500 pass
498
501
499 def depart_compound(self, node):
502 def depart_compound(self, node):
500 pass
503 pass
501
504
502 def visit_copyright(self, node):
505 def visit_copyright(self, node):
503 self.visit_docinfo_item(node, 'copyright')
506 self.visit_docinfo_item(node, 'copyright')
504
507
505 def visit_danger(self, node):
508 def visit_danger(self, node):
506 self.visit_admonition(node, 'danger')
509 self.visit_admonition(node, 'danger')
507
510
508 depart_danger = depart_admonition
511 depart_danger = depart_admonition
509
512
510 def visit_date(self, node):
513 def visit_date(self, node):
511 self.visit_docinfo_item(node, 'date')
514 self.visit_docinfo_item(node, 'date')
512
515
513 def visit_decoration(self, node):
516 def visit_decoration(self, node):
514 pass
517 pass
515
518
516 def depart_decoration(self, node):
519 def depart_decoration(self, node):
517 pass
520 pass
518
521
519 def visit_definition(self, node):
522 def visit_definition(self, node):
520 pass
523 pass
521
524
522 def depart_definition(self, node):
525 def depart_definition(self, node):
523 pass
526 pass
524
527
525 def visit_definition_list(self, node):
528 def visit_definition_list(self, node):
526 self.indent(DEFINITION_LIST_INDENT)
529 self.indent(DEFINITION_LIST_INDENT)
527
530
528 def depart_definition_list(self, node):
531 def depart_definition_list(self, node):
529 self.dedent()
532 self.dedent()
530
533
531 def visit_definition_list_item(self, node):
534 def visit_definition_list_item(self, node):
532 self.body.append(self.defs['definition_list_item'][0])
535 self.body.append(self.defs['definition_list_item'][0])
533
536
534 def depart_definition_list_item(self, node):
537 def depart_definition_list_item(self, node):
535 self.body.append(self.defs['definition_list_item'][1])
538 self.body.append(self.defs['definition_list_item'][1])
536
539
537 def visit_description(self, node):
540 def visit_description(self, node):
538 pass
541 pass
539
542
540 def depart_description(self, node):
543 def depart_description(self, node):
541 pass
544 pass
542
545
543 def visit_docinfo(self, node):
546 def visit_docinfo(self, node):
544 self._in_docinfo = 1
547 self._in_docinfo = 1
545
548
546 def depart_docinfo(self, node):
549 def depart_docinfo(self, node):
547 self._in_docinfo = None
550 self._in_docinfo = None
548 # NOTE nothing should be written before this
551 # NOTE nothing should be written before this
549 self.append_header()
552 self.append_header()
550
553
551 def visit_doctest_block(self, node):
554 def visit_doctest_block(self, node):
552 self.body.append(self.defs['literal_block'][0])
555 self.body.append(self.defs['literal_block'][0])
553 self._in_literal = True
556 self._in_literal = True
554
557
555 def depart_doctest_block(self, node):
558 def depart_doctest_block(self, node):
556 self._in_literal = False
559 self._in_literal = False
557 self.body.append(self.defs['literal_block'][1])
560 self.body.append(self.defs['literal_block'][1])
558
561
559 def visit_document(self, node):
562 def visit_document(self, node):
560 # no blank line between comment and header.
563 # no blank line between comment and header.
561 self.body.append(self.comment(self.document_start).rstrip()+'\n')
564 self.body.append(self.comment(self.document_start).rstrip()+'\n')
562 # writing header is postboned
565 # writing header is postboned
563 self.header_written = 0
566 self.header_written = 0
564
567
565 def depart_document(self, node):
568 def depart_document(self, node):
566 if self._docinfo['author']:
569 if self._docinfo['author']:
567 self.body.append('.SH AUTHOR\n%s\n'
570 self.body.append('.SH AUTHOR\n%s\n'
568 % ', '.join(self._docinfo['author']))
571 % ', '.join(self._docinfo['author']))
569 skip = ('author', 'copyright', 'date',
572 skip = ('author', 'copyright', 'date',
570 'manual_group', 'manual_section',
573 'manual_group', 'manual_section',
571 'subtitle',
574 'subtitle',
572 'title', 'title_upper', 'version')
575 'title', 'title_upper', 'version')
573 for name in self._docinfo_keys:
576 for name in self._docinfo_keys:
574 if name == 'address':
577 if name == 'address':
575 self.body.append("\n%s:\n%s%s.nf\n%s\n.fi\n%s%s" % (
578 self.body.append("\n%s:\n%s%s.nf\n%s\n.fi\n%s%s" % (
576 self.language.labels.get(name, name),
579 self.language.labels.get(name, name),
577 self.defs['indent'][0] % 0,
580 self.defs['indent'][0] % 0,
578 self.defs['indent'][0] % BLOCKQOUTE_INDENT,
581 self.defs['indent'][0] % BLOCKQOUTE_INDENT,
579 self._docinfo[name],
582 self._docinfo[name],
580 self.defs['indent'][1],
583 self.defs['indent'][1],
581 self.defs['indent'][1]))
584 self.defs['indent'][1]))
582 elif not name in skip:
585 elif not name in skip:
583 if name in self._docinfo_names:
586 if name in self._docinfo_names:
584 label = self._docinfo_names[name]
587 label = self._docinfo_names[name]
585 else:
588 else:
586 label = self.language.labels.get(name, name)
589 label = self.language.labels.get(name, name)
587 self.body.append("\n%s: %s\n" % (label, self._docinfo[name]))
590 self.body.append("\n%s: %s\n" % (label, self._docinfo[name]))
588 if self._docinfo['copyright']:
591 if self._docinfo['copyright']:
589 self.body.append('.SH COPYRIGHT\n%s\n'
592 self.body.append('.SH COPYRIGHT\n%s\n'
590 % self._docinfo['copyright'])
593 % self._docinfo['copyright'])
591 self.body.append(self.comment(
594 self.body.append(self.comment(
592 'Generated by docutils manpage writer.\n'))
595 'Generated by docutils manpage writer.\n'))
593
596
594 def visit_emphasis(self, node):
597 def visit_emphasis(self, node):
595 self.body.append(self.defs['emphasis'][0])
598 self.body.append(self.defs['emphasis'][0])
596
599
597 def depart_emphasis(self, node):
600 def depart_emphasis(self, node):
598 self.body.append(self.defs['emphasis'][1])
601 self.body.append(self.defs['emphasis'][1])
599
602
600 def visit_entry(self, node):
603 def visit_entry(self, node):
601 # a cell in a table row
604 # a cell in a table row
602 if 'morerows' in node:
605 if 'morerows' in node:
603 self.document.reporter.warning('"table row spanning" not supported',
606 self.document.reporter.warning('"table row spanning" not supported',
604 base_node=node)
607 base_node=node)
605 if 'morecols' in node:
608 if 'morecols' in node:
606 self.document.reporter.warning(
609 self.document.reporter.warning(
607 '"table cell spanning" not supported', base_node=node)
610 '"table cell spanning" not supported', base_node=node)
608 self.context.append(len(self.body))
611 self.context.append(len(self.body))
609
612
610 def depart_entry(self, node):
613 def depart_entry(self, node):
611 start = self.context.pop()
614 start = self.context.pop()
612 self._active_table.append_cell(self.body[start:])
615 self._active_table.append_cell(self.body[start:])
613 del self.body[start:]
616 del self.body[start:]
614
617
615 def visit_enumerated_list(self, node):
618 def visit_enumerated_list(self, node):
616 self.list_start(node)
619 self.list_start(node)
617
620
618 def depart_enumerated_list(self, node):
621 def depart_enumerated_list(self, node):
619 self.list_end()
622 self.list_end()
620
623
621 def visit_error(self, node):
624 def visit_error(self, node):
622 self.visit_admonition(node, 'error')
625 self.visit_admonition(node, 'error')
623
626
624 depart_error = depart_admonition
627 depart_error = depart_admonition
625
628
626 def visit_field(self, node):
629 def visit_field(self, node):
627 pass
630 pass
628
631
629 def depart_field(self, node):
632 def depart_field(self, node):
630 pass
633 pass
631
634
632 def visit_field_body(self, node):
635 def visit_field_body(self, node):
633 if self._in_docinfo:
636 if self._in_docinfo:
634 name_normalized = self._field_name.lower().replace(" ","_")
637 name_normalized = self._field_name.lower().replace(" ","_")
635 self._docinfo_names[name_normalized] = self._field_name
638 self._docinfo_names[name_normalized] = self._field_name
636 self.visit_docinfo_item(node, name_normalized)
639 self.visit_docinfo_item(node, name_normalized)
637 raise nodes.SkipNode
640 raise nodes.SkipNode
638
641
639 def depart_field_body(self, node):
642 def depart_field_body(self, node):
640 pass
643 pass
641
644
642 def visit_field_list(self, node):
645 def visit_field_list(self, node):
643 self.indent(FIELD_LIST_INDENT)
646 self.indent(FIELD_LIST_INDENT)
644
647
645 def depart_field_list(self, node):
648 def depart_field_list(self, node):
646 self.dedent()
649 self.dedent()
647
650
648 def visit_field_name(self, node):
651 def visit_field_name(self, node):
649 if self._in_docinfo:
652 if self._in_docinfo:
650 self._field_name = node.astext()
653 self._field_name = node.astext()
651 raise nodes.SkipNode
654 raise nodes.SkipNode
652 else:
655 else:
653 self.body.append(self.defs['field_name'][0])
656 self.body.append(self.defs['field_name'][0])
654
657
655 def depart_field_name(self, node):
658 def depart_field_name(self, node):
656 self.body.append(self.defs['field_name'][1])
659 self.body.append(self.defs['field_name'][1])
657
660
658 def visit_figure(self, node):
661 def visit_figure(self, node):
659 self.indent(2.5)
662 self.indent(2.5)
660 self.indent(0)
663 self.indent(0)
661
664
662 def depart_figure(self, node):
665 def depart_figure(self, node):
663 self.dedent()
666 self.dedent()
664 self.dedent()
667 self.dedent()
665
668
666 def visit_footer(self, node):
669 def visit_footer(self, node):
667 self.document.reporter.warning('"footer" not supported',
670 self.document.reporter.warning('"footer" not supported',
668 base_node=node)
671 base_node=node)
669
672
670 def depart_footer(self, node):
673 def depart_footer(self, node):
671 pass
674 pass
672
675
673 def visit_footnote(self, node):
676 def visit_footnote(self, node):
674 num, text = node.astext().split(None, 1)
677 num, text = node.astext().split(None, 1)
675 num = num.strip()
678 num = num.strip()
676 self.body.append('.IP [%s] 5\n' % self.deunicode(num))
679 self.body.append('.IP [%s] 5\n' % self.deunicode(num))
677
680
678 def depart_footnote(self, node):
681 def depart_footnote(self, node):
679 pass
682 pass
680
683
681 def footnote_backrefs(self, node):
684 def footnote_backrefs(self, node):
682 self.document.reporter.warning('"footnote_backrefs" not supported',
685 self.document.reporter.warning('"footnote_backrefs" not supported',
683 base_node=node)
686 base_node=node)
684
687
685 def visit_footnote_reference(self, node):
688 def visit_footnote_reference(self, node):
686 self.body.append('['+self.deunicode(node.astext())+']')
689 self.body.append('['+self.deunicode(node.astext())+']')
687 raise nodes.SkipNode
690 raise nodes.SkipNode
688
691
689 def depart_footnote_reference(self, node):
692 def depart_footnote_reference(self, node):
690 pass
693 pass
691
694
692 def visit_generated(self, node):
695 def visit_generated(self, node):
693 pass
696 pass
694
697
695 def depart_generated(self, node):
698 def depart_generated(self, node):
696 pass
699 pass
697
700
698 def visit_header(self, node):
701 def visit_header(self, node):
699 raise NotImplementedError, node.astext()
702 raise NotImplementedError, node.astext()
700
703
701 def depart_header(self, node):
704 def depart_header(self, node):
702 pass
705 pass
703
706
704 def visit_hint(self, node):
707 def visit_hint(self, node):
705 self.visit_admonition(node, 'hint')
708 self.visit_admonition(node, 'hint')
706
709
707 depart_hint = depart_admonition
710 depart_hint = depart_admonition
708
711
709 def visit_subscript(self, node):
712 def visit_subscript(self, node):
710 self.body.append('\\s-2\\d')
713 self.body.append('\\s-2\\d')
711
714
712 def depart_subscript(self, node):
715 def depart_subscript(self, node):
713 self.body.append('\\u\\s0')
716 self.body.append('\\u\\s0')
714
717
715 def visit_superscript(self, node):
718 def visit_superscript(self, node):
716 self.body.append('\\s-2\\u')
719 self.body.append('\\s-2\\u')
717
720
718 def depart_superscript(self, node):
721 def depart_superscript(self, node):
719 self.body.append('\\d\\s0')
722 self.body.append('\\d\\s0')
720
723
721 def visit_attribution(self, node):
724 def visit_attribution(self, node):
722 self.body.append('\\(em ')
725 self.body.append('\\(em ')
723
726
724 def depart_attribution(self, node):
727 def depart_attribution(self, node):
725 self.body.append('\n')
728 self.body.append('\n')
726
729
727 def visit_image(self, node):
730 def visit_image(self, node):
728 self.document.reporter.warning('"image" not supported',
731 self.document.reporter.warning('"image" not supported',
729 base_node=node)
732 base_node=node)
730 text = []
733 text = []
731 if 'alt' in node.attributes:
734 if 'alt' in node.attributes:
732 text.append(node.attributes['alt'])
735 text.append(node.attributes['alt'])
733 if 'uri' in node.attributes:
736 if 'uri' in node.attributes:
734 text.append(node.attributes['uri'])
737 text.append(node.attributes['uri'])
735 self.body.append('[image: %s]\n' % ('/'.join(text)))
738 self.body.append('[image: %s]\n' % ('/'.join(text)))
736 raise nodes.SkipNode
739 raise nodes.SkipNode
737
740
738 def visit_important(self, node):
741 def visit_important(self, node):
739 self.visit_admonition(node, 'important')
742 self.visit_admonition(node, 'important')
740
743
741 depart_important = depart_admonition
744 depart_important = depart_admonition
742
745
743 def visit_label(self, node):
746 def visit_label(self, node):
744 # footnote and citation
747 # footnote and citation
745 if (isinstance(node.parent, nodes.footnote)
748 if (isinstance(node.parent, nodes.footnote)
746 or isinstance(node.parent, nodes.citation)):
749 or isinstance(node.parent, nodes.citation)):
747 raise nodes.SkipNode
750 raise nodes.SkipNode
748 self.document.reporter.warning('"unsupported "label"',
751 self.document.reporter.warning('"unsupported "label"',
749 base_node=node)
752 base_node=node)
750 self.body.append('[')
753 self.body.append('[')
751
754
752 def depart_label(self, node):
755 def depart_label(self, node):
753 self.body.append(']\n')
756 self.body.append(']\n')
754
757
755 def visit_legend(self, node):
758 def visit_legend(self, node):
756 pass
759 pass
757
760
758 def depart_legend(self, node):
761 def depart_legend(self, node):
759 pass
762 pass
760
763
761 # WHAT should we use .INDENT, .UNINDENT ?
764 # WHAT should we use .INDENT, .UNINDENT ?
762 def visit_line_block(self, node):
765 def visit_line_block(self, node):
763 self._line_block += 1
766 self._line_block += 1
764 if self._line_block == 1:
767 if self._line_block == 1:
765 self.body.append('.sp\n')
768 self.body.append('.sp\n')
766 self.body.append('.nf\n')
769 self.body.append('.nf\n')
767 else:
770 else:
768 self.body.append('.in +2\n')
771 self.body.append('.in +2\n')
769
772
770 def depart_line_block(self, node):
773 def depart_line_block(self, node):
771 self._line_block -= 1
774 self._line_block -= 1
772 if self._line_block == 0:
775 if self._line_block == 0:
773 self.body.append('.fi\n')
776 self.body.append('.fi\n')
774 self.body.append('.sp\n')
777 self.body.append('.sp\n')
775 else:
778 else:
776 self.body.append('.in -2\n')
779 self.body.append('.in -2\n')
777
780
778 def visit_line(self, node):
781 def visit_line(self, node):
779 pass
782 pass
780
783
781 def depart_line(self, node):
784 def depart_line(self, node):
782 self.body.append('\n')
785 self.body.append('\n')
783
786
784 def visit_list_item(self, node):
787 def visit_list_item(self, node):
785 # man 7 man argues to use ".IP" instead of ".TP"
788 # man 7 man argues to use ".IP" instead of ".TP"
786 self.body.append('.IP %s %d\n' % (
789 self.body.append('.IP %s %d\n' % (
787 self._list_char[-1].next(),
790 self._list_char[-1].next(),
788 self._list_char[-1].get_width(),))
791 self._list_char[-1].get_width(),))
789
792
790 def depart_list_item(self, node):
793 def depart_list_item(self, node):
791 pass
794 pass
792
795
793 def visit_literal(self, node):
796 def visit_literal(self, node):
794 self.body.append(self.defs['literal'][0])
797 self.body.append(self.defs['literal'][0])
795
798
796 def depart_literal(self, node):
799 def depart_literal(self, node):
797 self.body.append(self.defs['literal'][1])
800 self.body.append(self.defs['literal'][1])
798
801
799 def visit_literal_block(self, node):
802 def visit_literal_block(self, node):
800 self.body.append(self.defs['literal_block'][0])
803 self.body.append(self.defs['literal_block'][0])
801 self._in_literal = True
804 self._in_literal = True
802
805
803 def depart_literal_block(self, node):
806 def depart_literal_block(self, node):
804 self._in_literal = False
807 self._in_literal = False
805 self.body.append(self.defs['literal_block'][1])
808 self.body.append(self.defs['literal_block'][1])
806
809
807 def visit_meta(self, node):
810 def visit_meta(self, node):
808 raise NotImplementedError, node.astext()
811 raise NotImplementedError, node.astext()
809
812
810 def depart_meta(self, node):
813 def depart_meta(self, node):
811 pass
814 pass
812
815
813 def visit_note(self, node):
816 def visit_note(self, node):
814 self.visit_admonition(node, 'note')
817 self.visit_admonition(node, 'note')
815
818
816 depart_note = depart_admonition
819 depart_note = depart_admonition
817
820
818 def indent(self, by=0.5):
821 def indent(self, by=0.5):
819 # if we are in a section ".SH" there already is a .RS
822 # if we are in a section ".SH" there already is a .RS
820 step = self._indent[-1]
823 step = self._indent[-1]
821 self._indent.append(by)
824 self._indent.append(by)
822 self.body.append(self.defs['indent'][0] % step)
825 self.body.append(self.defs['indent'][0] % step)
823
826
824 def dedent(self):
827 def dedent(self):
825 self._indent.pop()
828 self._indent.pop()
826 self.body.append(self.defs['indent'][1])
829 self.body.append(self.defs['indent'][1])
827
830
828 def visit_option_list(self, node):
831 def visit_option_list(self, node):
829 self.indent(OPTION_LIST_INDENT)
832 self.indent(OPTION_LIST_INDENT)
830
833
831 def depart_option_list(self, node):
834 def depart_option_list(self, node):
832 self.dedent()
835 self.dedent()
833
836
834 def visit_option_list_item(self, node):
837 def visit_option_list_item(self, node):
835 # one item of the list
838 # one item of the list
836 self.body.append(self.defs['option_list_item'][0])
839 self.body.append(self.defs['option_list_item'][0])
837
840
838 def depart_option_list_item(self, node):
841 def depart_option_list_item(self, node):
839 self.body.append(self.defs['option_list_item'][1])
842 self.body.append(self.defs['option_list_item'][1])
840
843
841 def visit_option_group(self, node):
844 def visit_option_group(self, node):
842 # as one option could have several forms it is a group
845 # as one option could have several forms it is a group
843 # options without parameter bold only, .B, -v
846 # options without parameter bold only, .B, -v
844 # options with parameter bold italic, .BI, -f file
847 # options with parameter bold italic, .BI, -f file
845 #
848 #
846 # we do not know if .B or .BI
849 # we do not know if .B or .BI
847 self.context.append('.B') # blind guess
850 self.context.append('.B') # blind guess
848 self.context.append(len(self.body)) # to be able to insert later
851 self.context.append(len(self.body)) # to be able to insert later
849 self.context.append(0) # option counter
852 self.context.append(0) # option counter
850
853
851 def depart_option_group(self, node):
854 def depart_option_group(self, node):
852 self.context.pop() # the counter
855 self.context.pop() # the counter
853 start_position = self.context.pop()
856 start_position = self.context.pop()
854 text = self.body[start_position:]
857 text = self.body[start_position:]
855 del self.body[start_position:]
858 del self.body[start_position:]
856 self.body.append('%s%s\n' % (self.context.pop(), ''.join(text)))
859 self.body.append('%s%s\n' % (self.context.pop(), ''.join(text)))
857
860
858 def visit_option(self, node):
861 def visit_option(self, node):
859 # each form of the option will be presented separately
862 # each form of the option will be presented separately
860 if self.context[-1] > 0:
863 if self.context[-1] > 0:
861 self.body.append(', ')
864 self.body.append(', ')
862 if self.context[-3] == '.BI':
865 if self.context[-3] == '.BI':
863 self.body.append('\\')
866 self.body.append('\\')
864 self.body.append(' ')
867 self.body.append(' ')
865
868
866 def depart_option(self, node):
869 def depart_option(self, node):
867 self.context[-1] += 1
870 self.context[-1] += 1
868
871
869 def visit_option_string(self, node):
872 def visit_option_string(self, node):
870 # do not know if .B or .BI
873 # do not know if .B or .BI
871 pass
874 pass
872
875
873 def depart_option_string(self, node):
876 def depart_option_string(self, node):
874 pass
877 pass
875
878
876 def visit_option_argument(self, node):
879 def visit_option_argument(self, node):
877 self.context[-3] = '.BI' # bold/italic alternate
880 self.context[-3] = '.BI' # bold/italic alternate
878 if node['delimiter'] != ' ':
881 if node['delimiter'] != ' ':
879 self.body.append('\\fB%s ' % node['delimiter'])
882 self.body.append('\\fB%s ' % node['delimiter'])
880 elif self.body[len(self.body)-1].endswith('='):
883 elif self.body[len(self.body)-1].endswith('='):
881 # a blank only means no blank in output, just changing font
884 # a blank only means no blank in output, just changing font
882 self.body.append(' ')
885 self.body.append(' ')
883 else:
886 else:
884 # blank backslash blank, switch font then a blank
887 # blank backslash blank, switch font then a blank
885 self.body.append(' \\ ')
888 self.body.append(' \\ ')
886
889
887 def depart_option_argument(self, node):
890 def depart_option_argument(self, node):
888 pass
891 pass
889
892
890 def visit_organization(self, node):
893 def visit_organization(self, node):
891 self.visit_docinfo_item(node, 'organization')
894 self.visit_docinfo_item(node, 'organization')
892
895
893 def depart_organization(self, node):
896 def depart_organization(self, node):
894 pass
897 pass
895
898
896 def visit_paragraph(self, node):
899 def visit_paragraph(self, node):
897 # ``.PP`` : Start standard indented paragraph.
900 # ``.PP`` : Start standard indented paragraph.
898 # ``.LP`` : Start block paragraph, all except the first.
901 # ``.LP`` : Start block paragraph, all except the first.
899 # ``.P [type]`` : Start paragraph type.
902 # ``.P [type]`` : Start paragraph type.
900 # NOTE dont use paragraph starts because they reset indentation.
903 # NOTE dont use paragraph starts because they reset indentation.
901 # ``.sp`` is only vertical space
904 # ``.sp`` is only vertical space
902 self.ensure_eol()
905 self.ensure_eol()
903 self.body.append('.sp\n')
906 self.body.append('.sp\n')
904
907
905 def depart_paragraph(self, node):
908 def depart_paragraph(self, node):
906 self.body.append('\n')
909 self.body.append('\n')
907
910
908 def visit_problematic(self, node):
911 def visit_problematic(self, node):
909 self.body.append(self.defs['problematic'][0])
912 self.body.append(self.defs['problematic'][0])
910
913
911 def depart_problematic(self, node):
914 def depart_problematic(self, node):
912 self.body.append(self.defs['problematic'][1])
915 self.body.append(self.defs['problematic'][1])
913
916
914 def visit_raw(self, node):
917 def visit_raw(self, node):
915 if node.get('format') == 'manpage':
918 if node.get('format') == 'manpage':
916 self.body.append(node.astext() + "\n")
919 self.body.append(node.astext() + "\n")
917 # Keep non-manpage raw text out of output:
920 # Keep non-manpage raw text out of output:
918 raise nodes.SkipNode
921 raise nodes.SkipNode
919
922
920 def visit_reference(self, node):
923 def visit_reference(self, node):
921 """E.g. link or email address."""
924 """E.g. link or email address."""
922 self.body.append(self.defs['reference'][0])
925 self.body.append(self.defs['reference'][0])
923
926
924 def depart_reference(self, node):
927 def depart_reference(self, node):
925 self.body.append(self.defs['reference'][1])
928 self.body.append(self.defs['reference'][1])
926
929
927 def visit_revision(self, node):
930 def visit_revision(self, node):
928 self.visit_docinfo_item(node, 'revision')
931 self.visit_docinfo_item(node, 'revision')
929
932
930 depart_revision = depart_docinfo_item
933 depart_revision = depart_docinfo_item
931
934
932 def visit_row(self, node):
935 def visit_row(self, node):
933 self._active_table.new_row()
936 self._active_table.new_row()
934
937
935 def depart_row(self, node):
938 def depart_row(self, node):
936 pass
939 pass
937
940
938 def visit_section(self, node):
941 def visit_section(self, node):
939 self.section_level += 1
942 self.section_level += 1
940
943
941 def depart_section(self, node):
944 def depart_section(self, node):
942 self.section_level -= 1
945 self.section_level -= 1
943
946
944 def visit_status(self, node):
947 def visit_status(self, node):
945 self.visit_docinfo_item(node, 'status')
948 self.visit_docinfo_item(node, 'status')
946
949
947 depart_status = depart_docinfo_item
950 depart_status = depart_docinfo_item
948
951
949 def visit_strong(self, node):
952 def visit_strong(self, node):
950 self.body.append(self.defs['strong'][0])
953 self.body.append(self.defs['strong'][0])
951
954
952 def depart_strong(self, node):
955 def depart_strong(self, node):
953 self.body.append(self.defs['strong'][1])
956 self.body.append(self.defs['strong'][1])
954
957
955 def visit_substitution_definition(self, node):
958 def visit_substitution_definition(self, node):
956 """Internal only."""
959 """Internal only."""
957 raise nodes.SkipNode
960 raise nodes.SkipNode
958
961
959 def visit_substitution_reference(self, node):
962 def visit_substitution_reference(self, node):
960 self.document.reporter.warning('"substitution_reference" not supported',
963 self.document.reporter.warning('"substitution_reference" not supported',
961 base_node=node)
964 base_node=node)
962
965
963 def visit_subtitle(self, node):
966 def visit_subtitle(self, node):
964 if isinstance(node.parent, nodes.sidebar):
967 if isinstance(node.parent, nodes.sidebar):
965 self.body.append(self.defs['strong'][0])
968 self.body.append(self.defs['strong'][0])
966 elif isinstance(node.parent, nodes.document):
969 elif isinstance(node.parent, nodes.document):
967 self.visit_docinfo_item(node, 'subtitle')
970 self.visit_docinfo_item(node, 'subtitle')
968 elif isinstance(node.parent, nodes.section):
971 elif isinstance(node.parent, nodes.section):
969 self.body.append(self.defs['strong'][0])
972 self.body.append(self.defs['strong'][0])
970
973
971 def depart_subtitle(self, node):
974 def depart_subtitle(self, node):
972 # document subtitle calls SkipNode
975 # document subtitle calls SkipNode
973 self.body.append(self.defs['strong'][1]+'\n.PP\n')
976 self.body.append(self.defs['strong'][1]+'\n.PP\n')
974
977
975 def visit_system_message(self, node):
978 def visit_system_message(self, node):
976 # TODO add report_level
979 # TODO add report_level
977 #if node['level'] < self.document.reporter['writer'].report_level:
980 #if node['level'] < self.document.reporter['writer'].report_level:
978 # Level is too low to display:
981 # Level is too low to display:
979 # raise nodes.SkipNode
982 # raise nodes.SkipNode
980 attr = {}
983 attr = {}
981 backref_text = ''
984 backref_text = ''
982 if node.hasattr('id'):
985 if node.hasattr('id'):
983 attr['name'] = node['id']
986 attr['name'] = node['id']
984 if node.hasattr('line'):
987 if node.hasattr('line'):
985 line = ', line %s' % node['line']
988 line = ', line %s' % node['line']
986 else:
989 else:
987 line = ''
990 line = ''
988 self.body.append('.IP "System Message: %s/%s (%s:%s)"\n'
991 self.body.append('.IP "System Message: %s/%s (%s:%s)"\n'
989 % (node['type'], node['level'], node['source'], line))
992 % (node['type'], node['level'], node['source'], line))
990
993
991 def depart_system_message(self, node):
994 def depart_system_message(self, node):
992 pass
995 pass
993
996
994 def visit_table(self, node):
997 def visit_table(self, node):
995 self._active_table = Table()
998 self._active_table = Table()
996
999
997 def depart_table(self, node):
1000 def depart_table(self, node):
998 self.ensure_eol()
1001 self.ensure_eol()
999 self.body.extend(self._active_table.as_list())
1002 self.body.extend(self._active_table.as_list())
1000 self._active_table = None
1003 self._active_table = None
1001
1004
1002 def visit_target(self, node):
1005 def visit_target(self, node):
1003 # targets are in-document hyper targets, without any use for man-pages.
1006 # targets are in-document hyper targets, without any use for man-pages.
1004 raise nodes.SkipNode
1007 raise nodes.SkipNode
1005
1008
1006 def visit_tbody(self, node):
1009 def visit_tbody(self, node):
1007 pass
1010 pass
1008
1011
1009 def depart_tbody(self, node):
1012 def depart_tbody(self, node):
1010 pass
1013 pass
1011
1014
1012 def visit_term(self, node):
1015 def visit_term(self, node):
1013 self.body.append(self.defs['term'][0])
1016 self.body.append(self.defs['term'][0])
1014
1017
1015 def depart_term(self, node):
1018 def depart_term(self, node):
1016 self.body.append(self.defs['term'][1])
1019 self.body.append(self.defs['term'][1])
1017
1020
1018 def visit_tgroup(self, node):
1021 def visit_tgroup(self, node):
1019 pass
1022 pass
1020
1023
1021 def depart_tgroup(self, node):
1024 def depart_tgroup(self, node):
1022 pass
1025 pass
1023
1026
1024 def visit_thead(self, node):
1027 def visit_thead(self, node):
1025 # MAYBE double line '='
1028 # MAYBE double line '='
1026 pass
1029 pass
1027
1030
1028 def depart_thead(self, node):
1031 def depart_thead(self, node):
1029 # MAYBE double line '='
1032 # MAYBE double line '='
1030 pass
1033 pass
1031
1034
1032 def visit_tip(self, node):
1035 def visit_tip(self, node):
1033 self.visit_admonition(node, 'tip')
1036 self.visit_admonition(node, 'tip')
1034
1037
1035 depart_tip = depart_admonition
1038 depart_tip = depart_admonition
1036
1039
1037 def visit_title(self, node):
1040 def visit_title(self, node):
1038 if isinstance(node.parent, nodes.topic):
1041 if isinstance(node.parent, nodes.topic):
1039 self.body.append(self.defs['topic-title'][0])
1042 self.body.append(self.defs['topic-title'][0])
1040 elif isinstance(node.parent, nodes.sidebar):
1043 elif isinstance(node.parent, nodes.sidebar):
1041 self.body.append(self.defs['sidebar-title'][0])
1044 self.body.append(self.defs['sidebar-title'][0])
1042 elif isinstance(node.parent, nodes.admonition):
1045 elif isinstance(node.parent, nodes.admonition):
1043 self.body.append('.IP "')
1046 self.body.append('.IP "')
1044 elif self.section_level == 0:
1047 elif self.section_level == 0:
1045 self._docinfo['title'] = node.astext()
1048 self._docinfo['title'] = node.astext()
1046 # document title for .TH
1049 # document title for .TH
1047 self._docinfo['title_upper'] = node.astext().upper()
1050 self._docinfo['title_upper'] = node.astext().upper()
1048 raise nodes.SkipNode
1051 raise nodes.SkipNode
1049 elif self.section_level == 1:
1052 elif self.section_level == 1:
1050 self.body.append('.SH ')
1053 self.body.append('.SH ')
1051 for n in node.traverse(nodes.Text):
1054 for n in node.traverse(nodes.Text):
1052 n.parent.replace(n, nodes.Text(n.astext().upper()))
1055 n.parent.replace(n, nodes.Text(n.astext().upper()))
1053 else:
1056 else:
1054 self.body.append('.SS ')
1057 self.body.append('.SS ')
1055
1058
1056 def depart_title(self, node):
1059 def depart_title(self, node):
1057 if isinstance(node.parent, nodes.admonition):
1060 if isinstance(node.parent, nodes.admonition):
1058 self.body.append('"')
1061 self.body.append('"')
1059 self.body.append('\n')
1062 self.body.append('\n')
1060
1063
1061 def visit_title_reference(self, node):
1064 def visit_title_reference(self, node):
1062 """inline citation reference"""
1065 """inline citation reference"""
1063 self.body.append(self.defs['title_reference'][0])
1066 self.body.append(self.defs['title_reference'][0])
1064
1067
1065 def depart_title_reference(self, node):
1068 def depart_title_reference(self, node):
1066 self.body.append(self.defs['title_reference'][1])
1069 self.body.append(self.defs['title_reference'][1])
1067
1070
1068 def visit_topic(self, node):
1071 def visit_topic(self, node):
1069 pass
1072 pass
1070
1073
1071 def depart_topic(self, node):
1074 def depart_topic(self, node):
1072 pass
1075 pass
1073
1076
1074 def visit_sidebar(self, node):
1077 def visit_sidebar(self, node):
1075 pass
1078 pass
1076
1079
1077 def depart_sidebar(self, node):
1080 def depart_sidebar(self, node):
1078 pass
1081 pass
1079
1082
1080 def visit_rubric(self, node):
1083 def visit_rubric(self, node):
1081 pass
1084 pass
1082
1085
1083 def depart_rubric(self, node):
1086 def depart_rubric(self, node):
1084 pass
1087 pass
1085
1088
1086 def visit_transition(self, node):
1089 def visit_transition(self, node):
1087 # .PP Begin a new paragraph and reset prevailing indent.
1090 # .PP Begin a new paragraph and reset prevailing indent.
1088 # .sp N leaves N lines of blank space.
1091 # .sp N leaves N lines of blank space.
1089 # .ce centers the next line
1092 # .ce centers the next line
1090 self.body.append('\n.sp\n.ce\n----\n')
1093 self.body.append('\n.sp\n.ce\n----\n')
1091
1094
1092 def depart_transition(self, node):
1095 def depart_transition(self, node):
1093 self.body.append('\n.ce 0\n.sp\n')
1096 self.body.append('\n.ce 0\n.sp\n')
1094
1097
1095 def visit_version(self, node):
1098 def visit_version(self, node):
1096 self.visit_docinfo_item(node, 'version')
1099 self.visit_docinfo_item(node, 'version')
1097
1100
1098 def visit_warning(self, node):
1101 def visit_warning(self, node):
1099 self.visit_admonition(node, 'warning')
1102 self.visit_admonition(node, 'warning')
1100
1103
1101 depart_warning = depart_admonition
1104 depart_warning = depart_admonition
1102
1105
1103 def unimplemented_visit(self, node):
1106 def unimplemented_visit(self, node):
1104 raise NotImplementedError('visiting unimplemented node type: %s'
1107 raise NotImplementedError('visiting unimplemented node type: %s'
1105 % node.__class__.__name__)
1108 % node.__class__.__name__)
1106
1109
1107 # vim: set fileencoding=utf-8 et ts=4 ai :
1110 # vim: set fileencoding=utf-8 et ts=4 ai :
@@ -1,255 +1,256 b''
1 # changelog.py - changelog class for mercurial
1 # changelog.py - changelog class for mercurial
2 #
2 #
3 # Copyright 2005-2007 Matt Mackall <mpm@selenic.com>
3 # Copyright 2005-2007 Matt Mackall <mpm@selenic.com>
4 #
4 #
5 # This software may be used and distributed according to the terms of the
5 # This software may be used and distributed according to the terms of the
6 # GNU General Public License version 2 or any later version.
6 # GNU General Public License version 2 or any later version.
7
7
8 from node import bin, hex, nullid
8 from node import bin, hex, nullid
9 from i18n import _
9 from i18n import _
10 import util, error, revlog, encoding
10 import util, error, revlog, encoding
11
11
12 _defaultextra = {'branch': 'default'}
12 _defaultextra = {'branch': 'default'}
13
13
14 def _string_escape(text):
14 def _string_escape(text):
15 """
15 """
16 >>> d = {'nl': chr(10), 'bs': chr(92), 'cr': chr(13), 'nul': chr(0)}
16 >>> d = {'nl': chr(10), 'bs': chr(92), 'cr': chr(13), 'nul': chr(0)}
17 >>> s = "ab%(nl)scd%(bs)s%(bs)sn%(nul)sab%(cr)scd%(bs)s%(nl)s" % d
17 >>> s = "ab%(nl)scd%(bs)s%(bs)sn%(nul)sab%(cr)scd%(bs)s%(nl)s" % d
18 >>> s
18 >>> s
19 'ab\\ncd\\\\\\\\n\\x00ab\\rcd\\\\\\n'
19 'ab\\ncd\\\\\\\\n\\x00ab\\rcd\\\\\\n'
20 >>> res = _string_escape(s)
20 >>> res = _string_escape(s)
21 >>> s == res.decode('string_escape')
21 >>> s == res.decode('string_escape')
22 True
22 True
23 """
23 """
24 # subset of the string_escape codec
24 # subset of the string_escape codec
25 text = text.replace('\\', '\\\\').replace('\n', '\\n').replace('\r', '\\r')
25 text = text.replace('\\', '\\\\').replace('\n', '\\n').replace('\r', '\\r')
26 return text.replace('\0', '\\0')
26 return text.replace('\0', '\\0')
27
27
28 def decodeextra(text):
28 def decodeextra(text):
29 """
29 """
30 >>> decodeextra(encodeextra({'foo': 'bar', 'baz': chr(0) + '2'}))
30 >>> decodeextra(encodeextra({'foo': 'bar', 'baz': chr(0) + '2'}))
31 {'foo': 'bar', 'baz': '\\x002', 'branch': 'default'}
31 {'foo': 'bar', 'baz': '\\x002', 'branch': 'default'}
32 >>> decodeextra(encodeextra({'foo': 'bar', 'baz': chr(92) + chr(0) + '2'}))
32 >>> decodeextra(encodeextra({'foo': 'bar', 'baz': chr(92) + chr(0) + '2'}))
33 {'foo': 'bar', 'baz': '\\\\\\x002', 'branch': 'default'}
33 {'foo': 'bar', 'baz': '\\\\\\x002', 'branch': 'default'}
34 """
34 """
35 extra = _defaultextra.copy()
35 extra = _defaultextra.copy()
36 for l in text.split('\0'):
36 for l in text.split('\0'):
37 if l:
37 if l:
38 if '\\0' in l:
38 if '\\0' in l:
39 # fix up \0 without getting into trouble with \\0
39 # fix up \0 without getting into trouble with \\0
40 l = l.replace('\\\\', '\\\\\n')
40 l = l.replace('\\\\', '\\\\\n')
41 l = l.replace('\\0', '\0')
41 l = l.replace('\\0', '\0')
42 l = l.replace('\n', '')
42 l = l.replace('\n', '')
43 k, v = l.decode('string_escape').split(':', 1)
43 k, v = l.decode('string_escape').split(':', 1)
44 extra[k] = v
44 extra[k] = v
45 return extra
45 return extra
46
46
47 def encodeextra(d):
47 def encodeextra(d):
48 # keys must be sorted to produce a deterministic changelog entry
48 # keys must be sorted to produce a deterministic changelog entry
49 items = [_string_escape('%s:%s' % (k, d[k])) for k in sorted(d)]
49 items = [_string_escape('%s:%s' % (k, d[k])) for k in sorted(d)]
50 return "\0".join(items)
50 return "\0".join(items)
51
51
52 class appender(object):
52 class appender(object):
53 '''the changelog index must be updated last on disk, so we use this class
53 '''the changelog index must be updated last on disk, so we use this class
54 to delay writes to it'''
54 to delay writes to it'''
55 def __init__(self, fp, buf):
55 def __init__(self, fp, buf):
56 self.data = buf
56 self.data = buf
57 self.fp = fp
57 self.fp = fp
58 self.offset = fp.tell()
58 self.offset = fp.tell()
59 self.size = util.fstat(fp).st_size
59 self.size = util.fstat(fp).st_size
60
60
61 def end(self):
61 def end(self):
62 return self.size + len("".join(self.data))
62 return self.size + len("".join(self.data))
63 def tell(self):
63 def tell(self):
64 return self.offset
64 return self.offset
65 def flush(self):
65 def flush(self):
66 pass
66 pass
67 def close(self):
67 def close(self):
68 self.fp.close()
68 self.fp.close()
69
69
70 def seek(self, offset, whence=0):
70 def seek(self, offset, whence=0):
71 '''virtual file offset spans real file and data'''
71 '''virtual file offset spans real file and data'''
72 if whence == 0:
72 if whence == 0:
73 self.offset = offset
73 self.offset = offset
74 elif whence == 1:
74 elif whence == 1:
75 self.offset += offset
75 self.offset += offset
76 elif whence == 2:
76 elif whence == 2:
77 self.offset = self.end() + offset
77 self.offset = self.end() + offset
78 if self.offset < self.size:
78 if self.offset < self.size:
79 self.fp.seek(self.offset)
79 self.fp.seek(self.offset)
80
80
81 def read(self, count=-1):
81 def read(self, count=-1):
82 '''only trick here is reads that span real file and data'''
82 '''only trick here is reads that span real file and data'''
83 ret = ""
83 ret = ""
84 if self.offset < self.size:
84 if self.offset < self.size:
85 s = self.fp.read(count)
85 s = self.fp.read(count)
86 ret = s
86 ret = s
87 self.offset += len(s)
87 self.offset += len(s)
88 if count > 0:
88 if count > 0:
89 count -= len(s)
89 count -= len(s)
90 if count != 0:
90 if count != 0:
91 doff = self.offset - self.size
91 doff = self.offset - self.size
92 self.data.insert(0, "".join(self.data))
92 self.data.insert(0, "".join(self.data))
93 del self.data[1:]
93 del self.data[1:]
94 s = self.data[0][doff:doff + count]
94 s = self.data[0][doff:doff + count]
95 self.offset += len(s)
95 self.offset += len(s)
96 ret += s
96 ret += s
97 return ret
97 return ret
98
98
99 def write(self, s):
99 def write(self, s):
100 self.data.append(str(s))
100 self.data.append(str(s))
101 self.offset += len(s)
101 self.offset += len(s)
102
102
103 def delayopener(opener, target, divert, buf):
103 def delayopener(opener, target, divert, buf):
104 def o(name, mode='r'):
104 def o(name, mode='r'):
105 if name != target:
105 if name != target:
106 return opener(name, mode)
106 return opener(name, mode)
107 if divert:
107 if divert:
108 return opener(name + ".a", mode.replace('a', 'w'))
108 return opener(name + ".a", mode.replace('a', 'w'))
109 # otherwise, divert to memory
109 # otherwise, divert to memory
110 return appender(opener(name, mode), buf)
110 return appender(opener(name, mode), buf)
111 return o
111 return o
112
112
113 class changelog(revlog.revlog):
113 class changelog(revlog.revlog):
114 def __init__(self, opener):
114 def __init__(self, opener):
115 revlog.revlog.__init__(self, opener, "00changelog.i")
115 revlog.revlog.__init__(self, opener, "00changelog.i")
116 if self._initempty:
116 if self._initempty:
117 # changelogs don't benefit from generaldelta
117 # changelogs don't benefit from generaldelta
118 self.version &= ~revlog.REVLOGGENERALDELTA
118 self.version &= ~revlog.REVLOGGENERALDELTA
119 self._generaldelta = False
119 self._generaldelta = False
120 self._realopener = opener
120 self._realopener = opener
121 self._delayed = False
121 self._delayed = False
122 self._divert = False
122 self._divert = False
123 # hiddenrevs: revs that should be hidden by command and tools
123 # hiddenrevs: revs that should be hidden by command and tools
124 self.hiddenrevs = set()
124 self.hiddenrevs = set()
125
125
126 def delayupdate(self):
126 def delayupdate(self):
127 "delay visibility of index updates to other readers"
127 "delay visibility of index updates to other readers"
128 self._delayed = True
128 self._delayed = True
129 self._divert = (len(self) == 0)
129 self._divert = (len(self) == 0)
130 self._delaybuf = []
130 self._delaybuf = []
131 self.opener = delayopener(self._realopener, self.indexfile,
131 self.opener = delayopener(self._realopener, self.indexfile,
132 self._divert, self._delaybuf)
132 self._divert, self._delaybuf)
133
133
134 def finalize(self, tr):
134 def finalize(self, tr):
135 "finalize index updates"
135 "finalize index updates"
136 self._delayed = False
136 self._delayed = False
137 self.opener = self._realopener
137 self.opener = self._realopener
138 # move redirected index data back into place
138 # move redirected index data back into place
139 if self._divert:
139 if self._divert:
140 nfile = self.opener(self.indexfile + ".a")
140 nfile = self.opener(self.indexfile + ".a")
141 n = nfile.name
141 n = nfile.name
142 nfile.close()
142 nfile.close()
143 util.rename(n, n[:-2])
143 util.rename(n, n[:-2])
144 elif self._delaybuf:
144 elif self._delaybuf:
145 fp = self.opener(self.indexfile, 'a')
145 fp = self.opener(self.indexfile, 'a')
146 fp.write("".join(self._delaybuf))
146 fp.write("".join(self._delaybuf))
147 fp.close()
147 fp.close()
148 self._delaybuf = []
148 self._delaybuf = []
149 # split when we're done
149 # split when we're done
150 self.checkinlinesize(tr)
150 self.checkinlinesize(tr)
151
151
152 def readpending(self, file):
152 def readpending(self, file):
153 r = revlog.revlog(self.opener, file)
153 r = revlog.revlog(self.opener, file)
154 self.index = r.index
154 self.index = r.index
155 self.nodemap = r.nodemap
155 self.nodemap = r.nodemap
156 self._nodecache = r._nodecache
156 self._chunkcache = r._chunkcache
157 self._chunkcache = r._chunkcache
157
158
158 def writepending(self):
159 def writepending(self):
159 "create a file containing the unfinalized state for pretxnchangegroup"
160 "create a file containing the unfinalized state for pretxnchangegroup"
160 if self._delaybuf:
161 if self._delaybuf:
161 # make a temporary copy of the index
162 # make a temporary copy of the index
162 fp1 = self._realopener(self.indexfile)
163 fp1 = self._realopener(self.indexfile)
163 fp2 = self._realopener(self.indexfile + ".a", "w")
164 fp2 = self._realopener(self.indexfile + ".a", "w")
164 fp2.write(fp1.read())
165 fp2.write(fp1.read())
165 # add pending data
166 # add pending data
166 fp2.write("".join(self._delaybuf))
167 fp2.write("".join(self._delaybuf))
167 fp2.close()
168 fp2.close()
168 # switch modes so finalize can simply rename
169 # switch modes so finalize can simply rename
169 self._delaybuf = []
170 self._delaybuf = []
170 self._divert = True
171 self._divert = True
171
172
172 if self._divert:
173 if self._divert:
173 return True
174 return True
174
175
175 return False
176 return False
176
177
177 def checkinlinesize(self, tr, fp=None):
178 def checkinlinesize(self, tr, fp=None):
178 if not self._delayed:
179 if not self._delayed:
179 revlog.revlog.checkinlinesize(self, tr, fp)
180 revlog.revlog.checkinlinesize(self, tr, fp)
180
181
181 def read(self, node):
182 def read(self, node):
182 """
183 """
183 format used:
184 format used:
184 nodeid\n : manifest node in ascii
185 nodeid\n : manifest node in ascii
185 user\n : user, no \n or \r allowed
186 user\n : user, no \n or \r allowed
186 time tz extra\n : date (time is int or float, timezone is int)
187 time tz extra\n : date (time is int or float, timezone is int)
187 : extra is metadatas, encoded and separated by '\0'
188 : extra is metadatas, encoded and separated by '\0'
188 : older versions ignore it
189 : older versions ignore it
189 files\n\n : files modified by the cset, no \n or \r allowed
190 files\n\n : files modified by the cset, no \n or \r allowed
190 (.*) : comment (free text, ideally utf-8)
191 (.*) : comment (free text, ideally utf-8)
191
192
192 changelog v0 doesn't use extra
193 changelog v0 doesn't use extra
193 """
194 """
194 text = self.revision(node)
195 text = self.revision(node)
195 if not text:
196 if not text:
196 return (nullid, "", (0, 0), [], "", _defaultextra)
197 return (nullid, "", (0, 0), [], "", _defaultextra)
197 last = text.index("\n\n")
198 last = text.index("\n\n")
198 desc = encoding.tolocal(text[last + 2:])
199 desc = encoding.tolocal(text[last + 2:])
199 l = text[:last].split('\n')
200 l = text[:last].split('\n')
200 manifest = bin(l[0])
201 manifest = bin(l[0])
201 user = encoding.tolocal(l[1])
202 user = encoding.tolocal(l[1])
202
203
203 tdata = l[2].split(' ', 2)
204 tdata = l[2].split(' ', 2)
204 if len(tdata) != 3:
205 if len(tdata) != 3:
205 time = float(tdata[0])
206 time = float(tdata[0])
206 try:
207 try:
207 # various tools did silly things with the time zone field.
208 # various tools did silly things with the time zone field.
208 timezone = int(tdata[1])
209 timezone = int(tdata[1])
209 except ValueError:
210 except ValueError:
210 timezone = 0
211 timezone = 0
211 extra = _defaultextra
212 extra = _defaultextra
212 else:
213 else:
213 time, timezone = float(tdata[0]), int(tdata[1])
214 time, timezone = float(tdata[0]), int(tdata[1])
214 extra = decodeextra(tdata[2])
215 extra = decodeextra(tdata[2])
215
216
216 files = l[3:]
217 files = l[3:]
217 return (manifest, user, (time, timezone), files, desc, extra)
218 return (manifest, user, (time, timezone), files, desc, extra)
218
219
219 def add(self, manifest, files, desc, transaction, p1, p2,
220 def add(self, manifest, files, desc, transaction, p1, p2,
220 user, date=None, extra=None):
221 user, date=None, extra=None):
221 # Convert to UTF-8 encoded bytestrings as the very first
222 # Convert to UTF-8 encoded bytestrings as the very first
222 # thing: calling any method on a localstr object will turn it
223 # thing: calling any method on a localstr object will turn it
223 # into a str object and the cached UTF-8 string is thus lost.
224 # into a str object and the cached UTF-8 string is thus lost.
224 user, desc = encoding.fromlocal(user), encoding.fromlocal(desc)
225 user, desc = encoding.fromlocal(user), encoding.fromlocal(desc)
225
226
226 user = user.strip()
227 user = user.strip()
227 # An empty username or a username with a "\n" will make the
228 # An empty username or a username with a "\n" will make the
228 # revision text contain two "\n\n" sequences -> corrupt
229 # revision text contain two "\n\n" sequences -> corrupt
229 # repository since read cannot unpack the revision.
230 # repository since read cannot unpack the revision.
230 if not user:
231 if not user:
231 raise error.RevlogError(_("empty username"))
232 raise error.RevlogError(_("empty username"))
232 if "\n" in user:
233 if "\n" in user:
233 raise error.RevlogError(_("username %s contains a newline")
234 raise error.RevlogError(_("username %s contains a newline")
234 % repr(user))
235 % repr(user))
235
236
236 # strip trailing whitespace and leading and trailing empty lines
237 # strip trailing whitespace and leading and trailing empty lines
237 desc = '\n'.join([l.rstrip() for l in desc.splitlines()]).strip('\n')
238 desc = '\n'.join([l.rstrip() for l in desc.splitlines()]).strip('\n')
238
239
239 if date:
240 if date:
240 parseddate = "%d %d" % util.parsedate(date)
241 parseddate = "%d %d" % util.parsedate(date)
241 else:
242 else:
242 parseddate = "%d %d" % util.makedate()
243 parseddate = "%d %d" % util.makedate()
243 if extra:
244 if extra:
244 branch = extra.get("branch")
245 branch = extra.get("branch")
245 if branch in ("default", ""):
246 if branch in ("default", ""):
246 del extra["branch"]
247 del extra["branch"]
247 elif branch in (".", "null", "tip"):
248 elif branch in (".", "null", "tip"):
248 raise error.RevlogError(_('the name \'%s\' is reserved')
249 raise error.RevlogError(_('the name \'%s\' is reserved')
249 % branch)
250 % branch)
250 if extra:
251 if extra:
251 extra = encodeextra(extra)
252 extra = encodeextra(extra)
252 parseddate = "%s %s" % (parseddate, extra)
253 parseddate = "%s %s" % (parseddate, extra)
253 l = [hex(manifest), user, parseddate] + sorted(files) + ["", desc]
254 l = [hex(manifest), user, parseddate] + sorted(files) + ["", desc]
254 text = "\n".join(l)
255 text = "\n".join(l)
255 return self.addrevision(text, transaction, len(self), p1, p2)
256 return self.addrevision(text, transaction, len(self), p1, p2)
@@ -1,776 +1,786 b''
1 # dispatch.py - command dispatching for mercurial
1 # dispatch.py - command dispatching for mercurial
2 #
2 #
3 # Copyright 2005-2007 Matt Mackall <mpm@selenic.com>
3 # Copyright 2005-2007 Matt Mackall <mpm@selenic.com>
4 #
4 #
5 # This software may be used and distributed according to the terms of the
5 # This software may be used and distributed according to the terms of the
6 # GNU General Public License version 2 or any later version.
6 # GNU General Public License version 2 or any later version.
7
7
8 from i18n import _
8 from i18n import _
9 import os, sys, atexit, signal, pdb, socket, errno, shlex, time, traceback, re
9 import os, sys, atexit, signal, pdb, socket, errno, shlex, time, traceback, re
10 import util, commands, hg, fancyopts, extensions, hook, error
10 import util, commands, hg, fancyopts, extensions, hook, error
11 import cmdutil, encoding
11 import cmdutil, encoding
12 import ui as uimod
12 import ui as uimod
13
13
14 class request(object):
14 class request(object):
15 def __init__(self, args, ui=None, repo=None, fin=None, fout=None, ferr=None):
15 def __init__(self, args, ui=None, repo=None, fin=None, fout=None, ferr=None):
16 self.args = args
16 self.args = args
17 self.ui = ui
17 self.ui = ui
18 self.repo = repo
18 self.repo = repo
19
19
20 # input/output/error streams
20 # input/output/error streams
21 self.fin = fin
21 self.fin = fin
22 self.fout = fout
22 self.fout = fout
23 self.ferr = ferr
23 self.ferr = ferr
24
24
25 def run():
25 def run():
26 "run the command in sys.argv"
26 "run the command in sys.argv"
27 sys.exit((dispatch(request(sys.argv[1:])) or 0) & 255)
27 sys.exit((dispatch(request(sys.argv[1:])) or 0) & 255)
28
28
29 def dispatch(req):
29 def dispatch(req):
30 "run the command specified in req.args"
30 "run the command specified in req.args"
31 if req.ferr:
31 if req.ferr:
32 ferr = req.ferr
32 ferr = req.ferr
33 elif req.ui:
33 elif req.ui:
34 ferr = req.ui.ferr
34 ferr = req.ui.ferr
35 else:
35 else:
36 ferr = sys.stderr
36 ferr = sys.stderr
37
37
38 try:
38 try:
39 if not req.ui:
39 if not req.ui:
40 req.ui = uimod.ui()
40 req.ui = uimod.ui()
41 if '--traceback' in req.args:
41 if '--traceback' in req.args:
42 req.ui.setconfig('ui', 'traceback', 'on')
42 req.ui.setconfig('ui', 'traceback', 'on')
43
43
44 # set ui streams from the request
44 # set ui streams from the request
45 if req.fin:
45 if req.fin:
46 req.ui.fin = req.fin
46 req.ui.fin = req.fin
47 if req.fout:
47 if req.fout:
48 req.ui.fout = req.fout
48 req.ui.fout = req.fout
49 if req.ferr:
49 if req.ferr:
50 req.ui.ferr = req.ferr
50 req.ui.ferr = req.ferr
51 except util.Abort, inst:
51 except util.Abort, inst:
52 ferr.write(_("abort: %s\n") % inst)
52 ferr.write(_("abort: %s\n") % inst)
53 if inst.hint:
53 if inst.hint:
54 ferr.write(_("(%s)\n") % inst.hint)
54 ferr.write(_("(%s)\n") % inst.hint)
55 return -1
55 return -1
56 except error.ParseError, inst:
56 except error.ParseError, inst:
57 if len(inst.args) > 1:
57 if len(inst.args) > 1:
58 ferr.write(_("hg: parse error at %s: %s\n") %
58 ferr.write(_("hg: parse error at %s: %s\n") %
59 (inst.args[1], inst.args[0]))
59 (inst.args[1], inst.args[0]))
60 else:
60 else:
61 ferr.write(_("hg: parse error: %s\n") % inst.args[0])
61 ferr.write(_("hg: parse error: %s\n") % inst.args[0])
62 return -1
62 return -1
63
63
64 return _runcatch(req)
64 return _runcatch(req)
65
65
66 def _runcatch(req):
66 def _runcatch(req):
67 def catchterm(*args):
67 def catchterm(*args):
68 raise error.SignalInterrupt
68 raise error.SignalInterrupt
69
69
70 ui = req.ui
70 ui = req.ui
71 try:
71 try:
72 for name in 'SIGBREAK', 'SIGHUP', 'SIGTERM':
72 for name in 'SIGBREAK', 'SIGHUP', 'SIGTERM':
73 num = getattr(signal, name, None)
73 num = getattr(signal, name, None)
74 if num:
74 if num:
75 signal.signal(num, catchterm)
75 signal.signal(num, catchterm)
76 except ValueError:
76 except ValueError:
77 pass # happens if called in a thread
77 pass # happens if called in a thread
78
78
79 try:
79 try:
80 try:
80 try:
81 # enter the debugger before command execution
81 # enter the debugger before command execution
82 if '--debugger' in req.args:
82 if '--debugger' in req.args:
83 ui.warn(_("entering debugger - "
83 ui.warn(_("entering debugger - "
84 "type c to continue starting hg or h for help\n"))
84 "type c to continue starting hg or h for help\n"))
85 pdb.set_trace()
85 pdb.set_trace()
86 try:
86 try:
87 return _dispatch(req)
87 return _dispatch(req)
88 finally:
88 finally:
89 ui.flush()
89 ui.flush()
90 except:
90 except:
91 # enter the debugger when we hit an exception
91 # enter the debugger when we hit an exception
92 if '--debugger' in req.args:
92 if '--debugger' in req.args:
93 traceback.print_exc()
93 traceback.print_exc()
94 pdb.post_mortem(sys.exc_info()[2])
94 pdb.post_mortem(sys.exc_info()[2])
95 ui.traceback()
95 ui.traceback()
96 raise
96 raise
97
97
98 # Global exception handling, alphabetically
98 # Global exception handling, alphabetically
99 # Mercurial-specific first, followed by built-in and library exceptions
99 # Mercurial-specific first, followed by built-in and library exceptions
100 except error.AmbiguousCommand, inst:
100 except error.AmbiguousCommand, inst:
101 ui.warn(_("hg: command '%s' is ambiguous:\n %s\n") %
101 ui.warn(_("hg: command '%s' is ambiguous:\n %s\n") %
102 (inst.args[0], " ".join(inst.args[1])))
102 (inst.args[0], " ".join(inst.args[1])))
103 except error.ParseError, inst:
103 except error.ParseError, inst:
104 if len(inst.args) > 1:
104 if len(inst.args) > 1:
105 ui.warn(_("hg: parse error at %s: %s\n") %
105 ui.warn(_("hg: parse error at %s: %s\n") %
106 (inst.args[1], inst.args[0]))
106 (inst.args[1], inst.args[0]))
107 else:
107 else:
108 ui.warn(_("hg: parse error: %s\n") % inst.args[0])
108 ui.warn(_("hg: parse error: %s\n") % inst.args[0])
109 return -1
109 return -1
110 except error.LockHeld, inst:
110 except error.LockHeld, inst:
111 if inst.errno == errno.ETIMEDOUT:
111 if inst.errno == errno.ETIMEDOUT:
112 reason = _('timed out waiting for lock held by %s') % inst.locker
112 reason = _('timed out waiting for lock held by %s') % inst.locker
113 else:
113 else:
114 reason = _('lock held by %s') % inst.locker
114 reason = _('lock held by %s') % inst.locker
115 ui.warn(_("abort: %s: %s\n") % (inst.desc or inst.filename, reason))
115 ui.warn(_("abort: %s: %s\n") % (inst.desc or inst.filename, reason))
116 except error.LockUnavailable, inst:
116 except error.LockUnavailable, inst:
117 ui.warn(_("abort: could not lock %s: %s\n") %
117 ui.warn(_("abort: could not lock %s: %s\n") %
118 (inst.desc or inst.filename, inst.strerror))
118 (inst.desc or inst.filename, inst.strerror))
119 except error.CommandError, inst:
119 except error.CommandError, inst:
120 if inst.args[0]:
120 if inst.args[0]:
121 ui.warn(_("hg %s: %s\n") % (inst.args[0], inst.args[1]))
121 ui.warn(_("hg %s: %s\n") % (inst.args[0], inst.args[1]))
122 commands.help_(ui, inst.args[0], full=False, command=True)
122 commands.help_(ui, inst.args[0], full=False, command=True)
123 else:
123 else:
124 ui.warn(_("hg: %s\n") % inst.args[1])
124 ui.warn(_("hg: %s\n") % inst.args[1])
125 commands.help_(ui, 'shortlist')
125 commands.help_(ui, 'shortlist')
126 except error.OutOfBandError, inst:
126 except error.OutOfBandError, inst:
127 ui.warn(_("abort: remote error:\n"))
127 ui.warn(_("abort: remote error:\n"))
128 ui.warn(''.join(inst.args))
128 ui.warn(''.join(inst.args))
129 except error.RepoError, inst:
129 except error.RepoError, inst:
130 ui.warn(_("abort: %s!\n") % inst)
130 ui.warn(_("abort: %s!\n") % inst)
131 if inst.hint:
131 if inst.hint:
132 ui.warn(_("(%s)\n") % inst.hint)
132 ui.warn(_("(%s)\n") % inst.hint)
133 except error.ResponseError, inst:
133 except error.ResponseError, inst:
134 ui.warn(_("abort: %s") % inst.args[0])
134 ui.warn(_("abort: %s") % inst.args[0])
135 if not isinstance(inst.args[1], basestring):
135 if not isinstance(inst.args[1], basestring):
136 ui.warn(" %r\n" % (inst.args[1],))
136 ui.warn(" %r\n" % (inst.args[1],))
137 elif not inst.args[1]:
137 elif not inst.args[1]:
138 ui.warn(_(" empty string\n"))
138 ui.warn(_(" empty string\n"))
139 else:
139 else:
140 ui.warn("\n%r\n" % util.ellipsis(inst.args[1]))
140 ui.warn("\n%r\n" % util.ellipsis(inst.args[1]))
141 except error.RevlogError, inst:
141 except error.RevlogError, inst:
142 ui.warn(_("abort: %s!\n") % inst)
142 ui.warn(_("abort: %s!\n") % inst)
143 except error.SignalInterrupt:
143 except error.SignalInterrupt:
144 ui.warn(_("killed!\n"))
144 ui.warn(_("killed!\n"))
145 except error.UnknownCommand, inst:
145 except error.UnknownCommand, inst:
146 ui.warn(_("hg: unknown command '%s'\n") % inst.args[0])
146 ui.warn(_("hg: unknown command '%s'\n") % inst.args[0])
147 try:
147 try:
148 # check if the command is in a disabled extension
148 # check if the command is in a disabled extension
149 # (but don't check for extensions themselves)
149 # (but don't check for extensions themselves)
150 commands.help_(ui, inst.args[0], unknowncmd=True)
150 commands.help_(ui, inst.args[0], unknowncmd=True)
151 except error.UnknownCommand:
151 except error.UnknownCommand:
152 commands.help_(ui, 'shortlist')
152 commands.help_(ui, 'shortlist')
153 except util.Abort, inst:
153 except util.Abort, inst:
154 ui.warn(_("abort: %s\n") % inst)
154 ui.warn(_("abort: %s\n") % inst)
155 if inst.hint:
155 if inst.hint:
156 ui.warn(_("(%s)\n") % inst.hint)
156 ui.warn(_("(%s)\n") % inst.hint)
157 except ImportError, inst:
157 except ImportError, inst:
158 ui.warn(_("abort: %s!\n") % inst)
158 ui.warn(_("abort: %s!\n") % inst)
159 m = str(inst).split()[-1]
159 m = str(inst).split()[-1]
160 if m in "mpatch bdiff".split():
160 if m in "mpatch bdiff".split():
161 ui.warn(_("(did you forget to compile extensions?)\n"))
161 ui.warn(_("(did you forget to compile extensions?)\n"))
162 elif m in "zlib".split():
162 elif m in "zlib".split():
163 ui.warn(_("(is your Python install correct?)\n"))
163 ui.warn(_("(is your Python install correct?)\n"))
164 except IOError, inst:
164 except IOError, inst:
165 if util.safehasattr(inst, "code"):
165 if util.safehasattr(inst, "code"):
166 ui.warn(_("abort: %s\n") % inst)
166 ui.warn(_("abort: %s\n") % inst)
167 elif util.safehasattr(inst, "reason"):
167 elif util.safehasattr(inst, "reason"):
168 try: # usually it is in the form (errno, strerror)
168 try: # usually it is in the form (errno, strerror)
169 reason = inst.reason.args[1]
169 reason = inst.reason.args[1]
170 except (AttributeError, IndexError):
170 except (AttributeError, IndexError):
171 # it might be anything, for example a string
171 # it might be anything, for example a string
172 reason = inst.reason
172 reason = inst.reason
173 ui.warn(_("abort: error: %s\n") % reason)
173 ui.warn(_("abort: error: %s\n") % reason)
174 elif util.safehasattr(inst, "args") and inst.args[0] == errno.EPIPE:
174 elif util.safehasattr(inst, "args") and inst.args[0] == errno.EPIPE:
175 if ui.debugflag:
175 if ui.debugflag:
176 ui.warn(_("broken pipe\n"))
176 ui.warn(_("broken pipe\n"))
177 elif getattr(inst, "strerror", None):
177 elif getattr(inst, "strerror", None):
178 if getattr(inst, "filename", None):
178 if getattr(inst, "filename", None):
179 ui.warn(_("abort: %s: %s\n") % (inst.strerror, inst.filename))
179 ui.warn(_("abort: %s: %s\n") % (inst.strerror, inst.filename))
180 else:
180 else:
181 ui.warn(_("abort: %s\n") % inst.strerror)
181 ui.warn(_("abort: %s\n") % inst.strerror)
182 else:
182 else:
183 raise
183 raise
184 except OSError, inst:
184 except OSError, inst:
185 if getattr(inst, "filename", None):
185 if getattr(inst, "filename", None):
186 ui.warn(_("abort: %s: %s\n") % (inst.strerror, inst.filename))
186 ui.warn(_("abort: %s: %s\n") % (inst.strerror, inst.filename))
187 else:
187 else:
188 ui.warn(_("abort: %s\n") % inst.strerror)
188 ui.warn(_("abort: %s\n") % inst.strerror)
189 except KeyboardInterrupt:
189 except KeyboardInterrupt:
190 try:
190 try:
191 ui.warn(_("interrupted!\n"))
191 ui.warn(_("interrupted!\n"))
192 except IOError, inst:
192 except IOError, inst:
193 if inst.errno == errno.EPIPE:
193 if inst.errno == errno.EPIPE:
194 if ui.debugflag:
194 if ui.debugflag:
195 ui.warn(_("\nbroken pipe\n"))
195 ui.warn(_("\nbroken pipe\n"))
196 else:
196 else:
197 raise
197 raise
198 except MemoryError:
198 except MemoryError:
199 ui.warn(_("abort: out of memory\n"))
199 ui.warn(_("abort: out of memory\n"))
200 except SystemExit, inst:
200 except SystemExit, inst:
201 # Commands shouldn't sys.exit directly, but give a return code.
201 # Commands shouldn't sys.exit directly, but give a return code.
202 # Just in case catch this and and pass exit code to caller.
202 # Just in case catch this and and pass exit code to caller.
203 return inst.code
203 return inst.code
204 except socket.error, inst:
204 except socket.error, inst:
205 ui.warn(_("abort: %s\n") % inst.args[-1])
205 ui.warn(_("abort: %s\n") % inst.args[-1])
206 except:
206 except:
207 ui.warn(_("** unknown exception encountered,"
207 ui.warn(_("** unknown exception encountered,"
208 " please report by visiting\n"))
208 " please report by visiting\n"))
209 ui.warn(_("** http://mercurial.selenic.com/wiki/BugTracker\n"))
209 ui.warn(_("** http://mercurial.selenic.com/wiki/BugTracker\n"))
210 ui.warn(_("** Python %s\n") % sys.version.replace('\n', ''))
210 ui.warn(_("** Python %s\n") % sys.version.replace('\n', ''))
211 ui.warn(_("** Mercurial Distributed SCM (version %s)\n")
211 ui.warn(_("** Mercurial Distributed SCM (version %s)\n")
212 % util.version())
212 % util.version())
213 ui.warn(_("** Extensions loaded: %s\n")
213 ui.warn(_("** Extensions loaded: %s\n")
214 % ", ".join([x[0] for x in extensions.extensions()]))
214 % ", ".join([x[0] for x in extensions.extensions()]))
215 raise
215 raise
216
216
217 return -1
217 return -1
218
218
219 def aliasargs(fn, givenargs):
219 def aliasargs(fn, givenargs):
220 args = getattr(fn, 'args', [])
220 args = getattr(fn, 'args', [])
221 if args:
221 if args:
222 cmd = ' '.join(map(util.shellquote, args))
222 cmd = ' '.join(map(util.shellquote, args))
223
223
224 nums = []
224 nums = []
225 def replacer(m):
225 def replacer(m):
226 num = int(m.group(1)) - 1
226 num = int(m.group(1)) - 1
227 nums.append(num)
227 nums.append(num)
228 if num < len(givenargs):
228 if num < len(givenargs):
229 return givenargs[num]
229 return givenargs[num]
230 raise util.Abort(_('too few arguments for command alias'))
230 raise util.Abort(_('too few arguments for command alias'))
231 cmd = re.sub(r'\$(\d+|\$)', replacer, cmd)
231 cmd = re.sub(r'\$(\d+|\$)', replacer, cmd)
232 givenargs = [x for i, x in enumerate(givenargs)
232 givenargs = [x for i, x in enumerate(givenargs)
233 if i not in nums]
233 if i not in nums]
234 args = shlex.split(cmd)
234 args = shlex.split(cmd)
235 return args + givenargs
235 return args + givenargs
236
236
237 class cmdalias(object):
237 class cmdalias(object):
238 def __init__(self, name, definition, cmdtable):
238 def __init__(self, name, definition, cmdtable):
239 self.name = self.cmd = name
239 self.name = self.cmd = name
240 self.cmdname = ''
240 self.cmdname = ''
241 self.definition = definition
241 self.definition = definition
242 self.args = []
242 self.args = []
243 self.opts = []
243 self.opts = []
244 self.help = ''
244 self.help = ''
245 self.norepo = True
245 self.norepo = True
246 self.optionalrepo = False
246 self.badalias = False
247 self.badalias = False
247
248
248 try:
249 try:
249 aliases, entry = cmdutil.findcmd(self.name, cmdtable)
250 aliases, entry = cmdutil.findcmd(self.name, cmdtable)
250 for alias, e in cmdtable.iteritems():
251 for alias, e in cmdtable.iteritems():
251 if e is entry:
252 if e is entry:
252 self.cmd = alias
253 self.cmd = alias
253 break
254 break
254 self.shadows = True
255 self.shadows = True
255 except error.UnknownCommand:
256 except error.UnknownCommand:
256 self.shadows = False
257 self.shadows = False
257
258
258 if not self.definition:
259 if not self.definition:
259 def fn(ui, *args):
260 def fn(ui, *args):
260 ui.warn(_("no definition for alias '%s'\n") % self.name)
261 ui.warn(_("no definition for alias '%s'\n") % self.name)
261 return 1
262 return 1
262 self.fn = fn
263 self.fn = fn
263 self.badalias = True
264 self.badalias = True
264 return
265 return
265
266
266 if self.definition.startswith('!'):
267 if self.definition.startswith('!'):
267 self.shell = True
268 self.shell = True
268 def fn(ui, *args):
269 def fn(ui, *args):
269 env = {'HG_ARGS': ' '.join((self.name,) + args)}
270 env = {'HG_ARGS': ' '.join((self.name,) + args)}
270 def _checkvar(m):
271 def _checkvar(m):
271 if m.groups()[0] == '$':
272 if m.groups()[0] == '$':
272 return m.group()
273 return m.group()
273 elif int(m.groups()[0]) <= len(args):
274 elif int(m.groups()[0]) <= len(args):
274 return m.group()
275 return m.group()
275 else:
276 else:
276 ui.debug("No argument found for substitution "
277 ui.debug("No argument found for substitution "
277 "of %i variable in alias '%s' definition."
278 "of %i variable in alias '%s' definition."
278 % (int(m.groups()[0]), self.name))
279 % (int(m.groups()[0]), self.name))
279 return ''
280 return ''
280 cmd = re.sub(r'\$(\d+|\$)', _checkvar, self.definition[1:])
281 cmd = re.sub(r'\$(\d+|\$)', _checkvar, self.definition[1:])
281 replace = dict((str(i + 1), arg) for i, arg in enumerate(args))
282 replace = dict((str(i + 1), arg) for i, arg in enumerate(args))
282 replace['0'] = self.name
283 replace['0'] = self.name
283 replace['@'] = ' '.join(args)
284 replace['@'] = ' '.join(args)
284 cmd = util.interpolate(r'\$', replace, cmd, escape_prefix=True)
285 cmd = util.interpolate(r'\$', replace, cmd, escape_prefix=True)
285 return util.system(cmd, environ=env, out=ui.fout)
286 return util.system(cmd, environ=env, out=ui.fout)
286 self.fn = fn
287 self.fn = fn
287 return
288 return
288
289
289 args = shlex.split(self.definition)
290 args = shlex.split(self.definition)
290 self.cmdname = cmd = args.pop(0)
291 self.cmdname = cmd = args.pop(0)
291 args = map(util.expandpath, args)
292 args = map(util.expandpath, args)
292
293
293 for invalidarg in ("--cwd", "-R", "--repository", "--repo"):
294 for invalidarg in ("--cwd", "-R", "--repository", "--repo"):
294 if _earlygetopt([invalidarg], args):
295 if _earlygetopt([invalidarg], args):
295 def fn(ui, *args):
296 def fn(ui, *args):
296 ui.warn(_("error in definition for alias '%s': %s may only "
297 ui.warn(_("error in definition for alias '%s': %s may only "
297 "be given on the command line\n")
298 "be given on the command line\n")
298 % (self.name, invalidarg))
299 % (self.name, invalidarg))
299 return 1
300 return 1
300
301
301 self.fn = fn
302 self.fn = fn
302 self.badalias = True
303 self.badalias = True
303 return
304 return
304
305
305 try:
306 try:
306 tableentry = cmdutil.findcmd(cmd, cmdtable, False)[1]
307 tableentry = cmdutil.findcmd(cmd, cmdtable, False)[1]
307 if len(tableentry) > 2:
308 if len(tableentry) > 2:
308 self.fn, self.opts, self.help = tableentry
309 self.fn, self.opts, self.help = tableentry
309 else:
310 else:
310 self.fn, self.opts = tableentry
311 self.fn, self.opts = tableentry
311
312
312 self.args = aliasargs(self.fn, args)
313 self.args = aliasargs(self.fn, args)
313 if cmd not in commands.norepo.split(' '):
314 if cmd not in commands.norepo.split(' '):
314 self.norepo = False
315 self.norepo = False
316 if cmd in commands.optionalrepo.split(' '):
317 self.optionalrepo = True
315 if self.help.startswith("hg " + cmd):
318 if self.help.startswith("hg " + cmd):
316 # drop prefix in old-style help lines so hg shows the alias
319 # drop prefix in old-style help lines so hg shows the alias
317 self.help = self.help[4 + len(cmd):]
320 self.help = self.help[4 + len(cmd):]
318 self.__doc__ = self.fn.__doc__
321 self.__doc__ = self.fn.__doc__
319
322
320 except error.UnknownCommand:
323 except error.UnknownCommand:
321 def fn(ui, *args):
324 def fn(ui, *args):
322 ui.warn(_("alias '%s' resolves to unknown command '%s'\n") \
325 ui.warn(_("alias '%s' resolves to unknown command '%s'\n") \
323 % (self.name, cmd))
326 % (self.name, cmd))
324 try:
327 try:
325 # check if the command is in a disabled extension
328 # check if the command is in a disabled extension
326 commands.help_(ui, cmd, unknowncmd=True)
329 commands.help_(ui, cmd, unknowncmd=True)
327 except error.UnknownCommand:
330 except error.UnknownCommand:
328 pass
331 pass
329 return 1
332 return 1
330 self.fn = fn
333 self.fn = fn
331 self.badalias = True
334 self.badalias = True
332 except error.AmbiguousCommand:
335 except error.AmbiguousCommand:
333 def fn(ui, *args):
336 def fn(ui, *args):
334 ui.warn(_("alias '%s' resolves to ambiguous command '%s'\n") \
337 ui.warn(_("alias '%s' resolves to ambiguous command '%s'\n") \
335 % (self.name, cmd))
338 % (self.name, cmd))
336 return 1
339 return 1
337 self.fn = fn
340 self.fn = fn
338 self.badalias = True
341 self.badalias = True
339
342
340 def __call__(self, ui, *args, **opts):
343 def __call__(self, ui, *args, **opts):
341 if self.shadows:
344 if self.shadows:
342 ui.debug("alias '%s' shadows command '%s'\n" %
345 ui.debug("alias '%s' shadows command '%s'\n" %
343 (self.name, self.cmdname))
346 (self.name, self.cmdname))
344
347
345 if util.safehasattr(self, 'shell'):
348 if util.safehasattr(self, 'shell'):
346 return self.fn(ui, *args, **opts)
349 return self.fn(ui, *args, **opts)
347 else:
350 else:
348 try:
351 try:
349 util.checksignature(self.fn)(ui, *args, **opts)
352 util.checksignature(self.fn)(ui, *args, **opts)
350 except error.SignatureError:
353 except error.SignatureError:
351 args = ' '.join([self.cmdname] + self.args)
354 args = ' '.join([self.cmdname] + self.args)
352 ui.debug("alias '%s' expands to '%s'\n" % (self.name, args))
355 ui.debug("alias '%s' expands to '%s'\n" % (self.name, args))
353 raise
356 raise
354
357
355 def addaliases(ui, cmdtable):
358 def addaliases(ui, cmdtable):
356 # aliases are processed after extensions have been loaded, so they
359 # aliases are processed after extensions have been loaded, so they
357 # may use extension commands. Aliases can also use other alias definitions,
360 # may use extension commands. Aliases can also use other alias definitions,
358 # but only if they have been defined prior to the current definition.
361 # but only if they have been defined prior to the current definition.
359 for alias, definition in ui.configitems('alias'):
362 for alias, definition in ui.configitems('alias'):
360 aliasdef = cmdalias(alias, definition, cmdtable)
363 aliasdef = cmdalias(alias, definition, cmdtable)
361
364
362 try:
365 try:
363 olddef = cmdtable[aliasdef.cmd][0]
366 olddef = cmdtable[aliasdef.cmd][0]
364 if olddef.definition == aliasdef.definition:
367 if olddef.definition == aliasdef.definition:
365 continue
368 continue
366 except (KeyError, AttributeError):
369 except (KeyError, AttributeError):
367 # definition might not exist or it might not be a cmdalias
370 # definition might not exist or it might not be a cmdalias
368 pass
371 pass
369
372
370 cmdtable[aliasdef.name] = (aliasdef, aliasdef.opts, aliasdef.help)
373 cmdtable[aliasdef.name] = (aliasdef, aliasdef.opts, aliasdef.help)
371 if aliasdef.norepo:
374 if aliasdef.norepo:
372 commands.norepo += ' %s' % alias
375 commands.norepo += ' %s' % alias
376 if aliasdef.optionalrepo:
377 commands.optionalrepo += ' %s' % alias
373
378
374 def _parse(ui, args):
379 def _parse(ui, args):
375 options = {}
380 options = {}
376 cmdoptions = {}
381 cmdoptions = {}
377
382
378 try:
383 try:
379 args = fancyopts.fancyopts(args, commands.globalopts, options)
384 args = fancyopts.fancyopts(args, commands.globalopts, options)
380 except fancyopts.getopt.GetoptError, inst:
385 except fancyopts.getopt.GetoptError, inst:
381 raise error.CommandError(None, inst)
386 raise error.CommandError(None, inst)
382
387
383 if args:
388 if args:
384 cmd, args = args[0], args[1:]
389 cmd, args = args[0], args[1:]
385 aliases, entry = cmdutil.findcmd(cmd, commands.table,
390 aliases, entry = cmdutil.findcmd(cmd, commands.table,
386 ui.configbool("ui", "strict"))
391 ui.configbool("ui", "strict"))
387 cmd = aliases[0]
392 cmd = aliases[0]
388 args = aliasargs(entry[0], args)
393 args = aliasargs(entry[0], args)
389 defaults = ui.config("defaults", cmd)
394 defaults = ui.config("defaults", cmd)
390 if defaults:
395 if defaults:
391 args = map(util.expandpath, shlex.split(defaults)) + args
396 args = map(util.expandpath, shlex.split(defaults)) + args
392 c = list(entry[1])
397 c = list(entry[1])
393 else:
398 else:
394 cmd = None
399 cmd = None
395 c = []
400 c = []
396
401
397 # combine global options into local
402 # combine global options into local
398 for o in commands.globalopts:
403 for o in commands.globalopts:
399 c.append((o[0], o[1], options[o[1]], o[3]))
404 c.append((o[0], o[1], options[o[1]], o[3]))
400
405
401 try:
406 try:
402 args = fancyopts.fancyopts(args, c, cmdoptions, True)
407 args = fancyopts.fancyopts(args, c, cmdoptions, True)
403 except fancyopts.getopt.GetoptError, inst:
408 except fancyopts.getopt.GetoptError, inst:
404 raise error.CommandError(cmd, inst)
409 raise error.CommandError(cmd, inst)
405
410
406 # separate global options back out
411 # separate global options back out
407 for o in commands.globalopts:
412 for o in commands.globalopts:
408 n = o[1]
413 n = o[1]
409 options[n] = cmdoptions[n]
414 options[n] = cmdoptions[n]
410 del cmdoptions[n]
415 del cmdoptions[n]
411
416
412 return (cmd, cmd and entry[0] or None, args, options, cmdoptions)
417 return (cmd, cmd and entry[0] or None, args, options, cmdoptions)
413
418
414 def _parseconfig(ui, config):
419 def _parseconfig(ui, config):
415 """parse the --config options from the command line"""
420 """parse the --config options from the command line"""
416 configs = []
421 configs = []
417
422
418 for cfg in config:
423 for cfg in config:
419 try:
424 try:
420 name, value = cfg.split('=', 1)
425 name, value = cfg.split('=', 1)
421 section, name = name.split('.', 1)
426 section, name = name.split('.', 1)
422 if not section or not name:
427 if not section or not name:
423 raise IndexError
428 raise IndexError
424 ui.setconfig(section, name, value)
429 ui.setconfig(section, name, value)
425 configs.append((section, name, value))
430 configs.append((section, name, value))
426 except (IndexError, ValueError):
431 except (IndexError, ValueError):
427 raise util.Abort(_('malformed --config option: %r '
432 raise util.Abort(_('malformed --config option: %r '
428 '(use --config section.name=value)') % cfg)
433 '(use --config section.name=value)') % cfg)
429
434
430 return configs
435 return configs
431
436
432 def _earlygetopt(aliases, args):
437 def _earlygetopt(aliases, args):
433 """Return list of values for an option (or aliases).
438 """Return list of values for an option (or aliases).
434
439
435 The values are listed in the order they appear in args.
440 The values are listed in the order they appear in args.
436 The options and values are removed from args.
441 The options and values are removed from args.
437 """
442 """
438 try:
443 try:
439 argcount = args.index("--")
444 argcount = args.index("--")
440 except ValueError:
445 except ValueError:
441 argcount = len(args)
446 argcount = len(args)
442 shortopts = [opt for opt in aliases if len(opt) == 2]
447 shortopts = [opt for opt in aliases if len(opt) == 2]
443 values = []
448 values = []
444 pos = 0
449 pos = 0
445 while pos < argcount:
450 while pos < argcount:
446 if args[pos] in aliases:
451 if args[pos] in aliases:
447 if pos + 1 >= argcount:
452 if pos + 1 >= argcount:
448 # ignore and let getopt report an error if there is no value
453 # ignore and let getopt report an error if there is no value
449 break
454 break
450 del args[pos]
455 del args[pos]
451 values.append(args.pop(pos))
456 values.append(args.pop(pos))
452 argcount -= 2
457 argcount -= 2
453 elif args[pos][:2] in shortopts:
458 elif args[pos][:2] in shortopts:
454 # short option can have no following space, e.g. hg log -Rfoo
459 # short option can have no following space, e.g. hg log -Rfoo
455 values.append(args.pop(pos)[2:])
460 values.append(args.pop(pos)[2:])
456 argcount -= 1
461 argcount -= 1
457 else:
462 else:
458 pos += 1
463 pos += 1
459 return values
464 return values
460
465
461 def runcommand(lui, repo, cmd, fullargs, ui, options, d, cmdpats, cmdoptions):
466 def runcommand(lui, repo, cmd, fullargs, ui, options, d, cmdpats, cmdoptions):
462 # run pre-hook, and abort if it fails
467 # run pre-hook, and abort if it fails
463 ret = hook.hook(lui, repo, "pre-%s" % cmd, False, args=" ".join(fullargs),
468 ret = hook.hook(lui, repo, "pre-%s" % cmd, False, args=" ".join(fullargs),
464 pats=cmdpats, opts=cmdoptions)
469 pats=cmdpats, opts=cmdoptions)
465 if ret:
470 if ret:
466 return ret
471 return ret
467 ret = _runcommand(ui, options, cmd, d)
472 ret = _runcommand(ui, options, cmd, d)
468 # run post-hook, passing command result
473 # run post-hook, passing command result
469 hook.hook(lui, repo, "post-%s" % cmd, False, args=" ".join(fullargs),
474 hook.hook(lui, repo, "post-%s" % cmd, False, args=" ".join(fullargs),
470 result=ret, pats=cmdpats, opts=cmdoptions)
475 result=ret, pats=cmdpats, opts=cmdoptions)
471 return ret
476 return ret
472
477
473 def _getlocal(ui, rpath):
478 def _getlocal(ui, rpath):
474 """Return (path, local ui object) for the given target path.
479 """Return (path, local ui object) for the given target path.
475
480
476 Takes paths in [cwd]/.hg/hgrc into account."
481 Takes paths in [cwd]/.hg/hgrc into account."
477 """
482 """
478 try:
483 try:
479 wd = os.getcwd()
484 wd = os.getcwd()
480 except OSError, e:
485 except OSError, e:
481 raise util.Abort(_("error getting current working directory: %s") %
486 raise util.Abort(_("error getting current working directory: %s") %
482 e.strerror)
487 e.strerror)
483 path = cmdutil.findrepo(wd) or ""
488 path = cmdutil.findrepo(wd) or ""
484 if not path:
489 if not path:
485 lui = ui
490 lui = ui
486 else:
491 else:
487 lui = ui.copy()
492 lui = ui.copy()
488 lui.readconfig(os.path.join(path, ".hg", "hgrc"), path)
493 lui.readconfig(os.path.join(path, ".hg", "hgrc"), path)
489
494
490 if rpath and rpath[-1]:
495 if rpath and rpath[-1]:
491 path = lui.expandpath(rpath[-1])
496 path = lui.expandpath(rpath[-1])
492 lui = ui.copy()
497 lui = ui.copy()
493 lui.readconfig(os.path.join(path, ".hg", "hgrc"), path)
498 lui.readconfig(os.path.join(path, ".hg", "hgrc"), path)
494
499
495 return path, lui
500 return path, lui
496
501
497 def _checkshellalias(lui, ui, args):
502 def _checkshellalias(lui, ui, args):
498 norepo = commands.norepo
499 options = {}
503 options = {}
500
504
501 try:
505 try:
502 args = fancyopts.fancyopts(args, commands.globalopts, options)
506 args = fancyopts.fancyopts(args, commands.globalopts, options)
503 except fancyopts.getopt.GetoptError:
507 except fancyopts.getopt.GetoptError:
504 return
508 return
505
509
506 if not args:
510 if not args:
507 return
511 return
508
512
513 norepo = commands.norepo
514 optionalrepo = commands.optionalrepo
515 def restorecommands():
516 commands.norepo = norepo
517 commands.optionalrepo = optionalrepo
518
509 cmdtable = commands.table.copy()
519 cmdtable = commands.table.copy()
510 addaliases(lui, cmdtable)
520 addaliases(lui, cmdtable)
511
521
512 cmd = args[0]
522 cmd = args[0]
513 try:
523 try:
514 aliases, entry = cmdutil.findcmd(cmd, cmdtable,
524 aliases, entry = cmdutil.findcmd(cmd, cmdtable,
515 lui.configbool("ui", "strict"))
525 lui.configbool("ui", "strict"))
516 except (error.AmbiguousCommand, error.UnknownCommand):
526 except (error.AmbiguousCommand, error.UnknownCommand):
517 commands.norepo = norepo
527 restorecommands()
518 return
528 return
519
529
520 cmd = aliases[0]
530 cmd = aliases[0]
521 fn = entry[0]
531 fn = entry[0]
522
532
523 if cmd and util.safehasattr(fn, 'shell'):
533 if cmd and util.safehasattr(fn, 'shell'):
524 d = lambda: fn(ui, *args[1:])
534 d = lambda: fn(ui, *args[1:])
525 return lambda: runcommand(lui, None, cmd, args[:1], ui, options, d, [], {})
535 return lambda: runcommand(lui, None, cmd, args[:1], ui, options, d, [], {})
526
536
527 commands.norepo = norepo
537 restorecommands()
528
538
529 _loaded = set()
539 _loaded = set()
530 def _dispatch(req):
540 def _dispatch(req):
531 args = req.args
541 args = req.args
532 ui = req.ui
542 ui = req.ui
533
543
534 # read --config before doing anything else
544 # read --config before doing anything else
535 # (e.g. to change trust settings for reading .hg/hgrc)
545 # (e.g. to change trust settings for reading .hg/hgrc)
536 cfgs = _parseconfig(ui, _earlygetopt(['--config'], args))
546 cfgs = _parseconfig(ui, _earlygetopt(['--config'], args))
537
547
538 # check for cwd
548 # check for cwd
539 cwd = _earlygetopt(['--cwd'], args)
549 cwd = _earlygetopt(['--cwd'], args)
540 if cwd:
550 if cwd:
541 os.chdir(cwd[-1])
551 os.chdir(cwd[-1])
542
552
543 rpath = _earlygetopt(["-R", "--repository", "--repo"], args)
553 rpath = _earlygetopt(["-R", "--repository", "--repo"], args)
544 path, lui = _getlocal(ui, rpath)
554 path, lui = _getlocal(ui, rpath)
545
555
546 # Now that we're operating in the right directory/repository with
556 # Now that we're operating in the right directory/repository with
547 # the right config settings, check for shell aliases
557 # the right config settings, check for shell aliases
548 shellaliasfn = _checkshellalias(lui, ui, args)
558 shellaliasfn = _checkshellalias(lui, ui, args)
549 if shellaliasfn:
559 if shellaliasfn:
550 return shellaliasfn()
560 return shellaliasfn()
551
561
552 # Configure extensions in phases: uisetup, extsetup, cmdtable, and
562 # Configure extensions in phases: uisetup, extsetup, cmdtable, and
553 # reposetup. Programs like TortoiseHg will call _dispatch several
563 # reposetup. Programs like TortoiseHg will call _dispatch several
554 # times so we keep track of configured extensions in _loaded.
564 # times so we keep track of configured extensions in _loaded.
555 extensions.loadall(lui)
565 extensions.loadall(lui)
556 exts = [ext for ext in extensions.extensions() if ext[0] not in _loaded]
566 exts = [ext for ext in extensions.extensions() if ext[0] not in _loaded]
557 # Propagate any changes to lui.__class__ by extensions
567 # Propagate any changes to lui.__class__ by extensions
558 ui.__class__ = lui.__class__
568 ui.__class__ = lui.__class__
559
569
560 # (uisetup and extsetup are handled in extensions.loadall)
570 # (uisetup and extsetup are handled in extensions.loadall)
561
571
562 for name, module in exts:
572 for name, module in exts:
563 cmdtable = getattr(module, 'cmdtable', {})
573 cmdtable = getattr(module, 'cmdtable', {})
564 overrides = [cmd for cmd in cmdtable if cmd in commands.table]
574 overrides = [cmd for cmd in cmdtable if cmd in commands.table]
565 if overrides:
575 if overrides:
566 ui.warn(_("extension '%s' overrides commands: %s\n")
576 ui.warn(_("extension '%s' overrides commands: %s\n")
567 % (name, " ".join(overrides)))
577 % (name, " ".join(overrides)))
568 commands.table.update(cmdtable)
578 commands.table.update(cmdtable)
569 _loaded.add(name)
579 _loaded.add(name)
570
580
571 # (reposetup is handled in hg.repository)
581 # (reposetup is handled in hg.repository)
572
582
573 addaliases(lui, commands.table)
583 addaliases(lui, commands.table)
574
584
575 # check for fallback encoding
585 # check for fallback encoding
576 fallback = lui.config('ui', 'fallbackencoding')
586 fallback = lui.config('ui', 'fallbackencoding')
577 if fallback:
587 if fallback:
578 encoding.fallbackencoding = fallback
588 encoding.fallbackencoding = fallback
579
589
580 fullargs = args
590 fullargs = args
581 cmd, func, args, options, cmdoptions = _parse(lui, args)
591 cmd, func, args, options, cmdoptions = _parse(lui, args)
582
592
583 if options["config"]:
593 if options["config"]:
584 raise util.Abort(_("option --config may not be abbreviated!"))
594 raise util.Abort(_("option --config may not be abbreviated!"))
585 if options["cwd"]:
595 if options["cwd"]:
586 raise util.Abort(_("option --cwd may not be abbreviated!"))
596 raise util.Abort(_("option --cwd may not be abbreviated!"))
587 if options["repository"]:
597 if options["repository"]:
588 raise util.Abort(_(
598 raise util.Abort(_(
589 "option -R has to be separated from other options (e.g. not -qR) "
599 "option -R has to be separated from other options (e.g. not -qR) "
590 "and --repository may only be abbreviated as --repo!"))
600 "and --repository may only be abbreviated as --repo!"))
591
601
592 if options["encoding"]:
602 if options["encoding"]:
593 encoding.encoding = options["encoding"]
603 encoding.encoding = options["encoding"]
594 if options["encodingmode"]:
604 if options["encodingmode"]:
595 encoding.encodingmode = options["encodingmode"]
605 encoding.encodingmode = options["encodingmode"]
596 if options["time"]:
606 if options["time"]:
597 def get_times():
607 def get_times():
598 t = os.times()
608 t = os.times()
599 if t[4] == 0.0: # Windows leaves this as zero, so use time.clock()
609 if t[4] == 0.0: # Windows leaves this as zero, so use time.clock()
600 t = (t[0], t[1], t[2], t[3], time.clock())
610 t = (t[0], t[1], t[2], t[3], time.clock())
601 return t
611 return t
602 s = get_times()
612 s = get_times()
603 def print_time():
613 def print_time():
604 t = get_times()
614 t = get_times()
605 ui.warn(_("Time: real %.3f secs (user %.3f+%.3f sys %.3f+%.3f)\n") %
615 ui.warn(_("Time: real %.3f secs (user %.3f+%.3f sys %.3f+%.3f)\n") %
606 (t[4]-s[4], t[0]-s[0], t[2]-s[2], t[1]-s[1], t[3]-s[3]))
616 (t[4]-s[4], t[0]-s[0], t[2]-s[2], t[1]-s[1], t[3]-s[3]))
607 atexit.register(print_time)
617 atexit.register(print_time)
608
618
609 uis = set([ui, lui])
619 uis = set([ui, lui])
610
620
611 if req.repo:
621 if req.repo:
612 uis.add(req.repo.ui)
622 uis.add(req.repo.ui)
613
623
614 # copy configs that were passed on the cmdline (--config) to the repo ui
624 # copy configs that were passed on the cmdline (--config) to the repo ui
615 for cfg in cfgs:
625 for cfg in cfgs:
616 req.repo.ui.setconfig(*cfg)
626 req.repo.ui.setconfig(*cfg)
617
627
618 if options['verbose'] or options['debug'] or options['quiet']:
628 if options['verbose'] or options['debug'] or options['quiet']:
619 for opt in ('verbose', 'debug', 'quiet'):
629 for opt in ('verbose', 'debug', 'quiet'):
620 val = str(bool(options[opt]))
630 val = str(bool(options[opt]))
621 for ui_ in uis:
631 for ui_ in uis:
622 ui_.setconfig('ui', opt, val)
632 ui_.setconfig('ui', opt, val)
623
633
624 if options['traceback']:
634 if options['traceback']:
625 for ui_ in uis:
635 for ui_ in uis:
626 ui_.setconfig('ui', 'traceback', 'on')
636 ui_.setconfig('ui', 'traceback', 'on')
627
637
628 if options['noninteractive']:
638 if options['noninteractive']:
629 for ui_ in uis:
639 for ui_ in uis:
630 ui_.setconfig('ui', 'interactive', 'off')
640 ui_.setconfig('ui', 'interactive', 'off')
631
641
632 if cmdoptions.get('insecure', False):
642 if cmdoptions.get('insecure', False):
633 for ui_ in uis:
643 for ui_ in uis:
634 ui_.setconfig('web', 'cacerts', '')
644 ui_.setconfig('web', 'cacerts', '')
635
645
636 if options['version']:
646 if options['version']:
637 return commands.version_(ui)
647 return commands.version_(ui)
638 if options['help']:
648 if options['help']:
639 return commands.help_(ui, cmd)
649 return commands.help_(ui, cmd)
640 elif not cmd:
650 elif not cmd:
641 return commands.help_(ui, 'shortlist')
651 return commands.help_(ui, 'shortlist')
642
652
643 repo = None
653 repo = None
644 cmdpats = args[:]
654 cmdpats = args[:]
645 if cmd not in commands.norepo.split():
655 if cmd not in commands.norepo.split():
646 # use the repo from the request only if we don't have -R
656 # use the repo from the request only if we don't have -R
647 if not rpath and not cwd:
657 if not rpath and not cwd:
648 repo = req.repo
658 repo = req.repo
649
659
650 if repo:
660 if repo:
651 # set the descriptors of the repo ui to those of ui
661 # set the descriptors of the repo ui to those of ui
652 repo.ui.fin = ui.fin
662 repo.ui.fin = ui.fin
653 repo.ui.fout = ui.fout
663 repo.ui.fout = ui.fout
654 repo.ui.ferr = ui.ferr
664 repo.ui.ferr = ui.ferr
655 else:
665 else:
656 try:
666 try:
657 repo = hg.repository(ui, path=path)
667 repo = hg.repository(ui, path=path)
658 if not repo.local():
668 if not repo.local():
659 raise util.Abort(_("repository '%s' is not local") % path)
669 raise util.Abort(_("repository '%s' is not local") % path)
660 repo.ui.setconfig("bundle", "mainreporoot", repo.root)
670 repo.ui.setconfig("bundle", "mainreporoot", repo.root)
661 except error.RequirementError:
671 except error.RequirementError:
662 raise
672 raise
663 except error.RepoError:
673 except error.RepoError:
664 if cmd not in commands.optionalrepo.split():
674 if cmd not in commands.optionalrepo.split():
665 if args and not path: # try to infer -R from command args
675 if args and not path: # try to infer -R from command args
666 repos = map(cmdutil.findrepo, args)
676 repos = map(cmdutil.findrepo, args)
667 guess = repos[0]
677 guess = repos[0]
668 if guess and repos.count(guess) == len(repos):
678 if guess and repos.count(guess) == len(repos):
669 req.args = ['--repository', guess] + fullargs
679 req.args = ['--repository', guess] + fullargs
670 return _dispatch(req)
680 return _dispatch(req)
671 if not path:
681 if not path:
672 raise error.RepoError(_("no repository found in '%s'"
682 raise error.RepoError(_("no repository found in '%s'"
673 " (.hg not found)") % os.getcwd())
683 " (.hg not found)") % os.getcwd())
674 raise
684 raise
675 if repo:
685 if repo:
676 ui = repo.ui
686 ui = repo.ui
677 args.insert(0, repo)
687 args.insert(0, repo)
678 elif rpath:
688 elif rpath:
679 ui.warn(_("warning: --repository ignored\n"))
689 ui.warn(_("warning: --repository ignored\n"))
680
690
681 msg = ' '.join(' ' in a and repr(a) or a for a in fullargs)
691 msg = ' '.join(' ' in a and repr(a) or a for a in fullargs)
682 ui.log("command", msg + "\n")
692 ui.log("command", msg + "\n")
683 d = lambda: util.checksignature(func)(ui, *args, **cmdoptions)
693 d = lambda: util.checksignature(func)(ui, *args, **cmdoptions)
684 try:
694 try:
685 return runcommand(lui, repo, cmd, fullargs, ui, options, d,
695 return runcommand(lui, repo, cmd, fullargs, ui, options, d,
686 cmdpats, cmdoptions)
696 cmdpats, cmdoptions)
687 finally:
697 finally:
688 if repo and repo != req.repo:
698 if repo and repo != req.repo:
689 repo.close()
699 repo.close()
690
700
691 def lsprofile(ui, func, fp):
701 def lsprofile(ui, func, fp):
692 format = ui.config('profiling', 'format', default='text')
702 format = ui.config('profiling', 'format', default='text')
693 field = ui.config('profiling', 'sort', default='inlinetime')
703 field = ui.config('profiling', 'sort', default='inlinetime')
694 climit = ui.configint('profiling', 'nested', default=5)
704 climit = ui.configint('profiling', 'nested', default=5)
695
705
696 if not format in ['text', 'kcachegrind']:
706 if not format in ['text', 'kcachegrind']:
697 ui.warn(_("unrecognized profiling format '%s'"
707 ui.warn(_("unrecognized profiling format '%s'"
698 " - Ignored\n") % format)
708 " - Ignored\n") % format)
699 format = 'text'
709 format = 'text'
700
710
701 try:
711 try:
702 from mercurial import lsprof
712 from mercurial import lsprof
703 except ImportError:
713 except ImportError:
704 raise util.Abort(_(
714 raise util.Abort(_(
705 'lsprof not available - install from '
715 'lsprof not available - install from '
706 'http://codespeak.net/svn/user/arigo/hack/misc/lsprof/'))
716 'http://codespeak.net/svn/user/arigo/hack/misc/lsprof/'))
707 p = lsprof.Profiler()
717 p = lsprof.Profiler()
708 p.enable(subcalls=True)
718 p.enable(subcalls=True)
709 try:
719 try:
710 return func()
720 return func()
711 finally:
721 finally:
712 p.disable()
722 p.disable()
713
723
714 if format == 'kcachegrind':
724 if format == 'kcachegrind':
715 import lsprofcalltree
725 import lsprofcalltree
716 calltree = lsprofcalltree.KCacheGrind(p)
726 calltree = lsprofcalltree.KCacheGrind(p)
717 calltree.output(fp)
727 calltree.output(fp)
718 else:
728 else:
719 # format == 'text'
729 # format == 'text'
720 stats = lsprof.Stats(p.getstats())
730 stats = lsprof.Stats(p.getstats())
721 stats.sort(field)
731 stats.sort(field)
722 stats.pprint(limit=30, file=fp, climit=climit)
732 stats.pprint(limit=30, file=fp, climit=climit)
723
733
724 def statprofile(ui, func, fp):
734 def statprofile(ui, func, fp):
725 try:
735 try:
726 import statprof
736 import statprof
727 except ImportError:
737 except ImportError:
728 raise util.Abort(_(
738 raise util.Abort(_(
729 'statprof not available - install using "easy_install statprof"'))
739 'statprof not available - install using "easy_install statprof"'))
730
740
731 freq = ui.configint('profiling', 'freq', default=1000)
741 freq = ui.configint('profiling', 'freq', default=1000)
732 if freq > 0:
742 if freq > 0:
733 statprof.reset(freq)
743 statprof.reset(freq)
734 else:
744 else:
735 ui.warn(_("invalid sampling frequency '%s' - ignoring\n") % freq)
745 ui.warn(_("invalid sampling frequency '%s' - ignoring\n") % freq)
736
746
737 statprof.start()
747 statprof.start()
738 try:
748 try:
739 return func()
749 return func()
740 finally:
750 finally:
741 statprof.stop()
751 statprof.stop()
742 statprof.display(fp)
752 statprof.display(fp)
743
753
744 def _runcommand(ui, options, cmd, cmdfunc):
754 def _runcommand(ui, options, cmd, cmdfunc):
745 def checkargs():
755 def checkargs():
746 try:
756 try:
747 return cmdfunc()
757 return cmdfunc()
748 except error.SignatureError:
758 except error.SignatureError:
749 raise error.CommandError(cmd, _("invalid arguments"))
759 raise error.CommandError(cmd, _("invalid arguments"))
750
760
751 if options['profile']:
761 if options['profile']:
752 profiler = os.getenv('HGPROF')
762 profiler = os.getenv('HGPROF')
753 if profiler is None:
763 if profiler is None:
754 profiler = ui.config('profiling', 'type', default='ls')
764 profiler = ui.config('profiling', 'type', default='ls')
755 if profiler not in ('ls', 'stat'):
765 if profiler not in ('ls', 'stat'):
756 ui.warn(_("unrecognized profiler '%s' - ignored\n") % profiler)
766 ui.warn(_("unrecognized profiler '%s' - ignored\n") % profiler)
757 profiler = 'ls'
767 profiler = 'ls'
758
768
759 output = ui.config('profiling', 'output')
769 output = ui.config('profiling', 'output')
760
770
761 if output:
771 if output:
762 path = ui.expandpath(output)
772 path = ui.expandpath(output)
763 fp = open(path, 'wb')
773 fp = open(path, 'wb')
764 else:
774 else:
765 fp = sys.stderr
775 fp = sys.stderr
766
776
767 try:
777 try:
768 if profiler == 'ls':
778 if profiler == 'ls':
769 return lsprofile(ui, checkargs, fp)
779 return lsprofile(ui, checkargs, fp)
770 else:
780 else:
771 return statprofile(ui, checkargs, fp)
781 return statprofile(ui, checkargs, fp)
772 finally:
782 finally:
773 if output:
783 if output:
774 fp.close()
784 fp.close()
775 else:
785 else:
776 return checkargs()
786 return checkargs()
@@ -1,1207 +1,1207 b''
1 /*
1 /*
2 parsers.c - efficient content parsing
2 parsers.c - efficient content parsing
3
3
4 Copyright 2008 Matt Mackall <mpm@selenic.com> and others
4 Copyright 2008 Matt Mackall <mpm@selenic.com> and others
5
5
6 This software may be used and distributed according to the terms of
6 This software may be used and distributed according to the terms of
7 the GNU General Public License, incorporated herein by reference.
7 the GNU General Public License, incorporated herein by reference.
8 */
8 */
9
9
10 #include <Python.h>
10 #include <Python.h>
11 #include <ctype.h>
11 #include <ctype.h>
12 #include <string.h>
12 #include <string.h>
13
13
14 #include "util.h"
14 #include "util.h"
15
15
16 static inline int hexdigit(const char *p, Py_ssize_t off)
16 static inline int hexdigit(const char *p, Py_ssize_t off)
17 {
17 {
18 char c = p[off];
18 char c = p[off];
19
19
20 if (c >= '0' && c <= '9')
20 if (c >= '0' && c <= '9')
21 return c - '0';
21 return c - '0';
22 if (c >= 'a' && c <= 'f')
22 if (c >= 'a' && c <= 'f')
23 return c - 'a' + 10;
23 return c - 'a' + 10;
24 if (c >= 'A' && c <= 'F')
24 if (c >= 'A' && c <= 'F')
25 return c - 'A' + 10;
25 return c - 'A' + 10;
26
26
27 PyErr_SetString(PyExc_ValueError, "input contains non-hex character");
27 PyErr_SetString(PyExc_ValueError, "input contains non-hex character");
28 return 0;
28 return 0;
29 }
29 }
30
30
31 /*
31 /*
32 * Turn a hex-encoded string into binary.
32 * Turn a hex-encoded string into binary.
33 */
33 */
34 static PyObject *unhexlify(const char *str, int len)
34 static PyObject *unhexlify(const char *str, int len)
35 {
35 {
36 PyObject *ret;
36 PyObject *ret;
37 char *d;
37 char *d;
38 int i;
38 int i;
39
39
40 ret = PyBytes_FromStringAndSize(NULL, len / 2);
40 ret = PyBytes_FromStringAndSize(NULL, len / 2);
41
41
42 if (!ret)
42 if (!ret)
43 return NULL;
43 return NULL;
44
44
45 d = PyBytes_AsString(ret);
45 d = PyBytes_AsString(ret);
46
46
47 for (i = 0; i < len;) {
47 for (i = 0; i < len;) {
48 int hi = hexdigit(str, i++);
48 int hi = hexdigit(str, i++);
49 int lo = hexdigit(str, i++);
49 int lo = hexdigit(str, i++);
50 *d++ = (hi << 4) | lo;
50 *d++ = (hi << 4) | lo;
51 }
51 }
52
52
53 return ret;
53 return ret;
54 }
54 }
55
55
56 /*
56 /*
57 * This code assumes that a manifest is stitched together with newline
57 * This code assumes that a manifest is stitched together with newline
58 * ('\n') characters.
58 * ('\n') characters.
59 */
59 */
60 static PyObject *parse_manifest(PyObject *self, PyObject *args)
60 static PyObject *parse_manifest(PyObject *self, PyObject *args)
61 {
61 {
62 PyObject *mfdict, *fdict;
62 PyObject *mfdict, *fdict;
63 char *str, *cur, *start, *zero;
63 char *str, *cur, *start, *zero;
64 int len;
64 int len;
65
65
66 if (!PyArg_ParseTuple(args, "O!O!s#:parse_manifest",
66 if (!PyArg_ParseTuple(args, "O!O!s#:parse_manifest",
67 &PyDict_Type, &mfdict,
67 &PyDict_Type, &mfdict,
68 &PyDict_Type, &fdict,
68 &PyDict_Type, &fdict,
69 &str, &len))
69 &str, &len))
70 goto quit;
70 goto quit;
71
71
72 for (start = cur = str, zero = NULL; cur < str + len; cur++) {
72 for (start = cur = str, zero = NULL; cur < str + len; cur++) {
73 PyObject *file = NULL, *node = NULL;
73 PyObject *file = NULL, *node = NULL;
74 PyObject *flags = NULL;
74 PyObject *flags = NULL;
75 int nlen;
75 int nlen;
76
76
77 if (!*cur) {
77 if (!*cur) {
78 zero = cur;
78 zero = cur;
79 continue;
79 continue;
80 }
80 }
81 else if (*cur != '\n')
81 else if (*cur != '\n')
82 continue;
82 continue;
83
83
84 if (!zero) {
84 if (!zero) {
85 PyErr_SetString(PyExc_ValueError,
85 PyErr_SetString(PyExc_ValueError,
86 "manifest entry has no separator");
86 "manifest entry has no separator");
87 goto quit;
87 goto quit;
88 }
88 }
89
89
90 file = PyBytes_FromStringAndSize(start, zero - start);
90 file = PyBytes_FromStringAndSize(start, zero - start);
91
91
92 if (!file)
92 if (!file)
93 goto bail;
93 goto bail;
94
94
95 nlen = cur - zero - 1;
95 nlen = cur - zero - 1;
96
96
97 node = unhexlify(zero + 1, nlen > 40 ? 40 : nlen);
97 node = unhexlify(zero + 1, nlen > 40 ? 40 : nlen);
98 if (!node)
98 if (!node)
99 goto bail;
99 goto bail;
100
100
101 if (nlen > 40) {
101 if (nlen > 40) {
102 flags = PyBytes_FromStringAndSize(zero + 41,
102 flags = PyBytes_FromStringAndSize(zero + 41,
103 nlen - 40);
103 nlen - 40);
104 if (!flags)
104 if (!flags)
105 goto bail;
105 goto bail;
106
106
107 if (PyDict_SetItem(fdict, file, flags) == -1)
107 if (PyDict_SetItem(fdict, file, flags) == -1)
108 goto bail;
108 goto bail;
109 }
109 }
110
110
111 if (PyDict_SetItem(mfdict, file, node) == -1)
111 if (PyDict_SetItem(mfdict, file, node) == -1)
112 goto bail;
112 goto bail;
113
113
114 start = cur + 1;
114 start = cur + 1;
115 zero = NULL;
115 zero = NULL;
116
116
117 Py_XDECREF(flags);
117 Py_XDECREF(flags);
118 Py_XDECREF(node);
118 Py_XDECREF(node);
119 Py_XDECREF(file);
119 Py_XDECREF(file);
120 continue;
120 continue;
121 bail:
121 bail:
122 Py_XDECREF(flags);
122 Py_XDECREF(flags);
123 Py_XDECREF(node);
123 Py_XDECREF(node);
124 Py_XDECREF(file);
124 Py_XDECREF(file);
125 goto quit;
125 goto quit;
126 }
126 }
127
127
128 if (len > 0 && *(cur - 1) != '\n') {
128 if (len > 0 && *(cur - 1) != '\n') {
129 PyErr_SetString(PyExc_ValueError,
129 PyErr_SetString(PyExc_ValueError,
130 "manifest contains trailing garbage");
130 "manifest contains trailing garbage");
131 goto quit;
131 goto quit;
132 }
132 }
133
133
134 Py_INCREF(Py_None);
134 Py_INCREF(Py_None);
135 return Py_None;
135 return Py_None;
136 quit:
136 quit:
137 return NULL;
137 return NULL;
138 }
138 }
139
139
140 static PyObject *parse_dirstate(PyObject *self, PyObject *args)
140 static PyObject *parse_dirstate(PyObject *self, PyObject *args)
141 {
141 {
142 PyObject *dmap, *cmap, *parents = NULL, *ret = NULL;
142 PyObject *dmap, *cmap, *parents = NULL, *ret = NULL;
143 PyObject *fname = NULL, *cname = NULL, *entry = NULL;
143 PyObject *fname = NULL, *cname = NULL, *entry = NULL;
144 char *str, *cur, *end, *cpos;
144 char *str, *cur, *end, *cpos;
145 int state, mode, size, mtime;
145 int state, mode, size, mtime;
146 unsigned int flen;
146 unsigned int flen;
147 int len;
147 int len;
148
148
149 if (!PyArg_ParseTuple(args, "O!O!s#:parse_dirstate",
149 if (!PyArg_ParseTuple(args, "O!O!s#:parse_dirstate",
150 &PyDict_Type, &dmap,
150 &PyDict_Type, &dmap,
151 &PyDict_Type, &cmap,
151 &PyDict_Type, &cmap,
152 &str, &len))
152 &str, &len))
153 goto quit;
153 goto quit;
154
154
155 /* read parents */
155 /* read parents */
156 if (len < 40)
156 if (len < 40)
157 goto quit;
157 goto quit;
158
158
159 parents = Py_BuildValue("s#s#", str, 20, str + 20, 20);
159 parents = Py_BuildValue("s#s#", str, 20, str + 20, 20);
160 if (!parents)
160 if (!parents)
161 goto quit;
161 goto quit;
162
162
163 /* read filenames */
163 /* read filenames */
164 cur = str + 40;
164 cur = str + 40;
165 end = str + len;
165 end = str + len;
166
166
167 while (cur < end - 17) {
167 while (cur < end - 17) {
168 /* unpack header */
168 /* unpack header */
169 state = *cur;
169 state = *cur;
170 mode = getbe32(cur + 1);
170 mode = getbe32(cur + 1);
171 size = getbe32(cur + 5);
171 size = getbe32(cur + 5);
172 mtime = getbe32(cur + 9);
172 mtime = getbe32(cur + 9);
173 flen = getbe32(cur + 13);
173 flen = getbe32(cur + 13);
174 cur += 17;
174 cur += 17;
175 if (cur + flen > end || cur + flen < cur) {
175 if (cur + flen > end || cur + flen < cur) {
176 PyErr_SetString(PyExc_ValueError, "overflow in dirstate");
176 PyErr_SetString(PyExc_ValueError, "overflow in dirstate");
177 goto quit;
177 goto quit;
178 }
178 }
179
179
180 entry = Py_BuildValue("ciii", state, mode, size, mtime);
180 entry = Py_BuildValue("ciii", state, mode, size, mtime);
181 if (!entry)
181 if (!entry)
182 goto quit;
182 goto quit;
183 PyObject_GC_UnTrack(entry); /* don't waste time with this */
183 PyObject_GC_UnTrack(entry); /* don't waste time with this */
184
184
185 cpos = memchr(cur, 0, flen);
185 cpos = memchr(cur, 0, flen);
186 if (cpos) {
186 if (cpos) {
187 fname = PyBytes_FromStringAndSize(cur, cpos - cur);
187 fname = PyBytes_FromStringAndSize(cur, cpos - cur);
188 cname = PyBytes_FromStringAndSize(cpos + 1,
188 cname = PyBytes_FromStringAndSize(cpos + 1,
189 flen - (cpos - cur) - 1);
189 flen - (cpos - cur) - 1);
190 if (!fname || !cname ||
190 if (!fname || !cname ||
191 PyDict_SetItem(cmap, fname, cname) == -1 ||
191 PyDict_SetItem(cmap, fname, cname) == -1 ||
192 PyDict_SetItem(dmap, fname, entry) == -1)
192 PyDict_SetItem(dmap, fname, entry) == -1)
193 goto quit;
193 goto quit;
194 Py_DECREF(cname);
194 Py_DECREF(cname);
195 } else {
195 } else {
196 fname = PyBytes_FromStringAndSize(cur, flen);
196 fname = PyBytes_FromStringAndSize(cur, flen);
197 if (!fname ||
197 if (!fname ||
198 PyDict_SetItem(dmap, fname, entry) == -1)
198 PyDict_SetItem(dmap, fname, entry) == -1)
199 goto quit;
199 goto quit;
200 }
200 }
201 cur += flen;
201 cur += flen;
202 Py_DECREF(fname);
202 Py_DECREF(fname);
203 Py_DECREF(entry);
203 Py_DECREF(entry);
204 fname = cname = entry = NULL;
204 fname = cname = entry = NULL;
205 }
205 }
206
206
207 ret = parents;
207 ret = parents;
208 Py_INCREF(ret);
208 Py_INCREF(ret);
209 quit:
209 quit:
210 Py_XDECREF(fname);
210 Py_XDECREF(fname);
211 Py_XDECREF(cname);
211 Py_XDECREF(cname);
212 Py_XDECREF(entry);
212 Py_XDECREF(entry);
213 Py_XDECREF(parents);
213 Py_XDECREF(parents);
214 return ret;
214 return ret;
215 }
215 }
216
216
217 /*
217 /*
218 * A base-16 trie for fast node->rev mapping.
218 * A base-16 trie for fast node->rev mapping.
219 *
219 *
220 * Positive value is index of the next node in the trie
220 * Positive value is index of the next node in the trie
221 * Negative value is a leaf: -(rev + 1)
221 * Negative value is a leaf: -(rev + 1)
222 * Zero is empty
222 * Zero is empty
223 */
223 */
224 typedef struct {
224 typedef struct {
225 int children[16];
225 int children[16];
226 } nodetree;
226 } nodetree;
227
227
228 /*
228 /*
229 * This class has two behaviours.
229 * This class has two behaviours.
230 *
230 *
231 * When used in a list-like way (with integer keys), we decode an
231 * When used in a list-like way (with integer keys), we decode an
232 * entry in a RevlogNG index file on demand. Our last entry is a
232 * entry in a RevlogNG index file on demand. Our last entry is a
233 * sentinel, always a nullid. We have limited support for
233 * sentinel, always a nullid. We have limited support for
234 * integer-keyed insert and delete, only at elements right before the
234 * integer-keyed insert and delete, only at elements right before the
235 * sentinel.
235 * sentinel.
236 *
236 *
237 * With string keys, we lazily perform a reverse mapping from node to
237 * With string keys, we lazily perform a reverse mapping from node to
238 * rev, using a base-16 trie.
238 * rev, using a base-16 trie.
239 */
239 */
240 typedef struct {
240 typedef struct {
241 PyObject_HEAD
241 PyObject_HEAD
242 /* Type-specific fields go here. */
242 /* Type-specific fields go here. */
243 PyObject *data; /* raw bytes of index */
243 PyObject *data; /* raw bytes of index */
244 PyObject **cache; /* cached tuples */
244 PyObject **cache; /* cached tuples */
245 const char **offsets; /* populated on demand */
245 const char **offsets; /* populated on demand */
246 Py_ssize_t raw_length; /* original number of elements */
246 Py_ssize_t raw_length; /* original number of elements */
247 Py_ssize_t length; /* current number of elements */
247 Py_ssize_t length; /* current number of elements */
248 PyObject *added; /* populated on demand */
248 PyObject *added; /* populated on demand */
249 nodetree *nt; /* base-16 trie */
249 nodetree *nt; /* base-16 trie */
250 int ntlength; /* # nodes in use */
250 int ntlength; /* # nodes in use */
251 int ntcapacity; /* # nodes allocated */
251 int ntcapacity; /* # nodes allocated */
252 int ntdepth; /* maximum depth of tree */
252 int ntdepth; /* maximum depth of tree */
253 int ntsplits; /* # splits performed */
253 int ntsplits; /* # splits performed */
254 int ntrev; /* last rev scanned */
254 int ntrev; /* last rev scanned */
255 int ntlookups; /* # lookups */
255 int ntlookups; /* # lookups */
256 int ntmisses; /* # lookups that miss the cache */
256 int ntmisses; /* # lookups that miss the cache */
257 int inlined;
257 int inlined;
258 } indexObject;
258 } indexObject;
259
259
260 static Py_ssize_t index_length(const indexObject *self)
260 static Py_ssize_t index_length(const indexObject *self)
261 {
261 {
262 if (self->added == NULL)
262 if (self->added == NULL)
263 return self->length;
263 return self->length;
264 return self->length + PyList_GET_SIZE(self->added);
264 return self->length + PyList_GET_SIZE(self->added);
265 }
265 }
266
266
267 static PyObject *nullentry;
267 static PyObject *nullentry;
268 static const char nullid[20];
268 static const char nullid[20];
269
269
270 static long inline_scan(indexObject *self, const char **offsets);
270 static long inline_scan(indexObject *self, const char **offsets);
271
271
272 #if LONG_MAX == 0x7fffffffL
272 #if LONG_MAX == 0x7fffffffL
273 static char *tuple_format = "Kiiiiiis#";
273 static char *tuple_format = "Kiiiiiis#";
274 #else
274 #else
275 static char *tuple_format = "kiiiiiis#";
275 static char *tuple_format = "kiiiiiis#";
276 #endif
276 #endif
277
277
278 /*
278 /*
279 * Return a pointer to the beginning of a RevlogNG record.
279 * Return a pointer to the beginning of a RevlogNG record.
280 */
280 */
281 static const char *index_deref(indexObject *self, Py_ssize_t pos)
281 static const char *index_deref(indexObject *self, Py_ssize_t pos)
282 {
282 {
283 if (self->inlined && pos > 0) {
283 if (self->inlined && pos > 0) {
284 if (self->offsets == NULL) {
284 if (self->offsets == NULL) {
285 self->offsets = malloc(self->raw_length *
285 self->offsets = malloc(self->raw_length *
286 sizeof(*self->offsets));
286 sizeof(*self->offsets));
287 if (self->offsets == NULL)
287 if (self->offsets == NULL)
288 return (const char *)PyErr_NoMemory();
288 return (const char *)PyErr_NoMemory();
289 inline_scan(self, self->offsets);
289 inline_scan(self, self->offsets);
290 }
290 }
291 return self->offsets[pos];
291 return self->offsets[pos];
292 }
292 }
293
293
294 return PyString_AS_STRING(self->data) + pos * 64;
294 return PyString_AS_STRING(self->data) + pos * 64;
295 }
295 }
296
296
297 /*
297 /*
298 * RevlogNG format (all in big endian, data may be inlined):
298 * RevlogNG format (all in big endian, data may be inlined):
299 * 6 bytes: offset
299 * 6 bytes: offset
300 * 2 bytes: flags
300 * 2 bytes: flags
301 * 4 bytes: compressed length
301 * 4 bytes: compressed length
302 * 4 bytes: uncompressed length
302 * 4 bytes: uncompressed length
303 * 4 bytes: base revision
303 * 4 bytes: base revision
304 * 4 bytes: link revision
304 * 4 bytes: link revision
305 * 4 bytes: parent 1 revision
305 * 4 bytes: parent 1 revision
306 * 4 bytes: parent 2 revision
306 * 4 bytes: parent 2 revision
307 * 32 bytes: nodeid (only 20 bytes used)
307 * 32 bytes: nodeid (only 20 bytes used)
308 */
308 */
309 static PyObject *index_get(indexObject *self, Py_ssize_t pos)
309 static PyObject *index_get(indexObject *self, Py_ssize_t pos)
310 {
310 {
311 uint64_t offset_flags;
311 uint64_t offset_flags;
312 int comp_len, uncomp_len, base_rev, link_rev, parent_1, parent_2;
312 int comp_len, uncomp_len, base_rev, link_rev, parent_1, parent_2;
313 const char *c_node_id;
313 const char *c_node_id;
314 const char *data;
314 const char *data;
315 Py_ssize_t length = index_length(self);
315 Py_ssize_t length = index_length(self);
316 PyObject *entry;
316 PyObject *entry;
317
317
318 if (pos < 0)
318 if (pos < 0)
319 pos += length;
319 pos += length;
320
320
321 if (pos < 0 || pos >= length) {
321 if (pos < 0 || pos >= length) {
322 PyErr_SetString(PyExc_IndexError, "revlog index out of range");
322 PyErr_SetString(PyExc_IndexError, "revlog index out of range");
323 return NULL;
323 return NULL;
324 }
324 }
325
325
326 if (pos == length - 1) {
326 if (pos == length - 1) {
327 Py_INCREF(nullentry);
327 Py_INCREF(nullentry);
328 return nullentry;
328 return nullentry;
329 }
329 }
330
330
331 if (pos >= self->length - 1) {
331 if (pos >= self->length - 1) {
332 PyObject *obj;
332 PyObject *obj;
333 obj = PyList_GET_ITEM(self->added, pos - self->length + 1);
333 obj = PyList_GET_ITEM(self->added, pos - self->length + 1);
334 Py_INCREF(obj);
334 Py_INCREF(obj);
335 return obj;
335 return obj;
336 }
336 }
337
337
338 if (self->cache) {
338 if (self->cache) {
339 if (self->cache[pos]) {
339 if (self->cache[pos]) {
340 Py_INCREF(self->cache[pos]);
340 Py_INCREF(self->cache[pos]);
341 return self->cache[pos];
341 return self->cache[pos];
342 }
342 }
343 } else {
343 } else {
344 self->cache = calloc(self->raw_length, sizeof(PyObject *));
344 self->cache = calloc(self->raw_length, sizeof(PyObject *));
345 if (self->cache == NULL)
345 if (self->cache == NULL)
346 return PyErr_NoMemory();
346 return PyErr_NoMemory();
347 }
347 }
348
348
349 data = index_deref(self, pos);
349 data = index_deref(self, pos);
350 if (data == NULL)
350 if (data == NULL)
351 return NULL;
351 return NULL;
352
352
353 offset_flags = getbe32(data + 4);
353 offset_flags = getbe32(data + 4);
354 if (pos == 0) /* mask out version number for the first entry */
354 if (pos == 0) /* mask out version number for the first entry */
355 offset_flags &= 0xFFFF;
355 offset_flags &= 0xFFFF;
356 else {
356 else {
357 uint32_t offset_high = getbe32(data);
357 uint32_t offset_high = getbe32(data);
358 offset_flags |= ((uint64_t)offset_high) << 32;
358 offset_flags |= ((uint64_t)offset_high) << 32;
359 }
359 }
360
360
361 comp_len = getbe32(data + 8);
361 comp_len = getbe32(data + 8);
362 uncomp_len = getbe32(data + 12);
362 uncomp_len = getbe32(data + 12);
363 base_rev = getbe32(data + 16);
363 base_rev = getbe32(data + 16);
364 link_rev = getbe32(data + 20);
364 link_rev = getbe32(data + 20);
365 parent_1 = getbe32(data + 24);
365 parent_1 = getbe32(data + 24);
366 parent_2 = getbe32(data + 28);
366 parent_2 = getbe32(data + 28);
367 c_node_id = data + 32;
367 c_node_id = data + 32;
368
368
369 entry = Py_BuildValue(tuple_format, offset_flags, comp_len,
369 entry = Py_BuildValue(tuple_format, offset_flags, comp_len,
370 uncomp_len, base_rev, link_rev,
370 uncomp_len, base_rev, link_rev,
371 parent_1, parent_2, c_node_id, 20);
371 parent_1, parent_2, c_node_id, 20);
372
372
373 if (entry)
373 if (entry)
374 PyObject_GC_UnTrack(entry);
374 PyObject_GC_UnTrack(entry);
375
375
376 self->cache[pos] = entry;
376 self->cache[pos] = entry;
377 Py_INCREF(entry);
377 Py_INCREF(entry);
378
378
379 return entry;
379 return entry;
380 }
380 }
381
381
382 /*
382 /*
383 * Return the 20-byte SHA of the node corresponding to the given rev.
383 * Return the 20-byte SHA of the node corresponding to the given rev.
384 */
384 */
385 static const char *index_node(indexObject *self, Py_ssize_t pos)
385 static const char *index_node(indexObject *self, Py_ssize_t pos)
386 {
386 {
387 Py_ssize_t length = index_length(self);
387 Py_ssize_t length = index_length(self);
388 const char *data;
388 const char *data;
389
389
390 if (pos == length - 1)
390 if (pos == length - 1)
391 return nullid;
391 return nullid;
392
392
393 if (pos >= length)
393 if (pos >= length)
394 return NULL;
394 return NULL;
395
395
396 if (pos >= self->length - 1) {
396 if (pos >= self->length - 1) {
397 PyObject *tuple, *str;
397 PyObject *tuple, *str;
398 tuple = PyList_GET_ITEM(self->added, pos - self->length + 1);
398 tuple = PyList_GET_ITEM(self->added, pos - self->length + 1);
399 str = PyTuple_GetItem(tuple, 7);
399 str = PyTuple_GetItem(tuple, 7);
400 return str ? PyString_AS_STRING(str) : NULL;
400 return str ? PyString_AS_STRING(str) : NULL;
401 }
401 }
402
402
403 data = index_deref(self, pos);
403 data = index_deref(self, pos);
404 return data ? data + 32 : NULL;
404 return data ? data + 32 : NULL;
405 }
405 }
406
406
407 static int nt_insert(indexObject *self, const char *node, int rev);
407 static int nt_insert(indexObject *self, const char *node, int rev);
408
408
409 static int node_check(PyObject *obj, char **node, Py_ssize_t *nodelen)
409 static int node_check(PyObject *obj, char **node, Py_ssize_t *nodelen)
410 {
410 {
411 if (PyString_AsStringAndSize(obj, node, nodelen) == -1)
411 if (PyString_AsStringAndSize(obj, node, nodelen) == -1)
412 return -1;
412 return -1;
413 if (*nodelen == 20)
413 if (*nodelen == 20)
414 return 0;
414 return 0;
415 PyErr_SetString(PyExc_ValueError, "20-byte hash required");
415 PyErr_SetString(PyExc_ValueError, "20-byte hash required");
416 return -1;
416 return -1;
417 }
417 }
418
418
419 static PyObject *index_insert(indexObject *self, PyObject *args)
419 static PyObject *index_insert(indexObject *self, PyObject *args)
420 {
420 {
421 PyObject *obj;
421 PyObject *obj;
422 char *node;
422 char *node;
423 long offset;
423 long offset;
424 Py_ssize_t len, nodelen;
424 Py_ssize_t len, nodelen;
425
425
426 if (!PyArg_ParseTuple(args, "lO", &offset, &obj))
426 if (!PyArg_ParseTuple(args, "lO", &offset, &obj))
427 return NULL;
427 return NULL;
428
428
429 if (!PyTuple_Check(obj) || PyTuple_GET_SIZE(obj) != 8) {
429 if (!PyTuple_Check(obj) || PyTuple_GET_SIZE(obj) != 8) {
430 PyErr_SetString(PyExc_TypeError, "8-tuple required");
430 PyErr_SetString(PyExc_TypeError, "8-tuple required");
431 return NULL;
431 return NULL;
432 }
432 }
433
433
434 if (node_check(PyTuple_GET_ITEM(obj, 7), &node, &nodelen) == -1)
434 if (node_check(PyTuple_GET_ITEM(obj, 7), &node, &nodelen) == -1)
435 return NULL;
435 return NULL;
436
436
437 len = index_length(self);
437 len = index_length(self);
438
438
439 if (offset < 0)
439 if (offset < 0)
440 offset += len;
440 offset += len;
441
441
442 if (offset != len - 1) {
442 if (offset != len - 1) {
443 PyErr_SetString(PyExc_IndexError,
443 PyErr_SetString(PyExc_IndexError,
444 "insert only supported at index -1");
444 "insert only supported at index -1");
445 return NULL;
445 return NULL;
446 }
446 }
447
447
448 if (offset > INT_MAX) {
448 if (offset > INT_MAX) {
449 PyErr_SetString(PyExc_ValueError,
449 PyErr_SetString(PyExc_ValueError,
450 "currently only 2**31 revs supported");
450 "currently only 2**31 revs supported");
451 return NULL;
451 return NULL;
452 }
452 }
453
453
454 if (self->added == NULL) {
454 if (self->added == NULL) {
455 self->added = PyList_New(0);
455 self->added = PyList_New(0);
456 if (self->added == NULL)
456 if (self->added == NULL)
457 return NULL;
457 return NULL;
458 }
458 }
459
459
460 if (PyList_Append(self->added, obj) == -1)
460 if (PyList_Append(self->added, obj) == -1)
461 return NULL;
461 return NULL;
462
462
463 if (self->nt)
463 if (self->nt)
464 nt_insert(self, node, (int)offset);
464 nt_insert(self, node, (int)offset);
465
465
466 Py_RETURN_NONE;
466 Py_RETURN_NONE;
467 }
467 }
468
468
469 static void _index_clearcaches(indexObject *self)
469 static void _index_clearcaches(indexObject *self)
470 {
470 {
471 if (self->cache) {
471 if (self->cache) {
472 Py_ssize_t i;
472 Py_ssize_t i;
473
473
474 for (i = 0; i < self->raw_length; i++) {
474 for (i = 0; i < self->raw_length; i++) {
475 if (self->cache[i]) {
475 if (self->cache[i]) {
476 Py_DECREF(self->cache[i]);
476 Py_DECREF(self->cache[i]);
477 self->cache[i] = NULL;
477 self->cache[i] = NULL;
478 }
478 }
479 }
479 }
480 free(self->cache);
480 free(self->cache);
481 self->cache = NULL;
481 self->cache = NULL;
482 }
482 }
483 if (self->offsets) {
483 if (self->offsets) {
484 free(self->offsets);
484 free(self->offsets);
485 self->offsets = NULL;
485 self->offsets = NULL;
486 }
486 }
487 if (self->nt) {
487 if (self->nt) {
488 free(self->nt);
488 free(self->nt);
489 self->nt = NULL;
489 self->nt = NULL;
490 }
490 }
491 }
491 }
492
492
493 static PyObject *index_clearcaches(indexObject *self)
493 static PyObject *index_clearcaches(indexObject *self)
494 {
494 {
495 _index_clearcaches(self);
495 _index_clearcaches(self);
496 self->ntlength = self->ntcapacity = 0;
496 self->ntlength = self->ntcapacity = 0;
497 self->ntdepth = self->ntsplits = 0;
497 self->ntdepth = self->ntsplits = 0;
498 self->ntrev = -1;
498 self->ntrev = -1;
499 self->ntlookups = self->ntmisses = 0;
499 self->ntlookups = self->ntmisses = 0;
500 Py_RETURN_NONE;
500 Py_RETURN_NONE;
501 }
501 }
502
502
503 static PyObject *index_stats(indexObject *self)
503 static PyObject *index_stats(indexObject *self)
504 {
504 {
505 PyObject *obj = PyDict_New();
505 PyObject *obj = PyDict_New();
506
506
507 if (obj == NULL)
507 if (obj == NULL)
508 return NULL;
508 return NULL;
509
509
510 #define istat(__n, __d) \
510 #define istat(__n, __d) \
511 if (PyDict_SetItemString(obj, __d, PyInt_FromLong(self->__n)) == -1) \
511 if (PyDict_SetItemString(obj, __d, PyInt_FromLong(self->__n)) == -1) \
512 goto bail;
512 goto bail;
513
513
514 if (self->added) {
514 if (self->added) {
515 Py_ssize_t len = PyList_GET_SIZE(self->added);
515 Py_ssize_t len = PyList_GET_SIZE(self->added);
516 if (PyDict_SetItemString(obj, "index entries added",
516 if (PyDict_SetItemString(obj, "index entries added",
517 PyInt_FromLong(len)) == -1)
517 PyInt_FromLong(len)) == -1)
518 goto bail;
518 goto bail;
519 }
519 }
520
520
521 if (self->raw_length != self->length - 1)
521 if (self->raw_length != self->length - 1)
522 istat(raw_length, "revs on disk");
522 istat(raw_length, "revs on disk");
523 istat(length, "revs in memory");
523 istat(length, "revs in memory");
524 istat(ntcapacity, "node trie capacity");
524 istat(ntcapacity, "node trie capacity");
525 istat(ntdepth, "node trie depth");
525 istat(ntdepth, "node trie depth");
526 istat(ntlength, "node trie count");
526 istat(ntlength, "node trie count");
527 istat(ntlookups, "node trie lookups");
527 istat(ntlookups, "node trie lookups");
528 istat(ntmisses, "node trie misses");
528 istat(ntmisses, "node trie misses");
529 istat(ntrev, "node trie last rev scanned");
529 istat(ntrev, "node trie last rev scanned");
530 istat(ntsplits, "node trie splits");
530 istat(ntsplits, "node trie splits");
531
531
532 #undef istat
532 #undef istat
533
533
534 return obj;
534 return obj;
535
535
536 bail:
536 bail:
537 Py_XDECREF(obj);
537 Py_XDECREF(obj);
538 return NULL;
538 return NULL;
539 }
539 }
540
540
541 static inline int nt_level(const char *node, Py_ssize_t level)
541 static inline int nt_level(const char *node, Py_ssize_t level)
542 {
542 {
543 int v = node[level>>1];
543 int v = node[level>>1];
544 if (!(level & 1))
544 if (!(level & 1))
545 v >>= 4;
545 v >>= 4;
546 return v & 0xf;
546 return v & 0xf;
547 }
547 }
548
548
549 /*
549 /*
550 * Return values:
550 * Return values:
551 *
551 *
552 * -4: match is ambiguous (multiple candidates)
552 * -4: match is ambiguous (multiple candidates)
553 * -2: not found
553 * -2: not found
554 * rest: valid rev
554 * rest: valid rev
555 */
555 */
556 static int nt_find(indexObject *self, const char *node, Py_ssize_t nodelen)
556 static int nt_find(indexObject *self, const char *node, Py_ssize_t nodelen)
557 {
557 {
558 int level, off;
558 int level, off;
559
559
560 if (nodelen == 20 && node[0] == '\0' && memcmp(node, nullid, 20) == 0)
560 if (nodelen == 20 && node[0] == '\0' && memcmp(node, nullid, 20) == 0)
561 return -1;
561 return -1;
562
562
563 if (self->nt == NULL)
563 if (self->nt == NULL)
564 return -2;
564 return -2;
565
565
566 for (level = off = 0; level < nodelen; level++) {
566 for (level = off = 0; level < nodelen; level++) {
567 int k = nt_level(node, level);
567 int k = nt_level(node, level);
568 nodetree *n = &self->nt[off];
568 nodetree *n = &self->nt[off];
569 int v = n->children[k];
569 int v = n->children[k];
570
570
571 if (v < 0) {
571 if (v < 0) {
572 const char *n;
572 const char *n;
573 v = -v - 1;
573 v = -v - 1;
574 n = index_node(self, v);
574 n = index_node(self, v);
575 if (n == NULL)
575 if (n == NULL)
576 return -2;
576 return -2;
577 return memcmp(node, n, nodelen > 20 ? 20 : nodelen)
577 return memcmp(node, n, nodelen > 20 ? 20 : nodelen)
578 ? -2 : v;
578 ? -2 : v;
579 }
579 }
580 if (v == 0)
580 if (v == 0)
581 return -2;
581 return -2;
582 off = v;
582 off = v;
583 }
583 }
584 /* multiple matches against an ambiguous prefix */
584 /* multiple matches against an ambiguous prefix */
585 return -4;
585 return -4;
586 }
586 }
587
587
588 static int nt_new(indexObject *self)
588 static int nt_new(indexObject *self)
589 {
589 {
590 if (self->ntlength == self->ntcapacity) {
590 if (self->ntlength == self->ntcapacity) {
591 self->ntcapacity *= 2;
591 self->ntcapacity *= 2;
592 self->nt = realloc(self->nt,
592 self->nt = realloc(self->nt,
593 self->ntcapacity * sizeof(nodetree));
593 self->ntcapacity * sizeof(nodetree));
594 if (self->nt == NULL) {
594 if (self->nt == NULL) {
595 PyErr_SetString(PyExc_MemoryError, "out of memory");
595 PyErr_SetString(PyExc_MemoryError, "out of memory");
596 return -1;
596 return -1;
597 }
597 }
598 memset(&self->nt[self->ntlength], 0,
598 memset(&self->nt[self->ntlength], 0,
599 sizeof(nodetree) * (self->ntcapacity - self->ntlength));
599 sizeof(nodetree) * (self->ntcapacity - self->ntlength));
600 }
600 }
601 return self->ntlength++;
601 return self->ntlength++;
602 }
602 }
603
603
604 static int nt_insert(indexObject *self, const char *node, int rev)
604 static int nt_insert(indexObject *self, const char *node, int rev)
605 {
605 {
606 int level = 0;
606 int level = 0;
607 int off = 0;
607 int off = 0;
608
608
609 while (level < 20) {
609 while (level < 20) {
610 int k = nt_level(node, level);
610 int k = nt_level(node, level);
611 nodetree *n;
611 nodetree *n;
612 int v;
612 int v;
613
613
614 n = &self->nt[off];
614 n = &self->nt[off];
615 v = n->children[k];
615 v = n->children[k];
616
616
617 if (v == 0) {
617 if (v == 0) {
618 n->children[k] = -rev - 1;
618 n->children[k] = -rev - 1;
619 return 0;
619 return 0;
620 }
620 }
621 if (v < 0) {
621 if (v < 0) {
622 const char *oldnode = index_node(self, -v - 1);
622 const char *oldnode = index_node(self, -v - 1);
623 int noff;
623 int noff;
624
624
625 if (!oldnode || !memcmp(oldnode, node, 20)) {
625 if (!oldnode || !memcmp(oldnode, node, 20)) {
626 n->children[k] = -rev - 1;
626 n->children[k] = -rev - 1;
627 return 0;
627 return 0;
628 }
628 }
629 noff = nt_new(self);
629 noff = nt_new(self);
630 if (noff == -1)
630 if (noff == -1)
631 return -1;
631 return -1;
632 /* self->nt may have been changed by realloc */
632 /* self->nt may have been changed by realloc */
633 self->nt[off].children[k] = noff;
633 self->nt[off].children[k] = noff;
634 off = noff;
634 off = noff;
635 n = &self->nt[off];
635 n = &self->nt[off];
636 n->children[nt_level(oldnode, ++level)] = v;
636 n->children[nt_level(oldnode, ++level)] = v;
637 if (level > self->ntdepth)
637 if (level > self->ntdepth)
638 self->ntdepth = level;
638 self->ntdepth = level;
639 self->ntsplits += 1;
639 self->ntsplits += 1;
640 } else {
640 } else {
641 level += 1;
641 level += 1;
642 off = v;
642 off = v;
643 }
643 }
644 }
644 }
645
645
646 return -1;
646 return -1;
647 }
647 }
648
648
649 static int nt_init(indexObject *self)
649 static int nt_init(indexObject *self)
650 {
650 {
651 if (self->nt == NULL) {
651 if (self->nt == NULL) {
652 self->ntcapacity = self->raw_length < 4
652 self->ntcapacity = self->raw_length < 4
653 ? 4 : self->raw_length / 2;
653 ? 4 : self->raw_length / 2;
654 self->nt = calloc(self->ntcapacity, sizeof(nodetree));
654 self->nt = calloc(self->ntcapacity, sizeof(nodetree));
655 if (self->nt == NULL) {
655 if (self->nt == NULL) {
656 PyErr_NoMemory();
656 PyErr_NoMemory();
657 return -1;
657 return -1;
658 }
658 }
659 self->ntlength = 1;
659 self->ntlength = 1;
660 self->ntrev = (int)index_length(self) - 1;
660 self->ntrev = (int)index_length(self) - 1;
661 self->ntlookups = 1;
661 self->ntlookups = 1;
662 self->ntmisses = 0;
662 self->ntmisses = 0;
663 }
663 }
664 return 0;
664 return 0;
665 }
665 }
666
666
667 /*
667 /*
668 * Return values:
668 * Return values:
669 *
669 *
670 * -3: error (exception set)
670 * -3: error (exception set)
671 * -2: not found (no exception set)
671 * -2: not found (no exception set)
672 * rest: valid rev
672 * rest: valid rev
673 */
673 */
674 static int index_find_node(indexObject *self,
674 static int index_find_node(indexObject *self,
675 const char *node, Py_ssize_t nodelen)
675 const char *node, Py_ssize_t nodelen)
676 {
676 {
677 int rev;
677 int rev;
678
678
679 self->ntlookups++;
679 self->ntlookups++;
680 rev = nt_find(self, node, nodelen);
680 rev = nt_find(self, node, nodelen);
681 if (rev >= -1)
681 if (rev >= -1)
682 return rev;
682 return rev;
683
683
684 if (nt_init(self) == -1)
684 if (nt_init(self) == -1)
685 return -3;
685 return -3;
686
686
687 /*
687 /*
688 * For the first handful of lookups, we scan the entire index,
688 * For the first handful of lookups, we scan the entire index,
689 * and cache only the matching nodes. This optimizes for cases
689 * and cache only the matching nodes. This optimizes for cases
690 * like "hg tip", where only a few nodes are accessed.
690 * like "hg tip", where only a few nodes are accessed.
691 *
691 *
692 * After that, we cache every node we visit, using a single
692 * After that, we cache every node we visit, using a single
693 * scan amortized over multiple lookups. This gives the best
693 * scan amortized over multiple lookups. This gives the best
694 * bulk performance, e.g. for "hg log".
694 * bulk performance, e.g. for "hg log".
695 */
695 */
696 if (self->ntmisses++ < 4) {
696 if (self->ntmisses++ < 4) {
697 for (rev = self->ntrev - 1; rev >= 0; rev--) {
697 for (rev = self->ntrev - 1; rev >= 0; rev--) {
698 const char *n = index_node(self, rev);
698 const char *n = index_node(self, rev);
699 if (n == NULL)
699 if (n == NULL)
700 return -2;
700 return -2;
701 if (memcmp(node, n, nodelen > 20 ? 20 : nodelen) == 0) {
701 if (memcmp(node, n, nodelen > 20 ? 20 : nodelen) == 0) {
702 if (nt_insert(self, n, rev) == -1)
702 if (nt_insert(self, n, rev) == -1)
703 return -3;
703 return -3;
704 break;
704 break;
705 }
705 }
706 }
706 }
707 } else {
707 } else {
708 for (rev = self->ntrev - 1; rev >= 0; rev--) {
708 for (rev = self->ntrev - 1; rev >= 0; rev--) {
709 const char *n = index_node(self, rev);
709 const char *n = index_node(self, rev);
710 if (n == NULL) {
710 if (n == NULL) {
711 self->ntrev = rev + 1;
711 self->ntrev = rev + 1;
712 return -2;
712 return -2;
713 }
713 }
714 if (nt_insert(self, n, rev) == -1) {
714 if (nt_insert(self, n, rev) == -1) {
715 self->ntrev = rev + 1;
715 self->ntrev = rev + 1;
716 return -3;
716 return -3;
717 }
717 }
718 if (memcmp(node, n, nodelen > 20 ? 20 : nodelen) == 0) {
718 if (memcmp(node, n, nodelen > 20 ? 20 : nodelen) == 0) {
719 break;
719 break;
720 }
720 }
721 }
721 }
722 self->ntrev = rev;
722 self->ntrev = rev;
723 }
723 }
724
724
725 if (rev >= 0)
725 if (rev >= 0)
726 return rev;
726 return rev;
727 return -2;
727 return -2;
728 }
728 }
729
729
730 static PyObject *raise_revlog_error(void)
730 static PyObject *raise_revlog_error(void)
731 {
731 {
732 static PyObject *errclass;
732 static PyObject *errclass;
733 PyObject *mod = NULL, *errobj;
733 PyObject *mod = NULL, *errobj;
734
734
735 if (errclass == NULL) {
735 if (errclass == NULL) {
736 PyObject *dict;
736 PyObject *dict;
737
737
738 mod = PyImport_ImportModule("mercurial.error");
738 mod = PyImport_ImportModule("mercurial.error");
739 if (mod == NULL)
739 if (mod == NULL)
740 goto classfail;
740 goto classfail;
741
741
742 dict = PyModule_GetDict(mod);
742 dict = PyModule_GetDict(mod);
743 if (dict == NULL)
743 if (dict == NULL)
744 goto classfail;
744 goto classfail;
745
745
746 errclass = PyDict_GetItemString(dict, "RevlogError");
746 errclass = PyDict_GetItemString(dict, "RevlogError");
747 if (errclass == NULL) {
747 if (errclass == NULL) {
748 PyErr_SetString(PyExc_SystemError,
748 PyErr_SetString(PyExc_SystemError,
749 "could not find RevlogError");
749 "could not find RevlogError");
750 goto classfail;
750 goto classfail;
751 }
751 }
752 Py_INCREF(errclass);
752 Py_INCREF(errclass);
753 }
753 }
754
754
755 errobj = PyObject_CallFunction(errclass, NULL);
755 errobj = PyObject_CallFunction(errclass, NULL);
756 if (errobj == NULL)
756 if (errobj == NULL)
757 return NULL;
757 return NULL;
758 PyErr_SetObject(errclass, errobj);
758 PyErr_SetObject(errclass, errobj);
759 return errobj;
759 return errobj;
760
760
761 classfail:
761 classfail:
762 Py_XDECREF(mod);
762 Py_XDECREF(mod);
763 return NULL;
763 return NULL;
764 }
764 }
765
765
766 static PyObject *index_getitem(indexObject *self, PyObject *value)
766 static PyObject *index_getitem(indexObject *self, PyObject *value)
767 {
767 {
768 char *node;
768 char *node;
769 Py_ssize_t nodelen;
769 Py_ssize_t nodelen;
770 int rev;
770 int rev;
771
771
772 if (PyInt_Check(value))
772 if (PyInt_Check(value))
773 return index_get(self, PyInt_AS_LONG(value));
773 return index_get(self, PyInt_AS_LONG(value));
774
774
775 if (PyString_AsStringAndSize(value, &node, &nodelen) == -1)
775 if (PyString_AsStringAndSize(value, &node, &nodelen) == -1)
776 return NULL;
776 return NULL;
777 rev = index_find_node(self, node, nodelen);
777 rev = index_find_node(self, node, nodelen);
778 if (rev >= -1)
778 if (rev >= -1)
779 return PyInt_FromLong(rev);
779 return PyInt_FromLong(rev);
780 if (rev == -2)
780 if (rev == -2)
781 raise_revlog_error();
781 raise_revlog_error();
782 return NULL;
782 return NULL;
783 }
783 }
784
784
785 static PyObject *index_m_get(indexObject *self, PyObject *args)
785 static PyObject *index_m_get(indexObject *self, PyObject *args)
786 {
786 {
787 char *node;
787 char *node;
788 int nodelen, rev;
788 int nodelen, rev;
789
789
790 if (!PyArg_ParseTuple(args, "s#", &node, &nodelen))
790 if (!PyArg_ParseTuple(args, "s#", &node, &nodelen))
791 return NULL;
791 return NULL;
792
792
793 rev = index_find_node(self, node, nodelen);
793 rev = index_find_node(self, node, nodelen);
794 if (rev == -3)
794 if (rev == -3)
795 return NULL;
795 return NULL;
796 if (rev == -2)
796 if (rev == -2)
797 Py_RETURN_NONE;
797 Py_RETURN_NONE;
798 return PyInt_FromLong(rev);
798 return PyInt_FromLong(rev);
799 }
799 }
800
800
801 static int index_contains(indexObject *self, PyObject *value)
801 static int index_contains(indexObject *self, PyObject *value)
802 {
802 {
803 char *node;
803 char *node;
804 Py_ssize_t nodelen;
804 Py_ssize_t nodelen;
805
805
806 if (PyInt_Check(value)) {
806 if (PyInt_Check(value)) {
807 long rev = PyInt_AS_LONG(value);
807 long rev = PyInt_AS_LONG(value);
808 return rev >= -1 && rev < index_length(self);
808 return rev >= -1 && rev < index_length(self);
809 }
809 }
810
810
811 if (!PyString_Check(value))
811 if (!PyString_Check(value))
812 return 0;
812 return 0;
813
813
814 node = PyString_AS_STRING(value);
814 node = PyString_AS_STRING(value);
815 nodelen = PyString_GET_SIZE(value);
815 nodelen = PyString_GET_SIZE(value);
816
816
817 switch (index_find_node(self, node, nodelen)) {
817 switch (index_find_node(self, node, nodelen)) {
818 case -3:
818 case -3:
819 return -1;
819 return -1;
820 case -2:
820 case -2:
821 return 0;
821 return 0;
822 default:
822 default:
823 return 1;
823 return 1;
824 }
824 }
825 }
825 }
826
826
827 /*
827 /*
828 * Invalidate any trie entries introduced by added revs.
828 * Invalidate any trie entries introduced by added revs.
829 */
829 */
830 static void nt_invalidate_added(indexObject *self, Py_ssize_t start)
830 static void nt_invalidate_added(indexObject *self, Py_ssize_t start)
831 {
831 {
832 Py_ssize_t i, len = PyList_GET_SIZE(self->added);
832 Py_ssize_t i, len = PyList_GET_SIZE(self->added);
833
833
834 for (i = start; i < len; i++) {
834 for (i = start; i < len; i++) {
835 PyObject *tuple = PyList_GET_ITEM(self->added, i);
835 PyObject *tuple = PyList_GET_ITEM(self->added, i);
836 PyObject *node = PyTuple_GET_ITEM(tuple, 7);
836 PyObject *node = PyTuple_GET_ITEM(tuple, 7);
837
837
838 nt_insert(self, PyString_AS_STRING(node), -1);
838 nt_insert(self, PyString_AS_STRING(node), -1);
839 }
839 }
840
840
841 if (start == 0) {
841 if (start == 0) {
842 Py_DECREF(self->added);
842 Py_DECREF(self->added);
843 self->added = NULL;
843 self->added = NULL;
844 }
844 }
845 }
845 }
846
846
847 /*
847 /*
848 * Delete a numeric range of revs, which must be at the end of the
848 * Delete a numeric range of revs, which must be at the end of the
849 * range, but exclude the sentinel nullid entry.
849 * range, but exclude the sentinel nullid entry.
850 */
850 */
851 static int index_slice_del(indexObject *self, PyObject *item)
851 static int index_slice_del(indexObject *self, PyObject *item)
852 {
852 {
853 Py_ssize_t start, stop, step, slicelength;
853 Py_ssize_t start, stop, step, slicelength;
854 Py_ssize_t length = index_length(self);
854 Py_ssize_t length = index_length(self);
855
855
856 if (PySlice_GetIndicesEx((PySliceObject*)item, length,
856 if (PySlice_GetIndicesEx((PySliceObject*)item, length,
857 &start, &stop, &step, &slicelength) < 0)
857 &start, &stop, &step, &slicelength) < 0)
858 return -1;
858 return -1;
859
859
860 if (slicelength <= 0)
860 if (slicelength <= 0)
861 return 0;
861 return 0;
862
862
863 if ((step < 0 && start < stop) || (step > 0 && start > stop))
863 if ((step < 0 && start < stop) || (step > 0 && start > stop))
864 stop = start;
864 stop = start;
865
865
866 if (step < 0) {
866 if (step < 0) {
867 stop = start + 1;
867 stop = start + 1;
868 start = stop + step*(slicelength - 1) - 1;
868 start = stop + step*(slicelength - 1) - 1;
869 step = -step;
869 step = -step;
870 }
870 }
871
871
872 if (step != 1) {
872 if (step != 1) {
873 PyErr_SetString(PyExc_ValueError,
873 PyErr_SetString(PyExc_ValueError,
874 "revlog index delete requires step size of 1");
874 "revlog index delete requires step size of 1");
875 return -1;
875 return -1;
876 }
876 }
877
877
878 if (stop != length - 1) {
878 if (stop != length - 1) {
879 PyErr_SetString(PyExc_IndexError,
879 PyErr_SetString(PyExc_IndexError,
880 "revlog index deletion indices are invalid");
880 "revlog index deletion indices are invalid");
881 return -1;
881 return -1;
882 }
882 }
883
883
884 if (start < self->length - 1) {
884 if (start < self->length - 1) {
885 if (self->nt) {
885 if (self->nt) {
886 Py_ssize_t i;
886 Py_ssize_t i;
887
887
888 for (i = start + 1; i < self->length - 1; i++) {
888 for (i = start + 1; i < self->length - 1; i++) {
889 const char *node = index_node(self, i);
889 const char *node = index_node(self, i);
890
890
891 if (node)
891 if (node)
892 nt_insert(self, node, -1);
892 nt_insert(self, node, -1);
893 }
893 }
894 if (self->added)
894 if (self->added)
895 nt_invalidate_added(self, 0);
895 nt_invalidate_added(self, 0);
896 if (self->ntrev > start)
896 if (self->ntrev > start)
897 self->ntrev = (int)start;
897 self->ntrev = (int)start;
898 }
898 }
899 self->length = start + 1;
899 self->length = start + 1;
900 return 0;
900 return 0;
901 }
901 }
902
902
903 if (self->nt) {
903 if (self->nt) {
904 nt_invalidate_added(self, start - self->length + 1);
904 nt_invalidate_added(self, start - self->length + 1);
905 if (self->ntrev > start)
905 if (self->ntrev > start)
906 self->ntrev = (int)start;
906 self->ntrev = (int)start;
907 }
907 }
908 return self->added
908 return self->added
909 ? PyList_SetSlice(self->added, start - self->length + 1,
909 ? PyList_SetSlice(self->added, start - self->length + 1,
910 PyList_GET_SIZE(self->added), NULL)
910 PyList_GET_SIZE(self->added), NULL)
911 : 0;
911 : 0;
912 }
912 }
913
913
914 /*
914 /*
915 * Supported ops:
915 * Supported ops:
916 *
916 *
917 * slice deletion
917 * slice deletion
918 * string assignment (extend node->rev mapping)
918 * string assignment (extend node->rev mapping)
919 * string deletion (shrink node->rev mapping)
919 * string deletion (shrink node->rev mapping)
920 */
920 */
921 static int index_assign_subscript(indexObject *self, PyObject *item,
921 static int index_assign_subscript(indexObject *self, PyObject *item,
922 PyObject *value)
922 PyObject *value)
923 {
923 {
924 char *node;
924 char *node;
925 Py_ssize_t nodelen;
925 Py_ssize_t nodelen;
926 long rev;
926 long rev;
927
927
928 if (PySlice_Check(item) && value == NULL)
928 if (PySlice_Check(item) && value == NULL)
929 return index_slice_del(self, item);
929 return index_slice_del(self, item);
930
930
931 if (node_check(item, &node, &nodelen) == -1)
931 if (node_check(item, &node, &nodelen) == -1)
932 return -1;
932 return -1;
933
933
934 if (value == NULL)
934 if (value == NULL)
935 return self->nt ? nt_insert(self, node, -1) : 0;
935 return self->nt ? nt_insert(self, node, -1) : 0;
936 rev = PyInt_AsLong(value);
936 rev = PyInt_AsLong(value);
937 if (rev > INT_MAX || rev < 0) {
937 if (rev > INT_MAX || rev < 0) {
938 if (!PyErr_Occurred())
938 if (!PyErr_Occurred())
939 PyErr_SetString(PyExc_ValueError, "rev out of range");
939 PyErr_SetString(PyExc_ValueError, "rev out of range");
940 return -1;
940 return -1;
941 }
941 }
942 return nt_insert(self, node, (int)rev);
942 return nt_insert(self, node, (int)rev);
943 }
943 }
944
944
945 /*
945 /*
946 * Find all RevlogNG entries in an index that has inline data. Update
946 * Find all RevlogNG entries in an index that has inline data. Update
947 * the optional "offsets" table with those entries.
947 * the optional "offsets" table with those entries.
948 */
948 */
949 static long inline_scan(indexObject *self, const char **offsets)
949 static long inline_scan(indexObject *self, const char **offsets)
950 {
950 {
951 const char *data = PyString_AS_STRING(self->data);
951 const char *data = PyString_AS_STRING(self->data);
952 const char *end = data + PyString_GET_SIZE(self->data);
952 const char *end = data + PyString_GET_SIZE(self->data);
953 const long hdrsize = 64;
953 const long hdrsize = 64;
954 long incr = hdrsize;
954 long incr = hdrsize;
955 Py_ssize_t len = 0;
955 Py_ssize_t len = 0;
956
956
957 while (data + hdrsize <= end) {
957 while (data + hdrsize <= end) {
958 uint32_t comp_len;
958 uint32_t comp_len;
959 const char *old_data;
959 const char *old_data;
960 /* 3rd element of header is length of compressed inline data */
960 /* 3rd element of header is length of compressed inline data */
961 comp_len = getbe32(data + 8);
961 comp_len = getbe32(data + 8);
962 incr = hdrsize + comp_len;
962 incr = hdrsize + comp_len;
963 if (incr < hdrsize)
963 if (incr < hdrsize)
964 break;
964 break;
965 if (offsets)
965 if (offsets)
966 offsets[len] = data;
966 offsets[len] = data;
967 len++;
967 len++;
968 old_data = data;
968 old_data = data;
969 data += incr;
969 data += incr;
970 if (data <= old_data)
970 if (data <= old_data)
971 break;
971 break;
972 }
972 }
973
973
974 if (data != end && data + hdrsize != end) {
974 if (data != end && data + hdrsize != end) {
975 if (!PyErr_Occurred())
975 if (!PyErr_Occurred())
976 PyErr_SetString(PyExc_ValueError, "corrupt index file");
976 PyErr_SetString(PyExc_ValueError, "corrupt index file");
977 return -1;
977 return -1;
978 }
978 }
979
979
980 return len;
980 return len;
981 }
981 }
982
982
983 static int index_init(indexObject *self, PyObject *args)
983 static int index_init(indexObject *self, PyObject *args)
984 {
984 {
985 PyObject *data_obj, *inlined_obj;
985 PyObject *data_obj, *inlined_obj;
986 Py_ssize_t size;
986 Py_ssize_t size;
987
987
988 if (!PyArg_ParseTuple(args, "OO", &data_obj, &inlined_obj))
988 if (!PyArg_ParseTuple(args, "OO", &data_obj, &inlined_obj))
989 return -1;
989 return -1;
990 if (!PyString_Check(data_obj)) {
990 if (!PyString_Check(data_obj)) {
991 PyErr_SetString(PyExc_TypeError, "data is not a string");
991 PyErr_SetString(PyExc_TypeError, "data is not a string");
992 return -1;
992 return -1;
993 }
993 }
994 size = PyString_GET_SIZE(data_obj);
994 size = PyString_GET_SIZE(data_obj);
995
995
996 self->inlined = inlined_obj && PyObject_IsTrue(inlined_obj);
996 self->inlined = inlined_obj && PyObject_IsTrue(inlined_obj);
997 self->data = data_obj;
997 self->data = data_obj;
998 self->cache = NULL;
998 self->cache = NULL;
999
999
1000 self->added = NULL;
1000 self->added = NULL;
1001 self->offsets = NULL;
1001 self->offsets = NULL;
1002 self->nt = NULL;
1002 self->nt = NULL;
1003 self->ntlength = self->ntcapacity = 0;
1003 self->ntlength = self->ntcapacity = 0;
1004 self->ntdepth = self->ntsplits = 0;
1004 self->ntdepth = self->ntsplits = 0;
1005 self->ntlookups = self->ntmisses = 0;
1005 self->ntlookups = self->ntmisses = 0;
1006 self->ntrev = -1;
1006 self->ntrev = -1;
1007 Py_INCREF(self->data);
1007 Py_INCREF(self->data);
1008
1008
1009 if (self->inlined) {
1009 if (self->inlined) {
1010 long len = inline_scan(self, NULL);
1010 long len = inline_scan(self, NULL);
1011 if (len == -1)
1011 if (len == -1)
1012 goto bail;
1012 goto bail;
1013 self->raw_length = len;
1013 self->raw_length = len;
1014 self->length = len + 1;
1014 self->length = len + 1;
1015 } else {
1015 } else {
1016 if (size % 64) {
1016 if (size % 64) {
1017 PyErr_SetString(PyExc_ValueError, "corrupt index file");
1017 PyErr_SetString(PyExc_ValueError, "corrupt index file");
1018 goto bail;
1018 goto bail;
1019 }
1019 }
1020 self->raw_length = size / 64;
1020 self->raw_length = size / 64;
1021 self->length = self->raw_length + 1;
1021 self->length = self->raw_length + 1;
1022 }
1022 }
1023
1023
1024 return 0;
1024 return 0;
1025 bail:
1025 bail:
1026 return -1;
1026 return -1;
1027 }
1027 }
1028
1028
1029 static PyObject *index_nodemap(indexObject *self)
1029 static PyObject *index_nodemap(indexObject *self)
1030 {
1030 {
1031 Py_INCREF(self);
1031 Py_INCREF(self);
1032 return (PyObject *)self;
1032 return (PyObject *)self;
1033 }
1033 }
1034
1034
1035 static void index_dealloc(indexObject *self)
1035 static void index_dealloc(indexObject *self)
1036 {
1036 {
1037 _index_clearcaches(self);
1037 _index_clearcaches(self);
1038 Py_DECREF(self->data);
1038 Py_DECREF(self->data);
1039 Py_XDECREF(self->added);
1039 Py_XDECREF(self->added);
1040 PyObject_Del(self);
1040 PyObject_Del(self);
1041 }
1041 }
1042
1042
1043 static PySequenceMethods index_sequence_methods = {
1043 static PySequenceMethods index_sequence_methods = {
1044 (lenfunc)index_length, /* sq_length */
1044 (lenfunc)index_length, /* sq_length */
1045 0, /* sq_concat */
1045 0, /* sq_concat */
1046 0, /* sq_repeat */
1046 0, /* sq_repeat */
1047 (ssizeargfunc)index_get, /* sq_item */
1047 (ssizeargfunc)index_get, /* sq_item */
1048 0, /* sq_slice */
1048 0, /* sq_slice */
1049 0, /* sq_ass_item */
1049 0, /* sq_ass_item */
1050 0, /* sq_ass_slice */
1050 0, /* sq_ass_slice */
1051 (objobjproc)index_contains, /* sq_contains */
1051 (objobjproc)index_contains, /* sq_contains */
1052 };
1052 };
1053
1053
1054 static PyMappingMethods index_mapping_methods = {
1054 static PyMappingMethods index_mapping_methods = {
1055 (lenfunc)index_length, /* mp_length */
1055 (lenfunc)index_length, /* mp_length */
1056 (binaryfunc)index_getitem, /* mp_subscript */
1056 (binaryfunc)index_getitem, /* mp_subscript */
1057 (objobjargproc)index_assign_subscript, /* mp_ass_subscript */
1057 (objobjargproc)index_assign_subscript, /* mp_ass_subscript */
1058 };
1058 };
1059
1059
1060 static PyMethodDef index_methods[] = {
1060 static PyMethodDef index_methods[] = {
1061 {"clearcaches", (PyCFunction)index_clearcaches, METH_NOARGS,
1061 {"clearcaches", (PyCFunction)index_clearcaches, METH_NOARGS,
1062 "clear the index caches"},
1062 "clear the index caches"},
1063 {"get", (PyCFunction)index_m_get, METH_VARARGS,
1063 {"get", (PyCFunction)index_m_get, METH_VARARGS,
1064 "get an index entry"},
1064 "get an index entry"},
1065 {"insert", (PyCFunction)index_insert, METH_VARARGS,
1065 {"insert", (PyCFunction)index_insert, METH_VARARGS,
1066 "insert an index entry"},
1066 "insert an index entry"},
1067 {"stats", (PyCFunction)index_stats, METH_NOARGS,
1067 {"stats", (PyCFunction)index_stats, METH_NOARGS,
1068 "stats for the index"},
1068 "stats for the index"},
1069 {NULL} /* Sentinel */
1069 {NULL} /* Sentinel */
1070 };
1070 };
1071
1071
1072 static PyGetSetDef index_getset[] = {
1072 static PyGetSetDef index_getset[] = {
1073 {"nodemap", (getter)index_nodemap, NULL, "nodemap", NULL},
1073 {"nodemap", (getter)index_nodemap, NULL, "nodemap", NULL},
1074 {NULL} /* Sentinel */
1074 {NULL} /* Sentinel */
1075 };
1075 };
1076
1076
1077 static PyTypeObject indexType = {
1077 static PyTypeObject indexType = {
1078 PyObject_HEAD_INIT(NULL)
1078 PyObject_HEAD_INIT(NULL)
1079 0, /* ob_size */
1079 0, /* ob_size */
1080 "parsers.index", /* tp_name */
1080 "parsers.index", /* tp_name */
1081 sizeof(indexObject), /* tp_basicsize */
1081 sizeof(indexObject), /* tp_basicsize */
1082 0, /* tp_itemsize */
1082 0, /* tp_itemsize */
1083 (destructor)index_dealloc, /* tp_dealloc */
1083 (destructor)index_dealloc, /* tp_dealloc */
1084 0, /* tp_print */
1084 0, /* tp_print */
1085 0, /* tp_getattr */
1085 0, /* tp_getattr */
1086 0, /* tp_setattr */
1086 0, /* tp_setattr */
1087 0, /* tp_compare */
1087 0, /* tp_compare */
1088 0, /* tp_repr */
1088 0, /* tp_repr */
1089 0, /* tp_as_number */
1089 0, /* tp_as_number */
1090 &index_sequence_methods, /* tp_as_sequence */
1090 &index_sequence_methods, /* tp_as_sequence */
1091 &index_mapping_methods, /* tp_as_mapping */
1091 &index_mapping_methods, /* tp_as_mapping */
1092 0, /* tp_hash */
1092 0, /* tp_hash */
1093 0, /* tp_call */
1093 0, /* tp_call */
1094 0, /* tp_str */
1094 0, /* tp_str */
1095 0, /* tp_getattro */
1095 0, /* tp_getattro */
1096 0, /* tp_setattro */
1096 0, /* tp_setattro */
1097 0, /* tp_as_buffer */
1097 0, /* tp_as_buffer */
1098 Py_TPFLAGS_DEFAULT, /* tp_flags */
1098 Py_TPFLAGS_DEFAULT, /* tp_flags */
1099 "revlog index", /* tp_doc */
1099 "revlog index", /* tp_doc */
1100 0, /* tp_traverse */
1100 0, /* tp_traverse */
1101 0, /* tp_clear */
1101 0, /* tp_clear */
1102 0, /* tp_richcompare */
1102 0, /* tp_richcompare */
1103 0, /* tp_weaklistoffset */
1103 0, /* tp_weaklistoffset */
1104 0, /* tp_iter */
1104 0, /* tp_iter */
1105 0, /* tp_iternext */
1105 0, /* tp_iternext */
1106 index_methods, /* tp_methods */
1106 index_methods, /* tp_methods */
1107 0, /* tp_members */
1107 0, /* tp_members */
1108 index_getset, /* tp_getset */
1108 index_getset, /* tp_getset */
1109 0, /* tp_base */
1109 0, /* tp_base */
1110 0, /* tp_dict */
1110 0, /* tp_dict */
1111 0, /* tp_descr_get */
1111 0, /* tp_descr_get */
1112 0, /* tp_descr_set */
1112 0, /* tp_descr_set */
1113 0, /* tp_dictoffset */
1113 0, /* tp_dictoffset */
1114 (initproc)index_init, /* tp_init */
1114 (initproc)index_init, /* tp_init */
1115 0, /* tp_alloc */
1115 0, /* tp_alloc */
1116 PyType_GenericNew, /* tp_new */
1117 };
1116 };
1118
1117
1119 /*
1118 /*
1120 * returns a tuple of the form (index, index, cache) with elements as
1119 * returns a tuple of the form (index, index, cache) with elements as
1121 * follows:
1120 * follows:
1122 *
1121 *
1123 * index: an index object that lazily parses RevlogNG records
1122 * index: an index object that lazily parses RevlogNG records
1124 * cache: if data is inlined, a tuple (index_file_content, 0), else None
1123 * cache: if data is inlined, a tuple (index_file_content, 0), else None
1125 *
1124 *
1126 * added complications are for backwards compatibility
1125 * added complications are for backwards compatibility
1127 */
1126 */
1128 static PyObject *parse_index2(PyObject *self, PyObject *args)
1127 static PyObject *parse_index2(PyObject *self, PyObject *args)
1129 {
1128 {
1130 PyObject *tuple = NULL, *cache = NULL;
1129 PyObject *tuple = NULL, *cache = NULL;
1131 indexObject *idx;
1130 indexObject *idx;
1132 int ret;
1131 int ret;
1133
1132
1134 idx = PyObject_New(indexObject, &indexType);
1133 idx = PyObject_New(indexObject, &indexType);
1135 if (idx == NULL)
1134 if (idx == NULL)
1136 goto bail;
1135 goto bail;
1137
1136
1138 ret = index_init(idx, args);
1137 ret = index_init(idx, args);
1139 if (ret == -1)
1138 if (ret == -1)
1140 goto bail;
1139 goto bail;
1141
1140
1142 if (idx->inlined) {
1141 if (idx->inlined) {
1143 cache = Py_BuildValue("iO", 0, idx->data);
1142 cache = Py_BuildValue("iO", 0, idx->data);
1144 if (cache == NULL)
1143 if (cache == NULL)
1145 goto bail;
1144 goto bail;
1146 } else {
1145 } else {
1147 cache = Py_None;
1146 cache = Py_None;
1148 Py_INCREF(cache);
1147 Py_INCREF(cache);
1149 }
1148 }
1150
1149
1151 tuple = Py_BuildValue("NN", idx, cache);
1150 tuple = Py_BuildValue("NN", idx, cache);
1152 if (!tuple)
1151 if (!tuple)
1153 goto bail;
1152 goto bail;
1154 return tuple;
1153 return tuple;
1155
1154
1156 bail:
1155 bail:
1157 Py_XDECREF(idx);
1156 Py_XDECREF(idx);
1158 Py_XDECREF(cache);
1157 Py_XDECREF(cache);
1159 Py_XDECREF(tuple);
1158 Py_XDECREF(tuple);
1160 return NULL;
1159 return NULL;
1161 }
1160 }
1162
1161
1163 static char parsers_doc[] = "Efficient content parsing.";
1162 static char parsers_doc[] = "Efficient content parsing.";
1164
1163
1165 static PyMethodDef methods[] = {
1164 static PyMethodDef methods[] = {
1166 {"parse_manifest", parse_manifest, METH_VARARGS, "parse a manifest\n"},
1165 {"parse_manifest", parse_manifest, METH_VARARGS, "parse a manifest\n"},
1167 {"parse_dirstate", parse_dirstate, METH_VARARGS, "parse a dirstate\n"},
1166 {"parse_dirstate", parse_dirstate, METH_VARARGS, "parse a dirstate\n"},
1168 {"parse_index2", parse_index2, METH_VARARGS, "parse a revlog index\n"},
1167 {"parse_index2", parse_index2, METH_VARARGS, "parse a revlog index\n"},
1169 {NULL, NULL}
1168 {NULL, NULL}
1170 };
1169 };
1171
1170
1172 static void module_init(PyObject *mod)
1171 static void module_init(PyObject *mod)
1173 {
1172 {
1173 indexType.tp_new = PyType_GenericNew;
1174 if (PyType_Ready(&indexType) < 0)
1174 if (PyType_Ready(&indexType) < 0)
1175 return;
1175 return;
1176 Py_INCREF(&indexType);
1176 Py_INCREF(&indexType);
1177
1177
1178 PyModule_AddObject(mod, "index", (PyObject *)&indexType);
1178 PyModule_AddObject(mod, "index", (PyObject *)&indexType);
1179
1179
1180 nullentry = Py_BuildValue("iiiiiiis#", 0, 0, 0,
1180 nullentry = Py_BuildValue("iiiiiiis#", 0, 0, 0,
1181 -1, -1, -1, -1, nullid, 20);
1181 -1, -1, -1, -1, nullid, 20);
1182 if (nullentry)
1182 if (nullentry)
1183 PyObject_GC_UnTrack(nullentry);
1183 PyObject_GC_UnTrack(nullentry);
1184 }
1184 }
1185
1185
1186 #ifdef IS_PY3K
1186 #ifdef IS_PY3K
1187 static struct PyModuleDef parsers_module = {
1187 static struct PyModuleDef parsers_module = {
1188 PyModuleDef_HEAD_INIT,
1188 PyModuleDef_HEAD_INIT,
1189 "parsers",
1189 "parsers",
1190 parsers_doc,
1190 parsers_doc,
1191 -1,
1191 -1,
1192 methods
1192 methods
1193 };
1193 };
1194
1194
1195 PyMODINIT_FUNC PyInit_parsers(void)
1195 PyMODINIT_FUNC PyInit_parsers(void)
1196 {
1196 {
1197 PyObject *mod = PyModule_Create(&parsers_module);
1197 PyObject *mod = PyModule_Create(&parsers_module);
1198 module_init(mod);
1198 module_init(mod);
1199 return mod;
1199 return mod;
1200 }
1200 }
1201 #else
1201 #else
1202 PyMODINIT_FUNC initparsers(void)
1202 PyMODINIT_FUNC initparsers(void)
1203 {
1203 {
1204 PyObject *mod = Py_InitModule3("parsers", methods, parsers_doc);
1204 PyObject *mod = Py_InitModule3("parsers", methods, parsers_doc);
1205 module_init(mod);
1205 module_init(mod);
1206 }
1206 }
1207 #endif
1207 #endif
@@ -1,416 +1,426 b''
1 $ "$TESTDIR/hghave" system-sh || exit 80
1 $ "$TESTDIR/hghave" system-sh || exit 80
2
2
3 $ HGFOO=BAR; export HGFOO
3 $ HGFOO=BAR; export HGFOO
4 $ cat >> $HGRCPATH <<EOF
4 $ cat >> $HGRCPATH <<EOF
5 > [extensions]
5 > [extensions]
6 > graphlog=
6 > graphlog=
7 >
7 >
8 > [alias]
8 > [alias]
9 > # should clobber ci but not commit (issue2993)
9 > # should clobber ci but not commit (issue2993)
10 > ci = version
10 > ci = version
11 > myinit = init
11 > myinit = init
12 > optionalrepo = showconfig alias.myinit
12 > cleanstatus = status -c
13 > cleanstatus = status -c
13 > unknown = bargle
14 > unknown = bargle
14 > ambiguous = s
15 > ambiguous = s
15 > recursive = recursive
16 > recursive = recursive
16 > nodefinition =
17 > nodefinition =
17 > no--cwd = status --cwd elsewhere
18 > no--cwd = status --cwd elsewhere
18 > no-R = status -R elsewhere
19 > no-R = status -R elsewhere
19 > no--repo = status --repo elsewhere
20 > no--repo = status --repo elsewhere
20 > no--repository = status --repository elsewhere
21 > no--repository = status --repository elsewhere
21 > mylog = log
22 > mylog = log
22 > lognull = log -r null
23 > lognull = log -r null
23 > shortlog = log --template '{rev} {node|short} | {date|isodate}\n'
24 > shortlog = log --template '{rev} {node|short} | {date|isodate}\n'
24 > positional = log --template '{\$2} {\$1} | {date|isodate}\n'
25 > positional = log --template '{\$2} {\$1} | {date|isodate}\n'
25 > dln = lognull --debug
26 > dln = lognull --debug
26 > nousage = rollback
27 > nousage = rollback
27 > put = export -r 0 -o "\$FOO/%R.diff"
28 > put = export -r 0 -o "\$FOO/%R.diff"
28 > blank = !echo
29 > blank = !echo
29 > self = !echo '\$0'
30 > self = !echo '\$0'
30 > echo = !echo '\$@'
31 > echo = !echo '\$@'
31 > echo1 = !echo '\$1'
32 > echo1 = !echo '\$1'
32 > echo2 = !echo '\$2'
33 > echo2 = !echo '\$2'
33 > echo13 = !echo '\$1' '\$3'
34 > echo13 = !echo '\$1' '\$3'
34 > count = !hg log -r '\$@' --template='.' | wc -c | sed -e 's/ //g'
35 > count = !hg log -r '\$@' --template='.' | wc -c | sed -e 's/ //g'
35 > mcount = !hg log \$@ --template='.' | wc -c | sed -e 's/ //g'
36 > mcount = !hg log \$@ --template='.' | wc -c | sed -e 's/ //g'
36 > rt = root
37 > rt = root
37 > tglog = glog --template "{rev}:{node|short}: '{desc}' {branches}\n"
38 > tglog = glog --template "{rev}:{node|short}: '{desc}' {branches}\n"
38 > idalias = id
39 > idalias = id
39 > idaliaslong = id
40 > idaliaslong = id
40 > idaliasshell = !echo test
41 > idaliasshell = !echo test
41 > parentsshell1 = !echo one
42 > parentsshell1 = !echo one
42 > parentsshell2 = !echo two
43 > parentsshell2 = !echo two
43 > escaped1 = !echo 'test\$\$test'
44 > escaped1 = !echo 'test\$\$test'
44 > escaped2 = !echo "HGFOO is \$\$HGFOO"
45 > escaped2 = !echo "HGFOO is \$\$HGFOO"
45 > escaped3 = !echo "\$1 is \$\$\$1"
46 > escaped3 = !echo "\$1 is \$\$\$1"
46 > escaped4 = !echo '\$\$0' '\$\$@'
47 > escaped4 = !echo '\$\$0' '\$\$@'
47 >
48 >
48 > [defaults]
49 > [defaults]
49 > mylog = -q
50 > mylog = -q
50 > lognull = -q
51 > lognull = -q
51 > log = -v
52 > log = -v
52 > EOF
53 > EOF
53
54
54
55
55 basic
56 basic
56
57
57 $ hg myinit alias
58 $ hg myinit alias
58
59
59
60
60 unknown
61 unknown
61
62
62 $ hg unknown
63 $ hg unknown
63 alias 'unknown' resolves to unknown command 'bargle'
64 alias 'unknown' resolves to unknown command 'bargle'
64 $ hg help unknown
65 $ hg help unknown
65 alias 'unknown' resolves to unknown command 'bargle'
66 alias 'unknown' resolves to unknown command 'bargle'
66
67
67
68
68 ambiguous
69 ambiguous
69
70
70 $ hg ambiguous
71 $ hg ambiguous
71 alias 'ambiguous' resolves to ambiguous command 's'
72 alias 'ambiguous' resolves to ambiguous command 's'
72 $ hg help ambiguous
73 $ hg help ambiguous
73 alias 'ambiguous' resolves to ambiguous command 's'
74 alias 'ambiguous' resolves to ambiguous command 's'
74
75
75
76
76 recursive
77 recursive
77
78
78 $ hg recursive
79 $ hg recursive
79 alias 'recursive' resolves to unknown command 'recursive'
80 alias 'recursive' resolves to unknown command 'recursive'
80 $ hg help recursive
81 $ hg help recursive
81 alias 'recursive' resolves to unknown command 'recursive'
82 alias 'recursive' resolves to unknown command 'recursive'
82
83
83
84
84 no definition
85 no definition
85
86
86 $ hg nodef
87 $ hg nodef
87 no definition for alias 'nodefinition'
88 no definition for alias 'nodefinition'
88 $ hg help nodef
89 $ hg help nodef
89 no definition for alias 'nodefinition'
90 no definition for alias 'nodefinition'
90
91
91
92
92 invalid options
93 invalid options
93
94
94 $ hg no--cwd
95 $ hg no--cwd
95 error in definition for alias 'no--cwd': --cwd may only be given on the command line
96 error in definition for alias 'no--cwd': --cwd may only be given on the command line
96 $ hg help no--cwd
97 $ hg help no--cwd
97 error in definition for alias 'no--cwd': --cwd may only be given on the command line
98 error in definition for alias 'no--cwd': --cwd may only be given on the command line
98 $ hg no-R
99 $ hg no-R
99 error in definition for alias 'no-R': -R may only be given on the command line
100 error in definition for alias 'no-R': -R may only be given on the command line
100 $ hg help no-R
101 $ hg help no-R
101 error in definition for alias 'no-R': -R may only be given on the command line
102 error in definition for alias 'no-R': -R may only be given on the command line
102 $ hg no--repo
103 $ hg no--repo
103 error in definition for alias 'no--repo': --repo may only be given on the command line
104 error in definition for alias 'no--repo': --repo may only be given on the command line
104 $ hg help no--repo
105 $ hg help no--repo
105 error in definition for alias 'no--repo': --repo may only be given on the command line
106 error in definition for alias 'no--repo': --repo may only be given on the command line
106 $ hg no--repository
107 $ hg no--repository
107 error in definition for alias 'no--repository': --repository may only be given on the command line
108 error in definition for alias 'no--repository': --repository may only be given on the command line
108 $ hg help no--repository
109 $ hg help no--repository
109 error in definition for alias 'no--repository': --repository may only be given on the command line
110 error in definition for alias 'no--repository': --repository may only be given on the command line
110
111
112 optional repository
113
114 $ hg optionalrepo
115 init
111 $ cd alias
116 $ cd alias
112
117 $ cat > .hg/hgrc <<EOF
118 > [alias]
119 > myinit = init -q
120 > EOF
121 $ hg optionalrepo
122 init -q
113
123
114 no usage
124 no usage
115
125
116 $ hg nousage
126 $ hg nousage
117 no rollback information available
127 no rollback information available
118
128
119 $ echo foo > foo
129 $ echo foo > foo
120 $ hg commit -Amfoo
130 $ hg commit -Amfoo
121 adding foo
131 adding foo
122
132
123
133
124 with opts
134 with opts
125
135
126 $ hg cleanst
136 $ hg cleanst
127 C foo
137 C foo
128
138
129
139
130 with opts and whitespace
140 with opts and whitespace
131
141
132 $ hg shortlog
142 $ hg shortlog
133 0 e63c23eaa88a | 1970-01-01 00:00 +0000
143 0 e63c23eaa88a | 1970-01-01 00:00 +0000
134
144
135 positional arguments
145 positional arguments
136
146
137 $ hg positional
147 $ hg positional
138 abort: too few arguments for command alias
148 abort: too few arguments for command alias
139 [255]
149 [255]
140 $ hg positional a
150 $ hg positional a
141 abort: too few arguments for command alias
151 abort: too few arguments for command alias
142 [255]
152 [255]
143 $ hg positional 'node|short' rev
153 $ hg positional 'node|short' rev
144 0 e63c23eaa88a | 1970-01-01 00:00 +0000
154 0 e63c23eaa88a | 1970-01-01 00:00 +0000
145
155
146 interaction with defaults
156 interaction with defaults
147
157
148 $ hg mylog
158 $ hg mylog
149 0:e63c23eaa88a
159 0:e63c23eaa88a
150 $ hg lognull
160 $ hg lognull
151 -1:000000000000
161 -1:000000000000
152
162
153
163
154 properly recursive
164 properly recursive
155
165
156 $ hg dln
166 $ hg dln
157 changeset: -1:0000000000000000000000000000000000000000
167 changeset: -1:0000000000000000000000000000000000000000
158 parent: -1:0000000000000000000000000000000000000000
168 parent: -1:0000000000000000000000000000000000000000
159 parent: -1:0000000000000000000000000000000000000000
169 parent: -1:0000000000000000000000000000000000000000
160 manifest: -1:0000000000000000000000000000000000000000
170 manifest: -1:0000000000000000000000000000000000000000
161 user:
171 user:
162 date: Thu Jan 01 00:00:00 1970 +0000
172 date: Thu Jan 01 00:00:00 1970 +0000
163 extra: branch=default
173 extra: branch=default
164
174
165
175
166
176
167 path expanding
177 path expanding
168
178
169 $ FOO=`pwd` hg put
179 $ FOO=`pwd` hg put
170 $ cat 0.diff
180 $ cat 0.diff
171 # HG changeset patch
181 # HG changeset patch
172 # User test
182 # User test
173 # Date 0 0
183 # Date 0 0
174 # Node ID e63c23eaa88ae77967edcf4ea194d31167c478b0
184 # Node ID e63c23eaa88ae77967edcf4ea194d31167c478b0
175 # Parent 0000000000000000000000000000000000000000
185 # Parent 0000000000000000000000000000000000000000
176 foo
186 foo
177
187
178 diff -r 000000000000 -r e63c23eaa88a foo
188 diff -r 000000000000 -r e63c23eaa88a foo
179 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
189 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
180 +++ b/foo Thu Jan 01 00:00:00 1970 +0000
190 +++ b/foo Thu Jan 01 00:00:00 1970 +0000
181 @@ -0,0 +1,1 @@
191 @@ -0,0 +1,1 @@
182 +foo
192 +foo
183
193
184
194
185 simple shell aliases
195 simple shell aliases
186
196
187 $ hg blank
197 $ hg blank
188
198
189 $ hg blank foo
199 $ hg blank foo
190
200
191 $ hg self
201 $ hg self
192 self
202 self
193 $ hg echo
203 $ hg echo
194
204
195 $ hg echo foo
205 $ hg echo foo
196 foo
206 foo
197 $ hg echo 'test $2' foo
207 $ hg echo 'test $2' foo
198 test $2 foo
208 test $2 foo
199 $ hg echo1 foo bar baz
209 $ hg echo1 foo bar baz
200 foo
210 foo
201 $ hg echo2 foo bar baz
211 $ hg echo2 foo bar baz
202 bar
212 bar
203 $ hg echo13 foo bar baz test
213 $ hg echo13 foo bar baz test
204 foo baz
214 foo baz
205 $ hg echo2 foo
215 $ hg echo2 foo
206
216
207 $ echo bar > bar
217 $ echo bar > bar
208 $ hg commit -qA -m bar
218 $ hg commit -qA -m bar
209 $ hg count .
219 $ hg count .
210 1
220 1
211 $ hg count 'branch(default)'
221 $ hg count 'branch(default)'
212 2
222 2
213 $ hg mcount -r '"branch(default)"'
223 $ hg mcount -r '"branch(default)"'
214 2
224 2
215
225
216 $ hg tglog
226 $ hg tglog
217 @ 1:7e7f92de180e: 'bar'
227 @ 1:7e7f92de180e: 'bar'
218 |
228 |
219 o 0:e63c23eaa88a: 'foo'
229 o 0:e63c23eaa88a: 'foo'
220
230
221
231
222
232
223 shadowing
233 shadowing
224
234
225 $ hg i
235 $ hg i
226 hg: command 'i' is ambiguous:
236 hg: command 'i' is ambiguous:
227 idalias idaliaslong idaliasshell identify import incoming init
237 idalias idaliaslong idaliasshell identify import incoming init
228 [255]
238 [255]
229 $ hg id
239 $ hg id
230 7e7f92de180e tip
240 7e7f92de180e tip
231 $ hg ida
241 $ hg ida
232 hg: command 'ida' is ambiguous:
242 hg: command 'ida' is ambiguous:
233 idalias idaliaslong idaliasshell
243 idalias idaliaslong idaliasshell
234 [255]
244 [255]
235 $ hg idalias
245 $ hg idalias
236 7e7f92de180e tip
246 7e7f92de180e tip
237 $ hg idaliasl
247 $ hg idaliasl
238 7e7f92de180e tip
248 7e7f92de180e tip
239 $ hg idaliass
249 $ hg idaliass
240 test
250 test
241 $ hg parentsshell
251 $ hg parentsshell
242 hg: command 'parentsshell' is ambiguous:
252 hg: command 'parentsshell' is ambiguous:
243 parentsshell1 parentsshell2
253 parentsshell1 parentsshell2
244 [255]
254 [255]
245 $ hg parentsshell1
255 $ hg parentsshell1
246 one
256 one
247 $ hg parentsshell2
257 $ hg parentsshell2
248 two
258 two
249
259
250
260
251 shell aliases with global options
261 shell aliases with global options
252
262
253 $ hg init sub
263 $ hg init sub
254 $ cd sub
264 $ cd sub
255 $ hg count 'branch(default)'
265 $ hg count 'branch(default)'
256 0
266 0
257 $ hg -v count 'branch(default)'
267 $ hg -v count 'branch(default)'
258 0
268 0
259 $ hg -R .. count 'branch(default)'
269 $ hg -R .. count 'branch(default)'
260 0
270 0
261 $ hg --cwd .. count 'branch(default)'
271 $ hg --cwd .. count 'branch(default)'
262 2
272 2
263 $ hg echo --cwd ..
273 $ hg echo --cwd ..
264
274
265
275
266
276
267 repo specific shell aliases
277 repo specific shell aliases
268
278
269 $ cat >> .hg/hgrc <<EOF
279 $ cat >> .hg/hgrc <<EOF
270 > [alias]
280 > [alias]
271 > subalias = !echo sub \$@
281 > subalias = !echo sub \$@
272 > EOF
282 > EOF
273 $ cat >> ../.hg/hgrc <<EOF
283 $ cat >> ../.hg/hgrc <<EOF
274 > [alias]
284 > [alias]
275 > mainalias = !echo main \$@
285 > mainalias = !echo main \$@
276 > EOF
286 > EOF
277
287
278
288
279 shell alias defined in current repo
289 shell alias defined in current repo
280
290
281 $ hg subalias
291 $ hg subalias
282 sub
292 sub
283 $ hg --cwd .. subalias > /dev/null
293 $ hg --cwd .. subalias > /dev/null
284 hg: unknown command 'subalias'
294 hg: unknown command 'subalias'
285 [255]
295 [255]
286 $ hg -R .. subalias > /dev/null
296 $ hg -R .. subalias > /dev/null
287 hg: unknown command 'subalias'
297 hg: unknown command 'subalias'
288 [255]
298 [255]
289
299
290
300
291 shell alias defined in other repo
301 shell alias defined in other repo
292
302
293 $ hg mainalias > /dev/null
303 $ hg mainalias > /dev/null
294 hg: unknown command 'mainalias'
304 hg: unknown command 'mainalias'
295 [255]
305 [255]
296 $ hg -R .. mainalias
306 $ hg -R .. mainalias
297 main
307 main
298 $ hg --cwd .. mainalias
308 $ hg --cwd .. mainalias
299 main
309 main
300
310
301
311
302 shell aliases with escaped $ chars
312 shell aliases with escaped $ chars
303
313
304 $ hg escaped1
314 $ hg escaped1
305 test$test
315 test$test
306 $ hg escaped2
316 $ hg escaped2
307 HGFOO is BAR
317 HGFOO is BAR
308 $ hg escaped3 HGFOO
318 $ hg escaped3 HGFOO
309 HGFOO is BAR
319 HGFOO is BAR
310 $ hg escaped4 test
320 $ hg escaped4 test
311 $0 $@
321 $0 $@
312
322
313
323
314 invalid arguments
324 invalid arguments
315
325
316 $ hg rt foo
326 $ hg rt foo
317 hg rt: invalid arguments
327 hg rt: invalid arguments
318 hg rt
328 hg rt
319
329
320 alias for: hg root
330 alias for: hg root
321
331
322 use "hg help rt" to show the full help text
332 use "hg help rt" to show the full help text
323 [255]
333 [255]
324
334
325 invalid global arguments for normal commands, aliases, and shell aliases
335 invalid global arguments for normal commands, aliases, and shell aliases
326
336
327 $ hg --invalid root
337 $ hg --invalid root
328 hg: option --invalid not recognized
338 hg: option --invalid not recognized
329 Mercurial Distributed SCM
339 Mercurial Distributed SCM
330
340
331 basic commands:
341 basic commands:
332
342
333 add add the specified files on the next commit
343 add add the specified files on the next commit
334 annotate show changeset information by line for each file
344 annotate show changeset information by line for each file
335 clone make a copy of an existing repository
345 clone make a copy of an existing repository
336 commit commit the specified files or all outstanding changes
346 commit commit the specified files or all outstanding changes
337 diff diff repository (or selected files)
347 diff diff repository (or selected files)
338 export dump the header and diffs for one or more changesets
348 export dump the header and diffs for one or more changesets
339 forget forget the specified files on the next commit
349 forget forget the specified files on the next commit
340 init create a new repository in the given directory
350 init create a new repository in the given directory
341 log show revision history of entire repository or files
351 log show revision history of entire repository or files
342 merge merge working directory with another revision
352 merge merge working directory with another revision
343 phase set or show the current phase name
353 phase set or show the current phase name
344 pull pull changes from the specified source
354 pull pull changes from the specified source
345 push push changes to the specified destination
355 push push changes to the specified destination
346 remove remove the specified files on the next commit
356 remove remove the specified files on the next commit
347 serve start stand-alone webserver
357 serve start stand-alone webserver
348 status show changed files in the working directory
358 status show changed files in the working directory
349 summary summarize working directory state
359 summary summarize working directory state
350 update update working directory (or switch revisions)
360 update update working directory (or switch revisions)
351
361
352 use "hg help" for the full list of commands or "hg -v" for details
362 use "hg help" for the full list of commands or "hg -v" for details
353 [255]
363 [255]
354 $ hg --invalid mylog
364 $ hg --invalid mylog
355 hg: option --invalid not recognized
365 hg: option --invalid not recognized
356 Mercurial Distributed SCM
366 Mercurial Distributed SCM
357
367
358 basic commands:
368 basic commands:
359
369
360 add add the specified files on the next commit
370 add add the specified files on the next commit
361 annotate show changeset information by line for each file
371 annotate show changeset information by line for each file
362 clone make a copy of an existing repository
372 clone make a copy of an existing repository
363 commit commit the specified files or all outstanding changes
373 commit commit the specified files or all outstanding changes
364 diff diff repository (or selected files)
374 diff diff repository (or selected files)
365 export dump the header and diffs for one or more changesets
375 export dump the header and diffs for one or more changesets
366 forget forget the specified files on the next commit
376 forget forget the specified files on the next commit
367 init create a new repository in the given directory
377 init create a new repository in the given directory
368 log show revision history of entire repository or files
378 log show revision history of entire repository or files
369 merge merge working directory with another revision
379 merge merge working directory with another revision
370 phase set or show the current phase name
380 phase set or show the current phase name
371 pull pull changes from the specified source
381 pull pull changes from the specified source
372 push push changes to the specified destination
382 push push changes to the specified destination
373 remove remove the specified files on the next commit
383 remove remove the specified files on the next commit
374 serve start stand-alone webserver
384 serve start stand-alone webserver
375 status show changed files in the working directory
385 status show changed files in the working directory
376 summary summarize working directory state
386 summary summarize working directory state
377 update update working directory (or switch revisions)
387 update update working directory (or switch revisions)
378
388
379 use "hg help" for the full list of commands or "hg -v" for details
389 use "hg help" for the full list of commands or "hg -v" for details
380 [255]
390 [255]
381 $ hg --invalid blank
391 $ hg --invalid blank
382 hg: option --invalid not recognized
392 hg: option --invalid not recognized
383 Mercurial Distributed SCM
393 Mercurial Distributed SCM
384
394
385 basic commands:
395 basic commands:
386
396
387 add add the specified files on the next commit
397 add add the specified files on the next commit
388 annotate show changeset information by line for each file
398 annotate show changeset information by line for each file
389 clone make a copy of an existing repository
399 clone make a copy of an existing repository
390 commit commit the specified files or all outstanding changes
400 commit commit the specified files or all outstanding changes
391 diff diff repository (or selected files)
401 diff diff repository (or selected files)
392 export dump the header and diffs for one or more changesets
402 export dump the header and diffs for one or more changesets
393 forget forget the specified files on the next commit
403 forget forget the specified files on the next commit
394 init create a new repository in the given directory
404 init create a new repository in the given directory
395 log show revision history of entire repository or files
405 log show revision history of entire repository or files
396 merge merge working directory with another revision
406 merge merge working directory with another revision
397 phase set or show the current phase name
407 phase set or show the current phase name
398 pull pull changes from the specified source
408 pull pull changes from the specified source
399 push push changes to the specified destination
409 push push changes to the specified destination
400 remove remove the specified files on the next commit
410 remove remove the specified files on the next commit
401 serve start stand-alone webserver
411 serve start stand-alone webserver
402 status show changed files in the working directory
412 status show changed files in the working directory
403 summary summarize working directory state
413 summary summarize working directory state
404 update update working directory (or switch revisions)
414 update update working directory (or switch revisions)
405
415
406 use "hg help" for the full list of commands or "hg -v" for details
416 use "hg help" for the full list of commands or "hg -v" for details
407 [255]
417 [255]
408
418
409 This should show id:
419 This should show id:
410
420
411 $ hg --config alias.log='id' log
421 $ hg --config alias.log='id' log
412 000000000000 tip
422 000000000000 tip
413
423
414 This shouldn't:
424 This shouldn't:
415
425
416 $ hg --config alias.log='id' history
426 $ hg --config alias.log='id' history
@@ -1,163 +1,163 b''
1 $ "$TESTDIR/hghave" icasefs || exit 80
1 $ "$TESTDIR/hghave" icasefs || exit 80
2
2
3 $ hg debugfs | grep 'case-sensitive:'
3 $ hg debugfs | grep 'case-sensitive:'
4 case-sensitive: no
4 case-sensitive: no
5
5
6 test file addition with bad case
6 test file addition with bad case
7
7
8 $ hg init repo1
8 $ hg init repo1
9 $ cd repo1
9 $ cd repo1
10 $ echo a > a
10 $ echo a > a
11 $ hg add A
11 $ hg add A
12 adding a
12 adding a
13 $ hg st
13 $ hg st
14 A a
14 A a
15 $ hg ci -m adda
15 $ hg ci -m adda
16 $ hg manifest
16 $ hg manifest
17 a
17 a
18 $ cd ..
18 $ cd ..
19
19
20 test case collision on rename (issue750)
20 test case collision on rename (issue750)
21
21
22 $ hg init repo2
22 $ hg init repo2
23 $ cd repo2
23 $ cd repo2
24 $ echo a > a
24 $ echo a > a
25 $ hg --debug ci -Am adda
25 $ hg --debug ci -Am adda
26 adding a
26 adding a
27 a
27 a
28 committed changeset 0:07f4944404050f47db2e5c5071e0e84e7a27bba9
28 committed changeset 0:07f4944404050f47db2e5c5071e0e84e7a27bba9
29
29
30 Case-changing renames should work:
30 Case-changing renames should work:
31
31
32 $ hg mv a A
32 $ hg mv a A
33 $ hg mv A a
33 $ hg mv A a
34 $ hg st
34 $ hg st
35
35
36 test changing case of path components
36 test changing case of path components
37
37
38 $ mkdir D
38 $ mkdir D
39 $ echo b > D/b
39 $ echo b > D/b
40 $ hg ci -Am addb D/b
40 $ hg ci -Am addb D/b
41 $ hg mv D/b d/b
41 $ hg mv D/b d/b
42 D/b: not overwriting - file exists
42 D/b: not overwriting - file exists
43 $ hg mv D/b d/c
43 $ hg mv D/b d/c
44 $ hg st
44 $ hg st
45 A D/c
45 A D/c
46 R D/b
46 R D/b
47 $ mv D temp
47 $ mv D temp
48 $ mv temp d
48 $ mv temp d
49 $ hg st
49 $ hg st
50 A D/c
50 A D/c
51 R D/b
51 R D/b
52 $ hg revert -aq
52 $ hg revert -aq
53 $ rm d/c
53 $ rm d/c
54 $ echo c > D/c
54 $ echo c > D/c
55 $ hg add D/c
55 $ hg add D/c
56 $ hg st
56 $ hg st
57 A D/c
57 A D/c
58 $ hg ci -m addc D/c
58 $ hg ci -m addc D/c
59 $ hg mv d/b d/e
59 $ hg mv d/b d/e
60 moving D/b to D/e
60 moving D/b to D/e (glob)
61 $ hg st
61 $ hg st
62 A D/e
62 A D/e
63 R D/b
63 R D/b
64 $ hg revert -aq
64 $ hg revert -aq
65 $ rm d/e
65 $ rm d/e
66 $ hg mv d/b D/B
66 $ hg mv d/b D/B
67 moving D/b to D/B
67 moving D/b to D/B (glob)
68 $ hg st
68 $ hg st
69 A D/B
69 A D/B
70 R D/b
70 R D/b
71 $ cd ..
71 $ cd ..
72
72
73 test case collision between revisions (issue912)
73 test case collision between revisions (issue912)
74
74
75 $ hg init repo3
75 $ hg init repo3
76 $ cd repo3
76 $ cd repo3
77 $ echo a > a
77 $ echo a > a
78 $ hg ci -Am adda
78 $ hg ci -Am adda
79 adding a
79 adding a
80 $ hg rm a
80 $ hg rm a
81 $ hg ci -Am removea
81 $ hg ci -Am removea
82 $ echo A > A
82 $ echo A > A
83
83
84 on linux hfs keeps the old case stored, force it
84 on linux hfs keeps the old case stored, force it
85
85
86 $ mv a aa
86 $ mv a aa
87 $ mv aa A
87 $ mv aa A
88 $ hg ci -Am addA
88 $ hg ci -Am addA
89 adding A
89 adding A
90
90
91 used to fail under case insensitive fs
91 used to fail under case insensitive fs
92
92
93 $ hg up -C 0
93 $ hg up -C 0
94 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
94 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
95 $ hg up -C
95 $ hg up -C
96 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
96 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
97
97
98 no clobbering of untracked files with wrong casing
98 no clobbering of untracked files with wrong casing
99
99
100 $ hg up -r null
100 $ hg up -r null
101 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
101 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
102 $ echo gold > a
102 $ echo gold > a
103 $ hg up
103 $ hg up
104 A: untracked file differs
104 A: untracked file differs
105 abort: untracked files in working directory differ from files in requested revision
105 abort: untracked files in working directory differ from files in requested revision
106 [255]
106 [255]
107 $ cat a
107 $ cat a
108 gold
108 gold
109
109
110 $ cd ..
110 $ cd ..
111
111
112 issue 3342: file in nested directory causes unexpected abort
112 issue 3342: file in nested directory causes unexpected abort
113
113
114 $ hg init issue3342
114 $ hg init issue3342
115 $ cd issue3342
115 $ cd issue3342
116
116
117 $ mkdir -p a/B/c/D
117 $ mkdir -p a/B/c/D
118 $ echo e > a/B/c/D/e
118 $ echo e > a/B/c/D/e
119 $ hg add a/B/c/D/e
119 $ hg add a/B/c/D/e
120
120
121 $ cd ..
121 $ cd ..
122
122
123 issue 3340: mq does not handle case changes correctly
123 issue 3340: mq does not handle case changes correctly
124
124
125 in addition to reported case, 'hg qrefresh' is also tested against
125 in addition to reported case, 'hg qrefresh' is also tested against
126 case changes.
126 case changes.
127
127
128 $ echo "[extensions]" >> $HGRCPATH
128 $ echo "[extensions]" >> $HGRCPATH
129 $ echo "mq=" >> $HGRCPATH
129 $ echo "mq=" >> $HGRCPATH
130
130
131 $ hg init issue3340
131 $ hg init issue3340
132 $ cd issue3340
132 $ cd issue3340
133
133
134 $ echo a > mIxEdCaSe
134 $ echo a > mIxEdCaSe
135 $ hg add mIxEdCaSe
135 $ hg add mIxEdCaSe
136 $ hg commit -m '#0'
136 $ hg commit -m '#0'
137 $ hg rename mIxEdCaSe tmp
137 $ hg rename mIxEdCaSe tmp
138 $ hg rename tmp MiXeDcAsE
138 $ hg rename tmp MiXeDcAsE
139 $ hg status -A
139 $ hg status -A
140 A MiXeDcAsE
140 A MiXeDcAsE
141 mIxEdCaSe
141 mIxEdCaSe
142 R mIxEdCaSe
142 R mIxEdCaSe
143 $ hg qnew changecase
143 $ hg qnew changecase
144 $ hg status -A
144 $ hg status -A
145 C MiXeDcAsE
145 C MiXeDcAsE
146
146
147 $ hg qpop -a
147 $ hg qpop -a
148 popping changecase
148 popping changecase
149 patch queue now empty
149 patch queue now empty
150 $ hg qnew refresh-casechange
150 $ hg qnew refresh-casechange
151 $ hg status -A
151 $ hg status -A
152 C mIxEdCaSe
152 C mIxEdCaSe
153 $ hg rename mIxEdCaSe tmp
153 $ hg rename mIxEdCaSe tmp
154 $ hg rename tmp MiXeDcAsE
154 $ hg rename tmp MiXeDcAsE
155 $ hg status -A
155 $ hg status -A
156 A MiXeDcAsE
156 A MiXeDcAsE
157 mIxEdCaSe
157 mIxEdCaSe
158 R mIxEdCaSe
158 R mIxEdCaSe
159 $ hg qrefresh
159 $ hg qrefresh
160 $ hg status -A
160 $ hg status -A
161 C MiXeDcAsE
161 C MiXeDcAsE
162
162
163 $ cd ..
163 $ cd ..
@@ -1,581 +1,608 b''
1 $ "$TESTDIR/hghave" system-sh || exit 80
1 $ "$TESTDIR/hghave" system-sh || exit 80
2
2
3 commit hooks can see env vars
3 commit hooks can see env vars
4
4
5 $ hg init a
5 $ hg init a
6 $ cd a
6 $ cd a
7 $ echo "[hooks]" > .hg/hgrc
7 $ echo "[hooks]" > .hg/hgrc
8 $ echo 'commit = unset HG_LOCAL HG_TAG; python "$TESTDIR"/printenv.py commit' >> .hg/hgrc
8 $ echo 'commit = unset HG_LOCAL HG_TAG; python "$TESTDIR"/printenv.py commit' >> .hg/hgrc
9 $ echo 'commit.b = unset HG_LOCAL HG_TAG; python "$TESTDIR"/printenv.py commit.b' >> .hg/hgrc
9 $ echo 'commit.b = unset HG_LOCAL HG_TAG; python "$TESTDIR"/printenv.py commit.b' >> .hg/hgrc
10 $ echo 'precommit = unset HG_LOCAL HG_NODE HG_TAG; python "$TESTDIR"/printenv.py precommit' >> .hg/hgrc
10 $ echo 'precommit = unset HG_LOCAL HG_NODE HG_TAG; python "$TESTDIR"/printenv.py precommit' >> .hg/hgrc
11 $ echo 'pretxncommit = unset HG_LOCAL HG_TAG; python "$TESTDIR"/printenv.py pretxncommit' >> .hg/hgrc
11 $ echo 'pretxncommit = unset HG_LOCAL HG_TAG; python "$TESTDIR"/printenv.py pretxncommit' >> .hg/hgrc
12 $ echo 'pretxncommit.tip = hg -q tip' >> .hg/hgrc
12 $ echo 'pretxncommit.tip = hg -q tip' >> .hg/hgrc
13 $ echo 'pre-identify = python "$TESTDIR"/printenv.py pre-identify 1' >> .hg/hgrc
13 $ echo 'pre-identify = python "$TESTDIR"/printenv.py pre-identify 1' >> .hg/hgrc
14 $ echo 'pre-cat = python "$TESTDIR"/printenv.py pre-cat' >> .hg/hgrc
14 $ echo 'pre-cat = python "$TESTDIR"/printenv.py pre-cat' >> .hg/hgrc
15 $ echo 'post-cat = python "$TESTDIR"/printenv.py post-cat' >> .hg/hgrc
15 $ echo 'post-cat = python "$TESTDIR"/printenv.py post-cat' >> .hg/hgrc
16 $ echo a > a
16 $ echo a > a
17 $ hg add a
17 $ hg add a
18 $ hg commit -m a
18 $ hg commit -m a
19 precommit hook: HG_PARENT1=0000000000000000000000000000000000000000
19 precommit hook: HG_PARENT1=0000000000000000000000000000000000000000
20 pretxncommit hook: HG_NODE=cb9a9f314b8b07ba71012fcdbc544b5a4d82ff5b HG_PARENT1=0000000000000000000000000000000000000000 HG_PENDING=$TESTTMP/a
20 pretxncommit hook: HG_NODE=cb9a9f314b8b07ba71012fcdbc544b5a4d82ff5b HG_PARENT1=0000000000000000000000000000000000000000 HG_PENDING=$TESTTMP/a
21 0:cb9a9f314b8b
21 0:cb9a9f314b8b
22 commit hook: HG_NODE=cb9a9f314b8b07ba71012fcdbc544b5a4d82ff5b HG_PARENT1=0000000000000000000000000000000000000000
22 commit hook: HG_NODE=cb9a9f314b8b07ba71012fcdbc544b5a4d82ff5b HG_PARENT1=0000000000000000000000000000000000000000
23 commit.b hook: HG_NODE=cb9a9f314b8b07ba71012fcdbc544b5a4d82ff5b HG_PARENT1=0000000000000000000000000000000000000000
23 commit.b hook: HG_NODE=cb9a9f314b8b07ba71012fcdbc544b5a4d82ff5b HG_PARENT1=0000000000000000000000000000000000000000
24
24
25 $ hg clone . ../b
25 $ hg clone . ../b
26 updating to branch default
26 updating to branch default
27 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
27 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
28 $ cd ../b
28 $ cd ../b
29
29
30 changegroup hooks can see env vars
30 changegroup hooks can see env vars
31
31
32 $ echo '[hooks]' > .hg/hgrc
32 $ echo '[hooks]' > .hg/hgrc
33 $ echo 'prechangegroup = python "$TESTDIR"/printenv.py prechangegroup' >> .hg/hgrc
33 $ echo 'prechangegroup = python "$TESTDIR"/printenv.py prechangegroup' >> .hg/hgrc
34 $ echo 'changegroup = python "$TESTDIR"/printenv.py changegroup' >> .hg/hgrc
34 $ echo 'changegroup = python "$TESTDIR"/printenv.py changegroup' >> .hg/hgrc
35 $ echo 'incoming = python "$TESTDIR"/printenv.py incoming' >> .hg/hgrc
35 $ echo 'incoming = python "$TESTDIR"/printenv.py incoming' >> .hg/hgrc
36
36
37 pretxncommit and commit hooks can see both parents of merge
37 pretxncommit and commit hooks can see both parents of merge
38
38
39 $ cd ../a
39 $ cd ../a
40 $ echo b >> a
40 $ echo b >> a
41 $ hg commit -m a1 -d "1 0"
41 $ hg commit -m a1 -d "1 0"
42 precommit hook: HG_PARENT1=cb9a9f314b8b07ba71012fcdbc544b5a4d82ff5b
42 precommit hook: HG_PARENT1=cb9a9f314b8b07ba71012fcdbc544b5a4d82ff5b
43 pretxncommit hook: HG_NODE=ab228980c14deea8b9555d91c9581127383e40fd HG_PARENT1=cb9a9f314b8b07ba71012fcdbc544b5a4d82ff5b HG_PENDING=$TESTTMP/a
43 pretxncommit hook: HG_NODE=ab228980c14deea8b9555d91c9581127383e40fd HG_PARENT1=cb9a9f314b8b07ba71012fcdbc544b5a4d82ff5b HG_PENDING=$TESTTMP/a
44 1:ab228980c14d
44 1:ab228980c14d
45 commit hook: HG_NODE=ab228980c14deea8b9555d91c9581127383e40fd HG_PARENT1=cb9a9f314b8b07ba71012fcdbc544b5a4d82ff5b
45 commit hook: HG_NODE=ab228980c14deea8b9555d91c9581127383e40fd HG_PARENT1=cb9a9f314b8b07ba71012fcdbc544b5a4d82ff5b
46 commit.b hook: HG_NODE=ab228980c14deea8b9555d91c9581127383e40fd HG_PARENT1=cb9a9f314b8b07ba71012fcdbc544b5a4d82ff5b
46 commit.b hook: HG_NODE=ab228980c14deea8b9555d91c9581127383e40fd HG_PARENT1=cb9a9f314b8b07ba71012fcdbc544b5a4d82ff5b
47 $ hg update -C 0
47 $ hg update -C 0
48 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
48 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
49 $ echo b > b
49 $ echo b > b
50 $ hg add b
50 $ hg add b
51 $ hg commit -m b -d '1 0'
51 $ hg commit -m b -d '1 0'
52 precommit hook: HG_PARENT1=cb9a9f314b8b07ba71012fcdbc544b5a4d82ff5b
52 precommit hook: HG_PARENT1=cb9a9f314b8b07ba71012fcdbc544b5a4d82ff5b
53 pretxncommit hook: HG_NODE=ee9deb46ab31e4cc3310f3cf0c3d668e4d8fffc2 HG_PARENT1=cb9a9f314b8b07ba71012fcdbc544b5a4d82ff5b HG_PENDING=$TESTTMP/a
53 pretxncommit hook: HG_NODE=ee9deb46ab31e4cc3310f3cf0c3d668e4d8fffc2 HG_PARENT1=cb9a9f314b8b07ba71012fcdbc544b5a4d82ff5b HG_PENDING=$TESTTMP/a
54 2:ee9deb46ab31
54 2:ee9deb46ab31
55 commit hook: HG_NODE=ee9deb46ab31e4cc3310f3cf0c3d668e4d8fffc2 HG_PARENT1=cb9a9f314b8b07ba71012fcdbc544b5a4d82ff5b
55 commit hook: HG_NODE=ee9deb46ab31e4cc3310f3cf0c3d668e4d8fffc2 HG_PARENT1=cb9a9f314b8b07ba71012fcdbc544b5a4d82ff5b
56 commit.b hook: HG_NODE=ee9deb46ab31e4cc3310f3cf0c3d668e4d8fffc2 HG_PARENT1=cb9a9f314b8b07ba71012fcdbc544b5a4d82ff5b
56 commit.b hook: HG_NODE=ee9deb46ab31e4cc3310f3cf0c3d668e4d8fffc2 HG_PARENT1=cb9a9f314b8b07ba71012fcdbc544b5a4d82ff5b
57 created new head
57 created new head
58 $ hg merge 1
58 $ hg merge 1
59 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
59 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
60 (branch merge, don't forget to commit)
60 (branch merge, don't forget to commit)
61 $ hg commit -m merge -d '2 0'
61 $ hg commit -m merge -d '2 0'
62 precommit hook: HG_PARENT1=ee9deb46ab31e4cc3310f3cf0c3d668e4d8fffc2 HG_PARENT2=ab228980c14deea8b9555d91c9581127383e40fd
62 precommit hook: HG_PARENT1=ee9deb46ab31e4cc3310f3cf0c3d668e4d8fffc2 HG_PARENT2=ab228980c14deea8b9555d91c9581127383e40fd
63 pretxncommit hook: HG_NODE=07f3376c1e655977439df2a814e3cc14b27abac2 HG_PARENT1=ee9deb46ab31e4cc3310f3cf0c3d668e4d8fffc2 HG_PARENT2=ab228980c14deea8b9555d91c9581127383e40fd HG_PENDING=$TESTTMP/a
63 pretxncommit hook: HG_NODE=07f3376c1e655977439df2a814e3cc14b27abac2 HG_PARENT1=ee9deb46ab31e4cc3310f3cf0c3d668e4d8fffc2 HG_PARENT2=ab228980c14deea8b9555d91c9581127383e40fd HG_PENDING=$TESTTMP/a
64 3:07f3376c1e65
64 3:07f3376c1e65
65 commit hook: HG_NODE=07f3376c1e655977439df2a814e3cc14b27abac2 HG_PARENT1=ee9deb46ab31e4cc3310f3cf0c3d668e4d8fffc2 HG_PARENT2=ab228980c14deea8b9555d91c9581127383e40fd
65 commit hook: HG_NODE=07f3376c1e655977439df2a814e3cc14b27abac2 HG_PARENT1=ee9deb46ab31e4cc3310f3cf0c3d668e4d8fffc2 HG_PARENT2=ab228980c14deea8b9555d91c9581127383e40fd
66 commit.b hook: HG_NODE=07f3376c1e655977439df2a814e3cc14b27abac2 HG_PARENT1=ee9deb46ab31e4cc3310f3cf0c3d668e4d8fffc2 HG_PARENT2=ab228980c14deea8b9555d91c9581127383e40fd
66 commit.b hook: HG_NODE=07f3376c1e655977439df2a814e3cc14b27abac2 HG_PARENT1=ee9deb46ab31e4cc3310f3cf0c3d668e4d8fffc2 HG_PARENT2=ab228980c14deea8b9555d91c9581127383e40fd
67
67
68 test generic hooks
68 test generic hooks
69
69
70 $ hg id
70 $ hg id
71 pre-identify hook: HG_ARGS=id HG_OPTS={'bookmarks': None, 'branch': None, 'id': None, 'insecure': None, 'num': None, 'remotecmd': '', 'rev': '', 'ssh': '', 'tags': None} HG_PATS=[]
71 pre-identify hook: HG_ARGS=id HG_OPTS={'bookmarks': None, 'branch': None, 'id': None, 'insecure': None, 'num': None, 'remotecmd': '', 'rev': '', 'ssh': '', 'tags': None} HG_PATS=[]
72 warning: pre-identify hook exited with status 1
72 warning: pre-identify hook exited with status 1
73 [1]
73 [1]
74 $ hg cat b
74 $ hg cat b
75 pre-cat hook: HG_ARGS=cat b HG_OPTS={'decode': None, 'exclude': [], 'include': [], 'output': '', 'rev': ''} HG_PATS=['b']
75 pre-cat hook: HG_ARGS=cat b HG_OPTS={'decode': None, 'exclude': [], 'include': [], 'output': '', 'rev': ''} HG_PATS=['b']
76 b
76 b
77 post-cat hook: HG_ARGS=cat b HG_OPTS={'decode': None, 'exclude': [], 'include': [], 'output': '', 'rev': ''} HG_PATS=['b'] HG_RESULT=0
77 post-cat hook: HG_ARGS=cat b HG_OPTS={'decode': None, 'exclude': [], 'include': [], 'output': '', 'rev': ''} HG_PATS=['b'] HG_RESULT=0
78
78
79 $ cd ../b
79 $ cd ../b
80 $ hg pull ../a
80 $ hg pull ../a
81 pulling from ../a
81 pulling from ../a
82 searching for changes
82 searching for changes
83 prechangegroup hook: HG_SOURCE=pull HG_URL=file:$TESTTMP/a
83 prechangegroup hook: HG_SOURCE=pull HG_URL=file:$TESTTMP/a
84 adding changesets
84 adding changesets
85 adding manifests
85 adding manifests
86 adding file changes
86 adding file changes
87 added 3 changesets with 2 changes to 2 files
87 added 3 changesets with 2 changes to 2 files
88 changegroup hook: HG_NODE=ab228980c14deea8b9555d91c9581127383e40fd HG_SOURCE=pull HG_URL=file:$TESTTMP/a
88 changegroup hook: HG_NODE=ab228980c14deea8b9555d91c9581127383e40fd HG_SOURCE=pull HG_URL=file:$TESTTMP/a
89 incoming hook: HG_NODE=ab228980c14deea8b9555d91c9581127383e40fd HG_SOURCE=pull HG_URL=file:$TESTTMP/a
89 incoming hook: HG_NODE=ab228980c14deea8b9555d91c9581127383e40fd HG_SOURCE=pull HG_URL=file:$TESTTMP/a
90 incoming hook: HG_NODE=ee9deb46ab31e4cc3310f3cf0c3d668e4d8fffc2 HG_SOURCE=pull HG_URL=file:$TESTTMP/a
90 incoming hook: HG_NODE=ee9deb46ab31e4cc3310f3cf0c3d668e4d8fffc2 HG_SOURCE=pull HG_URL=file:$TESTTMP/a
91 incoming hook: HG_NODE=07f3376c1e655977439df2a814e3cc14b27abac2 HG_SOURCE=pull HG_URL=file:$TESTTMP/a
91 incoming hook: HG_NODE=07f3376c1e655977439df2a814e3cc14b27abac2 HG_SOURCE=pull HG_URL=file:$TESTTMP/a
92 (run 'hg update' to get a working copy)
92 (run 'hg update' to get a working copy)
93
93
94 tag hooks can see env vars
94 tag hooks can see env vars
95
95
96 $ cd ../a
96 $ cd ../a
97 $ echo 'pretag = python "$TESTDIR"/printenv.py pretag' >> .hg/hgrc
97 $ echo 'pretag = python "$TESTDIR"/printenv.py pretag' >> .hg/hgrc
98 $ echo 'tag = unset HG_PARENT1 HG_PARENT2; python "$TESTDIR"/printenv.py tag' >> .hg/hgrc
98 $ echo 'tag = unset HG_PARENT1 HG_PARENT2; python "$TESTDIR"/printenv.py tag' >> .hg/hgrc
99 $ hg tag -d '3 0' a
99 $ hg tag -d '3 0' a
100 pretag hook: HG_LOCAL=0 HG_NODE=07f3376c1e655977439df2a814e3cc14b27abac2 HG_TAG=a
100 pretag hook: HG_LOCAL=0 HG_NODE=07f3376c1e655977439df2a814e3cc14b27abac2 HG_TAG=a
101 precommit hook: HG_PARENT1=07f3376c1e655977439df2a814e3cc14b27abac2
101 precommit hook: HG_PARENT1=07f3376c1e655977439df2a814e3cc14b27abac2
102 pretxncommit hook: HG_NODE=539e4b31b6dc99b3cfbaa6b53cbc1c1f9a1e3a10 HG_PARENT1=07f3376c1e655977439df2a814e3cc14b27abac2 HG_PENDING=$TESTTMP/a
102 pretxncommit hook: HG_NODE=539e4b31b6dc99b3cfbaa6b53cbc1c1f9a1e3a10 HG_PARENT1=07f3376c1e655977439df2a814e3cc14b27abac2 HG_PENDING=$TESTTMP/a
103 4:539e4b31b6dc
103 4:539e4b31b6dc
104 commit hook: HG_NODE=539e4b31b6dc99b3cfbaa6b53cbc1c1f9a1e3a10 HG_PARENT1=07f3376c1e655977439df2a814e3cc14b27abac2
104 commit hook: HG_NODE=539e4b31b6dc99b3cfbaa6b53cbc1c1f9a1e3a10 HG_PARENT1=07f3376c1e655977439df2a814e3cc14b27abac2
105 commit.b hook: HG_NODE=539e4b31b6dc99b3cfbaa6b53cbc1c1f9a1e3a10 HG_PARENT1=07f3376c1e655977439df2a814e3cc14b27abac2
105 commit.b hook: HG_NODE=539e4b31b6dc99b3cfbaa6b53cbc1c1f9a1e3a10 HG_PARENT1=07f3376c1e655977439df2a814e3cc14b27abac2
106 tag hook: HG_LOCAL=0 HG_NODE=07f3376c1e655977439df2a814e3cc14b27abac2 HG_TAG=a
106 tag hook: HG_LOCAL=0 HG_NODE=07f3376c1e655977439df2a814e3cc14b27abac2 HG_TAG=a
107 $ hg tag -l la
107 $ hg tag -l la
108 pretag hook: HG_LOCAL=1 HG_NODE=539e4b31b6dc99b3cfbaa6b53cbc1c1f9a1e3a10 HG_TAG=la
108 pretag hook: HG_LOCAL=1 HG_NODE=539e4b31b6dc99b3cfbaa6b53cbc1c1f9a1e3a10 HG_TAG=la
109 tag hook: HG_LOCAL=1 HG_NODE=539e4b31b6dc99b3cfbaa6b53cbc1c1f9a1e3a10 HG_TAG=la
109 tag hook: HG_LOCAL=1 HG_NODE=539e4b31b6dc99b3cfbaa6b53cbc1c1f9a1e3a10 HG_TAG=la
110
110
111 pretag hook can forbid tagging
111 pretag hook can forbid tagging
112
112
113 $ echo 'pretag.forbid = python "$TESTDIR"/printenv.py pretag.forbid 1' >> .hg/hgrc
113 $ echo 'pretag.forbid = python "$TESTDIR"/printenv.py pretag.forbid 1' >> .hg/hgrc
114 $ hg tag -d '4 0' fa
114 $ hg tag -d '4 0' fa
115 pretag hook: HG_LOCAL=0 HG_NODE=539e4b31b6dc99b3cfbaa6b53cbc1c1f9a1e3a10 HG_TAG=fa
115 pretag hook: HG_LOCAL=0 HG_NODE=539e4b31b6dc99b3cfbaa6b53cbc1c1f9a1e3a10 HG_TAG=fa
116 pretag.forbid hook: HG_LOCAL=0 HG_NODE=539e4b31b6dc99b3cfbaa6b53cbc1c1f9a1e3a10 HG_TAG=fa
116 pretag.forbid hook: HG_LOCAL=0 HG_NODE=539e4b31b6dc99b3cfbaa6b53cbc1c1f9a1e3a10 HG_TAG=fa
117 abort: pretag.forbid hook exited with status 1
117 abort: pretag.forbid hook exited with status 1
118 [255]
118 [255]
119 $ hg tag -l fla
119 $ hg tag -l fla
120 pretag hook: HG_LOCAL=1 HG_NODE=539e4b31b6dc99b3cfbaa6b53cbc1c1f9a1e3a10 HG_TAG=fla
120 pretag hook: HG_LOCAL=1 HG_NODE=539e4b31b6dc99b3cfbaa6b53cbc1c1f9a1e3a10 HG_TAG=fla
121 pretag.forbid hook: HG_LOCAL=1 HG_NODE=539e4b31b6dc99b3cfbaa6b53cbc1c1f9a1e3a10 HG_TAG=fla
121 pretag.forbid hook: HG_LOCAL=1 HG_NODE=539e4b31b6dc99b3cfbaa6b53cbc1c1f9a1e3a10 HG_TAG=fla
122 abort: pretag.forbid hook exited with status 1
122 abort: pretag.forbid hook exited with status 1
123 [255]
123 [255]
124
124
125 pretxncommit hook can see changeset, can roll back txn, changeset no
125 pretxncommit hook can see changeset, can roll back txn, changeset no
126 more there after
126 more there after
127
127
128 $ echo 'pretxncommit.forbid0 = hg tip -q' >> .hg/hgrc
128 $ echo 'pretxncommit.forbid0 = hg tip -q' >> .hg/hgrc
129 $ echo 'pretxncommit.forbid1 = python "$TESTDIR"/printenv.py pretxncommit.forbid 1' >> .hg/hgrc
129 $ echo 'pretxncommit.forbid1 = python "$TESTDIR"/printenv.py pretxncommit.forbid 1' >> .hg/hgrc
130 $ echo z > z
130 $ echo z > z
131 $ hg add z
131 $ hg add z
132 $ hg -q tip
132 $ hg -q tip
133 4:539e4b31b6dc
133 4:539e4b31b6dc
134 $ hg commit -m 'fail' -d '4 0'
134 $ hg commit -m 'fail' -d '4 0'
135 precommit hook: HG_PARENT1=539e4b31b6dc99b3cfbaa6b53cbc1c1f9a1e3a10
135 precommit hook: HG_PARENT1=539e4b31b6dc99b3cfbaa6b53cbc1c1f9a1e3a10
136 pretxncommit hook: HG_NODE=6f611f8018c10e827fee6bd2bc807f937e761567 HG_PARENT1=539e4b31b6dc99b3cfbaa6b53cbc1c1f9a1e3a10 HG_PENDING=$TESTTMP/a
136 pretxncommit hook: HG_NODE=6f611f8018c10e827fee6bd2bc807f937e761567 HG_PARENT1=539e4b31b6dc99b3cfbaa6b53cbc1c1f9a1e3a10 HG_PENDING=$TESTTMP/a
137 5:6f611f8018c1
137 5:6f611f8018c1
138 5:6f611f8018c1
138 5:6f611f8018c1
139 pretxncommit.forbid hook: HG_NODE=6f611f8018c10e827fee6bd2bc807f937e761567 HG_PARENT1=539e4b31b6dc99b3cfbaa6b53cbc1c1f9a1e3a10 HG_PENDING=$TESTTMP/a
139 pretxncommit.forbid hook: HG_NODE=6f611f8018c10e827fee6bd2bc807f937e761567 HG_PARENT1=539e4b31b6dc99b3cfbaa6b53cbc1c1f9a1e3a10 HG_PENDING=$TESTTMP/a
140 transaction abort!
140 transaction abort!
141 rollback completed
141 rollback completed
142 abort: pretxncommit.forbid1 hook exited with status 1
142 abort: pretxncommit.forbid1 hook exited with status 1
143 [255]
143 [255]
144 $ hg -q tip
144 $ hg -q tip
145 4:539e4b31b6dc
145 4:539e4b31b6dc
146
146
147 precommit hook can prevent commit
147 precommit hook can prevent commit
148
148
149 $ echo 'precommit.forbid = python "$TESTDIR"/printenv.py precommit.forbid 1' >> .hg/hgrc
149 $ echo 'precommit.forbid = python "$TESTDIR"/printenv.py precommit.forbid 1' >> .hg/hgrc
150 $ hg commit -m 'fail' -d '4 0'
150 $ hg commit -m 'fail' -d '4 0'
151 precommit hook: HG_PARENT1=539e4b31b6dc99b3cfbaa6b53cbc1c1f9a1e3a10
151 precommit hook: HG_PARENT1=539e4b31b6dc99b3cfbaa6b53cbc1c1f9a1e3a10
152 precommit.forbid hook: HG_PARENT1=539e4b31b6dc99b3cfbaa6b53cbc1c1f9a1e3a10
152 precommit.forbid hook: HG_PARENT1=539e4b31b6dc99b3cfbaa6b53cbc1c1f9a1e3a10
153 abort: precommit.forbid hook exited with status 1
153 abort: precommit.forbid hook exited with status 1
154 [255]
154 [255]
155 $ hg -q tip
155 $ hg -q tip
156 4:539e4b31b6dc
156 4:539e4b31b6dc
157
157
158 preupdate hook can prevent update
158 preupdate hook can prevent update
159
159
160 $ echo 'preupdate = python "$TESTDIR"/printenv.py preupdate' >> .hg/hgrc
160 $ echo 'preupdate = python "$TESTDIR"/printenv.py preupdate' >> .hg/hgrc
161 $ hg update 1
161 $ hg update 1
162 preupdate hook: HG_PARENT1=ab228980c14d
162 preupdate hook: HG_PARENT1=ab228980c14d
163 0 files updated, 0 files merged, 2 files removed, 0 files unresolved
163 0 files updated, 0 files merged, 2 files removed, 0 files unresolved
164
164
165 update hook
165 update hook
166
166
167 $ echo 'update = python "$TESTDIR"/printenv.py update' >> .hg/hgrc
167 $ echo 'update = python "$TESTDIR"/printenv.py update' >> .hg/hgrc
168 $ hg update
168 $ hg update
169 preupdate hook: HG_PARENT1=539e4b31b6dc
169 preupdate hook: HG_PARENT1=539e4b31b6dc
170 update hook: HG_ERROR=0 HG_PARENT1=539e4b31b6dc
170 update hook: HG_ERROR=0 HG_PARENT1=539e4b31b6dc
171 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
171 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
172
172
173 pushkey hook
173 pushkey hook
174
174
175 $ echo 'pushkey = python "$TESTDIR"/printenv.py pushkey' >> .hg/hgrc
175 $ echo 'pushkey = python "$TESTDIR"/printenv.py pushkey' >> .hg/hgrc
176 $ cd ../b
176 $ cd ../b
177 $ hg bookmark -r null foo
177 $ hg bookmark -r null foo
178 $ hg push -B foo ../a
178 $ hg push -B foo ../a
179 pushing to ../a
179 pushing to ../a
180 searching for changes
180 searching for changes
181 no changes found
181 no changes found
182 exporting bookmark foo
182 exporting bookmark foo
183 pushkey hook: HG_KEY=foo HG_NAMESPACE=bookmarks HG_NEW=0000000000000000000000000000000000000000 HG_RET=1
183 pushkey hook: HG_KEY=foo HG_NAMESPACE=bookmarks HG_NEW=0000000000000000000000000000000000000000 HG_RET=1
184 [1]
184 [1]
185 $ cd ../a
185 $ cd ../a
186
186
187 listkeys hook
187 listkeys hook
188
188
189 $ echo 'listkeys = python "$TESTDIR"/printenv.py listkeys' >> .hg/hgrc
189 $ echo 'listkeys = python "$TESTDIR"/printenv.py listkeys' >> .hg/hgrc
190 $ hg bookmark -r null bar
190 $ hg bookmark -r null bar
191 $ cd ../b
191 $ cd ../b
192 $ hg pull -B bar ../a
192 $ hg pull -B bar ../a
193 pulling from ../a
193 pulling from ../a
194 listkeys hook: HG_NAMESPACE=bookmarks HG_VALUES={'bar': '0000000000000000000000000000000000000000', 'foo': '0000000000000000000000000000000000000000'}
194 listkeys hook: HG_NAMESPACE=bookmarks HG_VALUES={'bar': '0000000000000000000000000000000000000000', 'foo': '0000000000000000000000000000000000000000'}
195 no changes found
195 no changes found
196 listkeys hook: HG_NAMESPACE=phases HG_VALUES={'cb9a9f314b8b07ba71012fcdbc544b5a4d82ff5b': '1', 'publishing': 'True'}
196 listkeys hook: HG_NAMESPACE=phases HG_VALUES={'cb9a9f314b8b07ba71012fcdbc544b5a4d82ff5b': '1', 'publishing': 'True'}
197 listkeys hook: HG_NAMESPACE=bookmarks HG_VALUES={'bar': '0000000000000000000000000000000000000000', 'foo': '0000000000000000000000000000000000000000'}
197 listkeys hook: HG_NAMESPACE=bookmarks HG_VALUES={'bar': '0000000000000000000000000000000000000000', 'foo': '0000000000000000000000000000000000000000'}
198 importing bookmark bar
198 importing bookmark bar
199 $ cd ../a
199 $ cd ../a
200
200
201 test that prepushkey can prevent incoming keys
201 test that prepushkey can prevent incoming keys
202
202
203 $ echo 'prepushkey = python "$TESTDIR"/printenv.py prepushkey.forbid 1' >> .hg/hgrc
203 $ echo 'prepushkey = python "$TESTDIR"/printenv.py prepushkey.forbid 1' >> .hg/hgrc
204 $ cd ../b
204 $ cd ../b
205 $ hg bookmark -r null baz
205 $ hg bookmark -r null baz
206 $ hg push -B baz ../a
206 $ hg push -B baz ../a
207 pushing to ../a
207 pushing to ../a
208 searching for changes
208 searching for changes
209 no changes found
209 no changes found
210 listkeys hook: HG_NAMESPACE=phases HG_VALUES={'cb9a9f314b8b07ba71012fcdbc544b5a4d82ff5b': '1', 'publishing': 'True'}
210 listkeys hook: HG_NAMESPACE=phases HG_VALUES={'cb9a9f314b8b07ba71012fcdbc544b5a4d82ff5b': '1', 'publishing': 'True'}
211 listkeys hook: HG_NAMESPACE=bookmarks HG_VALUES={'bar': '0000000000000000000000000000000000000000', 'foo': '0000000000000000000000000000000000000000'}
211 listkeys hook: HG_NAMESPACE=bookmarks HG_VALUES={'bar': '0000000000000000000000000000000000000000', 'foo': '0000000000000000000000000000000000000000'}
212 listkeys hook: HG_NAMESPACE=bookmarks HG_VALUES={'bar': '0000000000000000000000000000000000000000', 'foo': '0000000000000000000000000000000000000000'}
212 listkeys hook: HG_NAMESPACE=bookmarks HG_VALUES={'bar': '0000000000000000000000000000000000000000', 'foo': '0000000000000000000000000000000000000000'}
213 exporting bookmark baz
213 exporting bookmark baz
214 prepushkey.forbid hook: HG_KEY=baz HG_NAMESPACE=bookmarks HG_NEW=0000000000000000000000000000000000000000
214 prepushkey.forbid hook: HG_KEY=baz HG_NAMESPACE=bookmarks HG_NEW=0000000000000000000000000000000000000000
215 abort: prepushkey hook exited with status 1
215 abort: prepushkey hook exited with status 1
216 [255]
216 [255]
217 $ cd ../a
217 $ cd ../a
218
218
219 test that prelistkeys can prevent listing keys
219 test that prelistkeys can prevent listing keys
220
220
221 $ echo 'prelistkeys = python "$TESTDIR"/printenv.py prelistkeys.forbid 1' >> .hg/hgrc
221 $ echo 'prelistkeys = python "$TESTDIR"/printenv.py prelistkeys.forbid 1' >> .hg/hgrc
222 $ hg bookmark -r null quux
222 $ hg bookmark -r null quux
223 $ cd ../b
223 $ cd ../b
224 $ hg pull -B quux ../a
224 $ hg pull -B quux ../a
225 pulling from ../a
225 pulling from ../a
226 prelistkeys.forbid hook: HG_NAMESPACE=bookmarks
226 prelistkeys.forbid hook: HG_NAMESPACE=bookmarks
227 abort: prelistkeys hook exited with status 1
227 abort: prelistkeys hook exited with status 1
228 [255]
228 [255]
229 $ cd ../a
229 $ cd ../a
230
230
231 prechangegroup hook can prevent incoming changes
231 prechangegroup hook can prevent incoming changes
232
232
233 $ cd ../b
233 $ cd ../b
234 $ hg -q tip
234 $ hg -q tip
235 3:07f3376c1e65
235 3:07f3376c1e65
236 $ echo '[hooks]' > .hg/hgrc
236 $ echo '[hooks]' > .hg/hgrc
237 $ echo 'prechangegroup.forbid = python "$TESTDIR"/printenv.py prechangegroup.forbid 1' >> .hg/hgrc
237 $ echo 'prechangegroup.forbid = python "$TESTDIR"/printenv.py prechangegroup.forbid 1' >> .hg/hgrc
238 $ hg pull ../a
238 $ hg pull ../a
239 pulling from ../a
239 pulling from ../a
240 searching for changes
240 searching for changes
241 prechangegroup.forbid hook: HG_SOURCE=pull HG_URL=file:$TESTTMP/a
241 prechangegroup.forbid hook: HG_SOURCE=pull HG_URL=file:$TESTTMP/a
242 abort: prechangegroup.forbid hook exited with status 1
242 abort: prechangegroup.forbid hook exited with status 1
243 [255]
243 [255]
244
244
245 pretxnchangegroup hook can see incoming changes, can roll back txn,
245 pretxnchangegroup hook can see incoming changes, can roll back txn,
246 incoming changes no longer there after
246 incoming changes no longer there after
247
247
248 $ echo '[hooks]' > .hg/hgrc
248 $ echo '[hooks]' > .hg/hgrc
249 $ echo 'pretxnchangegroup.forbid0 = hg tip -q' >> .hg/hgrc
249 $ echo 'pretxnchangegroup.forbid0 = hg tip -q' >> .hg/hgrc
250 $ echo 'pretxnchangegroup.forbid1 = python "$TESTDIR"/printenv.py pretxnchangegroup.forbid 1' >> .hg/hgrc
250 $ echo 'pretxnchangegroup.forbid1 = python "$TESTDIR"/printenv.py pretxnchangegroup.forbid 1' >> .hg/hgrc
251 $ hg pull ../a
251 $ hg pull ../a
252 pulling from ../a
252 pulling from ../a
253 searching for changes
253 searching for changes
254 adding changesets
254 adding changesets
255 adding manifests
255 adding manifests
256 adding file changes
256 adding file changes
257 added 1 changesets with 1 changes to 1 files
257 added 1 changesets with 1 changes to 1 files
258 4:539e4b31b6dc
258 4:539e4b31b6dc
259 pretxnchangegroup.forbid hook: HG_NODE=539e4b31b6dc99b3cfbaa6b53cbc1c1f9a1e3a10 HG_PENDING=$TESTTMP/b HG_SOURCE=pull HG_URL=file:$TESTTMP/a
259 pretxnchangegroup.forbid hook: HG_NODE=539e4b31b6dc99b3cfbaa6b53cbc1c1f9a1e3a10 HG_PENDING=$TESTTMP/b HG_SOURCE=pull HG_URL=file:$TESTTMP/a
260 transaction abort!
260 transaction abort!
261 rollback completed
261 rollback completed
262 abort: pretxnchangegroup.forbid1 hook exited with status 1
262 abort: pretxnchangegroup.forbid1 hook exited with status 1
263 [255]
263 [255]
264 $ hg -q tip
264 $ hg -q tip
265 3:07f3376c1e65
265 3:07f3376c1e65
266
266
267 outgoing hooks can see env vars
267 outgoing hooks can see env vars
268
268
269 $ rm .hg/hgrc
269 $ rm .hg/hgrc
270 $ echo '[hooks]' > ../a/.hg/hgrc
270 $ echo '[hooks]' > ../a/.hg/hgrc
271 $ echo 'preoutgoing = python "$TESTDIR"/printenv.py preoutgoing' >> ../a/.hg/hgrc
271 $ echo 'preoutgoing = python "$TESTDIR"/printenv.py preoutgoing' >> ../a/.hg/hgrc
272 $ echo 'outgoing = python "$TESTDIR"/printenv.py outgoing' >> ../a/.hg/hgrc
272 $ echo 'outgoing = python "$TESTDIR"/printenv.py outgoing' >> ../a/.hg/hgrc
273 $ hg pull ../a
273 $ hg pull ../a
274 pulling from ../a
274 pulling from ../a
275 searching for changes
275 searching for changes
276 preoutgoing hook: HG_SOURCE=pull
276 preoutgoing hook: HG_SOURCE=pull
277 adding changesets
277 adding changesets
278 outgoing hook: HG_NODE=539e4b31b6dc99b3cfbaa6b53cbc1c1f9a1e3a10 HG_SOURCE=pull
278 outgoing hook: HG_NODE=539e4b31b6dc99b3cfbaa6b53cbc1c1f9a1e3a10 HG_SOURCE=pull
279 adding manifests
279 adding manifests
280 adding file changes
280 adding file changes
281 added 1 changesets with 1 changes to 1 files
281 added 1 changesets with 1 changes to 1 files
282 (run 'hg update' to get a working copy)
282 (run 'hg update' to get a working copy)
283 $ hg rollback
283 $ hg rollback
284 repository tip rolled back to revision 3 (undo pull)
284 repository tip rolled back to revision 3 (undo pull)
285
285
286 preoutgoing hook can prevent outgoing changes
286 preoutgoing hook can prevent outgoing changes
287
287
288 $ echo 'preoutgoing.forbid = python "$TESTDIR"/printenv.py preoutgoing.forbid 1' >> ../a/.hg/hgrc
288 $ echo 'preoutgoing.forbid = python "$TESTDIR"/printenv.py preoutgoing.forbid 1' >> ../a/.hg/hgrc
289 $ hg pull ../a
289 $ hg pull ../a
290 pulling from ../a
290 pulling from ../a
291 searching for changes
291 searching for changes
292 preoutgoing hook: HG_SOURCE=pull
292 preoutgoing hook: HG_SOURCE=pull
293 preoutgoing.forbid hook: HG_SOURCE=pull
293 preoutgoing.forbid hook: HG_SOURCE=pull
294 abort: preoutgoing.forbid hook exited with status 1
294 abort: preoutgoing.forbid hook exited with status 1
295 [255]
295 [255]
296
296
297 outgoing hooks work for local clones
297 outgoing hooks work for local clones
298
298
299 $ cd ..
299 $ cd ..
300 $ echo '[hooks]' > a/.hg/hgrc
300 $ echo '[hooks]' > a/.hg/hgrc
301 $ echo 'preoutgoing = python "$TESTDIR"/printenv.py preoutgoing' >> a/.hg/hgrc
301 $ echo 'preoutgoing = python "$TESTDIR"/printenv.py preoutgoing' >> a/.hg/hgrc
302 $ echo 'outgoing = python "$TESTDIR"/printenv.py outgoing' >> a/.hg/hgrc
302 $ echo 'outgoing = python "$TESTDIR"/printenv.py outgoing' >> a/.hg/hgrc
303 $ hg clone a c
303 $ hg clone a c
304 preoutgoing hook: HG_SOURCE=clone
304 preoutgoing hook: HG_SOURCE=clone
305 outgoing hook: HG_NODE=0000000000000000000000000000000000000000 HG_SOURCE=clone
305 outgoing hook: HG_NODE=0000000000000000000000000000000000000000 HG_SOURCE=clone
306 updating to branch default
306 updating to branch default
307 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
307 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
308 $ rm -rf c
308 $ rm -rf c
309
309
310 preoutgoing hook can prevent outgoing changes for local clones
310 preoutgoing hook can prevent outgoing changes for local clones
311
311
312 $ echo 'preoutgoing.forbid = python "$TESTDIR"/printenv.py preoutgoing.forbid 1' >> a/.hg/hgrc
312 $ echo 'preoutgoing.forbid = python "$TESTDIR"/printenv.py preoutgoing.forbid 1' >> a/.hg/hgrc
313 $ hg clone a zzz
313 $ hg clone a zzz
314 preoutgoing hook: HG_SOURCE=clone
314 preoutgoing hook: HG_SOURCE=clone
315 preoutgoing.forbid hook: HG_SOURCE=clone
315 preoutgoing.forbid hook: HG_SOURCE=clone
316 abort: preoutgoing.forbid hook exited with status 1
316 abort: preoutgoing.forbid hook exited with status 1
317 [255]
317 [255]
318 $ cd b
318 $ cd b
319
319
320 $ cat > hooktests.py <<EOF
320 $ cat > hooktests.py <<EOF
321 > from mercurial import util
321 > from mercurial import util
322 >
322 >
323 > uncallable = 0
323 > uncallable = 0
324 >
324 >
325 > def printargs(args):
325 > def printargs(args):
326 > args.pop('ui', None)
326 > args.pop('ui', None)
327 > args.pop('repo', None)
327 > args.pop('repo', None)
328 > a = list(args.items())
328 > a = list(args.items())
329 > a.sort()
329 > a.sort()
330 > print 'hook args:'
330 > print 'hook args:'
331 > for k, v in a:
331 > for k, v in a:
332 > print ' ', k, v
332 > print ' ', k, v
333 >
333 >
334 > def passhook(**args):
334 > def passhook(**args):
335 > printargs(args)
335 > printargs(args)
336 >
336 >
337 > def failhook(**args):
337 > def failhook(**args):
338 > printargs(args)
338 > printargs(args)
339 > return True
339 > return True
340 >
340 >
341 > class LocalException(Exception):
341 > class LocalException(Exception):
342 > pass
342 > pass
343 >
343 >
344 > def raisehook(**args):
344 > def raisehook(**args):
345 > raise LocalException('exception from hook')
345 > raise LocalException('exception from hook')
346 >
346 >
347 > def aborthook(**args):
347 > def aborthook(**args):
348 > raise util.Abort('raise abort from hook')
348 > raise util.Abort('raise abort from hook')
349 >
349 >
350 > def brokenhook(**args):
350 > def brokenhook(**args):
351 > return 1 + {}
351 > return 1 + {}
352 >
352 >
353 > def verbosehook(ui, **args):
353 > def verbosehook(ui, **args):
354 > ui.note('verbose output from hook\n')
354 > ui.note('verbose output from hook\n')
355 >
355 >
356 > def printtags(ui, repo, **args):
356 > def printtags(ui, repo, **args):
357 > print repo.tags().keys()
357 > print repo.tags().keys()
358 >
358 >
359 > class container:
359 > class container:
360 > unreachable = 1
360 > unreachable = 1
361 > EOF
361 > EOF
362
362
363 test python hooks
363 test python hooks
364
364
365 $ PYTHONPATH="`pwd`:$PYTHONPATH"
365 $ PYTHONPATH="`pwd`:$PYTHONPATH"
366 $ export PYTHONPATH
366 $ export PYTHONPATH
367
367
368 $ echo '[hooks]' > ../a/.hg/hgrc
368 $ echo '[hooks]' > ../a/.hg/hgrc
369 $ echo 'preoutgoing.broken = python:hooktests.brokenhook' >> ../a/.hg/hgrc
369 $ echo 'preoutgoing.broken = python:hooktests.brokenhook' >> ../a/.hg/hgrc
370 $ hg pull ../a 2>&1 | grep 'raised an exception'
370 $ hg pull ../a 2>&1 | grep 'raised an exception'
371 error: preoutgoing.broken hook raised an exception: unsupported operand type(s) for +: 'int' and 'dict'
371 error: preoutgoing.broken hook raised an exception: unsupported operand type(s) for +: 'int' and 'dict'
372
372
373 $ echo '[hooks]' > ../a/.hg/hgrc
373 $ echo '[hooks]' > ../a/.hg/hgrc
374 $ echo 'preoutgoing.raise = python:hooktests.raisehook' >> ../a/.hg/hgrc
374 $ echo 'preoutgoing.raise = python:hooktests.raisehook' >> ../a/.hg/hgrc
375 $ hg pull ../a 2>&1 | grep 'raised an exception'
375 $ hg pull ../a 2>&1 | grep 'raised an exception'
376 error: preoutgoing.raise hook raised an exception: exception from hook
376 error: preoutgoing.raise hook raised an exception: exception from hook
377
377
378 $ echo '[hooks]' > ../a/.hg/hgrc
378 $ echo '[hooks]' > ../a/.hg/hgrc
379 $ echo 'preoutgoing.abort = python:hooktests.aborthook' >> ../a/.hg/hgrc
379 $ echo 'preoutgoing.abort = python:hooktests.aborthook' >> ../a/.hg/hgrc
380 $ hg pull ../a
380 $ hg pull ../a
381 pulling from ../a
381 pulling from ../a
382 searching for changes
382 searching for changes
383 error: preoutgoing.abort hook failed: raise abort from hook
383 error: preoutgoing.abort hook failed: raise abort from hook
384 abort: raise abort from hook
384 abort: raise abort from hook
385 [255]
385 [255]
386
386
387 $ echo '[hooks]' > ../a/.hg/hgrc
387 $ echo '[hooks]' > ../a/.hg/hgrc
388 $ echo 'preoutgoing.fail = python:hooktests.failhook' >> ../a/.hg/hgrc
388 $ echo 'preoutgoing.fail = python:hooktests.failhook' >> ../a/.hg/hgrc
389 $ hg pull ../a
389 $ hg pull ../a
390 pulling from ../a
390 pulling from ../a
391 searching for changes
391 searching for changes
392 hook args:
392 hook args:
393 hooktype preoutgoing
393 hooktype preoutgoing
394 source pull
394 source pull
395 abort: preoutgoing.fail hook failed
395 abort: preoutgoing.fail hook failed
396 [255]
396 [255]
397
397
398 $ echo '[hooks]' > ../a/.hg/hgrc
398 $ echo '[hooks]' > ../a/.hg/hgrc
399 $ echo 'preoutgoing.uncallable = python:hooktests.uncallable' >> ../a/.hg/hgrc
399 $ echo 'preoutgoing.uncallable = python:hooktests.uncallable' >> ../a/.hg/hgrc
400 $ hg pull ../a
400 $ hg pull ../a
401 pulling from ../a
401 pulling from ../a
402 searching for changes
402 searching for changes
403 abort: preoutgoing.uncallable hook is invalid ("hooktests.uncallable" is not callable)
403 abort: preoutgoing.uncallable hook is invalid ("hooktests.uncallable" is not callable)
404 [255]
404 [255]
405
405
406 $ echo '[hooks]' > ../a/.hg/hgrc
406 $ echo '[hooks]' > ../a/.hg/hgrc
407 $ echo 'preoutgoing.nohook = python:hooktests.nohook' >> ../a/.hg/hgrc
407 $ echo 'preoutgoing.nohook = python:hooktests.nohook' >> ../a/.hg/hgrc
408 $ hg pull ../a
408 $ hg pull ../a
409 pulling from ../a
409 pulling from ../a
410 searching for changes
410 searching for changes
411 abort: preoutgoing.nohook hook is invalid ("hooktests.nohook" is not defined)
411 abort: preoutgoing.nohook hook is invalid ("hooktests.nohook" is not defined)
412 [255]
412 [255]
413
413
414 $ echo '[hooks]' > ../a/.hg/hgrc
414 $ echo '[hooks]' > ../a/.hg/hgrc
415 $ echo 'preoutgoing.nomodule = python:nomodule' >> ../a/.hg/hgrc
415 $ echo 'preoutgoing.nomodule = python:nomodule' >> ../a/.hg/hgrc
416 $ hg pull ../a
416 $ hg pull ../a
417 pulling from ../a
417 pulling from ../a
418 searching for changes
418 searching for changes
419 abort: preoutgoing.nomodule hook is invalid ("nomodule" not in a module)
419 abort: preoutgoing.nomodule hook is invalid ("nomodule" not in a module)
420 [255]
420 [255]
421
421
422 $ echo '[hooks]' > ../a/.hg/hgrc
422 $ echo '[hooks]' > ../a/.hg/hgrc
423 $ echo 'preoutgoing.badmodule = python:nomodule.nowhere' >> ../a/.hg/hgrc
423 $ echo 'preoutgoing.badmodule = python:nomodule.nowhere' >> ../a/.hg/hgrc
424 $ hg pull ../a
424 $ hg pull ../a
425 pulling from ../a
425 pulling from ../a
426 searching for changes
426 searching for changes
427 abort: preoutgoing.badmodule hook is invalid (import of "nomodule" failed)
427 abort: preoutgoing.badmodule hook is invalid (import of "nomodule" failed)
428 [255]
428 [255]
429
429
430 $ echo '[hooks]' > ../a/.hg/hgrc
430 $ echo '[hooks]' > ../a/.hg/hgrc
431 $ echo 'preoutgoing.unreachable = python:hooktests.container.unreachable' >> ../a/.hg/hgrc
431 $ echo 'preoutgoing.unreachable = python:hooktests.container.unreachable' >> ../a/.hg/hgrc
432 $ hg pull ../a
432 $ hg pull ../a
433 pulling from ../a
433 pulling from ../a
434 searching for changes
434 searching for changes
435 abort: preoutgoing.unreachable hook is invalid (import of "hooktests.container" failed)
435 abort: preoutgoing.unreachable hook is invalid (import of "hooktests.container" failed)
436 [255]
436 [255]
437
437
438 $ echo '[hooks]' > ../a/.hg/hgrc
438 $ echo '[hooks]' > ../a/.hg/hgrc
439 $ echo 'preoutgoing.pass = python:hooktests.passhook' >> ../a/.hg/hgrc
439 $ echo 'preoutgoing.pass = python:hooktests.passhook' >> ../a/.hg/hgrc
440 $ hg pull ../a
440 $ hg pull ../a
441 pulling from ../a
441 pulling from ../a
442 searching for changes
442 searching for changes
443 hook args:
443 hook args:
444 hooktype preoutgoing
444 hooktype preoutgoing
445 source pull
445 source pull
446 adding changesets
446 adding changesets
447 adding manifests
447 adding manifests
448 adding file changes
448 adding file changes
449 added 1 changesets with 1 changes to 1 files
449 added 1 changesets with 1 changes to 1 files
450 (run 'hg update' to get a working copy)
450 (run 'hg update' to get a working copy)
451
451
452 make sure --traceback works
452 make sure --traceback works
453
453
454 $ echo '[hooks]' > .hg/hgrc
454 $ echo '[hooks]' > .hg/hgrc
455 $ echo 'commit.abort = python:hooktests.aborthook' >> .hg/hgrc
455 $ echo 'commit.abort = python:hooktests.aborthook' >> .hg/hgrc
456
456
457 $ echo aa > a
457 $ echo aa > a
458 $ hg --traceback commit -d '0 0' -ma 2>&1 | grep '^Traceback'
458 $ hg --traceback commit -d '0 0' -ma 2>&1 | grep '^Traceback'
459 Traceback (most recent call last):
459 Traceback (most recent call last):
460
460
461 $ cd ..
461 $ cd ..
462 $ hg init c
462 $ hg init c
463 $ cd c
463 $ cd c
464
464
465 $ cat > hookext.py <<EOF
465 $ cat > hookext.py <<EOF
466 > def autohook(**args):
466 > def autohook(**args):
467 > print "Automatically installed hook"
467 > print "Automatically installed hook"
468 >
468 >
469 > def reposetup(ui, repo):
469 > def reposetup(ui, repo):
470 > repo.ui.setconfig("hooks", "commit.auto", autohook)
470 > repo.ui.setconfig("hooks", "commit.auto", autohook)
471 > EOF
471 > EOF
472 $ echo '[extensions]' >> .hg/hgrc
472 $ echo '[extensions]' >> .hg/hgrc
473 $ echo 'hookext = hookext.py' >> .hg/hgrc
473 $ echo 'hookext = hookext.py' >> .hg/hgrc
474
474
475 $ touch foo
475 $ touch foo
476 $ hg add foo
476 $ hg add foo
477 $ hg ci -d '0 0' -m 'add foo'
477 $ hg ci -d '0 0' -m 'add foo'
478 Automatically installed hook
478 Automatically installed hook
479 $ echo >> foo
479 $ echo >> foo
480 $ hg ci --debug -d '0 0' -m 'change foo'
480 $ hg ci --debug -d '0 0' -m 'change foo'
481 foo
481 foo
482 calling hook commit.auto: <function autohook at *> (glob)
482 calling hook commit.auto: <function autohook at *> (glob)
483 Automatically installed hook
483 Automatically installed hook
484 committed changeset 1:52998019f6252a2b893452765fcb0a47351a5708
484 committed changeset 1:52998019f6252a2b893452765fcb0a47351a5708
485
485
486 $ hg showconfig hooks
486 $ hg showconfig hooks
487 hooks.commit.auto=<function autohook at *> (glob)
487 hooks.commit.auto=<function autohook at *> (glob)
488
488
489 test python hook configured with python:[file]:[hook] syntax
489 test python hook configured with python:[file]:[hook] syntax
490
490
491 $ cd ..
491 $ cd ..
492 $ mkdir d
492 $ mkdir d
493 $ cd d
493 $ cd d
494 $ hg init repo
494 $ hg init repo
495 $ mkdir hooks
495 $ mkdir hooks
496
496
497 $ cd hooks
497 $ cd hooks
498 $ cat > testhooks.py <<EOF
498 $ cat > testhooks.py <<EOF
499 > def testhook(**args):
499 > def testhook(**args):
500 > print 'hook works'
500 > print 'hook works'
501 > EOF
501 > EOF
502 $ echo '[hooks]' > ../repo/.hg/hgrc
502 $ echo '[hooks]' > ../repo/.hg/hgrc
503 $ echo "pre-commit.test = python:`pwd`/testhooks.py:testhook" >> ../repo/.hg/hgrc
503 $ echo "pre-commit.test = python:`pwd`/testhooks.py:testhook" >> ../repo/.hg/hgrc
504
504
505 $ cd ../repo
505 $ cd ../repo
506 $ hg commit -d '0 0'
506 $ hg commit -d '0 0'
507 hook works
507 hook works
508 nothing changed
508 nothing changed
509 [1]
509 [1]
510
510
511 $ cd ../../b
511 $ cd ../../b
512
512
513 make sure --traceback works on hook import failure
513 make sure --traceback works on hook import failure
514
514
515 $ cat > importfail.py <<EOF
515 $ cat > importfail.py <<EOF
516 > import somebogusmodule
516 > import somebogusmodule
517 > # dereference something in the module to force demandimport to load it
517 > # dereference something in the module to force demandimport to load it
518 > somebogusmodule.whatever
518 > somebogusmodule.whatever
519 > EOF
519 > EOF
520
520
521 $ echo '[hooks]' > .hg/hgrc
521 $ echo '[hooks]' > .hg/hgrc
522 $ echo 'precommit.importfail = python:importfail.whatever' >> .hg/hgrc
522 $ echo 'precommit.importfail = python:importfail.whatever' >> .hg/hgrc
523
523
524 $ echo a >> a
524 $ echo a >> a
525 $ hg --traceback commit -ma 2>&1 | egrep '^(exception|Traceback|ImportError)'
525 $ hg --traceback commit -ma 2>&1 | egrep '^(exception|Traceback|ImportError)'
526 exception from first failed import attempt:
526 exception from first failed import attempt:
527 Traceback (most recent call last):
527 Traceback (most recent call last):
528 ImportError: No module named somebogusmodule
528 ImportError: No module named somebogusmodule
529 exception from second failed import attempt:
529 exception from second failed import attempt:
530 Traceback (most recent call last):
530 Traceback (most recent call last):
531 ImportError: No module named hgext_importfail
531 ImportError: No module named hgext_importfail
532 Traceback (most recent call last):
532 Traceback (most recent call last):
533
533
534 Issue1827: Hooks Update & Commit not completely post operation
534 Issue1827: Hooks Update & Commit not completely post operation
535
535
536 commit and update hooks should run after command completion
536 commit and update hooks should run after command completion
537
537
538 $ echo '[hooks]' > .hg/hgrc
538 $ echo '[hooks]' > .hg/hgrc
539 $ echo 'commit = hg id' >> .hg/hgrc
539 $ echo 'commit = hg id' >> .hg/hgrc
540 $ echo 'update = hg id' >> .hg/hgrc
540 $ echo 'update = hg id' >> .hg/hgrc
541 $ echo bb > a
541 $ echo bb > a
542 $ hg ci -ma
542 $ hg ci -ma
543 223eafe2750c tip
543 223eafe2750c tip
544 $ hg up 0
544 $ hg up 0
545 cb9a9f314b8b
545 cb9a9f314b8b
546 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
546 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
547
547
548 make sure --verbose (and --quiet/--debug etc.) are propogated to the local ui
548 make sure --verbose (and --quiet/--debug etc.) are propogated to the local ui
549 that is passed to pre/post hooks
549 that is passed to pre/post hooks
550
550
551 $ echo '[hooks]' > .hg/hgrc
551 $ echo '[hooks]' > .hg/hgrc
552 $ echo 'pre-identify = python:hooktests.verbosehook' >> .hg/hgrc
552 $ echo 'pre-identify = python:hooktests.verbosehook' >> .hg/hgrc
553 $ hg id
553 $ hg id
554 cb9a9f314b8b
554 cb9a9f314b8b
555 $ hg id --verbose
555 $ hg id --verbose
556 calling hook pre-identify: hooktests.verbosehook
556 calling hook pre-identify: hooktests.verbosehook
557 verbose output from hook
557 verbose output from hook
558 cb9a9f314b8b
558 cb9a9f314b8b
559
559
560 Ensure hooks can be prioritized
560 Ensure hooks can be prioritized
561
561
562 $ echo '[hooks]' > .hg/hgrc
562 $ echo '[hooks]' > .hg/hgrc
563 $ echo 'pre-identify.a = python:hooktests.verbosehook' >> .hg/hgrc
563 $ echo 'pre-identify.a = python:hooktests.verbosehook' >> .hg/hgrc
564 $ echo 'pre-identify.b = python:hooktests.verbosehook' >> .hg/hgrc
564 $ echo 'pre-identify.b = python:hooktests.verbosehook' >> .hg/hgrc
565 $ echo 'priority.pre-identify.b = 1' >> .hg/hgrc
565 $ echo 'priority.pre-identify.b = 1' >> .hg/hgrc
566 $ echo 'pre-identify.c = python:hooktests.verbosehook' >> .hg/hgrc
566 $ echo 'pre-identify.c = python:hooktests.verbosehook' >> .hg/hgrc
567 $ hg id --verbose
567 $ hg id --verbose
568 calling hook pre-identify.b: hooktests.verbosehook
568 calling hook pre-identify.b: hooktests.verbosehook
569 verbose output from hook
569 verbose output from hook
570 calling hook pre-identify.a: hooktests.verbosehook
570 calling hook pre-identify.a: hooktests.verbosehook
571 verbose output from hook
571 verbose output from hook
572 calling hook pre-identify.c: hooktests.verbosehook
572 calling hook pre-identify.c: hooktests.verbosehook
573 verbose output from hook
573 verbose output from hook
574 cb9a9f314b8b
574 cb9a9f314b8b
575
575
576 new tags must be visible in pretxncommit (issue3210)
576 new tags must be visible in pretxncommit (issue3210)
577
577
578 $ echo 'pretxncommit.printtags = python:hooktests.printtags' >> .hg/hgrc
578 $ echo 'pretxncommit.printtags = python:hooktests.printtags' >> .hg/hgrc
579 $ hg tag -f foo
579 $ hg tag -f foo
580 ['a', 'foo', 'tip']
580 ['a', 'foo', 'tip']
581
581
582 new commits must be visible in pretxnchangegroup (issue3428)
583
584 $ cd ..
585 $ hg init to
586 $ echo '[hooks]' >> to/.hg/hgrc
587 $ echo 'pretxnchangegroup = hg --traceback tip' >> to/.hg/hgrc
588 $ echo a >> to/a
589 $ hg --cwd to ci -Ama
590 adding a
591 $ hg clone to from
592 updating to branch default
593 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
594 $ echo aa >> from/a
595 $ hg --cwd from ci -mb
596 $ hg --cwd from push
597 pushing to $TESTTMP/to
598 searching for changes
599 adding changesets
600 adding manifests
601 adding file changes
602 added 1 changesets with 1 changes to 1 files
603 changeset: 1:9836a07b9b9d
604 tag: tip
605 user: test
606 date: Thu Jan 01 00:00:00 1970 +0000
607 summary: b
608
@@ -1,122 +1,126 b''
1 from mercurial import parsers
1 from mercurial import parsers
2 from mercurial.node import nullid, nullrev
2 from mercurial.node import nullid, nullrev
3 import struct
3 import struct
4
4
5 # This unit test compares the return value of the original Python
5 # This unit test compares the return value of the original Python
6 # implementation of parseindex and the new C implementation for
6 # implementation of parseindex and the new C implementation for
7 # an index file with and without inlined data
7 # an index file with and without inlined data
8
8
9 # original python implementation
9 # original python implementation
10 def gettype(q):
10 def gettype(q):
11 return int(q & 0xFFFF)
11 return int(q & 0xFFFF)
12
12
13 def offset_type(offset, type):
13 def offset_type(offset, type):
14 return long(long(offset) << 16 | type)
14 return long(long(offset) << 16 | type)
15
15
16 indexformatng = ">Qiiiiii20s12x"
16 indexformatng = ">Qiiiiii20s12x"
17
17
18 def py_parseindex(data, inline) :
18 def py_parseindex(data, inline) :
19 s = 64
19 s = 64
20 cache = None
20 cache = None
21 index = []
21 index = []
22 nodemap = {nullid: nullrev}
22 nodemap = {nullid: nullrev}
23 n = off = 0
23 n = off = 0
24
24
25 l = len(data) - s
25 l = len(data) - s
26 append = index.append
26 append = index.append
27 if inline:
27 if inline:
28 cache = (0, data)
28 cache = (0, data)
29 while off <= l:
29 while off <= l:
30 e = struct.unpack(indexformatng, data[off:off + s])
30 e = struct.unpack(indexformatng, data[off:off + s])
31 nodemap[e[7]] = n
31 nodemap[e[7]] = n
32 append(e)
32 append(e)
33 n += 1
33 n += 1
34 if e[1] < 0:
34 if e[1] < 0:
35 break
35 break
36 off += e[1] + s
36 off += e[1] + s
37 else:
37 else:
38 while off <= l:
38 while off <= l:
39 e = struct.unpack(indexformatng, data[off:off + s])
39 e = struct.unpack(indexformatng, data[off:off + s])
40 nodemap[e[7]] = n
40 nodemap[e[7]] = n
41 append(e)
41 append(e)
42 n += 1
42 n += 1
43 off += s
43 off += s
44
44
45 e = list(index[0])
45 e = list(index[0])
46 type = gettype(e[0])
46 type = gettype(e[0])
47 e[0] = offset_type(0, type)
47 e[0] = offset_type(0, type)
48 index[0] = tuple(e)
48 index[0] = tuple(e)
49
49
50 # add the magic null revision at -1
50 # add the magic null revision at -1
51 index.append((0, 0, 0, -1, -1, -1, -1, nullid))
51 index.append((0, 0, 0, -1, -1, -1, -1, nullid))
52
52
53 return index, cache
53 return index, cache
54
54
55 data_inlined = '\x00\x01\x00\x01\x00\x00\x00\x00\x00\x00\x01\x8c' \
55 data_inlined = '\x00\x01\x00\x01\x00\x00\x00\x00\x00\x00\x01\x8c' \
56 '\x00\x00\x04\x07\x00\x00\x00\x00\x00\x00\x15\x15\xff\xff\xff' \
56 '\x00\x00\x04\x07\x00\x00\x00\x00\x00\x00\x15\x15\xff\xff\xff' \
57 '\xff\xff\xff\xff\xff\xebG\x97\xb7\x1fB\x04\xcf\x13V\x81\tw\x1b' \
57 '\xff\xff\xff\xff\xff\xebG\x97\xb7\x1fB\x04\xcf\x13V\x81\tw\x1b' \
58 'w\xdduR\xda\xc6\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' \
58 'w\xdduR\xda\xc6\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' \
59 'x\x9c\x9d\x93?O\xc30\x10\xc5\xf7|\x8a\xdb\x9a\xa8m\x06\xd8*\x95' \
59 'x\x9c\x9d\x93?O\xc30\x10\xc5\xf7|\x8a\xdb\x9a\xa8m\x06\xd8*\x95' \
60 '\x81B\xa1\xa2\xa2R\xcb\x86Pd\x9a\x0b5$vd_\x04\xfd\xf6\x9c\xff@' \
60 '\x81B\xa1\xa2\xa2R\xcb\x86Pd\x9a\x0b5$vd_\x04\xfd\xf6\x9c\xff@' \
61 '\x11!\x0b\xd9\xec\xf7\xbbw\xe7gG6\xad6\x04\xdaN\xc0\x92\xa0$)' \
61 '\x11!\x0b\xd9\xec\xf7\xbbw\xe7gG6\xad6\x04\xdaN\xc0\x92\xa0$)' \
62 '\xb1\x82\xa2\xd1%\x16\xa4\x8b7\xa9\xca\xd4-\xb2Y\x02\xfc\xc9' \
62 '\xb1\x82\xa2\xd1%\x16\xa4\x8b7\xa9\xca\xd4-\xb2Y\x02\xfc\xc9' \
63 '\xcaS\xf9\xaeX\xed\xb6\xd77Q\x02\x83\xd4\x19\xf5--Y\xea\xe1W' \
63 '\xcaS\xf9\xaeX\xed\xb6\xd77Q\x02\x83\xd4\x19\xf5--Y\xea\xe1W' \
64 '\xab\xed\x10\xceR\x0f_\xdf\xdf\r\xe1,\xf5\xf0\xcb\xf5 \xceR\x0f' \
64 '\xab\xed\x10\xceR\x0f_\xdf\xdf\r\xe1,\xf5\xf0\xcb\xf5 \xceR\x0f' \
65 '_\xdc\x0e\x0e\xc3R\x0f_\xae\x96\x9b!\x9e\xa5\x1e\xbf\xdb,\x06' \
65 '_\xdc\x0e\x0e\xc3R\x0f_\xae\x96\x9b!\x9e\xa5\x1e\xbf\xdb,\x06' \
66 '\xc7q\x9a/\x88\x82\xc3B\xea\xb5\xb4TJ\x93\xb6\x82\x0e\xe16\xe6' \
66 '\xc7q\x9a/\x88\x82\xc3B\xea\xb5\xb4TJ\x93\xb6\x82\x0e\xe16\xe6' \
67 'KQ\xdb\xaf\xecG\xa3\xd1 \x01\xd3\x0b_^\xe8\xaa\xa0\xae\xad\xd1' \
67 'KQ\xdb\xaf\xecG\xa3\xd1 \x01\xd3\x0b_^\xe8\xaa\xa0\xae\xad\xd1' \
68 '&\xbef\x1bz\x08\xb0|\xc9Xz\x06\xf6Z\x91\x90J\xaa\x17\x90\xaa' \
68 '&\xbef\x1bz\x08\xb0|\xc9Xz\x06\xf6Z\x91\x90J\xaa\x17\x90\xaa' \
69 '\xd2\xa6\x11$5C\xcf\xba#\xa0\x03\x02*2\x92-\xfc\xb1\x94\xdf\xe2' \
69 '\xd2\xa6\x11$5C\xcf\xba#\xa0\x03\x02*2\x92-\xfc\xb1\x94\xdf\xe2' \
70 '\xae\xb8\'m\x8ey0^\x85\xd3\x82\xb4\xf0`:\x9c\x00\x8a\xfd\x01' \
70 '\xae\xb8\'m\x8ey0^\x85\xd3\x82\xb4\xf0`:\x9c\x00\x8a\xfd\x01' \
71 '\xb0\xc6\x86\x8b\xdd\xae\x80\xf3\xa9\x9fd\x16\n\x00R%\x1a\x06' \
71 '\xb0\xc6\x86\x8b\xdd\xae\x80\xf3\xa9\x9fd\x16\n\x00R%\x1a\x06' \
72 '\xe9\xd8b\x98\x1d\xf4\xf3+\x9bf\x01\xd8p\x1b\xf3.\xed\x9f^g\xc3' \
72 '\xe9\xd8b\x98\x1d\xf4\xf3+\x9bf\x01\xd8p\x1b\xf3.\xed\x9f^g\xc3' \
73 '^\xd9W81T\xdb\xd5\x04sx|\xf2\xeb\xd6`%?x\xed"\x831\xbf\xf3\xdc' \
73 '^\xd9W81T\xdb\xd5\x04sx|\xf2\xeb\xd6`%?x\xed"\x831\xbf\xf3\xdc' \
74 'b\xeb%gaY\xe1\xad\x9f\xb9f\'1w\xa9\xa5a\x83s\x82J\xb98\xbc4\x8b' \
74 'b\xeb%gaY\xe1\xad\x9f\xb9f\'1w\xa9\xa5a\x83s\x82J\xb98\xbc4\x8b' \
75 '\x83\x00\x9f$z\xb8#\xa5\xb1\xdf\x98\xd9\xec\x1b\x89O\xe3Ts\x9a4' \
75 '\x83\x00\x9f$z\xb8#\xa5\xb1\xdf\x98\xd9\xec\x1b\x89O\xe3Ts\x9a4' \
76 '\x17m\x8b\xfc\x8f\xa5\x95\x9a\xfc\xfa\xed,\xe5|\xa1\xfe\x15\xb9' \
76 '\x17m\x8b\xfc\x8f\xa5\x95\x9a\xfc\xfa\xed,\xe5|\xa1\xfe\x15\xb9' \
77 '\xbc\xb2\x93\x1f\xf2\x95\xff\xdf,\x1a\xc5\xe7\x17*\x93Oz:>\x0e'
77 '\xbc\xb2\x93\x1f\xf2\x95\xff\xdf,\x1a\xc5\xe7\x17*\x93Oz:>\x0e'
78
78
79 data_non_inlined = '\x00\x00\x00\x01\x00\x00\x00\x00\x00\x01D\x19' \
79 data_non_inlined = '\x00\x00\x00\x01\x00\x00\x00\x00\x00\x01D\x19' \
80 '\x00\x07e\x12\x00\x00\x00\x00\x00\x00\x00\x00\xff\xff\xff\xff' \
80 '\x00\x07e\x12\x00\x00\x00\x00\x00\x00\x00\x00\xff\xff\xff\xff' \
81 '\xff\xff\xff\xff\xd1\xf4\xbb\xb0\xbe\xfc\x13\xbd\x8c\xd3\x9d' \
81 '\xff\xff\xff\xff\xd1\xf4\xbb\xb0\xbe\xfc\x13\xbd\x8c\xd3\x9d' \
82 '\x0f\xcd\xd9;\x8c\x07\x8cJ/\x00\x00\x00\x00\x00\x00\x00\x00\x00' \
82 '\x0f\xcd\xd9;\x8c\x07\x8cJ/\x00\x00\x00\x00\x00\x00\x00\x00\x00' \
83 '\x00\x00\x00\x00\x00\x00\x01D\x19\x00\x00\x00\x00\x00\xdf\x00' \
83 '\x00\x00\x00\x00\x00\x00\x01D\x19\x00\x00\x00\x00\x00\xdf\x00' \
84 '\x00\x01q\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\xff' \
84 '\x00\x01q\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\xff' \
85 '\xff\xff\xff\xc1\x12\xb9\x04\x96\xa4Z1t\x91\xdfsJ\x90\xf0\x9bh' \
85 '\xff\xff\xff\xc1\x12\xb9\x04\x96\xa4Z1t\x91\xdfsJ\x90\xf0\x9bh' \
86 '\x07l&\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' \
86 '\x07l&\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' \
87 '\x00\x01D\xf8\x00\x00\x00\x00\x01\x1b\x00\x00\x01\xb8\x00\x00' \
87 '\x00\x01D\xf8\x00\x00\x00\x00\x01\x1b\x00\x00\x01\xb8\x00\x00' \
88 '\x00\x01\x00\x00\x00\x02\x00\x00\x00\x01\xff\xff\xff\xff\x02\n' \
88 '\x00\x01\x00\x00\x00\x02\x00\x00\x00\x01\xff\xff\xff\xff\x02\n' \
89 '\x0e\xc6&\xa1\x92\xae6\x0b\x02i\xfe-\xe5\xbao\x05\xd1\xe7\x00' \
89 '\x0e\xc6&\xa1\x92\xae6\x0b\x02i\xfe-\xe5\xbao\x05\xd1\xe7\x00' \
90 '\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01F' \
90 '\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01F' \
91 '\x13\x00\x00\x00\x00\x01\xec\x00\x00\x03\x06\x00\x00\x00\x01' \
91 '\x13\x00\x00\x00\x00\x01\xec\x00\x00\x03\x06\x00\x00\x00\x01' \
92 '\x00\x00\x00\x03\x00\x00\x00\x02\xff\xff\xff\xff\x12\xcb\xeby1' \
92 '\x00\x00\x00\x03\x00\x00\x00\x02\xff\xff\xff\xff\x12\xcb\xeby1' \
93 '\xb6\r\x98B\xcb\x07\xbd`\x8f\x92\xd9\xc4\x84\xbdK\x00\x00\x00' \
93 '\xb6\r\x98B\xcb\x07\xbd`\x8f\x92\xd9\xc4\x84\xbdK\x00\x00\x00' \
94 '\x00\x00\x00\x00\x00\x00\x00\x00\x00'
94 '\x00\x00\x00\x00\x00\x00\x00\x00\x00'
95
95
96 def parse_index2(data, inline):
96 def parse_index2(data, inline):
97 index, chunkcache = parsers.parse_index2(data, inline)
97 index, chunkcache = parsers.parse_index2(data, inline)
98 return list(index), chunkcache
98 return list(index), chunkcache
99
99
100 def runtest() :
100 def runtest() :
101 py_res_1 = py_parseindex(data_inlined, True)
101 py_res_1 = py_parseindex(data_inlined, True)
102 c_res_1 = parse_index2(data_inlined, True)
102 c_res_1 = parse_index2(data_inlined, True)
103
103
104 py_res_2 = py_parseindex(data_non_inlined, False)
104 py_res_2 = py_parseindex(data_non_inlined, False)
105 c_res_2 = parse_index2(data_non_inlined, False)
105 c_res_2 = parse_index2(data_non_inlined, False)
106
106
107 if py_res_1 != c_res_1:
107 if py_res_1 != c_res_1:
108 print "Parse index result (with inlined data) differs!"
108 print "Parse index result (with inlined data) differs!"
109
109
110 if py_res_2 != c_res_2:
110 if py_res_2 != c_res_2:
111 print "Parse index result (no inlined data) differs!"
111 print "Parse index result (no inlined data) differs!"
112
112
113 ix = parsers.parse_index2(data_inlined, True)[0]
113 ix = parsers.parse_index2(data_inlined, True)[0]
114 for i, r in enumerate(ix):
114 for i, r in enumerate(ix):
115 if r[7] == nullid:
115 if r[7] == nullid:
116 i = -1
116 i = -1
117 if ix[r[7]] != i:
117 try:
118 print 'Reverse lookup inconsistent for %r' % r[7].encode('hex')
118 if ix[r[7]] != i:
119 print 'Reverse lookup inconsistent for %r' % r[7].encode('hex')
120 except TypeError:
121 # pure version doesn't support this
122 break
119
123
120 print "done"
124 print "done"
121
125
122 runtest()
126 runtest()
@@ -1,318 +1,335 b''
1
1
2
2
3 This test tries to exercise the ssh functionality with a dummy script
3 This test tries to exercise the ssh functionality with a dummy script
4
4
5 creating 'remote' repo
5 creating 'remote' repo
6
6
7 $ hg init remote
7 $ hg init remote
8 $ cd remote
8 $ cd remote
9 $ echo this > foo
9 $ echo this > foo
10 $ echo this > fooO
10 $ echo this > fooO
11 $ hg ci -A -m "init" foo fooO
11 $ hg ci -A -m "init" foo fooO
12 $ cat <<EOF > .hg/hgrc
12 $ cat <<EOF > .hg/hgrc
13 > [server]
13 > [server]
14 > uncompressed = True
14 > uncompressed = True
15 >
15 >
16 > [hooks]
16 > [hooks]
17 > changegroup = python "$TESTDIR"/printenv.py changegroup-in-remote 0 ../dummylog
17 > changegroup = python "$TESTDIR"/printenv.py changegroup-in-remote 0 ../dummylog
18 > EOF
18 > EOF
19 $ cd ..
19 $ cd ..
20
20
21 repo not found error
21 repo not found error
22
22
23 $ hg clone -e "python \"$TESTDIR/dummyssh\"" ssh://user@dummy/nonexistent local
23 $ hg clone -e "python \"$TESTDIR/dummyssh\"" ssh://user@dummy/nonexistent local
24 remote: abort: There is no Mercurial repository here (.hg not found)!
24 remote: abort: There is no Mercurial repository here (.hg not found)!
25 abort: no suitable response from remote hg!
25 abort: no suitable response from remote hg!
26 [255]
26 [255]
27
27
28 non-existent absolute path
28 non-existent absolute path
29
29
30 $ hg clone -e "python \"$TESTDIR/dummyssh\"" ssh://user@dummy//`pwd`/nonexistent local
30 $ hg clone -e "python \"$TESTDIR/dummyssh\"" ssh://user@dummy//`pwd`/nonexistent local
31 remote: abort: There is no Mercurial repository here (.hg not found)!
31 remote: abort: There is no Mercurial repository here (.hg not found)!
32 abort: no suitable response from remote hg!
32 abort: no suitable response from remote hg!
33 [255]
33 [255]
34
34
35 clone remote via stream
35 clone remote via stream
36
36
37 $ hg clone -e "python \"$TESTDIR/dummyssh\"" --uncompressed ssh://user@dummy/remote local-stream
37 $ hg clone -e "python \"$TESTDIR/dummyssh\"" --uncompressed ssh://user@dummy/remote local-stream
38 streaming all changes
38 streaming all changes
39 4 files to transfer, 392 bytes of data
39 4 files to transfer, 392 bytes of data
40 transferred 392 bytes in * seconds (*/sec) (glob)
40 transferred 392 bytes in * seconds (*/sec) (glob)
41 updating to branch default
41 updating to branch default
42 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
42 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
43 $ cd local-stream
43 $ cd local-stream
44 $ hg verify
44 $ hg verify
45 checking changesets
45 checking changesets
46 checking manifests
46 checking manifests
47 crosschecking files in changesets and manifests
47 crosschecking files in changesets and manifests
48 checking files
48 checking files
49 2 files, 1 changesets, 2 total revisions
49 2 files, 1 changesets, 2 total revisions
50 $ cd ..
50 $ cd ..
51
51
52 clone remote via pull
52 clone remote via pull
53
53
54 $ hg clone -e "python \"$TESTDIR/dummyssh\"" ssh://user@dummy/remote local
54 $ hg clone -e "python \"$TESTDIR/dummyssh\"" ssh://user@dummy/remote local
55 requesting all changes
55 requesting all changes
56 adding changesets
56 adding changesets
57 adding manifests
57 adding manifests
58 adding file changes
58 adding file changes
59 added 1 changesets with 2 changes to 2 files
59 added 1 changesets with 2 changes to 2 files
60 updating to branch default
60 updating to branch default
61 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
61 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
62
62
63 verify
63 verify
64
64
65 $ cd local
65 $ cd local
66 $ hg verify
66 $ hg verify
67 checking changesets
67 checking changesets
68 checking manifests
68 checking manifests
69 crosschecking files in changesets and manifests
69 crosschecking files in changesets and manifests
70 checking files
70 checking files
71 2 files, 1 changesets, 2 total revisions
71 2 files, 1 changesets, 2 total revisions
72 $ echo '[hooks]' >> .hg/hgrc
72 $ echo '[hooks]' >> .hg/hgrc
73 $ echo 'changegroup = python "$TESTDIR"/printenv.py changegroup-in-local 0 ../dummylog' >> .hg/hgrc
73 $ echo 'changegroup = python "$TESTDIR"/printenv.py changegroup-in-local 0 ../dummylog' >> .hg/hgrc
74
74
75 empty default pull
75 empty default pull
76
76
77 $ hg paths
77 $ hg paths
78 default = ssh://user@dummy/remote
78 default = ssh://user@dummy/remote
79 $ hg pull -e "python \"$TESTDIR/dummyssh\""
79 $ hg pull -e "python \"$TESTDIR/dummyssh\""
80 pulling from ssh://user@dummy/remote
80 pulling from ssh://user@dummy/remote
81 searching for changes
81 searching for changes
82 no changes found
82 no changes found
83
83
84 local change
84 local change
85
85
86 $ echo bleah > foo
86 $ echo bleah > foo
87 $ hg ci -m "add"
87 $ hg ci -m "add"
88
88
89 updating rc
89 updating rc
90
90
91 $ echo "default-push = ssh://user@dummy/remote" >> .hg/hgrc
91 $ echo "default-push = ssh://user@dummy/remote" >> .hg/hgrc
92 $ echo "[ui]" >> .hg/hgrc
92 $ echo "[ui]" >> .hg/hgrc
93 $ echo "ssh = python \"$TESTDIR/dummyssh\"" >> .hg/hgrc
93 $ echo "ssh = python \"$TESTDIR/dummyssh\"" >> .hg/hgrc
94
94
95 find outgoing
95 find outgoing
96
96
97 $ hg out ssh://user@dummy/remote
97 $ hg out ssh://user@dummy/remote
98 comparing with ssh://user@dummy/remote
98 comparing with ssh://user@dummy/remote
99 searching for changes
99 searching for changes
100 changeset: 1:a28a9d1a809c
100 changeset: 1:a28a9d1a809c
101 tag: tip
101 tag: tip
102 user: test
102 user: test
103 date: Thu Jan 01 00:00:00 1970 +0000
103 date: Thu Jan 01 00:00:00 1970 +0000
104 summary: add
104 summary: add
105
105
106
106
107 find incoming on the remote side
107 find incoming on the remote side
108
108
109 $ hg incoming -R ../remote -e "python \"$TESTDIR/dummyssh\"" ssh://user@dummy/local
109 $ hg incoming -R ../remote -e "python \"$TESTDIR/dummyssh\"" ssh://user@dummy/local
110 comparing with ssh://user@dummy/local
110 comparing with ssh://user@dummy/local
111 searching for changes
111 searching for changes
112 changeset: 1:a28a9d1a809c
112 changeset: 1:a28a9d1a809c
113 tag: tip
113 tag: tip
114 user: test
114 user: test
115 date: Thu Jan 01 00:00:00 1970 +0000
115 date: Thu Jan 01 00:00:00 1970 +0000
116 summary: add
116 summary: add
117
117
118
118
119 find incoming on the remote side (using absolute path)
119 find incoming on the remote side (using absolute path)
120
120
121 $ hg incoming -R ../remote -e "python \"$TESTDIR/dummyssh\"" "ssh://user@dummy/`pwd`"
121 $ hg incoming -R ../remote -e "python \"$TESTDIR/dummyssh\"" "ssh://user@dummy/`pwd`"
122 comparing with ssh://user@dummy/$TESTTMP/local
122 comparing with ssh://user@dummy/$TESTTMP/local
123 searching for changes
123 searching for changes
124 changeset: 1:a28a9d1a809c
124 changeset: 1:a28a9d1a809c
125 tag: tip
125 tag: tip
126 user: test
126 user: test
127 date: Thu Jan 01 00:00:00 1970 +0000
127 date: Thu Jan 01 00:00:00 1970 +0000
128 summary: add
128 summary: add
129
129
130
130
131 push
131 push
132
132
133 $ hg push
133 $ hg push
134 pushing to ssh://user@dummy/remote
134 pushing to ssh://user@dummy/remote
135 searching for changes
135 searching for changes
136 remote: adding changesets
136 remote: adding changesets
137 remote: adding manifests
137 remote: adding manifests
138 remote: adding file changes
138 remote: adding file changes
139 remote: added 1 changesets with 1 changes to 1 files
139 remote: added 1 changesets with 1 changes to 1 files
140 $ cd ../remote
140 $ cd ../remote
141
141
142 check remote tip
142 check remote tip
143
143
144 $ hg tip
144 $ hg tip
145 changeset: 1:a28a9d1a809c
145 changeset: 1:a28a9d1a809c
146 tag: tip
146 tag: tip
147 user: test
147 user: test
148 date: Thu Jan 01 00:00:00 1970 +0000
148 date: Thu Jan 01 00:00:00 1970 +0000
149 summary: add
149 summary: add
150
150
151 $ hg verify
151 $ hg verify
152 checking changesets
152 checking changesets
153 checking manifests
153 checking manifests
154 crosschecking files in changesets and manifests
154 crosschecking files in changesets and manifests
155 checking files
155 checking files
156 2 files, 2 changesets, 3 total revisions
156 2 files, 2 changesets, 3 total revisions
157 $ hg cat -r tip foo
157 $ hg cat -r tip foo
158 bleah
158 bleah
159 $ echo z > z
159 $ echo z > z
160 $ hg ci -A -m z z
160 $ hg ci -A -m z z
161 created new head
161 created new head
162
162
163 test pushkeys and bookmarks
163 test pushkeys and bookmarks
164
164
165 $ cd ../local
165 $ cd ../local
166 $ hg debugpushkey --config ui.ssh="python \"$TESTDIR/dummyssh\"" ssh://user@dummy/remote namespaces
166 $ hg debugpushkey --config ui.ssh="python \"$TESTDIR/dummyssh\"" ssh://user@dummy/remote namespaces
167 bookmarks
167 bookmarks
168 phases
168 phases
169 namespaces
169 namespaces
170 $ hg book foo -r 0
170 $ hg book foo -r 0
171 $ hg out -B
171 $ hg out -B
172 comparing with ssh://user@dummy/remote
172 comparing with ssh://user@dummy/remote
173 searching for changed bookmarks
173 searching for changed bookmarks
174 foo 1160648e36ce
174 foo 1160648e36ce
175 $ hg push -B foo
175 $ hg push -B foo
176 pushing to ssh://user@dummy/remote
176 pushing to ssh://user@dummy/remote
177 searching for changes
177 searching for changes
178 no changes found
178 no changes found
179 exporting bookmark foo
179 exporting bookmark foo
180 [1]
180 [1]
181 $ hg debugpushkey --config ui.ssh="python \"$TESTDIR/dummyssh\"" ssh://user@dummy/remote bookmarks
181 $ hg debugpushkey --config ui.ssh="python \"$TESTDIR/dummyssh\"" ssh://user@dummy/remote bookmarks
182 foo 1160648e36cec0054048a7edc4110c6f84fde594
182 foo 1160648e36cec0054048a7edc4110c6f84fde594
183 $ hg book -f foo
183 $ hg book -f foo
184 $ hg push --traceback
184 $ hg push --traceback
185 pushing to ssh://user@dummy/remote
185 pushing to ssh://user@dummy/remote
186 searching for changes
186 searching for changes
187 no changes found
187 no changes found
188 updating bookmark foo
188 updating bookmark foo
189 [1]
189 [1]
190 $ hg book -d foo
190 $ hg book -d foo
191 $ hg in -B
191 $ hg in -B
192 comparing with ssh://user@dummy/remote
192 comparing with ssh://user@dummy/remote
193 searching for changed bookmarks
193 searching for changed bookmarks
194 foo a28a9d1a809c
194 foo a28a9d1a809c
195 $ hg book -f -r 0 foo
195 $ hg book -f -r 0 foo
196 $ hg pull -B foo
196 $ hg pull -B foo
197 pulling from ssh://user@dummy/remote
197 pulling from ssh://user@dummy/remote
198 no changes found
198 no changes found
199 updating bookmark foo
199 updating bookmark foo
200 importing bookmark foo
200 importing bookmark foo
201 $ hg book -d foo
201 $ hg book -d foo
202 $ hg push -B foo
202 $ hg push -B foo
203 pushing to ssh://user@dummy/remote
203 pushing to ssh://user@dummy/remote
204 searching for changes
204 searching for changes
205 no changes found
205 no changes found
206 deleting remote bookmark foo
206 deleting remote bookmark foo
207 [1]
207 [1]
208
208
209 a bad, evil hook that prints to stdout
209 a bad, evil hook that prints to stdout
210
210
211 $ cat <<EOF > $TESTTMP/badhook
211 $ cat <<EOF > $TESTTMP/badhook
212 > import sys
212 > import sys
213 > sys.stdout.write("KABOOM\n")
213 > sys.stdout.write("KABOOM\n")
214 > EOF
214 > EOF
215
215
216 $ echo '[hooks]' >> ../remote/.hg/hgrc
216 $ echo '[hooks]' >> ../remote/.hg/hgrc
217 $ echo "changegroup.stdout = python $TESTTMP/badhook" >> ../remote/.hg/hgrc
217 $ echo "changegroup.stdout = python $TESTTMP/badhook" >> ../remote/.hg/hgrc
218 $ echo r > r
218 $ echo r > r
219 $ hg ci -A -m z r
219 $ hg ci -A -m z r
220
220
221 push should succeed even though it has an unexpected response
221 push should succeed even though it has an unexpected response
222
222
223 $ hg push
223 $ hg push
224 pushing to ssh://user@dummy/remote
224 pushing to ssh://user@dummy/remote
225 searching for changes
225 searching for changes
226 note: unsynced remote changes!
226 note: unsynced remote changes!
227 remote: adding changesets
227 remote: adding changesets
228 remote: adding manifests
228 remote: adding manifests
229 remote: adding file changes
229 remote: adding file changes
230 remote: added 1 changesets with 1 changes to 1 files
230 remote: added 1 changesets with 1 changes to 1 files
231 remote: KABOOM
231 remote: KABOOM
232 $ hg -R ../remote heads
232 $ hg -R ../remote heads
233 changeset: 3:1383141674ec
233 changeset: 3:1383141674ec
234 tag: tip
234 tag: tip
235 parent: 1:a28a9d1a809c
235 parent: 1:a28a9d1a809c
236 user: test
236 user: test
237 date: Thu Jan 01 00:00:00 1970 +0000
237 date: Thu Jan 01 00:00:00 1970 +0000
238 summary: z
238 summary: z
239
239
240 changeset: 2:6c0482d977a3
240 changeset: 2:6c0482d977a3
241 parent: 0:1160648e36ce
241 parent: 0:1160648e36ce
242 user: test
242 user: test
243 date: Thu Jan 01 00:00:00 1970 +0000
243 date: Thu Jan 01 00:00:00 1970 +0000
244 summary: z
244 summary: z
245
245
246
246
247 clone bookmarks
247 clone bookmarks
248
248
249 $ hg -R ../remote bookmark test
249 $ hg -R ../remote bookmark test
250 $ hg -R ../remote bookmarks
250 $ hg -R ../remote bookmarks
251 * test 2:6c0482d977a3
251 * test 2:6c0482d977a3
252 $ hg clone -e "python \"$TESTDIR/dummyssh\"" ssh://user@dummy/remote local-bookmarks
252 $ hg clone -e "python \"$TESTDIR/dummyssh\"" ssh://user@dummy/remote local-bookmarks
253 requesting all changes
253 requesting all changes
254 adding changesets
254 adding changesets
255 adding manifests
255 adding manifests
256 adding file changes
256 adding file changes
257 added 4 changesets with 5 changes to 4 files (+1 heads)
257 added 4 changesets with 5 changes to 4 files (+1 heads)
258 updating to branch default
258 updating to branch default
259 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
259 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
260 $ hg -R local-bookmarks bookmarks
260 $ hg -R local-bookmarks bookmarks
261 test 2:6c0482d977a3
261 test 2:6c0482d977a3
262
262
263 passwords in ssh urls are not supported
263 passwords in ssh urls are not supported
264 (we use a glob here because different Python versions give different
264 (we use a glob here because different Python versions give different
265 results here)
265 results here)
266
266
267 $ hg push ssh://user:erroneouspwd@dummy/remote
267 $ hg push ssh://user:erroneouspwd@dummy/remote
268 pushing to ssh://user:*@dummy/remote (glob)
268 pushing to ssh://user:*@dummy/remote (glob)
269 abort: password in URL not supported!
269 abort: password in URL not supported!
270 [255]
270 [255]
271
271
272 $ cd ..
272 $ cd ..
273
273
274 Test remote paths with spaces (issue2983):
274 Test remote paths with spaces (issue2983):
275
275
276 $ hg init --ssh "python \"$TESTDIR/dummyssh\"" "ssh://user@dummy/a repo"
276 $ hg init --ssh "python \"$TESTDIR/dummyssh\"" "ssh://user@dummy/a repo"
277 $ hg -R 'a repo' tag tag
277 $ hg -R 'a repo' tag tag
278 $ hg id --ssh "python \"$TESTDIR/dummyssh\"" "ssh://user@dummy/a repo"
278 $ hg id --ssh "python \"$TESTDIR/dummyssh\"" "ssh://user@dummy/a repo"
279 3fb238f49e8c
279 3fb238f49e8c
280
280
281 Test hg-ssh:
281 Test hg-ssh using a helper script that will restore PYTHONPATH (which might
282 have been cleared by a hg.exe wrapper) and invoke hg-ssh with the right
283 parameters:
282
284
283 $ SSH_ORIGINAL_COMMAND="'hg' -R 'a repo' serve --stdio" hg id --ssh "python \"$TESTDIR\"/../contrib/hg-ssh \"$TESTTMP/a repo\"" "ssh://user@dummy/a repo"
285 $ cat > ssh.sh << EOF
286 > userhost="\$1"
287 > SSH_ORIGINAL_COMMAND="\$2"
288 > export SSH_ORIGINAL_COMMAND
289 > PYTHONPATH="$PYTHONPATH"
290 > export PYTHONPATH
291 > python "$TESTDIR/../contrib/hg-ssh" "$TESTTMP/a repo"
292 > EOF
293
294 $ hg id --ssh "sh ssh.sh" "ssh://user@dummy/a repo"
284 3fb238f49e8c
295 3fb238f49e8c
285 $ SSH_ORIGINAL_COMMAND="'hg' -R 'a repo' serve --stdio" hg id --ssh "python \"$TESTDIR\"/../contrib/hg-ssh \"$TESTTMP\"" "ssh://user@dummy/a repo"
296
286 remote: Illegal repository '$TESTTMP/a repo' (glob)
297 $ hg id --ssh "sh ssh.sh" "ssh://user@dummy/a'repo"
298 remote: Illegal repository "$TESTTMP/a'repo" (glob)
287 abort: no suitable response from remote hg!
299 abort: no suitable response from remote hg!
288 [255]
300 [255]
289 $ SSH_ORIGINAL_COMMAND="'hg' -R 'a'repo' serve --stdio" hg id --ssh "python \"$TESTDIR\"/../contrib/hg-ssh \"$TESTTMP\"" "ssh://user@dummy/a repo"
301
290 remote: Illegal command "'hg' -R 'a'repo' serve --stdio": No closing quotation
302 $ hg id --ssh "sh ssh.sh" --remotecmd hacking "ssh://user@dummy/a'repo"
303 remote: Illegal command "hacking -R 'a'\''repo' serve --stdio"
291 abort: no suitable response from remote hg!
304 abort: no suitable response from remote hg!
292 [255]
305 [255]
293
306
307 $ SSH_ORIGINAL_COMMAND="'hg' -R 'a'repo' serve --stdio" python "$TESTDIR/../contrib/hg-ssh"
308 Illegal command "'hg' -R 'a'repo' serve --stdio": No closing quotation
309 [255]
310
294 $ cat dummylog
311 $ cat dummylog
295 Got arguments 1:user@dummy 2:hg -R nonexistent serve --stdio
312 Got arguments 1:user@dummy 2:hg -R nonexistent serve --stdio
296 Got arguments 1:user@dummy 2:hg -R /$TESTTMP/nonexistent serve --stdio
313 Got arguments 1:user@dummy 2:hg -R /$TESTTMP/nonexistent serve --stdio
297 Got arguments 1:user@dummy 2:hg -R remote serve --stdio
314 Got arguments 1:user@dummy 2:hg -R remote serve --stdio
298 Got arguments 1:user@dummy 2:hg -R remote serve --stdio
315 Got arguments 1:user@dummy 2:hg -R remote serve --stdio
299 Got arguments 1:user@dummy 2:hg -R remote serve --stdio
316 Got arguments 1:user@dummy 2:hg -R remote serve --stdio
300 Got arguments 1:user@dummy 2:hg -R remote serve --stdio
317 Got arguments 1:user@dummy 2:hg -R remote serve --stdio
301 Got arguments 1:user@dummy 2:hg -R local serve --stdio
318 Got arguments 1:user@dummy 2:hg -R local serve --stdio
302 Got arguments 1:user@dummy 2:hg -R $TESTTMP/local serve --stdio
319 Got arguments 1:user@dummy 2:hg -R $TESTTMP/local serve --stdio
303 Got arguments 1:user@dummy 2:hg -R remote serve --stdio
320 Got arguments 1:user@dummy 2:hg -R remote serve --stdio
304 changegroup-in-remote hook: HG_NODE=a28a9d1a809cab7d4e2fde4bee738a9ede948b60 HG_SOURCE=serve HG_URL=remote:ssh:127.0.0.1
321 changegroup-in-remote hook: HG_NODE=a28a9d1a809cab7d4e2fde4bee738a9ede948b60 HG_SOURCE=serve HG_URL=remote:ssh:127.0.0.1
305 Got arguments 1:user@dummy 2:hg -R remote serve --stdio
322 Got arguments 1:user@dummy 2:hg -R remote serve --stdio
306 Got arguments 1:user@dummy 2:hg -R remote serve --stdio
323 Got arguments 1:user@dummy 2:hg -R remote serve --stdio
307 Got arguments 1:user@dummy 2:hg -R remote serve --stdio
324 Got arguments 1:user@dummy 2:hg -R remote serve --stdio
308 Got arguments 1:user@dummy 2:hg -R remote serve --stdio
325 Got arguments 1:user@dummy 2:hg -R remote serve --stdio
309 Got arguments 1:user@dummy 2:hg -R remote serve --stdio
326 Got arguments 1:user@dummy 2:hg -R remote serve --stdio
310 Got arguments 1:user@dummy 2:hg -R remote serve --stdio
327 Got arguments 1:user@dummy 2:hg -R remote serve --stdio
311 Got arguments 1:user@dummy 2:hg -R remote serve --stdio
328 Got arguments 1:user@dummy 2:hg -R remote serve --stdio
312 Got arguments 1:user@dummy 2:hg -R remote serve --stdio
329 Got arguments 1:user@dummy 2:hg -R remote serve --stdio
313 Got arguments 1:user@dummy 2:hg -R remote serve --stdio
330 Got arguments 1:user@dummy 2:hg -R remote serve --stdio
314 changegroup-in-remote hook: HG_NODE=1383141674ec756a6056f6a9097618482fe0f4a6 HG_SOURCE=serve HG_URL=remote:ssh:127.0.0.1
331 changegroup-in-remote hook: HG_NODE=1383141674ec756a6056f6a9097618482fe0f4a6 HG_SOURCE=serve HG_URL=remote:ssh:127.0.0.1
315 Got arguments 1:user@dummy 2:hg -R remote serve --stdio
332 Got arguments 1:user@dummy 2:hg -R remote serve --stdio
316 Got arguments 1:user@dummy 2:hg init 'a repo'
333 Got arguments 1:user@dummy 2:hg init 'a repo'
317 Got arguments 1:user@dummy 2:hg -R 'a repo' serve --stdio
334 Got arguments 1:user@dummy 2:hg -R 'a repo' serve --stdio
318 Got arguments 1:user@dummy 2:hg -R 'a repo' serve --stdio
335 Got arguments 1:user@dummy 2:hg -R 'a repo' serve --stdio
General Comments 0
You need to be logged in to leave comments. Login now