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( |
|
43 | sys.stderr.write('Illegal command "%s": %s\n' % (orig_cmd, e)) | |
44 |
sys.exit( |
|
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( |
|
52 | sys.stderr.write('Illegal repository "%s"\n' % repo) | |
53 |
sys.exit( |
|
53 | sys.exit(255) | |
54 | else: |
|
54 | else: | |
55 |
sys.stderr.write( |
|
55 | sys.stderr.write('Illegal command "%s"\n' % orig_cmd) | |
56 |
sys.exit( |
|
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