##// END OF EJS Templates
py3: conditionalize the raise statement...
Pulkit Goyal -
r29759:e584c623 default
parent child Browse files
Show More
@@ -1,1611 +1,1614 b''
1 # bundle2.py - generic container format to transmit arbitrary data.
1 # bundle2.py - generic container format to transmit arbitrary data.
2 #
2 #
3 # Copyright 2013 Facebook, Inc.
3 # Copyright 2013 Facebook, Inc.
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 """Handling of the new bundle2 format
7 """Handling of the new bundle2 format
8
8
9 The goal of bundle2 is to act as an atomically packet to transmit a set of
9 The goal of bundle2 is to act as an atomically packet to transmit a set of
10 payloads in an application agnostic way. It consist in a sequence of "parts"
10 payloads in an application agnostic way. It consist in a sequence of "parts"
11 that will be handed to and processed by the application layer.
11 that will be handed to and processed by the application layer.
12
12
13
13
14 General format architecture
14 General format architecture
15 ===========================
15 ===========================
16
16
17 The format is architectured as follow
17 The format is architectured as follow
18
18
19 - magic string
19 - magic string
20 - stream level parameters
20 - stream level parameters
21 - payload parts (any number)
21 - payload parts (any number)
22 - end of stream marker.
22 - end of stream marker.
23
23
24 the Binary format
24 the Binary format
25 ============================
25 ============================
26
26
27 All numbers are unsigned and big-endian.
27 All numbers are unsigned and big-endian.
28
28
29 stream level parameters
29 stream level parameters
30 ------------------------
30 ------------------------
31
31
32 Binary format is as follow
32 Binary format is as follow
33
33
34 :params size: int32
34 :params size: int32
35
35
36 The total number of Bytes used by the parameters
36 The total number of Bytes used by the parameters
37
37
38 :params value: arbitrary number of Bytes
38 :params value: arbitrary number of Bytes
39
39
40 A blob of `params size` containing the serialized version of all stream level
40 A blob of `params size` containing the serialized version of all stream level
41 parameters.
41 parameters.
42
42
43 The blob contains a space separated list of parameters. Parameters with value
43 The blob contains a space separated list of parameters. Parameters with value
44 are stored in the form `<name>=<value>`. Both name and value are urlquoted.
44 are stored in the form `<name>=<value>`. Both name and value are urlquoted.
45
45
46 Empty name are obviously forbidden.
46 Empty name are obviously forbidden.
47
47
48 Name MUST start with a letter. If this first letter is lower case, the
48 Name MUST start with a letter. If this first letter is lower case, the
49 parameter is advisory and can be safely ignored. However when the first
49 parameter is advisory and can be safely ignored. However when the first
50 letter is capital, the parameter is mandatory and the bundling process MUST
50 letter is capital, the parameter is mandatory and the bundling process MUST
51 stop if he is not able to proceed it.
51 stop if he is not able to proceed it.
52
52
53 Stream parameters use a simple textual format for two main reasons:
53 Stream parameters use a simple textual format for two main reasons:
54
54
55 - Stream level parameters should remain simple and we want to discourage any
55 - Stream level parameters should remain simple and we want to discourage any
56 crazy usage.
56 crazy usage.
57 - Textual data allow easy human inspection of a bundle2 header in case of
57 - Textual data allow easy human inspection of a bundle2 header in case of
58 troubles.
58 troubles.
59
59
60 Any Applicative level options MUST go into a bundle2 part instead.
60 Any Applicative level options MUST go into a bundle2 part instead.
61
61
62 Payload part
62 Payload part
63 ------------------------
63 ------------------------
64
64
65 Binary format is as follow
65 Binary format is as follow
66
66
67 :header size: int32
67 :header size: int32
68
68
69 The total number of Bytes used by the part header. When the header is empty
69 The total number of Bytes used by the part header. When the header is empty
70 (size = 0) this is interpreted as the end of stream marker.
70 (size = 0) this is interpreted as the end of stream marker.
71
71
72 :header:
72 :header:
73
73
74 The header defines how to interpret the part. It contains two piece of
74 The header defines how to interpret the part. It contains two piece of
75 data: the part type, and the part parameters.
75 data: the part type, and the part parameters.
76
76
77 The part type is used to route an application level handler, that can
77 The part type is used to route an application level handler, that can
78 interpret payload.
78 interpret payload.
79
79
80 Part parameters are passed to the application level handler. They are
80 Part parameters are passed to the application level handler. They are
81 meant to convey information that will help the application level object to
81 meant to convey information that will help the application level object to
82 interpret the part payload.
82 interpret the part payload.
83
83
84 The binary format of the header is has follow
84 The binary format of the header is has follow
85
85
86 :typesize: (one byte)
86 :typesize: (one byte)
87
87
88 :parttype: alphanumerical part name (restricted to [a-zA-Z0-9_:-]*)
88 :parttype: alphanumerical part name (restricted to [a-zA-Z0-9_:-]*)
89
89
90 :partid: A 32bits integer (unique in the bundle) that can be used to refer
90 :partid: A 32bits integer (unique in the bundle) that can be used to refer
91 to this part.
91 to this part.
92
92
93 :parameters:
93 :parameters:
94
94
95 Part's parameter may have arbitrary content, the binary structure is::
95 Part's parameter may have arbitrary content, the binary structure is::
96
96
97 <mandatory-count><advisory-count><param-sizes><param-data>
97 <mandatory-count><advisory-count><param-sizes><param-data>
98
98
99 :mandatory-count: 1 byte, number of mandatory parameters
99 :mandatory-count: 1 byte, number of mandatory parameters
100
100
101 :advisory-count: 1 byte, number of advisory parameters
101 :advisory-count: 1 byte, number of advisory parameters
102
102
103 :param-sizes:
103 :param-sizes:
104
104
105 N couple of bytes, where N is the total number of parameters. Each
105 N couple of bytes, where N is the total number of parameters. Each
106 couple contains (<size-of-key>, <size-of-value) for one parameter.
106 couple contains (<size-of-key>, <size-of-value) for one parameter.
107
107
108 :param-data:
108 :param-data:
109
109
110 A blob of bytes from which each parameter key and value can be
110 A blob of bytes from which each parameter key and value can be
111 retrieved using the list of size couples stored in the previous
111 retrieved using the list of size couples stored in the previous
112 field.
112 field.
113
113
114 Mandatory parameters comes first, then the advisory ones.
114 Mandatory parameters comes first, then the advisory ones.
115
115
116 Each parameter's key MUST be unique within the part.
116 Each parameter's key MUST be unique within the part.
117
117
118 :payload:
118 :payload:
119
119
120 payload is a series of `<chunksize><chunkdata>`.
120 payload is a series of `<chunksize><chunkdata>`.
121
121
122 `chunksize` is an int32, `chunkdata` are plain bytes (as much as
122 `chunksize` is an int32, `chunkdata` are plain bytes (as much as
123 `chunksize` says)` The payload part is concluded by a zero size chunk.
123 `chunksize` says)` The payload part is concluded by a zero size chunk.
124
124
125 The current implementation always produces either zero or one chunk.
125 The current implementation always produces either zero or one chunk.
126 This is an implementation limitation that will ultimately be lifted.
126 This is an implementation limitation that will ultimately be lifted.
127
127
128 `chunksize` can be negative to trigger special case processing. No such
128 `chunksize` can be negative to trigger special case processing. No such
129 processing is in place yet.
129 processing is in place yet.
130
130
131 Bundle processing
131 Bundle processing
132 ============================
132 ============================
133
133
134 Each part is processed in order using a "part handler". Handler are registered
134 Each part is processed in order using a "part handler". Handler are registered
135 for a certain part type.
135 for a certain part type.
136
136
137 The matching of a part to its handler is case insensitive. The case of the
137 The matching of a part to its handler is case insensitive. The case of the
138 part type is used to know if a part is mandatory or advisory. If the Part type
138 part type is used to know if a part is mandatory or advisory. If the Part type
139 contains any uppercase char it is considered mandatory. When no handler is
139 contains any uppercase char it is considered mandatory. When no handler is
140 known for a Mandatory part, the process is aborted and an exception is raised.
140 known for a Mandatory part, the process is aborted and an exception is raised.
141 If the part is advisory and no handler is known, the part is ignored. When the
141 If the part is advisory and no handler is known, the part is ignored. When the
142 process is aborted, the full bundle is still read from the stream to keep the
142 process is aborted, the full bundle is still read from the stream to keep the
143 channel usable. But none of the part read from an abort are processed. In the
143 channel usable. But none of the part read from an abort are processed. In the
144 future, dropping the stream may become an option for channel we do not care to
144 future, dropping the stream may become an option for channel we do not care to
145 preserve.
145 preserve.
146 """
146 """
147
147
148 from __future__ import absolute_import
148 from __future__ import absolute_import
149
149
150 import errno
150 import errno
151 import re
151 import re
152 import string
152 import string
153 import struct
153 import struct
154 import sys
154 import sys
155
155
156 from .i18n import _
156 from .i18n import _
157 from . import (
157 from . import (
158 changegroup,
158 changegroup,
159 error,
159 error,
160 obsolete,
160 obsolete,
161 pushkey,
161 pushkey,
162 tags,
162 tags,
163 url,
163 url,
164 util,
164 util,
165 )
165 )
166
166
167 urlerr = util.urlerr
167 urlerr = util.urlerr
168 urlreq = util.urlreq
168 urlreq = util.urlreq
169
169
170 _pack = struct.pack
170 _pack = struct.pack
171 _unpack = struct.unpack
171 _unpack = struct.unpack
172
172
173 _fstreamparamsize = '>i'
173 _fstreamparamsize = '>i'
174 _fpartheadersize = '>i'
174 _fpartheadersize = '>i'
175 _fparttypesize = '>B'
175 _fparttypesize = '>B'
176 _fpartid = '>I'
176 _fpartid = '>I'
177 _fpayloadsize = '>i'
177 _fpayloadsize = '>i'
178 _fpartparamcount = '>BB'
178 _fpartparamcount = '>BB'
179
179
180 preferedchunksize = 4096
180 preferedchunksize = 4096
181
181
182 _parttypeforbidden = re.compile('[^a-zA-Z0-9_:-]')
182 _parttypeforbidden = re.compile('[^a-zA-Z0-9_:-]')
183
183
184 def outdebug(ui, message):
184 def outdebug(ui, message):
185 """debug regarding output stream (bundling)"""
185 """debug regarding output stream (bundling)"""
186 if ui.configbool('devel', 'bundle2.debug', False):
186 if ui.configbool('devel', 'bundle2.debug', False):
187 ui.debug('bundle2-output: %s\n' % message)
187 ui.debug('bundle2-output: %s\n' % message)
188
188
189 def indebug(ui, message):
189 def indebug(ui, message):
190 """debug on input stream (unbundling)"""
190 """debug on input stream (unbundling)"""
191 if ui.configbool('devel', 'bundle2.debug', False):
191 if ui.configbool('devel', 'bundle2.debug', False):
192 ui.debug('bundle2-input: %s\n' % message)
192 ui.debug('bundle2-input: %s\n' % message)
193
193
194 def validateparttype(parttype):
194 def validateparttype(parttype):
195 """raise ValueError if a parttype contains invalid character"""
195 """raise ValueError if a parttype contains invalid character"""
196 if _parttypeforbidden.search(parttype):
196 if _parttypeforbidden.search(parttype):
197 raise ValueError(parttype)
197 raise ValueError(parttype)
198
198
199 def _makefpartparamsizes(nbparams):
199 def _makefpartparamsizes(nbparams):
200 """return a struct format to read part parameter sizes
200 """return a struct format to read part parameter sizes
201
201
202 The number parameters is variable so we need to build that format
202 The number parameters is variable so we need to build that format
203 dynamically.
203 dynamically.
204 """
204 """
205 return '>'+('BB'*nbparams)
205 return '>'+('BB'*nbparams)
206
206
207 parthandlermapping = {}
207 parthandlermapping = {}
208
208
209 def parthandler(parttype, params=()):
209 def parthandler(parttype, params=()):
210 """decorator that register a function as a bundle2 part handler
210 """decorator that register a function as a bundle2 part handler
211
211
212 eg::
212 eg::
213
213
214 @parthandler('myparttype', ('mandatory', 'param', 'handled'))
214 @parthandler('myparttype', ('mandatory', 'param', 'handled'))
215 def myparttypehandler(...):
215 def myparttypehandler(...):
216 '''process a part of type "my part".'''
216 '''process a part of type "my part".'''
217 ...
217 ...
218 """
218 """
219 validateparttype(parttype)
219 validateparttype(parttype)
220 def _decorator(func):
220 def _decorator(func):
221 lparttype = parttype.lower() # enforce lower case matching.
221 lparttype = parttype.lower() # enforce lower case matching.
222 assert lparttype not in parthandlermapping
222 assert lparttype not in parthandlermapping
223 parthandlermapping[lparttype] = func
223 parthandlermapping[lparttype] = func
224 func.params = frozenset(params)
224 func.params = frozenset(params)
225 return func
225 return func
226 return _decorator
226 return _decorator
227
227
228 class unbundlerecords(object):
228 class unbundlerecords(object):
229 """keep record of what happens during and unbundle
229 """keep record of what happens during and unbundle
230
230
231 New records are added using `records.add('cat', obj)`. Where 'cat' is a
231 New records are added using `records.add('cat', obj)`. Where 'cat' is a
232 category of record and obj is an arbitrary object.
232 category of record and obj is an arbitrary object.
233
233
234 `records['cat']` will return all entries of this category 'cat'.
234 `records['cat']` will return all entries of this category 'cat'.
235
235
236 Iterating on the object itself will yield `('category', obj)` tuples
236 Iterating on the object itself will yield `('category', obj)` tuples
237 for all entries.
237 for all entries.
238
238
239 All iterations happens in chronological order.
239 All iterations happens in chronological order.
240 """
240 """
241
241
242 def __init__(self):
242 def __init__(self):
243 self._categories = {}
243 self._categories = {}
244 self._sequences = []
244 self._sequences = []
245 self._replies = {}
245 self._replies = {}
246
246
247 def add(self, category, entry, inreplyto=None):
247 def add(self, category, entry, inreplyto=None):
248 """add a new record of a given category.
248 """add a new record of a given category.
249
249
250 The entry can then be retrieved in the list returned by
250 The entry can then be retrieved in the list returned by
251 self['category']."""
251 self['category']."""
252 self._categories.setdefault(category, []).append(entry)
252 self._categories.setdefault(category, []).append(entry)
253 self._sequences.append((category, entry))
253 self._sequences.append((category, entry))
254 if inreplyto is not None:
254 if inreplyto is not None:
255 self.getreplies(inreplyto).add(category, entry)
255 self.getreplies(inreplyto).add(category, entry)
256
256
257 def getreplies(self, partid):
257 def getreplies(self, partid):
258 """get the records that are replies to a specific part"""
258 """get the records that are replies to a specific part"""
259 return self._replies.setdefault(partid, unbundlerecords())
259 return self._replies.setdefault(partid, unbundlerecords())
260
260
261 def __getitem__(self, cat):
261 def __getitem__(self, cat):
262 return tuple(self._categories.get(cat, ()))
262 return tuple(self._categories.get(cat, ()))
263
263
264 def __iter__(self):
264 def __iter__(self):
265 return iter(self._sequences)
265 return iter(self._sequences)
266
266
267 def __len__(self):
267 def __len__(self):
268 return len(self._sequences)
268 return len(self._sequences)
269
269
270 def __nonzero__(self):
270 def __nonzero__(self):
271 return bool(self._sequences)
271 return bool(self._sequences)
272
272
273 class bundleoperation(object):
273 class bundleoperation(object):
274 """an object that represents a single bundling process
274 """an object that represents a single bundling process
275
275
276 Its purpose is to carry unbundle-related objects and states.
276 Its purpose is to carry unbundle-related objects and states.
277
277
278 A new object should be created at the beginning of each bundle processing.
278 A new object should be created at the beginning of each bundle processing.
279 The object is to be returned by the processing function.
279 The object is to be returned by the processing function.
280
280
281 The object has very little content now it will ultimately contain:
281 The object has very little content now it will ultimately contain:
282 * an access to the repo the bundle is applied to,
282 * an access to the repo the bundle is applied to,
283 * a ui object,
283 * a ui object,
284 * a way to retrieve a transaction to add changes to the repo,
284 * a way to retrieve a transaction to add changes to the repo,
285 * a way to record the result of processing each part,
285 * a way to record the result of processing each part,
286 * a way to construct a bundle response when applicable.
286 * a way to construct a bundle response when applicable.
287 """
287 """
288
288
289 def __init__(self, repo, transactiongetter, captureoutput=True):
289 def __init__(self, repo, transactiongetter, captureoutput=True):
290 self.repo = repo
290 self.repo = repo
291 self.ui = repo.ui
291 self.ui = repo.ui
292 self.records = unbundlerecords()
292 self.records = unbundlerecords()
293 self.gettransaction = transactiongetter
293 self.gettransaction = transactiongetter
294 self.reply = None
294 self.reply = None
295 self.captureoutput = captureoutput
295 self.captureoutput = captureoutput
296
296
297 class TransactionUnavailable(RuntimeError):
297 class TransactionUnavailable(RuntimeError):
298 pass
298 pass
299
299
300 def _notransaction():
300 def _notransaction():
301 """default method to get a transaction while processing a bundle
301 """default method to get a transaction while processing a bundle
302
302
303 Raise an exception to highlight the fact that no transaction was expected
303 Raise an exception to highlight the fact that no transaction was expected
304 to be created"""
304 to be created"""
305 raise TransactionUnavailable()
305 raise TransactionUnavailable()
306
306
307 def applybundle(repo, unbundler, tr, source=None, url=None, op=None):
307 def applybundle(repo, unbundler, tr, source=None, url=None, op=None):
308 # transform me into unbundler.apply() as soon as the freeze is lifted
308 # transform me into unbundler.apply() as soon as the freeze is lifted
309 tr.hookargs['bundle2'] = '1'
309 tr.hookargs['bundle2'] = '1'
310 if source is not None and 'source' not in tr.hookargs:
310 if source is not None and 'source' not in tr.hookargs:
311 tr.hookargs['source'] = source
311 tr.hookargs['source'] = source
312 if url is not None and 'url' not in tr.hookargs:
312 if url is not None and 'url' not in tr.hookargs:
313 tr.hookargs['url'] = url
313 tr.hookargs['url'] = url
314 return processbundle(repo, unbundler, lambda: tr, op=op)
314 return processbundle(repo, unbundler, lambda: tr, op=op)
315
315
316 def processbundle(repo, unbundler, transactiongetter=None, op=None):
316 def processbundle(repo, unbundler, transactiongetter=None, op=None):
317 """This function process a bundle, apply effect to/from a repo
317 """This function process a bundle, apply effect to/from a repo
318
318
319 It iterates over each part then searches for and uses the proper handling
319 It iterates over each part then searches for and uses the proper handling
320 code to process the part. Parts are processed in order.
320 code to process the part. Parts are processed in order.
321
321
322 This is very early version of this function that will be strongly reworked
322 This is very early version of this function that will be strongly reworked
323 before final usage.
323 before final usage.
324
324
325 Unknown Mandatory part will abort the process.
325 Unknown Mandatory part will abort the process.
326
326
327 It is temporarily possible to provide a prebuilt bundleoperation to the
327 It is temporarily possible to provide a prebuilt bundleoperation to the
328 function. This is used to ensure output is properly propagated in case of
328 function. This is used to ensure output is properly propagated in case of
329 an error during the unbundling. This output capturing part will likely be
329 an error during the unbundling. This output capturing part will likely be
330 reworked and this ability will probably go away in the process.
330 reworked and this ability will probably go away in the process.
331 """
331 """
332 if op is None:
332 if op is None:
333 if transactiongetter is None:
333 if transactiongetter is None:
334 transactiongetter = _notransaction
334 transactiongetter = _notransaction
335 op = bundleoperation(repo, transactiongetter)
335 op = bundleoperation(repo, transactiongetter)
336 # todo:
336 # todo:
337 # - replace this is a init function soon.
337 # - replace this is a init function soon.
338 # - exception catching
338 # - exception catching
339 unbundler.params
339 unbundler.params
340 if repo.ui.debugflag:
340 if repo.ui.debugflag:
341 msg = ['bundle2-input-bundle:']
341 msg = ['bundle2-input-bundle:']
342 if unbundler.params:
342 if unbundler.params:
343 msg.append(' %i params')
343 msg.append(' %i params')
344 if op.gettransaction is None:
344 if op.gettransaction is None:
345 msg.append(' no-transaction')
345 msg.append(' no-transaction')
346 else:
346 else:
347 msg.append(' with-transaction')
347 msg.append(' with-transaction')
348 msg.append('\n')
348 msg.append('\n')
349 repo.ui.debug(''.join(msg))
349 repo.ui.debug(''.join(msg))
350 iterparts = enumerate(unbundler.iterparts())
350 iterparts = enumerate(unbundler.iterparts())
351 part = None
351 part = None
352 nbpart = 0
352 nbpart = 0
353 try:
353 try:
354 for nbpart, part in iterparts:
354 for nbpart, part in iterparts:
355 _processpart(op, part)
355 _processpart(op, part)
356 except BaseException as exc:
356 except BaseException as exc:
357 for nbpart, part in iterparts:
357 for nbpart, part in iterparts:
358 # consume the bundle content
358 # consume the bundle content
359 part.seek(0, 2)
359 part.seek(0, 2)
360 # Small hack to let caller code distinguish exceptions from bundle2
360 # Small hack to let caller code distinguish exceptions from bundle2
361 # processing from processing the old format. This is mostly
361 # processing from processing the old format. This is mostly
362 # needed to handle different return codes to unbundle according to the
362 # needed to handle different return codes to unbundle according to the
363 # type of bundle. We should probably clean up or drop this return code
363 # type of bundle. We should probably clean up or drop this return code
364 # craziness in a future version.
364 # craziness in a future version.
365 exc.duringunbundle2 = True
365 exc.duringunbundle2 = True
366 salvaged = []
366 salvaged = []
367 replycaps = None
367 replycaps = None
368 if op.reply is not None:
368 if op.reply is not None:
369 salvaged = op.reply.salvageoutput()
369 salvaged = op.reply.salvageoutput()
370 replycaps = op.reply.capabilities
370 replycaps = op.reply.capabilities
371 exc._replycaps = replycaps
371 exc._replycaps = replycaps
372 exc._bundle2salvagedoutput = salvaged
372 exc._bundle2salvagedoutput = salvaged
373 raise
373 raise
374 finally:
374 finally:
375 repo.ui.debug('bundle2-input-bundle: %i parts total\n' % nbpart)
375 repo.ui.debug('bundle2-input-bundle: %i parts total\n' % nbpart)
376
376
377 return op
377 return op
378
378
379 def _processpart(op, part):
379 def _processpart(op, part):
380 """process a single part from a bundle
380 """process a single part from a bundle
381
381
382 The part is guaranteed to have been fully consumed when the function exits
382 The part is guaranteed to have been fully consumed when the function exits
383 (even if an exception is raised)."""
383 (even if an exception is raised)."""
384 status = 'unknown' # used by debug output
384 status = 'unknown' # used by debug output
385 try:
385 try:
386 try:
386 try:
387 handler = parthandlermapping.get(part.type)
387 handler = parthandlermapping.get(part.type)
388 if handler is None:
388 if handler is None:
389 status = 'unsupported-type'
389 status = 'unsupported-type'
390 raise error.BundleUnknownFeatureError(parttype=part.type)
390 raise error.BundleUnknownFeatureError(parttype=part.type)
391 indebug(op.ui, 'found a handler for part %r' % part.type)
391 indebug(op.ui, 'found a handler for part %r' % part.type)
392 unknownparams = part.mandatorykeys - handler.params
392 unknownparams = part.mandatorykeys - handler.params
393 if unknownparams:
393 if unknownparams:
394 unknownparams = list(unknownparams)
394 unknownparams = list(unknownparams)
395 unknownparams.sort()
395 unknownparams.sort()
396 status = 'unsupported-params (%s)' % unknownparams
396 status = 'unsupported-params (%s)' % unknownparams
397 raise error.BundleUnknownFeatureError(parttype=part.type,
397 raise error.BundleUnknownFeatureError(parttype=part.type,
398 params=unknownparams)
398 params=unknownparams)
399 status = 'supported'
399 status = 'supported'
400 except error.BundleUnknownFeatureError as exc:
400 except error.BundleUnknownFeatureError as exc:
401 if part.mandatory: # mandatory parts
401 if part.mandatory: # mandatory parts
402 raise
402 raise
403 indebug(op.ui, 'ignoring unsupported advisory part %s' % exc)
403 indebug(op.ui, 'ignoring unsupported advisory part %s' % exc)
404 return # skip to part processing
404 return # skip to part processing
405 finally:
405 finally:
406 if op.ui.debugflag:
406 if op.ui.debugflag:
407 msg = ['bundle2-input-part: "%s"' % part.type]
407 msg = ['bundle2-input-part: "%s"' % part.type]
408 if not part.mandatory:
408 if not part.mandatory:
409 msg.append(' (advisory)')
409 msg.append(' (advisory)')
410 nbmp = len(part.mandatorykeys)
410 nbmp = len(part.mandatorykeys)
411 nbap = len(part.params) - nbmp
411 nbap = len(part.params) - nbmp
412 if nbmp or nbap:
412 if nbmp or nbap:
413 msg.append(' (params:')
413 msg.append(' (params:')
414 if nbmp:
414 if nbmp:
415 msg.append(' %i mandatory' % nbmp)
415 msg.append(' %i mandatory' % nbmp)
416 if nbap:
416 if nbap:
417 msg.append(' %i advisory' % nbmp)
417 msg.append(' %i advisory' % nbmp)
418 msg.append(')')
418 msg.append(')')
419 msg.append(' %s\n' % status)
419 msg.append(' %s\n' % status)
420 op.ui.debug(''.join(msg))
420 op.ui.debug(''.join(msg))
421
421
422 # handler is called outside the above try block so that we don't
422 # handler is called outside the above try block so that we don't
423 # risk catching KeyErrors from anything other than the
423 # risk catching KeyErrors from anything other than the
424 # parthandlermapping lookup (any KeyError raised by handler()
424 # parthandlermapping lookup (any KeyError raised by handler()
425 # itself represents a defect of a different variety).
425 # itself represents a defect of a different variety).
426 output = None
426 output = None
427 if op.captureoutput and op.reply is not None:
427 if op.captureoutput and op.reply is not None:
428 op.ui.pushbuffer(error=True, subproc=True)
428 op.ui.pushbuffer(error=True, subproc=True)
429 output = ''
429 output = ''
430 try:
430 try:
431 handler(op, part)
431 handler(op, part)
432 finally:
432 finally:
433 if output is not None:
433 if output is not None:
434 output = op.ui.popbuffer()
434 output = op.ui.popbuffer()
435 if output:
435 if output:
436 outpart = op.reply.newpart('output', data=output,
436 outpart = op.reply.newpart('output', data=output,
437 mandatory=False)
437 mandatory=False)
438 outpart.addparam('in-reply-to', str(part.id), mandatory=False)
438 outpart.addparam('in-reply-to', str(part.id), mandatory=False)
439 finally:
439 finally:
440 # consume the part content to not corrupt the stream.
440 # consume the part content to not corrupt the stream.
441 part.seek(0, 2)
441 part.seek(0, 2)
442
442
443
443
444 def decodecaps(blob):
444 def decodecaps(blob):
445 """decode a bundle2 caps bytes blob into a dictionary
445 """decode a bundle2 caps bytes blob into a dictionary
446
446
447 The blob is a list of capabilities (one per line)
447 The blob is a list of capabilities (one per line)
448 Capabilities may have values using a line of the form::
448 Capabilities may have values using a line of the form::
449
449
450 capability=value1,value2,value3
450 capability=value1,value2,value3
451
451
452 The values are always a list."""
452 The values are always a list."""
453 caps = {}
453 caps = {}
454 for line in blob.splitlines():
454 for line in blob.splitlines():
455 if not line:
455 if not line:
456 continue
456 continue
457 if '=' not in line:
457 if '=' not in line:
458 key, vals = line, ()
458 key, vals = line, ()
459 else:
459 else:
460 key, vals = line.split('=', 1)
460 key, vals = line.split('=', 1)
461 vals = vals.split(',')
461 vals = vals.split(',')
462 key = urlreq.unquote(key)
462 key = urlreq.unquote(key)
463 vals = [urlreq.unquote(v) for v in vals]
463 vals = [urlreq.unquote(v) for v in vals]
464 caps[key] = vals
464 caps[key] = vals
465 return caps
465 return caps
466
466
467 def encodecaps(caps):
467 def encodecaps(caps):
468 """encode a bundle2 caps dictionary into a bytes blob"""
468 """encode a bundle2 caps dictionary into a bytes blob"""
469 chunks = []
469 chunks = []
470 for ca in sorted(caps):
470 for ca in sorted(caps):
471 vals = caps[ca]
471 vals = caps[ca]
472 ca = urlreq.quote(ca)
472 ca = urlreq.quote(ca)
473 vals = [urlreq.quote(v) for v in vals]
473 vals = [urlreq.quote(v) for v in vals]
474 if vals:
474 if vals:
475 ca = "%s=%s" % (ca, ','.join(vals))
475 ca = "%s=%s" % (ca, ','.join(vals))
476 chunks.append(ca)
476 chunks.append(ca)
477 return '\n'.join(chunks)
477 return '\n'.join(chunks)
478
478
479 bundletypes = {
479 bundletypes = {
480 "": ("", None), # only when using unbundle on ssh and old http servers
480 "": ("", None), # only when using unbundle on ssh and old http servers
481 # since the unification ssh accepts a header but there
481 # since the unification ssh accepts a header but there
482 # is no capability signaling it.
482 # is no capability signaling it.
483 "HG20": (), # special-cased below
483 "HG20": (), # special-cased below
484 "HG10UN": ("HG10UN", None),
484 "HG10UN": ("HG10UN", None),
485 "HG10BZ": ("HG10", 'BZ'),
485 "HG10BZ": ("HG10", 'BZ'),
486 "HG10GZ": ("HG10GZ", 'GZ'),
486 "HG10GZ": ("HG10GZ", 'GZ'),
487 }
487 }
488
488
489 # hgweb uses this list to communicate its preferred type
489 # hgweb uses this list to communicate its preferred type
490 bundlepriority = ['HG10GZ', 'HG10BZ', 'HG10UN']
490 bundlepriority = ['HG10GZ', 'HG10BZ', 'HG10UN']
491
491
492 class bundle20(object):
492 class bundle20(object):
493 """represent an outgoing bundle2 container
493 """represent an outgoing bundle2 container
494
494
495 Use the `addparam` method to add stream level parameter. and `newpart` to
495 Use the `addparam` method to add stream level parameter. and `newpart` to
496 populate it. Then call `getchunks` to retrieve all the binary chunks of
496 populate it. Then call `getchunks` to retrieve all the binary chunks of
497 data that compose the bundle2 container."""
497 data that compose the bundle2 container."""
498
498
499 _magicstring = 'HG20'
499 _magicstring = 'HG20'
500
500
501 def __init__(self, ui, capabilities=()):
501 def __init__(self, ui, capabilities=()):
502 self.ui = ui
502 self.ui = ui
503 self._params = []
503 self._params = []
504 self._parts = []
504 self._parts = []
505 self.capabilities = dict(capabilities)
505 self.capabilities = dict(capabilities)
506 self._compressor = util.compressors[None]()
506 self._compressor = util.compressors[None]()
507
507
508 def setcompression(self, alg):
508 def setcompression(self, alg):
509 """setup core part compression to <alg>"""
509 """setup core part compression to <alg>"""
510 if alg is None:
510 if alg is None:
511 return
511 return
512 assert not any(n.lower() == 'Compression' for n, v in self._params)
512 assert not any(n.lower() == 'Compression' for n, v in self._params)
513 self.addparam('Compression', alg)
513 self.addparam('Compression', alg)
514 self._compressor = util.compressors[alg]()
514 self._compressor = util.compressors[alg]()
515
515
516 @property
516 @property
517 def nbparts(self):
517 def nbparts(self):
518 """total number of parts added to the bundler"""
518 """total number of parts added to the bundler"""
519 return len(self._parts)
519 return len(self._parts)
520
520
521 # methods used to defines the bundle2 content
521 # methods used to defines the bundle2 content
522 def addparam(self, name, value=None):
522 def addparam(self, name, value=None):
523 """add a stream level parameter"""
523 """add a stream level parameter"""
524 if not name:
524 if not name:
525 raise ValueError('empty parameter name')
525 raise ValueError('empty parameter name')
526 if name[0] not in string.letters:
526 if name[0] not in string.letters:
527 raise ValueError('non letter first character: %r' % name)
527 raise ValueError('non letter first character: %r' % name)
528 self._params.append((name, value))
528 self._params.append((name, value))
529
529
530 def addpart(self, part):
530 def addpart(self, part):
531 """add a new part to the bundle2 container
531 """add a new part to the bundle2 container
532
532
533 Parts contains the actual applicative payload."""
533 Parts contains the actual applicative payload."""
534 assert part.id is None
534 assert part.id is None
535 part.id = len(self._parts) # very cheap counter
535 part.id = len(self._parts) # very cheap counter
536 self._parts.append(part)
536 self._parts.append(part)
537
537
538 def newpart(self, typeid, *args, **kwargs):
538 def newpart(self, typeid, *args, **kwargs):
539 """create a new part and add it to the containers
539 """create a new part and add it to the containers
540
540
541 As the part is directly added to the containers. For now, this means
541 As the part is directly added to the containers. For now, this means
542 that any failure to properly initialize the part after calling
542 that any failure to properly initialize the part after calling
543 ``newpart`` should result in a failure of the whole bundling process.
543 ``newpart`` should result in a failure of the whole bundling process.
544
544
545 You can still fall back to manually create and add if you need better
545 You can still fall back to manually create and add if you need better
546 control."""
546 control."""
547 part = bundlepart(typeid, *args, **kwargs)
547 part = bundlepart(typeid, *args, **kwargs)
548 self.addpart(part)
548 self.addpart(part)
549 return part
549 return part
550
550
551 # methods used to generate the bundle2 stream
551 # methods used to generate the bundle2 stream
552 def getchunks(self):
552 def getchunks(self):
553 if self.ui.debugflag:
553 if self.ui.debugflag:
554 msg = ['bundle2-output-bundle: "%s",' % self._magicstring]
554 msg = ['bundle2-output-bundle: "%s",' % self._magicstring]
555 if self._params:
555 if self._params:
556 msg.append(' (%i params)' % len(self._params))
556 msg.append(' (%i params)' % len(self._params))
557 msg.append(' %i parts total\n' % len(self._parts))
557 msg.append(' %i parts total\n' % len(self._parts))
558 self.ui.debug(''.join(msg))
558 self.ui.debug(''.join(msg))
559 outdebug(self.ui, 'start emission of %s stream' % self._magicstring)
559 outdebug(self.ui, 'start emission of %s stream' % self._magicstring)
560 yield self._magicstring
560 yield self._magicstring
561 param = self._paramchunk()
561 param = self._paramchunk()
562 outdebug(self.ui, 'bundle parameter: %s' % param)
562 outdebug(self.ui, 'bundle parameter: %s' % param)
563 yield _pack(_fstreamparamsize, len(param))
563 yield _pack(_fstreamparamsize, len(param))
564 if param:
564 if param:
565 yield param
565 yield param
566 # starting compression
566 # starting compression
567 for chunk in self._getcorechunk():
567 for chunk in self._getcorechunk():
568 yield self._compressor.compress(chunk)
568 yield self._compressor.compress(chunk)
569 yield self._compressor.flush()
569 yield self._compressor.flush()
570
570
571 def _paramchunk(self):
571 def _paramchunk(self):
572 """return a encoded version of all stream parameters"""
572 """return a encoded version of all stream parameters"""
573 blocks = []
573 blocks = []
574 for par, value in self._params:
574 for par, value in self._params:
575 par = urlreq.quote(par)
575 par = urlreq.quote(par)
576 if value is not None:
576 if value is not None:
577 value = urlreq.quote(value)
577 value = urlreq.quote(value)
578 par = '%s=%s' % (par, value)
578 par = '%s=%s' % (par, value)
579 blocks.append(par)
579 blocks.append(par)
580 return ' '.join(blocks)
580 return ' '.join(blocks)
581
581
582 def _getcorechunk(self):
582 def _getcorechunk(self):
583 """yield chunk for the core part of the bundle
583 """yield chunk for the core part of the bundle
584
584
585 (all but headers and parameters)"""
585 (all but headers and parameters)"""
586 outdebug(self.ui, 'start of parts')
586 outdebug(self.ui, 'start of parts')
587 for part in self._parts:
587 for part in self._parts:
588 outdebug(self.ui, 'bundle part: "%s"' % part.type)
588 outdebug(self.ui, 'bundle part: "%s"' % part.type)
589 for chunk in part.getchunks(ui=self.ui):
589 for chunk in part.getchunks(ui=self.ui):
590 yield chunk
590 yield chunk
591 outdebug(self.ui, 'end of bundle')
591 outdebug(self.ui, 'end of bundle')
592 yield _pack(_fpartheadersize, 0)
592 yield _pack(_fpartheadersize, 0)
593
593
594
594
595 def salvageoutput(self):
595 def salvageoutput(self):
596 """return a list with a copy of all output parts in the bundle
596 """return a list with a copy of all output parts in the bundle
597
597
598 This is meant to be used during error handling to make sure we preserve
598 This is meant to be used during error handling to make sure we preserve
599 server output"""
599 server output"""
600 salvaged = []
600 salvaged = []
601 for part in self._parts:
601 for part in self._parts:
602 if part.type.startswith('output'):
602 if part.type.startswith('output'):
603 salvaged.append(part.copy())
603 salvaged.append(part.copy())
604 return salvaged
604 return salvaged
605
605
606
606
607 class unpackermixin(object):
607 class unpackermixin(object):
608 """A mixin to extract bytes and struct data from a stream"""
608 """A mixin to extract bytes and struct data from a stream"""
609
609
610 def __init__(self, fp):
610 def __init__(self, fp):
611 self._fp = fp
611 self._fp = fp
612 self._seekable = (util.safehasattr(fp, 'seek') and
612 self._seekable = (util.safehasattr(fp, 'seek') and
613 util.safehasattr(fp, 'tell'))
613 util.safehasattr(fp, 'tell'))
614
614
615 def _unpack(self, format):
615 def _unpack(self, format):
616 """unpack this struct format from the stream"""
616 """unpack this struct format from the stream"""
617 data = self._readexact(struct.calcsize(format))
617 data = self._readexact(struct.calcsize(format))
618 return _unpack(format, data)
618 return _unpack(format, data)
619
619
620 def _readexact(self, size):
620 def _readexact(self, size):
621 """read exactly <size> bytes from the stream"""
621 """read exactly <size> bytes from the stream"""
622 return changegroup.readexactly(self._fp, size)
622 return changegroup.readexactly(self._fp, size)
623
623
624 def seek(self, offset, whence=0):
624 def seek(self, offset, whence=0):
625 """move the underlying file pointer"""
625 """move the underlying file pointer"""
626 if self._seekable:
626 if self._seekable:
627 return self._fp.seek(offset, whence)
627 return self._fp.seek(offset, whence)
628 else:
628 else:
629 raise NotImplementedError(_('File pointer is not seekable'))
629 raise NotImplementedError(_('File pointer is not seekable'))
630
630
631 def tell(self):
631 def tell(self):
632 """return the file offset, or None if file is not seekable"""
632 """return the file offset, or None if file is not seekable"""
633 if self._seekable:
633 if self._seekable:
634 try:
634 try:
635 return self._fp.tell()
635 return self._fp.tell()
636 except IOError as e:
636 except IOError as e:
637 if e.errno == errno.ESPIPE:
637 if e.errno == errno.ESPIPE:
638 self._seekable = False
638 self._seekable = False
639 else:
639 else:
640 raise
640 raise
641 return None
641 return None
642
642
643 def close(self):
643 def close(self):
644 """close underlying file"""
644 """close underlying file"""
645 if util.safehasattr(self._fp, 'close'):
645 if util.safehasattr(self._fp, 'close'):
646 return self._fp.close()
646 return self._fp.close()
647
647
648 def getunbundler(ui, fp, magicstring=None):
648 def getunbundler(ui, fp, magicstring=None):
649 """return a valid unbundler object for a given magicstring"""
649 """return a valid unbundler object for a given magicstring"""
650 if magicstring is None:
650 if magicstring is None:
651 magicstring = changegroup.readexactly(fp, 4)
651 magicstring = changegroup.readexactly(fp, 4)
652 magic, version = magicstring[0:2], magicstring[2:4]
652 magic, version = magicstring[0:2], magicstring[2:4]
653 if magic != 'HG':
653 if magic != 'HG':
654 raise error.Abort(_('not a Mercurial bundle'))
654 raise error.Abort(_('not a Mercurial bundle'))
655 unbundlerclass = formatmap.get(version)
655 unbundlerclass = formatmap.get(version)
656 if unbundlerclass is None:
656 if unbundlerclass is None:
657 raise error.Abort(_('unknown bundle version %s') % version)
657 raise error.Abort(_('unknown bundle version %s') % version)
658 unbundler = unbundlerclass(ui, fp)
658 unbundler = unbundlerclass(ui, fp)
659 indebug(ui, 'start processing of %s stream' % magicstring)
659 indebug(ui, 'start processing of %s stream' % magicstring)
660 return unbundler
660 return unbundler
661
661
662 class unbundle20(unpackermixin):
662 class unbundle20(unpackermixin):
663 """interpret a bundle2 stream
663 """interpret a bundle2 stream
664
664
665 This class is fed with a binary stream and yields parts through its
665 This class is fed with a binary stream and yields parts through its
666 `iterparts` methods."""
666 `iterparts` methods."""
667
667
668 _magicstring = 'HG20'
668 _magicstring = 'HG20'
669
669
670 def __init__(self, ui, fp):
670 def __init__(self, ui, fp):
671 """If header is specified, we do not read it out of the stream."""
671 """If header is specified, we do not read it out of the stream."""
672 self.ui = ui
672 self.ui = ui
673 self._decompressor = util.decompressors[None]
673 self._decompressor = util.decompressors[None]
674 self._compressed = None
674 self._compressed = None
675 super(unbundle20, self).__init__(fp)
675 super(unbundle20, self).__init__(fp)
676
676
677 @util.propertycache
677 @util.propertycache
678 def params(self):
678 def params(self):
679 """dictionary of stream level parameters"""
679 """dictionary of stream level parameters"""
680 indebug(self.ui, 'reading bundle2 stream parameters')
680 indebug(self.ui, 'reading bundle2 stream parameters')
681 params = {}
681 params = {}
682 paramssize = self._unpack(_fstreamparamsize)[0]
682 paramssize = self._unpack(_fstreamparamsize)[0]
683 if paramssize < 0:
683 if paramssize < 0:
684 raise error.BundleValueError('negative bundle param size: %i'
684 raise error.BundleValueError('negative bundle param size: %i'
685 % paramssize)
685 % paramssize)
686 if paramssize:
686 if paramssize:
687 params = self._readexact(paramssize)
687 params = self._readexact(paramssize)
688 params = self._processallparams(params)
688 params = self._processallparams(params)
689 return params
689 return params
690
690
691 def _processallparams(self, paramsblock):
691 def _processallparams(self, paramsblock):
692 """"""
692 """"""
693 params = util.sortdict()
693 params = util.sortdict()
694 for p in paramsblock.split(' '):
694 for p in paramsblock.split(' '):
695 p = p.split('=', 1)
695 p = p.split('=', 1)
696 p = [urlreq.unquote(i) for i in p]
696 p = [urlreq.unquote(i) for i in p]
697 if len(p) < 2:
697 if len(p) < 2:
698 p.append(None)
698 p.append(None)
699 self._processparam(*p)
699 self._processparam(*p)
700 params[p[0]] = p[1]
700 params[p[0]] = p[1]
701 return params
701 return params
702
702
703
703
704 def _processparam(self, name, value):
704 def _processparam(self, name, value):
705 """process a parameter, applying its effect if needed
705 """process a parameter, applying its effect if needed
706
706
707 Parameter starting with a lower case letter are advisory and will be
707 Parameter starting with a lower case letter are advisory and will be
708 ignored when unknown. Those starting with an upper case letter are
708 ignored when unknown. Those starting with an upper case letter are
709 mandatory and will this function will raise a KeyError when unknown.
709 mandatory and will this function will raise a KeyError when unknown.
710
710
711 Note: no option are currently supported. Any input will be either
711 Note: no option are currently supported. Any input will be either
712 ignored or failing.
712 ignored or failing.
713 """
713 """
714 if not name:
714 if not name:
715 raise ValueError('empty parameter name')
715 raise ValueError('empty parameter name')
716 if name[0] not in string.letters:
716 if name[0] not in string.letters:
717 raise ValueError('non letter first character: %r' % name)
717 raise ValueError('non letter first character: %r' % name)
718 try:
718 try:
719 handler = b2streamparamsmap[name.lower()]
719 handler = b2streamparamsmap[name.lower()]
720 except KeyError:
720 except KeyError:
721 if name[0].islower():
721 if name[0].islower():
722 indebug(self.ui, "ignoring unknown parameter %r" % name)
722 indebug(self.ui, "ignoring unknown parameter %r" % name)
723 else:
723 else:
724 raise error.BundleUnknownFeatureError(params=(name,))
724 raise error.BundleUnknownFeatureError(params=(name,))
725 else:
725 else:
726 handler(self, name, value)
726 handler(self, name, value)
727
727
728 def _forwardchunks(self):
728 def _forwardchunks(self):
729 """utility to transfer a bundle2 as binary
729 """utility to transfer a bundle2 as binary
730
730
731 This is made necessary by the fact the 'getbundle' command over 'ssh'
731 This is made necessary by the fact the 'getbundle' command over 'ssh'
732 have no way to know then the reply end, relying on the bundle to be
732 have no way to know then the reply end, relying on the bundle to be
733 interpreted to know its end. This is terrible and we are sorry, but we
733 interpreted to know its end. This is terrible and we are sorry, but we
734 needed to move forward to get general delta enabled.
734 needed to move forward to get general delta enabled.
735 """
735 """
736 yield self._magicstring
736 yield self._magicstring
737 assert 'params' not in vars(self)
737 assert 'params' not in vars(self)
738 paramssize = self._unpack(_fstreamparamsize)[0]
738 paramssize = self._unpack(_fstreamparamsize)[0]
739 if paramssize < 0:
739 if paramssize < 0:
740 raise error.BundleValueError('negative bundle param size: %i'
740 raise error.BundleValueError('negative bundle param size: %i'
741 % paramssize)
741 % paramssize)
742 yield _pack(_fstreamparamsize, paramssize)
742 yield _pack(_fstreamparamsize, paramssize)
743 if paramssize:
743 if paramssize:
744 params = self._readexact(paramssize)
744 params = self._readexact(paramssize)
745 self._processallparams(params)
745 self._processallparams(params)
746 yield params
746 yield params
747 assert self._decompressor is util.decompressors[None]
747 assert self._decompressor is util.decompressors[None]
748 # From there, payload might need to be decompressed
748 # From there, payload might need to be decompressed
749 self._fp = self._decompressor(self._fp)
749 self._fp = self._decompressor(self._fp)
750 emptycount = 0
750 emptycount = 0
751 while emptycount < 2:
751 while emptycount < 2:
752 # so we can brainlessly loop
752 # so we can brainlessly loop
753 assert _fpartheadersize == _fpayloadsize
753 assert _fpartheadersize == _fpayloadsize
754 size = self._unpack(_fpartheadersize)[0]
754 size = self._unpack(_fpartheadersize)[0]
755 yield _pack(_fpartheadersize, size)
755 yield _pack(_fpartheadersize, size)
756 if size:
756 if size:
757 emptycount = 0
757 emptycount = 0
758 else:
758 else:
759 emptycount += 1
759 emptycount += 1
760 continue
760 continue
761 if size == flaginterrupt:
761 if size == flaginterrupt:
762 continue
762 continue
763 elif size < 0:
763 elif size < 0:
764 raise error.BundleValueError('negative chunk size: %i')
764 raise error.BundleValueError('negative chunk size: %i')
765 yield self._readexact(size)
765 yield self._readexact(size)
766
766
767
767
768 def iterparts(self):
768 def iterparts(self):
769 """yield all parts contained in the stream"""
769 """yield all parts contained in the stream"""
770 # make sure param have been loaded
770 # make sure param have been loaded
771 self.params
771 self.params
772 # From there, payload need to be decompressed
772 # From there, payload need to be decompressed
773 self._fp = self._decompressor(self._fp)
773 self._fp = self._decompressor(self._fp)
774 indebug(self.ui, 'start extraction of bundle2 parts')
774 indebug(self.ui, 'start extraction of bundle2 parts')
775 headerblock = self._readpartheader()
775 headerblock = self._readpartheader()
776 while headerblock is not None:
776 while headerblock is not None:
777 part = unbundlepart(self.ui, headerblock, self._fp)
777 part = unbundlepart(self.ui, headerblock, self._fp)
778 yield part
778 yield part
779 part.seek(0, 2)
779 part.seek(0, 2)
780 headerblock = self._readpartheader()
780 headerblock = self._readpartheader()
781 indebug(self.ui, 'end of bundle2 stream')
781 indebug(self.ui, 'end of bundle2 stream')
782
782
783 def _readpartheader(self):
783 def _readpartheader(self):
784 """reads a part header size and return the bytes blob
784 """reads a part header size and return the bytes blob
785
785
786 returns None if empty"""
786 returns None if empty"""
787 headersize = self._unpack(_fpartheadersize)[0]
787 headersize = self._unpack(_fpartheadersize)[0]
788 if headersize < 0:
788 if headersize < 0:
789 raise error.BundleValueError('negative part header size: %i'
789 raise error.BundleValueError('negative part header size: %i'
790 % headersize)
790 % headersize)
791 indebug(self.ui, 'part header size: %i' % headersize)
791 indebug(self.ui, 'part header size: %i' % headersize)
792 if headersize:
792 if headersize:
793 return self._readexact(headersize)
793 return self._readexact(headersize)
794 return None
794 return None
795
795
796 def compressed(self):
796 def compressed(self):
797 self.params # load params
797 self.params # load params
798 return self._compressed
798 return self._compressed
799
799
800 formatmap = {'20': unbundle20}
800 formatmap = {'20': unbundle20}
801
801
802 b2streamparamsmap = {}
802 b2streamparamsmap = {}
803
803
804 def b2streamparamhandler(name):
804 def b2streamparamhandler(name):
805 """register a handler for a stream level parameter"""
805 """register a handler for a stream level parameter"""
806 def decorator(func):
806 def decorator(func):
807 assert name not in formatmap
807 assert name not in formatmap
808 b2streamparamsmap[name] = func
808 b2streamparamsmap[name] = func
809 return func
809 return func
810 return decorator
810 return decorator
811
811
812 @b2streamparamhandler('compression')
812 @b2streamparamhandler('compression')
813 def processcompression(unbundler, param, value):
813 def processcompression(unbundler, param, value):
814 """read compression parameter and install payload decompression"""
814 """read compression parameter and install payload decompression"""
815 if value not in util.decompressors:
815 if value not in util.decompressors:
816 raise error.BundleUnknownFeatureError(params=(param,),
816 raise error.BundleUnknownFeatureError(params=(param,),
817 values=(value,))
817 values=(value,))
818 unbundler._decompressor = util.decompressors[value]
818 unbundler._decompressor = util.decompressors[value]
819 if value is not None:
819 if value is not None:
820 unbundler._compressed = True
820 unbundler._compressed = True
821
821
822 class bundlepart(object):
822 class bundlepart(object):
823 """A bundle2 part contains application level payload
823 """A bundle2 part contains application level payload
824
824
825 The part `type` is used to route the part to the application level
825 The part `type` is used to route the part to the application level
826 handler.
826 handler.
827
827
828 The part payload is contained in ``part.data``. It could be raw bytes or a
828 The part payload is contained in ``part.data``. It could be raw bytes or a
829 generator of byte chunks.
829 generator of byte chunks.
830
830
831 You can add parameters to the part using the ``addparam`` method.
831 You can add parameters to the part using the ``addparam`` method.
832 Parameters can be either mandatory (default) or advisory. Remote side
832 Parameters can be either mandatory (default) or advisory. Remote side
833 should be able to safely ignore the advisory ones.
833 should be able to safely ignore the advisory ones.
834
834
835 Both data and parameters cannot be modified after the generation has begun.
835 Both data and parameters cannot be modified after the generation has begun.
836 """
836 """
837
837
838 def __init__(self, parttype, mandatoryparams=(), advisoryparams=(),
838 def __init__(self, parttype, mandatoryparams=(), advisoryparams=(),
839 data='', mandatory=True):
839 data='', mandatory=True):
840 validateparttype(parttype)
840 validateparttype(parttype)
841 self.id = None
841 self.id = None
842 self.type = parttype
842 self.type = parttype
843 self._data = data
843 self._data = data
844 self._mandatoryparams = list(mandatoryparams)
844 self._mandatoryparams = list(mandatoryparams)
845 self._advisoryparams = list(advisoryparams)
845 self._advisoryparams = list(advisoryparams)
846 # checking for duplicated entries
846 # checking for duplicated entries
847 self._seenparams = set()
847 self._seenparams = set()
848 for pname, __ in self._mandatoryparams + self._advisoryparams:
848 for pname, __ in self._mandatoryparams + self._advisoryparams:
849 if pname in self._seenparams:
849 if pname in self._seenparams:
850 raise RuntimeError('duplicated params: %s' % pname)
850 raise RuntimeError('duplicated params: %s' % pname)
851 self._seenparams.add(pname)
851 self._seenparams.add(pname)
852 # status of the part's generation:
852 # status of the part's generation:
853 # - None: not started,
853 # - None: not started,
854 # - False: currently generated,
854 # - False: currently generated,
855 # - True: generation done.
855 # - True: generation done.
856 self._generated = None
856 self._generated = None
857 self.mandatory = mandatory
857 self.mandatory = mandatory
858
858
859 def copy(self):
859 def copy(self):
860 """return a copy of the part
860 """return a copy of the part
861
861
862 The new part have the very same content but no partid assigned yet.
862 The new part have the very same content but no partid assigned yet.
863 Parts with generated data cannot be copied."""
863 Parts with generated data cannot be copied."""
864 assert not util.safehasattr(self.data, 'next')
864 assert not util.safehasattr(self.data, 'next')
865 return self.__class__(self.type, self._mandatoryparams,
865 return self.__class__(self.type, self._mandatoryparams,
866 self._advisoryparams, self._data, self.mandatory)
866 self._advisoryparams, self._data, self.mandatory)
867
867
868 # methods used to defines the part content
868 # methods used to defines the part content
869 @property
869 @property
870 def data(self):
870 def data(self):
871 return self._data
871 return self._data
872
872
873 @data.setter
873 @data.setter
874 def data(self, data):
874 def data(self, data):
875 if self._generated is not None:
875 if self._generated is not None:
876 raise error.ReadOnlyPartError('part is being generated')
876 raise error.ReadOnlyPartError('part is being generated')
877 self._data = data
877 self._data = data
878
878
879 @property
879 @property
880 def mandatoryparams(self):
880 def mandatoryparams(self):
881 # make it an immutable tuple to force people through ``addparam``
881 # make it an immutable tuple to force people through ``addparam``
882 return tuple(self._mandatoryparams)
882 return tuple(self._mandatoryparams)
883
883
884 @property
884 @property
885 def advisoryparams(self):
885 def advisoryparams(self):
886 # make it an immutable tuple to force people through ``addparam``
886 # make it an immutable tuple to force people through ``addparam``
887 return tuple(self._advisoryparams)
887 return tuple(self._advisoryparams)
888
888
889 def addparam(self, name, value='', mandatory=True):
889 def addparam(self, name, value='', mandatory=True):
890 if self._generated is not None:
890 if self._generated is not None:
891 raise error.ReadOnlyPartError('part is being generated')
891 raise error.ReadOnlyPartError('part is being generated')
892 if name in self._seenparams:
892 if name in self._seenparams:
893 raise ValueError('duplicated params: %s' % name)
893 raise ValueError('duplicated params: %s' % name)
894 self._seenparams.add(name)
894 self._seenparams.add(name)
895 params = self._advisoryparams
895 params = self._advisoryparams
896 if mandatory:
896 if mandatory:
897 params = self._mandatoryparams
897 params = self._mandatoryparams
898 params.append((name, value))
898 params.append((name, value))
899
899
900 # methods used to generates the bundle2 stream
900 # methods used to generates the bundle2 stream
901 def getchunks(self, ui):
901 def getchunks(self, ui):
902 if self._generated is not None:
902 if self._generated is not None:
903 raise RuntimeError('part can only be consumed once')
903 raise RuntimeError('part can only be consumed once')
904 self._generated = False
904 self._generated = False
905
905
906 if ui.debugflag:
906 if ui.debugflag:
907 msg = ['bundle2-output-part: "%s"' % self.type]
907 msg = ['bundle2-output-part: "%s"' % self.type]
908 if not self.mandatory:
908 if not self.mandatory:
909 msg.append(' (advisory)')
909 msg.append(' (advisory)')
910 nbmp = len(self.mandatoryparams)
910 nbmp = len(self.mandatoryparams)
911 nbap = len(self.advisoryparams)
911 nbap = len(self.advisoryparams)
912 if nbmp or nbap:
912 if nbmp or nbap:
913 msg.append(' (params:')
913 msg.append(' (params:')
914 if nbmp:
914 if nbmp:
915 msg.append(' %i mandatory' % nbmp)
915 msg.append(' %i mandatory' % nbmp)
916 if nbap:
916 if nbap:
917 msg.append(' %i advisory' % nbmp)
917 msg.append(' %i advisory' % nbmp)
918 msg.append(')')
918 msg.append(')')
919 if not self.data:
919 if not self.data:
920 msg.append(' empty payload')
920 msg.append(' empty payload')
921 elif util.safehasattr(self.data, 'next'):
921 elif util.safehasattr(self.data, 'next'):
922 msg.append(' streamed payload')
922 msg.append(' streamed payload')
923 else:
923 else:
924 msg.append(' %i bytes payload' % len(self.data))
924 msg.append(' %i bytes payload' % len(self.data))
925 msg.append('\n')
925 msg.append('\n')
926 ui.debug(''.join(msg))
926 ui.debug(''.join(msg))
927
927
928 #### header
928 #### header
929 if self.mandatory:
929 if self.mandatory:
930 parttype = self.type.upper()
930 parttype = self.type.upper()
931 else:
931 else:
932 parttype = self.type.lower()
932 parttype = self.type.lower()
933 outdebug(ui, 'part %s: "%s"' % (self.id, parttype))
933 outdebug(ui, 'part %s: "%s"' % (self.id, parttype))
934 ## parttype
934 ## parttype
935 header = [_pack(_fparttypesize, len(parttype)),
935 header = [_pack(_fparttypesize, len(parttype)),
936 parttype, _pack(_fpartid, self.id),
936 parttype, _pack(_fpartid, self.id),
937 ]
937 ]
938 ## parameters
938 ## parameters
939 # count
939 # count
940 manpar = self.mandatoryparams
940 manpar = self.mandatoryparams
941 advpar = self.advisoryparams
941 advpar = self.advisoryparams
942 header.append(_pack(_fpartparamcount, len(manpar), len(advpar)))
942 header.append(_pack(_fpartparamcount, len(manpar), len(advpar)))
943 # size
943 # size
944 parsizes = []
944 parsizes = []
945 for key, value in manpar:
945 for key, value in manpar:
946 parsizes.append(len(key))
946 parsizes.append(len(key))
947 parsizes.append(len(value))
947 parsizes.append(len(value))
948 for key, value in advpar:
948 for key, value in advpar:
949 parsizes.append(len(key))
949 parsizes.append(len(key))
950 parsizes.append(len(value))
950 parsizes.append(len(value))
951 paramsizes = _pack(_makefpartparamsizes(len(parsizes) / 2), *parsizes)
951 paramsizes = _pack(_makefpartparamsizes(len(parsizes) / 2), *parsizes)
952 header.append(paramsizes)
952 header.append(paramsizes)
953 # key, value
953 # key, value
954 for key, value in manpar:
954 for key, value in manpar:
955 header.append(key)
955 header.append(key)
956 header.append(value)
956 header.append(value)
957 for key, value in advpar:
957 for key, value in advpar:
958 header.append(key)
958 header.append(key)
959 header.append(value)
959 header.append(value)
960 ## finalize header
960 ## finalize header
961 headerchunk = ''.join(header)
961 headerchunk = ''.join(header)
962 outdebug(ui, 'header chunk size: %i' % len(headerchunk))
962 outdebug(ui, 'header chunk size: %i' % len(headerchunk))
963 yield _pack(_fpartheadersize, len(headerchunk))
963 yield _pack(_fpartheadersize, len(headerchunk))
964 yield headerchunk
964 yield headerchunk
965 ## payload
965 ## payload
966 try:
966 try:
967 for chunk in self._payloadchunks():
967 for chunk in self._payloadchunks():
968 outdebug(ui, 'payload chunk size: %i' % len(chunk))
968 outdebug(ui, 'payload chunk size: %i' % len(chunk))
969 yield _pack(_fpayloadsize, len(chunk))
969 yield _pack(_fpayloadsize, len(chunk))
970 yield chunk
970 yield chunk
971 except GeneratorExit:
971 except GeneratorExit:
972 # GeneratorExit means that nobody is listening for our
972 # GeneratorExit means that nobody is listening for our
973 # results anyway, so just bail quickly rather than trying
973 # results anyway, so just bail quickly rather than trying
974 # to produce an error part.
974 # to produce an error part.
975 ui.debug('bundle2-generatorexit\n')
975 ui.debug('bundle2-generatorexit\n')
976 raise
976 raise
977 except BaseException as exc:
977 except BaseException as exc:
978 # backup exception data for later
978 # backup exception data for later
979 ui.debug('bundle2-input-stream-interrupt: encoding exception %s'
979 ui.debug('bundle2-input-stream-interrupt: encoding exception %s'
980 % exc)
980 % exc)
981 exc_info = sys.exc_info()
981 exc_info = sys.exc_info()
982 msg = 'unexpected error: %s' % exc
982 msg = 'unexpected error: %s' % exc
983 interpart = bundlepart('error:abort', [('message', msg)],
983 interpart = bundlepart('error:abort', [('message', msg)],
984 mandatory=False)
984 mandatory=False)
985 interpart.id = 0
985 interpart.id = 0
986 yield _pack(_fpayloadsize, -1)
986 yield _pack(_fpayloadsize, -1)
987 for chunk in interpart.getchunks(ui=ui):
987 for chunk in interpart.getchunks(ui=ui):
988 yield chunk
988 yield chunk
989 outdebug(ui, 'closing payload chunk')
989 outdebug(ui, 'closing payload chunk')
990 # abort current part payload
990 # abort current part payload
991 yield _pack(_fpayloadsize, 0)
991 yield _pack(_fpayloadsize, 0)
992 raise exc_info[0], exc_info[1], exc_info[2]
992 if sys.version_info[0] >= 3:
993 raise exc_info[0](exc_info[1]).with_traceback(exc_info[2])
994 else:
995 exec("""raise exc_info[0], exc_info[1], exc_info[2]""")
993 # end of payload
996 # end of payload
994 outdebug(ui, 'closing payload chunk')
997 outdebug(ui, 'closing payload chunk')
995 yield _pack(_fpayloadsize, 0)
998 yield _pack(_fpayloadsize, 0)
996 self._generated = True
999 self._generated = True
997
1000
998 def _payloadchunks(self):
1001 def _payloadchunks(self):
999 """yield chunks of a the part payload
1002 """yield chunks of a the part payload
1000
1003
1001 Exists to handle the different methods to provide data to a part."""
1004 Exists to handle the different methods to provide data to a part."""
1002 # we only support fixed size data now.
1005 # we only support fixed size data now.
1003 # This will be improved in the future.
1006 # This will be improved in the future.
1004 if util.safehasattr(self.data, 'next'):
1007 if util.safehasattr(self.data, 'next'):
1005 buff = util.chunkbuffer(self.data)
1008 buff = util.chunkbuffer(self.data)
1006 chunk = buff.read(preferedchunksize)
1009 chunk = buff.read(preferedchunksize)
1007 while chunk:
1010 while chunk:
1008 yield chunk
1011 yield chunk
1009 chunk = buff.read(preferedchunksize)
1012 chunk = buff.read(preferedchunksize)
1010 elif len(self.data):
1013 elif len(self.data):
1011 yield self.data
1014 yield self.data
1012
1015
1013
1016
1014 flaginterrupt = -1
1017 flaginterrupt = -1
1015
1018
1016 class interrupthandler(unpackermixin):
1019 class interrupthandler(unpackermixin):
1017 """read one part and process it with restricted capability
1020 """read one part and process it with restricted capability
1018
1021
1019 This allows to transmit exception raised on the producer size during part
1022 This allows to transmit exception raised on the producer size during part
1020 iteration while the consumer is reading a part.
1023 iteration while the consumer is reading a part.
1021
1024
1022 Part processed in this manner only have access to a ui object,"""
1025 Part processed in this manner only have access to a ui object,"""
1023
1026
1024 def __init__(self, ui, fp):
1027 def __init__(self, ui, fp):
1025 super(interrupthandler, self).__init__(fp)
1028 super(interrupthandler, self).__init__(fp)
1026 self.ui = ui
1029 self.ui = ui
1027
1030
1028 def _readpartheader(self):
1031 def _readpartheader(self):
1029 """reads a part header size and return the bytes blob
1032 """reads a part header size and return the bytes blob
1030
1033
1031 returns None if empty"""
1034 returns None if empty"""
1032 headersize = self._unpack(_fpartheadersize)[0]
1035 headersize = self._unpack(_fpartheadersize)[0]
1033 if headersize < 0:
1036 if headersize < 0:
1034 raise error.BundleValueError('negative part header size: %i'
1037 raise error.BundleValueError('negative part header size: %i'
1035 % headersize)
1038 % headersize)
1036 indebug(self.ui, 'part header size: %i\n' % headersize)
1039 indebug(self.ui, 'part header size: %i\n' % headersize)
1037 if headersize:
1040 if headersize:
1038 return self._readexact(headersize)
1041 return self._readexact(headersize)
1039 return None
1042 return None
1040
1043
1041 def __call__(self):
1044 def __call__(self):
1042
1045
1043 self.ui.debug('bundle2-input-stream-interrupt:'
1046 self.ui.debug('bundle2-input-stream-interrupt:'
1044 ' opening out of band context\n')
1047 ' opening out of band context\n')
1045 indebug(self.ui, 'bundle2 stream interruption, looking for a part.')
1048 indebug(self.ui, 'bundle2 stream interruption, looking for a part.')
1046 headerblock = self._readpartheader()
1049 headerblock = self._readpartheader()
1047 if headerblock is None:
1050 if headerblock is None:
1048 indebug(self.ui, 'no part found during interruption.')
1051 indebug(self.ui, 'no part found during interruption.')
1049 return
1052 return
1050 part = unbundlepart(self.ui, headerblock, self._fp)
1053 part = unbundlepart(self.ui, headerblock, self._fp)
1051 op = interruptoperation(self.ui)
1054 op = interruptoperation(self.ui)
1052 _processpart(op, part)
1055 _processpart(op, part)
1053 self.ui.debug('bundle2-input-stream-interrupt:'
1056 self.ui.debug('bundle2-input-stream-interrupt:'
1054 ' closing out of band context\n')
1057 ' closing out of band context\n')
1055
1058
1056 class interruptoperation(object):
1059 class interruptoperation(object):
1057 """A limited operation to be use by part handler during interruption
1060 """A limited operation to be use by part handler during interruption
1058
1061
1059 It only have access to an ui object.
1062 It only have access to an ui object.
1060 """
1063 """
1061
1064
1062 def __init__(self, ui):
1065 def __init__(self, ui):
1063 self.ui = ui
1066 self.ui = ui
1064 self.reply = None
1067 self.reply = None
1065 self.captureoutput = False
1068 self.captureoutput = False
1066
1069
1067 @property
1070 @property
1068 def repo(self):
1071 def repo(self):
1069 raise RuntimeError('no repo access from stream interruption')
1072 raise RuntimeError('no repo access from stream interruption')
1070
1073
1071 def gettransaction(self):
1074 def gettransaction(self):
1072 raise TransactionUnavailable('no repo access from stream interruption')
1075 raise TransactionUnavailable('no repo access from stream interruption')
1073
1076
1074 class unbundlepart(unpackermixin):
1077 class unbundlepart(unpackermixin):
1075 """a bundle part read from a bundle"""
1078 """a bundle part read from a bundle"""
1076
1079
1077 def __init__(self, ui, header, fp):
1080 def __init__(self, ui, header, fp):
1078 super(unbundlepart, self).__init__(fp)
1081 super(unbundlepart, self).__init__(fp)
1079 self.ui = ui
1082 self.ui = ui
1080 # unbundle state attr
1083 # unbundle state attr
1081 self._headerdata = header
1084 self._headerdata = header
1082 self._headeroffset = 0
1085 self._headeroffset = 0
1083 self._initialized = False
1086 self._initialized = False
1084 self.consumed = False
1087 self.consumed = False
1085 # part data
1088 # part data
1086 self.id = None
1089 self.id = None
1087 self.type = None
1090 self.type = None
1088 self.mandatoryparams = None
1091 self.mandatoryparams = None
1089 self.advisoryparams = None
1092 self.advisoryparams = None
1090 self.params = None
1093 self.params = None
1091 self.mandatorykeys = ()
1094 self.mandatorykeys = ()
1092 self._payloadstream = None
1095 self._payloadstream = None
1093 self._readheader()
1096 self._readheader()
1094 self._mandatory = None
1097 self._mandatory = None
1095 self._chunkindex = [] #(payload, file) position tuples for chunk starts
1098 self._chunkindex = [] #(payload, file) position tuples for chunk starts
1096 self._pos = 0
1099 self._pos = 0
1097
1100
1098 def _fromheader(self, size):
1101 def _fromheader(self, size):
1099 """return the next <size> byte from the header"""
1102 """return the next <size> byte from the header"""
1100 offset = self._headeroffset
1103 offset = self._headeroffset
1101 data = self._headerdata[offset:(offset + size)]
1104 data = self._headerdata[offset:(offset + size)]
1102 self._headeroffset = offset + size
1105 self._headeroffset = offset + size
1103 return data
1106 return data
1104
1107
1105 def _unpackheader(self, format):
1108 def _unpackheader(self, format):
1106 """read given format from header
1109 """read given format from header
1107
1110
1108 This automatically compute the size of the format to read."""
1111 This automatically compute the size of the format to read."""
1109 data = self._fromheader(struct.calcsize(format))
1112 data = self._fromheader(struct.calcsize(format))
1110 return _unpack(format, data)
1113 return _unpack(format, data)
1111
1114
1112 def _initparams(self, mandatoryparams, advisoryparams):
1115 def _initparams(self, mandatoryparams, advisoryparams):
1113 """internal function to setup all logic related parameters"""
1116 """internal function to setup all logic related parameters"""
1114 # make it read only to prevent people touching it by mistake.
1117 # make it read only to prevent people touching it by mistake.
1115 self.mandatoryparams = tuple(mandatoryparams)
1118 self.mandatoryparams = tuple(mandatoryparams)
1116 self.advisoryparams = tuple(advisoryparams)
1119 self.advisoryparams = tuple(advisoryparams)
1117 # user friendly UI
1120 # user friendly UI
1118 self.params = util.sortdict(self.mandatoryparams)
1121 self.params = util.sortdict(self.mandatoryparams)
1119 self.params.update(self.advisoryparams)
1122 self.params.update(self.advisoryparams)
1120 self.mandatorykeys = frozenset(p[0] for p in mandatoryparams)
1123 self.mandatorykeys = frozenset(p[0] for p in mandatoryparams)
1121
1124
1122 def _payloadchunks(self, chunknum=0):
1125 def _payloadchunks(self, chunknum=0):
1123 '''seek to specified chunk and start yielding data'''
1126 '''seek to specified chunk and start yielding data'''
1124 if len(self._chunkindex) == 0:
1127 if len(self._chunkindex) == 0:
1125 assert chunknum == 0, 'Must start with chunk 0'
1128 assert chunknum == 0, 'Must start with chunk 0'
1126 self._chunkindex.append((0, super(unbundlepart, self).tell()))
1129 self._chunkindex.append((0, super(unbundlepart, self).tell()))
1127 else:
1130 else:
1128 assert chunknum < len(self._chunkindex), \
1131 assert chunknum < len(self._chunkindex), \
1129 'Unknown chunk %d' % chunknum
1132 'Unknown chunk %d' % chunknum
1130 super(unbundlepart, self).seek(self._chunkindex[chunknum][1])
1133 super(unbundlepart, self).seek(self._chunkindex[chunknum][1])
1131
1134
1132 pos = self._chunkindex[chunknum][0]
1135 pos = self._chunkindex[chunknum][0]
1133 payloadsize = self._unpack(_fpayloadsize)[0]
1136 payloadsize = self._unpack(_fpayloadsize)[0]
1134 indebug(self.ui, 'payload chunk size: %i' % payloadsize)
1137 indebug(self.ui, 'payload chunk size: %i' % payloadsize)
1135 while payloadsize:
1138 while payloadsize:
1136 if payloadsize == flaginterrupt:
1139 if payloadsize == flaginterrupt:
1137 # interruption detection, the handler will now read a
1140 # interruption detection, the handler will now read a
1138 # single part and process it.
1141 # single part and process it.
1139 interrupthandler(self.ui, self._fp)()
1142 interrupthandler(self.ui, self._fp)()
1140 elif payloadsize < 0:
1143 elif payloadsize < 0:
1141 msg = 'negative payload chunk size: %i' % payloadsize
1144 msg = 'negative payload chunk size: %i' % payloadsize
1142 raise error.BundleValueError(msg)
1145 raise error.BundleValueError(msg)
1143 else:
1146 else:
1144 result = self._readexact(payloadsize)
1147 result = self._readexact(payloadsize)
1145 chunknum += 1
1148 chunknum += 1
1146 pos += payloadsize
1149 pos += payloadsize
1147 if chunknum == len(self._chunkindex):
1150 if chunknum == len(self._chunkindex):
1148 self._chunkindex.append((pos,
1151 self._chunkindex.append((pos,
1149 super(unbundlepart, self).tell()))
1152 super(unbundlepart, self).tell()))
1150 yield result
1153 yield result
1151 payloadsize = self._unpack(_fpayloadsize)[0]
1154 payloadsize = self._unpack(_fpayloadsize)[0]
1152 indebug(self.ui, 'payload chunk size: %i' % payloadsize)
1155 indebug(self.ui, 'payload chunk size: %i' % payloadsize)
1153
1156
1154 def _findchunk(self, pos):
1157 def _findchunk(self, pos):
1155 '''for a given payload position, return a chunk number and offset'''
1158 '''for a given payload position, return a chunk number and offset'''
1156 for chunk, (ppos, fpos) in enumerate(self._chunkindex):
1159 for chunk, (ppos, fpos) in enumerate(self._chunkindex):
1157 if ppos == pos:
1160 if ppos == pos:
1158 return chunk, 0
1161 return chunk, 0
1159 elif ppos > pos:
1162 elif ppos > pos:
1160 return chunk - 1, pos - self._chunkindex[chunk - 1][0]
1163 return chunk - 1, pos - self._chunkindex[chunk - 1][0]
1161 raise ValueError('Unknown chunk')
1164 raise ValueError('Unknown chunk')
1162
1165
1163 def _readheader(self):
1166 def _readheader(self):
1164 """read the header and setup the object"""
1167 """read the header and setup the object"""
1165 typesize = self._unpackheader(_fparttypesize)[0]
1168 typesize = self._unpackheader(_fparttypesize)[0]
1166 self.type = self._fromheader(typesize)
1169 self.type = self._fromheader(typesize)
1167 indebug(self.ui, 'part type: "%s"' % self.type)
1170 indebug(self.ui, 'part type: "%s"' % self.type)
1168 self.id = self._unpackheader(_fpartid)[0]
1171 self.id = self._unpackheader(_fpartid)[0]
1169 indebug(self.ui, 'part id: "%s"' % self.id)
1172 indebug(self.ui, 'part id: "%s"' % self.id)
1170 # extract mandatory bit from type
1173 # extract mandatory bit from type
1171 self.mandatory = (self.type != self.type.lower())
1174 self.mandatory = (self.type != self.type.lower())
1172 self.type = self.type.lower()
1175 self.type = self.type.lower()
1173 ## reading parameters
1176 ## reading parameters
1174 # param count
1177 # param count
1175 mancount, advcount = self._unpackheader(_fpartparamcount)
1178 mancount, advcount = self._unpackheader(_fpartparamcount)
1176 indebug(self.ui, 'part parameters: %i' % (mancount + advcount))
1179 indebug(self.ui, 'part parameters: %i' % (mancount + advcount))
1177 # param size
1180 # param size
1178 fparamsizes = _makefpartparamsizes(mancount + advcount)
1181 fparamsizes = _makefpartparamsizes(mancount + advcount)
1179 paramsizes = self._unpackheader(fparamsizes)
1182 paramsizes = self._unpackheader(fparamsizes)
1180 # make it a list of couple again
1183 # make it a list of couple again
1181 paramsizes = zip(paramsizes[::2], paramsizes[1::2])
1184 paramsizes = zip(paramsizes[::2], paramsizes[1::2])
1182 # split mandatory from advisory
1185 # split mandatory from advisory
1183 mansizes = paramsizes[:mancount]
1186 mansizes = paramsizes[:mancount]
1184 advsizes = paramsizes[mancount:]
1187 advsizes = paramsizes[mancount:]
1185 # retrieve param value
1188 # retrieve param value
1186 manparams = []
1189 manparams = []
1187 for key, value in mansizes:
1190 for key, value in mansizes:
1188 manparams.append((self._fromheader(key), self._fromheader(value)))
1191 manparams.append((self._fromheader(key), self._fromheader(value)))
1189 advparams = []
1192 advparams = []
1190 for key, value in advsizes:
1193 for key, value in advsizes:
1191 advparams.append((self._fromheader(key), self._fromheader(value)))
1194 advparams.append((self._fromheader(key), self._fromheader(value)))
1192 self._initparams(manparams, advparams)
1195 self._initparams(manparams, advparams)
1193 ## part payload
1196 ## part payload
1194 self._payloadstream = util.chunkbuffer(self._payloadchunks())
1197 self._payloadstream = util.chunkbuffer(self._payloadchunks())
1195 # we read the data, tell it
1198 # we read the data, tell it
1196 self._initialized = True
1199 self._initialized = True
1197
1200
1198 def read(self, size=None):
1201 def read(self, size=None):
1199 """read payload data"""
1202 """read payload data"""
1200 if not self._initialized:
1203 if not self._initialized:
1201 self._readheader()
1204 self._readheader()
1202 if size is None:
1205 if size is None:
1203 data = self._payloadstream.read()
1206 data = self._payloadstream.read()
1204 else:
1207 else:
1205 data = self._payloadstream.read(size)
1208 data = self._payloadstream.read(size)
1206 self._pos += len(data)
1209 self._pos += len(data)
1207 if size is None or len(data) < size:
1210 if size is None or len(data) < size:
1208 if not self.consumed and self._pos:
1211 if not self.consumed and self._pos:
1209 self.ui.debug('bundle2-input-part: total payload size %i\n'
1212 self.ui.debug('bundle2-input-part: total payload size %i\n'
1210 % self._pos)
1213 % self._pos)
1211 self.consumed = True
1214 self.consumed = True
1212 return data
1215 return data
1213
1216
1214 def tell(self):
1217 def tell(self):
1215 return self._pos
1218 return self._pos
1216
1219
1217 def seek(self, offset, whence=0):
1220 def seek(self, offset, whence=0):
1218 if whence == 0:
1221 if whence == 0:
1219 newpos = offset
1222 newpos = offset
1220 elif whence == 1:
1223 elif whence == 1:
1221 newpos = self._pos + offset
1224 newpos = self._pos + offset
1222 elif whence == 2:
1225 elif whence == 2:
1223 if not self.consumed:
1226 if not self.consumed:
1224 self.read()
1227 self.read()
1225 newpos = self._chunkindex[-1][0] - offset
1228 newpos = self._chunkindex[-1][0] - offset
1226 else:
1229 else:
1227 raise ValueError('Unknown whence value: %r' % (whence,))
1230 raise ValueError('Unknown whence value: %r' % (whence,))
1228
1231
1229 if newpos > self._chunkindex[-1][0] and not self.consumed:
1232 if newpos > self._chunkindex[-1][0] and not self.consumed:
1230 self.read()
1233 self.read()
1231 if not 0 <= newpos <= self._chunkindex[-1][0]:
1234 if not 0 <= newpos <= self._chunkindex[-1][0]:
1232 raise ValueError('Offset out of range')
1235 raise ValueError('Offset out of range')
1233
1236
1234 if self._pos != newpos:
1237 if self._pos != newpos:
1235 chunk, internaloffset = self._findchunk(newpos)
1238 chunk, internaloffset = self._findchunk(newpos)
1236 self._payloadstream = util.chunkbuffer(self._payloadchunks(chunk))
1239 self._payloadstream = util.chunkbuffer(self._payloadchunks(chunk))
1237 adjust = self.read(internaloffset)
1240 adjust = self.read(internaloffset)
1238 if len(adjust) != internaloffset:
1241 if len(adjust) != internaloffset:
1239 raise error.Abort(_('Seek failed\n'))
1242 raise error.Abort(_('Seek failed\n'))
1240 self._pos = newpos
1243 self._pos = newpos
1241
1244
1242 # These are only the static capabilities.
1245 # These are only the static capabilities.
1243 # Check the 'getrepocaps' function for the rest.
1246 # Check the 'getrepocaps' function for the rest.
1244 capabilities = {'HG20': (),
1247 capabilities = {'HG20': (),
1245 'error': ('abort', 'unsupportedcontent', 'pushraced',
1248 'error': ('abort', 'unsupportedcontent', 'pushraced',
1246 'pushkey'),
1249 'pushkey'),
1247 'listkeys': (),
1250 'listkeys': (),
1248 'pushkey': (),
1251 'pushkey': (),
1249 'digests': tuple(sorted(util.DIGESTS.keys())),
1252 'digests': tuple(sorted(util.DIGESTS.keys())),
1250 'remote-changegroup': ('http', 'https'),
1253 'remote-changegroup': ('http', 'https'),
1251 'hgtagsfnodes': (),
1254 'hgtagsfnodes': (),
1252 }
1255 }
1253
1256
1254 def getrepocaps(repo, allowpushback=False):
1257 def getrepocaps(repo, allowpushback=False):
1255 """return the bundle2 capabilities for a given repo
1258 """return the bundle2 capabilities for a given repo
1256
1259
1257 Exists to allow extensions (like evolution) to mutate the capabilities.
1260 Exists to allow extensions (like evolution) to mutate the capabilities.
1258 """
1261 """
1259 caps = capabilities.copy()
1262 caps = capabilities.copy()
1260 caps['changegroup'] = tuple(sorted(
1263 caps['changegroup'] = tuple(sorted(
1261 changegroup.supportedincomingversions(repo)))
1264 changegroup.supportedincomingversions(repo)))
1262 if obsolete.isenabled(repo, obsolete.exchangeopt):
1265 if obsolete.isenabled(repo, obsolete.exchangeopt):
1263 supportedformat = tuple('V%i' % v for v in obsolete.formats)
1266 supportedformat = tuple('V%i' % v for v in obsolete.formats)
1264 caps['obsmarkers'] = supportedformat
1267 caps['obsmarkers'] = supportedformat
1265 if allowpushback:
1268 if allowpushback:
1266 caps['pushback'] = ()
1269 caps['pushback'] = ()
1267 return caps
1270 return caps
1268
1271
1269 def bundle2caps(remote):
1272 def bundle2caps(remote):
1270 """return the bundle capabilities of a peer as dict"""
1273 """return the bundle capabilities of a peer as dict"""
1271 raw = remote.capable('bundle2')
1274 raw = remote.capable('bundle2')
1272 if not raw and raw != '':
1275 if not raw and raw != '':
1273 return {}
1276 return {}
1274 capsblob = urlreq.unquote(remote.capable('bundle2'))
1277 capsblob = urlreq.unquote(remote.capable('bundle2'))
1275 return decodecaps(capsblob)
1278 return decodecaps(capsblob)
1276
1279
1277 def obsmarkersversion(caps):
1280 def obsmarkersversion(caps):
1278 """extract the list of supported obsmarkers versions from a bundle2caps dict
1281 """extract the list of supported obsmarkers versions from a bundle2caps dict
1279 """
1282 """
1280 obscaps = caps.get('obsmarkers', ())
1283 obscaps = caps.get('obsmarkers', ())
1281 return [int(c[1:]) for c in obscaps if c.startswith('V')]
1284 return [int(c[1:]) for c in obscaps if c.startswith('V')]
1282
1285
1283 def writebundle(ui, cg, filename, bundletype, vfs=None, compression=None):
1286 def writebundle(ui, cg, filename, bundletype, vfs=None, compression=None):
1284 """Write a bundle file and return its filename.
1287 """Write a bundle file and return its filename.
1285
1288
1286 Existing files will not be overwritten.
1289 Existing files will not be overwritten.
1287 If no filename is specified, a temporary file is created.
1290 If no filename is specified, a temporary file is created.
1288 bz2 compression can be turned off.
1291 bz2 compression can be turned off.
1289 The bundle file will be deleted in case of errors.
1292 The bundle file will be deleted in case of errors.
1290 """
1293 """
1291
1294
1292 if bundletype == "HG20":
1295 if bundletype == "HG20":
1293 bundle = bundle20(ui)
1296 bundle = bundle20(ui)
1294 bundle.setcompression(compression)
1297 bundle.setcompression(compression)
1295 part = bundle.newpart('changegroup', data=cg.getchunks())
1298 part = bundle.newpart('changegroup', data=cg.getchunks())
1296 part.addparam('version', cg.version)
1299 part.addparam('version', cg.version)
1297 if 'clcount' in cg.extras:
1300 if 'clcount' in cg.extras:
1298 part.addparam('nbchanges', str(cg.extras['clcount']),
1301 part.addparam('nbchanges', str(cg.extras['clcount']),
1299 mandatory=False)
1302 mandatory=False)
1300 chunkiter = bundle.getchunks()
1303 chunkiter = bundle.getchunks()
1301 else:
1304 else:
1302 # compression argument is only for the bundle2 case
1305 # compression argument is only for the bundle2 case
1303 assert compression is None
1306 assert compression is None
1304 if cg.version != '01':
1307 if cg.version != '01':
1305 raise error.Abort(_('old bundle types only supports v1 '
1308 raise error.Abort(_('old bundle types only supports v1 '
1306 'changegroups'))
1309 'changegroups'))
1307 header, comp = bundletypes[bundletype]
1310 header, comp = bundletypes[bundletype]
1308 if comp not in util.compressors:
1311 if comp not in util.compressors:
1309 raise error.Abort(_('unknown stream compression type: %s')
1312 raise error.Abort(_('unknown stream compression type: %s')
1310 % comp)
1313 % comp)
1311 z = util.compressors[comp]()
1314 z = util.compressors[comp]()
1312 subchunkiter = cg.getchunks()
1315 subchunkiter = cg.getchunks()
1313 def chunkiter():
1316 def chunkiter():
1314 yield header
1317 yield header
1315 for chunk in subchunkiter:
1318 for chunk in subchunkiter:
1316 yield z.compress(chunk)
1319 yield z.compress(chunk)
1317 yield z.flush()
1320 yield z.flush()
1318 chunkiter = chunkiter()
1321 chunkiter = chunkiter()
1319
1322
1320 # parse the changegroup data, otherwise we will block
1323 # parse the changegroup data, otherwise we will block
1321 # in case of sshrepo because we don't know the end of the stream
1324 # in case of sshrepo because we don't know the end of the stream
1322 return changegroup.writechunks(ui, chunkiter, filename, vfs=vfs)
1325 return changegroup.writechunks(ui, chunkiter, filename, vfs=vfs)
1323
1326
1324 @parthandler('changegroup', ('version', 'nbchanges', 'treemanifest'))
1327 @parthandler('changegroup', ('version', 'nbchanges', 'treemanifest'))
1325 def handlechangegroup(op, inpart):
1328 def handlechangegroup(op, inpart):
1326 """apply a changegroup part on the repo
1329 """apply a changegroup part on the repo
1327
1330
1328 This is a very early implementation that will massive rework before being
1331 This is a very early implementation that will massive rework before being
1329 inflicted to any end-user.
1332 inflicted to any end-user.
1330 """
1333 """
1331 # Make sure we trigger a transaction creation
1334 # Make sure we trigger a transaction creation
1332 #
1335 #
1333 # The addchangegroup function will get a transaction object by itself, but
1336 # The addchangegroup function will get a transaction object by itself, but
1334 # we need to make sure we trigger the creation of a transaction object used
1337 # we need to make sure we trigger the creation of a transaction object used
1335 # for the whole processing scope.
1338 # for the whole processing scope.
1336 op.gettransaction()
1339 op.gettransaction()
1337 unpackerversion = inpart.params.get('version', '01')
1340 unpackerversion = inpart.params.get('version', '01')
1338 # We should raise an appropriate exception here
1341 # We should raise an appropriate exception here
1339 cg = changegroup.getunbundler(unpackerversion, inpart, None)
1342 cg = changegroup.getunbundler(unpackerversion, inpart, None)
1340 # the source and url passed here are overwritten by the one contained in
1343 # the source and url passed here are overwritten by the one contained in
1341 # the transaction.hookargs argument. So 'bundle2' is a placeholder
1344 # the transaction.hookargs argument. So 'bundle2' is a placeholder
1342 nbchangesets = None
1345 nbchangesets = None
1343 if 'nbchanges' in inpart.params:
1346 if 'nbchanges' in inpart.params:
1344 nbchangesets = int(inpart.params.get('nbchanges'))
1347 nbchangesets = int(inpart.params.get('nbchanges'))
1345 if ('treemanifest' in inpart.params and
1348 if ('treemanifest' in inpart.params and
1346 'treemanifest' not in op.repo.requirements):
1349 'treemanifest' not in op.repo.requirements):
1347 if len(op.repo.changelog) != 0:
1350 if len(op.repo.changelog) != 0:
1348 raise error.Abort(_(
1351 raise error.Abort(_(
1349 "bundle contains tree manifests, but local repo is "
1352 "bundle contains tree manifests, but local repo is "
1350 "non-empty and does not use tree manifests"))
1353 "non-empty and does not use tree manifests"))
1351 op.repo.requirements.add('treemanifest')
1354 op.repo.requirements.add('treemanifest')
1352 op.repo._applyopenerreqs()
1355 op.repo._applyopenerreqs()
1353 op.repo._writerequirements()
1356 op.repo._writerequirements()
1354 ret = cg.apply(op.repo, 'bundle2', 'bundle2', expectedtotal=nbchangesets)
1357 ret = cg.apply(op.repo, 'bundle2', 'bundle2', expectedtotal=nbchangesets)
1355 op.records.add('changegroup', {'return': ret})
1358 op.records.add('changegroup', {'return': ret})
1356 if op.reply is not None:
1359 if op.reply is not None:
1357 # This is definitely not the final form of this
1360 # This is definitely not the final form of this
1358 # return. But one need to start somewhere.
1361 # return. But one need to start somewhere.
1359 part = op.reply.newpart('reply:changegroup', mandatory=False)
1362 part = op.reply.newpart('reply:changegroup', mandatory=False)
1360 part.addparam('in-reply-to', str(inpart.id), mandatory=False)
1363 part.addparam('in-reply-to', str(inpart.id), mandatory=False)
1361 part.addparam('return', '%i' % ret, mandatory=False)
1364 part.addparam('return', '%i' % ret, mandatory=False)
1362 assert not inpart.read()
1365 assert not inpart.read()
1363
1366
1364 _remotechangegroupparams = tuple(['url', 'size', 'digests'] +
1367 _remotechangegroupparams = tuple(['url', 'size', 'digests'] +
1365 ['digest:%s' % k for k in util.DIGESTS.keys()])
1368 ['digest:%s' % k for k in util.DIGESTS.keys()])
1366 @parthandler('remote-changegroup', _remotechangegroupparams)
1369 @parthandler('remote-changegroup', _remotechangegroupparams)
1367 def handleremotechangegroup(op, inpart):
1370 def handleremotechangegroup(op, inpart):
1368 """apply a bundle10 on the repo, given an url and validation information
1371 """apply a bundle10 on the repo, given an url and validation information
1369
1372
1370 All the information about the remote bundle to import are given as
1373 All the information about the remote bundle to import are given as
1371 parameters. The parameters include:
1374 parameters. The parameters include:
1372 - url: the url to the bundle10.
1375 - url: the url to the bundle10.
1373 - size: the bundle10 file size. It is used to validate what was
1376 - size: the bundle10 file size. It is used to validate what was
1374 retrieved by the client matches the server knowledge about the bundle.
1377 retrieved by the client matches the server knowledge about the bundle.
1375 - digests: a space separated list of the digest types provided as
1378 - digests: a space separated list of the digest types provided as
1376 parameters.
1379 parameters.
1377 - digest:<digest-type>: the hexadecimal representation of the digest with
1380 - digest:<digest-type>: the hexadecimal representation of the digest with
1378 that name. Like the size, it is used to validate what was retrieved by
1381 that name. Like the size, it is used to validate what was retrieved by
1379 the client matches what the server knows about the bundle.
1382 the client matches what the server knows about the bundle.
1380
1383
1381 When multiple digest types are given, all of them are checked.
1384 When multiple digest types are given, all of them are checked.
1382 """
1385 """
1383 try:
1386 try:
1384 raw_url = inpart.params['url']
1387 raw_url = inpart.params['url']
1385 except KeyError:
1388 except KeyError:
1386 raise error.Abort(_('remote-changegroup: missing "%s" param') % 'url')
1389 raise error.Abort(_('remote-changegroup: missing "%s" param') % 'url')
1387 parsed_url = util.url(raw_url)
1390 parsed_url = util.url(raw_url)
1388 if parsed_url.scheme not in capabilities['remote-changegroup']:
1391 if parsed_url.scheme not in capabilities['remote-changegroup']:
1389 raise error.Abort(_('remote-changegroup does not support %s urls') %
1392 raise error.Abort(_('remote-changegroup does not support %s urls') %
1390 parsed_url.scheme)
1393 parsed_url.scheme)
1391
1394
1392 try:
1395 try:
1393 size = int(inpart.params['size'])
1396 size = int(inpart.params['size'])
1394 except ValueError:
1397 except ValueError:
1395 raise error.Abort(_('remote-changegroup: invalid value for param "%s"')
1398 raise error.Abort(_('remote-changegroup: invalid value for param "%s"')
1396 % 'size')
1399 % 'size')
1397 except KeyError:
1400 except KeyError:
1398 raise error.Abort(_('remote-changegroup: missing "%s" param') % 'size')
1401 raise error.Abort(_('remote-changegroup: missing "%s" param') % 'size')
1399
1402
1400 digests = {}
1403 digests = {}
1401 for typ in inpart.params.get('digests', '').split():
1404 for typ in inpart.params.get('digests', '').split():
1402 param = 'digest:%s' % typ
1405 param = 'digest:%s' % typ
1403 try:
1406 try:
1404 value = inpart.params[param]
1407 value = inpart.params[param]
1405 except KeyError:
1408 except KeyError:
1406 raise error.Abort(_('remote-changegroup: missing "%s" param') %
1409 raise error.Abort(_('remote-changegroup: missing "%s" param') %
1407 param)
1410 param)
1408 digests[typ] = value
1411 digests[typ] = value
1409
1412
1410 real_part = util.digestchecker(url.open(op.ui, raw_url), size, digests)
1413 real_part = util.digestchecker(url.open(op.ui, raw_url), size, digests)
1411
1414
1412 # Make sure we trigger a transaction creation
1415 # Make sure we trigger a transaction creation
1413 #
1416 #
1414 # The addchangegroup function will get a transaction object by itself, but
1417 # The addchangegroup function will get a transaction object by itself, but
1415 # we need to make sure we trigger the creation of a transaction object used
1418 # we need to make sure we trigger the creation of a transaction object used
1416 # for the whole processing scope.
1419 # for the whole processing scope.
1417 op.gettransaction()
1420 op.gettransaction()
1418 from . import exchange
1421 from . import exchange
1419 cg = exchange.readbundle(op.repo.ui, real_part, raw_url)
1422 cg = exchange.readbundle(op.repo.ui, real_part, raw_url)
1420 if not isinstance(cg, changegroup.cg1unpacker):
1423 if not isinstance(cg, changegroup.cg1unpacker):
1421 raise error.Abort(_('%s: not a bundle version 1.0') %
1424 raise error.Abort(_('%s: not a bundle version 1.0') %
1422 util.hidepassword(raw_url))
1425 util.hidepassword(raw_url))
1423 ret = cg.apply(op.repo, 'bundle2', 'bundle2')
1426 ret = cg.apply(op.repo, 'bundle2', 'bundle2')
1424 op.records.add('changegroup', {'return': ret})
1427 op.records.add('changegroup', {'return': ret})
1425 if op.reply is not None:
1428 if op.reply is not None:
1426 # This is definitely not the final form of this
1429 # This is definitely not the final form of this
1427 # return. But one need to start somewhere.
1430 # return. But one need to start somewhere.
1428 part = op.reply.newpart('reply:changegroup')
1431 part = op.reply.newpart('reply:changegroup')
1429 part.addparam('in-reply-to', str(inpart.id), mandatory=False)
1432 part.addparam('in-reply-to', str(inpart.id), mandatory=False)
1430 part.addparam('return', '%i' % ret, mandatory=False)
1433 part.addparam('return', '%i' % ret, mandatory=False)
1431 try:
1434 try:
1432 real_part.validate()
1435 real_part.validate()
1433 except error.Abort as e:
1436 except error.Abort as e:
1434 raise error.Abort(_('bundle at %s is corrupted:\n%s') %
1437 raise error.Abort(_('bundle at %s is corrupted:\n%s') %
1435 (util.hidepassword(raw_url), str(e)))
1438 (util.hidepassword(raw_url), str(e)))
1436 assert not inpart.read()
1439 assert not inpart.read()
1437
1440
1438 @parthandler('reply:changegroup', ('return', 'in-reply-to'))
1441 @parthandler('reply:changegroup', ('return', 'in-reply-to'))
1439 def handlereplychangegroup(op, inpart):
1442 def handlereplychangegroup(op, inpart):
1440 ret = int(inpart.params['return'])
1443 ret = int(inpart.params['return'])
1441 replyto = int(inpart.params['in-reply-to'])
1444 replyto = int(inpart.params['in-reply-to'])
1442 op.records.add('changegroup', {'return': ret}, replyto)
1445 op.records.add('changegroup', {'return': ret}, replyto)
1443
1446
1444 @parthandler('check:heads')
1447 @parthandler('check:heads')
1445 def handlecheckheads(op, inpart):
1448 def handlecheckheads(op, inpart):
1446 """check that head of the repo did not change
1449 """check that head of the repo did not change
1447
1450
1448 This is used to detect a push race when using unbundle.
1451 This is used to detect a push race when using unbundle.
1449 This replaces the "heads" argument of unbundle."""
1452 This replaces the "heads" argument of unbundle."""
1450 h = inpart.read(20)
1453 h = inpart.read(20)
1451 heads = []
1454 heads = []
1452 while len(h) == 20:
1455 while len(h) == 20:
1453 heads.append(h)
1456 heads.append(h)
1454 h = inpart.read(20)
1457 h = inpart.read(20)
1455 assert not h
1458 assert not h
1456 # Trigger a transaction so that we are guaranteed to have the lock now.
1459 # Trigger a transaction so that we are guaranteed to have the lock now.
1457 if op.ui.configbool('experimental', 'bundle2lazylocking'):
1460 if op.ui.configbool('experimental', 'bundle2lazylocking'):
1458 op.gettransaction()
1461 op.gettransaction()
1459 if sorted(heads) != sorted(op.repo.heads()):
1462 if sorted(heads) != sorted(op.repo.heads()):
1460 raise error.PushRaced('repository changed while pushing - '
1463 raise error.PushRaced('repository changed while pushing - '
1461 'please try again')
1464 'please try again')
1462
1465
1463 @parthandler('output')
1466 @parthandler('output')
1464 def handleoutput(op, inpart):
1467 def handleoutput(op, inpart):
1465 """forward output captured on the server to the client"""
1468 """forward output captured on the server to the client"""
1466 for line in inpart.read().splitlines():
1469 for line in inpart.read().splitlines():
1467 op.ui.status(('remote: %s\n' % line))
1470 op.ui.status(('remote: %s\n' % line))
1468
1471
1469 @parthandler('replycaps')
1472 @parthandler('replycaps')
1470 def handlereplycaps(op, inpart):
1473 def handlereplycaps(op, inpart):
1471 """Notify that a reply bundle should be created
1474 """Notify that a reply bundle should be created
1472
1475
1473 The payload contains the capabilities information for the reply"""
1476 The payload contains the capabilities information for the reply"""
1474 caps = decodecaps(inpart.read())
1477 caps = decodecaps(inpart.read())
1475 if op.reply is None:
1478 if op.reply is None:
1476 op.reply = bundle20(op.ui, caps)
1479 op.reply = bundle20(op.ui, caps)
1477
1480
1478 class AbortFromPart(error.Abort):
1481 class AbortFromPart(error.Abort):
1479 """Sub-class of Abort that denotes an error from a bundle2 part."""
1482 """Sub-class of Abort that denotes an error from a bundle2 part."""
1480
1483
1481 @parthandler('error:abort', ('message', 'hint'))
1484 @parthandler('error:abort', ('message', 'hint'))
1482 def handleerrorabort(op, inpart):
1485 def handleerrorabort(op, inpart):
1483 """Used to transmit abort error over the wire"""
1486 """Used to transmit abort error over the wire"""
1484 raise AbortFromPart(inpart.params['message'],
1487 raise AbortFromPart(inpart.params['message'],
1485 hint=inpart.params.get('hint'))
1488 hint=inpart.params.get('hint'))
1486
1489
1487 @parthandler('error:pushkey', ('namespace', 'key', 'new', 'old', 'ret',
1490 @parthandler('error:pushkey', ('namespace', 'key', 'new', 'old', 'ret',
1488 'in-reply-to'))
1491 'in-reply-to'))
1489 def handleerrorpushkey(op, inpart):
1492 def handleerrorpushkey(op, inpart):
1490 """Used to transmit failure of a mandatory pushkey over the wire"""
1493 """Used to transmit failure of a mandatory pushkey over the wire"""
1491 kwargs = {}
1494 kwargs = {}
1492 for name in ('namespace', 'key', 'new', 'old', 'ret'):
1495 for name in ('namespace', 'key', 'new', 'old', 'ret'):
1493 value = inpart.params.get(name)
1496 value = inpart.params.get(name)
1494 if value is not None:
1497 if value is not None:
1495 kwargs[name] = value
1498 kwargs[name] = value
1496 raise error.PushkeyFailed(inpart.params['in-reply-to'], **kwargs)
1499 raise error.PushkeyFailed(inpart.params['in-reply-to'], **kwargs)
1497
1500
1498 @parthandler('error:unsupportedcontent', ('parttype', 'params'))
1501 @parthandler('error:unsupportedcontent', ('parttype', 'params'))
1499 def handleerrorunsupportedcontent(op, inpart):
1502 def handleerrorunsupportedcontent(op, inpart):
1500 """Used to transmit unknown content error over the wire"""
1503 """Used to transmit unknown content error over the wire"""
1501 kwargs = {}
1504 kwargs = {}
1502 parttype = inpart.params.get('parttype')
1505 parttype = inpart.params.get('parttype')
1503 if parttype is not None:
1506 if parttype is not None:
1504 kwargs['parttype'] = parttype
1507 kwargs['parttype'] = parttype
1505 params = inpart.params.get('params')
1508 params = inpart.params.get('params')
1506 if params is not None:
1509 if params is not None:
1507 kwargs['params'] = params.split('\0')
1510 kwargs['params'] = params.split('\0')
1508
1511
1509 raise error.BundleUnknownFeatureError(**kwargs)
1512 raise error.BundleUnknownFeatureError(**kwargs)
1510
1513
1511 @parthandler('error:pushraced', ('message',))
1514 @parthandler('error:pushraced', ('message',))
1512 def handleerrorpushraced(op, inpart):
1515 def handleerrorpushraced(op, inpart):
1513 """Used to transmit push race error over the wire"""
1516 """Used to transmit push race error over the wire"""
1514 raise error.ResponseError(_('push failed:'), inpart.params['message'])
1517 raise error.ResponseError(_('push failed:'), inpart.params['message'])
1515
1518
1516 @parthandler('listkeys', ('namespace',))
1519 @parthandler('listkeys', ('namespace',))
1517 def handlelistkeys(op, inpart):
1520 def handlelistkeys(op, inpart):
1518 """retrieve pushkey namespace content stored in a bundle2"""
1521 """retrieve pushkey namespace content stored in a bundle2"""
1519 namespace = inpart.params['namespace']
1522 namespace = inpart.params['namespace']
1520 r = pushkey.decodekeys(inpart.read())
1523 r = pushkey.decodekeys(inpart.read())
1521 op.records.add('listkeys', (namespace, r))
1524 op.records.add('listkeys', (namespace, r))
1522
1525
1523 @parthandler('pushkey', ('namespace', 'key', 'old', 'new'))
1526 @parthandler('pushkey', ('namespace', 'key', 'old', 'new'))
1524 def handlepushkey(op, inpart):
1527 def handlepushkey(op, inpart):
1525 """process a pushkey request"""
1528 """process a pushkey request"""
1526 dec = pushkey.decode
1529 dec = pushkey.decode
1527 namespace = dec(inpart.params['namespace'])
1530 namespace = dec(inpart.params['namespace'])
1528 key = dec(inpart.params['key'])
1531 key = dec(inpart.params['key'])
1529 old = dec(inpart.params['old'])
1532 old = dec(inpart.params['old'])
1530 new = dec(inpart.params['new'])
1533 new = dec(inpart.params['new'])
1531 # Grab the transaction to ensure that we have the lock before performing the
1534 # Grab the transaction to ensure that we have the lock before performing the
1532 # pushkey.
1535 # pushkey.
1533 if op.ui.configbool('experimental', 'bundle2lazylocking'):
1536 if op.ui.configbool('experimental', 'bundle2lazylocking'):
1534 op.gettransaction()
1537 op.gettransaction()
1535 ret = op.repo.pushkey(namespace, key, old, new)
1538 ret = op.repo.pushkey(namespace, key, old, new)
1536 record = {'namespace': namespace,
1539 record = {'namespace': namespace,
1537 'key': key,
1540 'key': key,
1538 'old': old,
1541 'old': old,
1539 'new': new}
1542 'new': new}
1540 op.records.add('pushkey', record)
1543 op.records.add('pushkey', record)
1541 if op.reply is not None:
1544 if op.reply is not None:
1542 rpart = op.reply.newpart('reply:pushkey')
1545 rpart = op.reply.newpart('reply:pushkey')
1543 rpart.addparam('in-reply-to', str(inpart.id), mandatory=False)
1546 rpart.addparam('in-reply-to', str(inpart.id), mandatory=False)
1544 rpart.addparam('return', '%i' % ret, mandatory=False)
1547 rpart.addparam('return', '%i' % ret, mandatory=False)
1545 if inpart.mandatory and not ret:
1548 if inpart.mandatory and not ret:
1546 kwargs = {}
1549 kwargs = {}
1547 for key in ('namespace', 'key', 'new', 'old', 'ret'):
1550 for key in ('namespace', 'key', 'new', 'old', 'ret'):
1548 if key in inpart.params:
1551 if key in inpart.params:
1549 kwargs[key] = inpart.params[key]
1552 kwargs[key] = inpart.params[key]
1550 raise error.PushkeyFailed(partid=str(inpart.id), **kwargs)
1553 raise error.PushkeyFailed(partid=str(inpart.id), **kwargs)
1551
1554
1552 @parthandler('reply:pushkey', ('return', 'in-reply-to'))
1555 @parthandler('reply:pushkey', ('return', 'in-reply-to'))
1553 def handlepushkeyreply(op, inpart):
1556 def handlepushkeyreply(op, inpart):
1554 """retrieve the result of a pushkey request"""
1557 """retrieve the result of a pushkey request"""
1555 ret = int(inpart.params['return'])
1558 ret = int(inpart.params['return'])
1556 partid = int(inpart.params['in-reply-to'])
1559 partid = int(inpart.params['in-reply-to'])
1557 op.records.add('pushkey', {'return': ret}, partid)
1560 op.records.add('pushkey', {'return': ret}, partid)
1558
1561
1559 @parthandler('obsmarkers')
1562 @parthandler('obsmarkers')
1560 def handleobsmarker(op, inpart):
1563 def handleobsmarker(op, inpart):
1561 """add a stream of obsmarkers to the repo"""
1564 """add a stream of obsmarkers to the repo"""
1562 tr = op.gettransaction()
1565 tr = op.gettransaction()
1563 markerdata = inpart.read()
1566 markerdata = inpart.read()
1564 if op.ui.config('experimental', 'obsmarkers-exchange-debug', False):
1567 if op.ui.config('experimental', 'obsmarkers-exchange-debug', False):
1565 op.ui.write(('obsmarker-exchange: %i bytes received\n')
1568 op.ui.write(('obsmarker-exchange: %i bytes received\n')
1566 % len(markerdata))
1569 % len(markerdata))
1567 # The mergemarkers call will crash if marker creation is not enabled.
1570 # The mergemarkers call will crash if marker creation is not enabled.
1568 # we want to avoid this if the part is advisory.
1571 # we want to avoid this if the part is advisory.
1569 if not inpart.mandatory and op.repo.obsstore.readonly:
1572 if not inpart.mandatory and op.repo.obsstore.readonly:
1570 op.repo.ui.debug('ignoring obsolescence markers, feature not enabled')
1573 op.repo.ui.debug('ignoring obsolescence markers, feature not enabled')
1571 return
1574 return
1572 new = op.repo.obsstore.mergemarkers(tr, markerdata)
1575 new = op.repo.obsstore.mergemarkers(tr, markerdata)
1573 if new:
1576 if new:
1574 op.repo.ui.status(_('%i new obsolescence markers\n') % new)
1577 op.repo.ui.status(_('%i new obsolescence markers\n') % new)
1575 op.records.add('obsmarkers', {'new': new})
1578 op.records.add('obsmarkers', {'new': new})
1576 if op.reply is not None:
1579 if op.reply is not None:
1577 rpart = op.reply.newpart('reply:obsmarkers')
1580 rpart = op.reply.newpart('reply:obsmarkers')
1578 rpart.addparam('in-reply-to', str(inpart.id), mandatory=False)
1581 rpart.addparam('in-reply-to', str(inpart.id), mandatory=False)
1579 rpart.addparam('new', '%i' % new, mandatory=False)
1582 rpart.addparam('new', '%i' % new, mandatory=False)
1580
1583
1581
1584
1582 @parthandler('reply:obsmarkers', ('new', 'in-reply-to'))
1585 @parthandler('reply:obsmarkers', ('new', 'in-reply-to'))
1583 def handleobsmarkerreply(op, inpart):
1586 def handleobsmarkerreply(op, inpart):
1584 """retrieve the result of a pushkey request"""
1587 """retrieve the result of a pushkey request"""
1585 ret = int(inpart.params['new'])
1588 ret = int(inpart.params['new'])
1586 partid = int(inpart.params['in-reply-to'])
1589 partid = int(inpart.params['in-reply-to'])
1587 op.records.add('obsmarkers', {'new': ret}, partid)
1590 op.records.add('obsmarkers', {'new': ret}, partid)
1588
1591
1589 @parthandler('hgtagsfnodes')
1592 @parthandler('hgtagsfnodes')
1590 def handlehgtagsfnodes(op, inpart):
1593 def handlehgtagsfnodes(op, inpart):
1591 """Applies .hgtags fnodes cache entries to the local repo.
1594 """Applies .hgtags fnodes cache entries to the local repo.
1592
1595
1593 Payload is pairs of 20 byte changeset nodes and filenodes.
1596 Payload is pairs of 20 byte changeset nodes and filenodes.
1594 """
1597 """
1595 # Grab the transaction so we ensure that we have the lock at this point.
1598 # Grab the transaction so we ensure that we have the lock at this point.
1596 if op.ui.configbool('experimental', 'bundle2lazylocking'):
1599 if op.ui.configbool('experimental', 'bundle2lazylocking'):
1597 op.gettransaction()
1600 op.gettransaction()
1598 cache = tags.hgtagsfnodescache(op.repo.unfiltered())
1601 cache = tags.hgtagsfnodescache(op.repo.unfiltered())
1599
1602
1600 count = 0
1603 count = 0
1601 while True:
1604 while True:
1602 node = inpart.read(20)
1605 node = inpart.read(20)
1603 fnode = inpart.read(20)
1606 fnode = inpart.read(20)
1604 if len(node) < 20 or len(fnode) < 20:
1607 if len(node) < 20 or len(fnode) < 20:
1605 op.ui.debug('ignoring incomplete received .hgtags fnodes data\n')
1608 op.ui.debug('ignoring incomplete received .hgtags fnodes data\n')
1606 break
1609 break
1607 cache.setfnode(node, fnode)
1610 cache.setfnode(node, fnode)
1608 count += 1
1611 count += 1
1609
1612
1610 cache.write()
1613 cache.write()
1611 op.ui.debug('applied %i hgtags fnodes cache entries\n' % count)
1614 op.ui.debug('applied %i hgtags fnodes cache entries\n' % count)
@@ -1,173 +1,173 b''
1 #require test-repo
1 #require test-repo
2
2
3 $ . "$TESTDIR/helpers-testrepo.sh"
3 $ . "$TESTDIR/helpers-testrepo.sh"
4 $ cd "$TESTDIR"/..
4 $ cd "$TESTDIR"/..
5
5
6 $ hg files 'set:(**.py)' | sed 's|\\|/|g' | xargs python contrib/check-py3-compat.py
6 $ hg files 'set:(**.py)' | sed 's|\\|/|g' | xargs python contrib/check-py3-compat.py
7 hgext/fsmonitor/pywatchman/__init__.py not using absolute_import
7 hgext/fsmonitor/pywatchman/__init__.py not using absolute_import
8 hgext/fsmonitor/pywatchman/__init__.py requires print_function
8 hgext/fsmonitor/pywatchman/__init__.py requires print_function
9 hgext/fsmonitor/pywatchman/capabilities.py not using absolute_import
9 hgext/fsmonitor/pywatchman/capabilities.py not using absolute_import
10 hgext/fsmonitor/pywatchman/pybser.py not using absolute_import
10 hgext/fsmonitor/pywatchman/pybser.py not using absolute_import
11 i18n/check-translation.py not using absolute_import
11 i18n/check-translation.py not using absolute_import
12 setup.py not using absolute_import
12 setup.py not using absolute_import
13 tests/test-demandimport.py not using absolute_import
13 tests/test-demandimport.py not using absolute_import
14
14
15 #if py3exe
15 #if py3exe
16 $ hg files 'set:(**.py)' | sed 's|\\|/|g' | xargs $PYTHON3 contrib/check-py3-compat.py
16 $ hg files 'set:(**.py)' | sed 's|\\|/|g' | xargs $PYTHON3 contrib/check-py3-compat.py
17 doc/hgmanpage.py: invalid syntax: invalid syntax (<unknown>, line *) (glob)
17 doc/hgmanpage.py: invalid syntax: invalid syntax (<unknown>, line *) (glob)
18 hgext/acl.py: error importing: <TypeError> str expected, not bytes (error at encoding.py:*) (glob)
18 hgext/acl.py: error importing: <TypeError> str expected, not bytes (error at encoding.py:*) (glob)
19 hgext/automv.py: error importing: <TypeError> str expected, not bytes (error at encoding.py:*) (glob)
19 hgext/automv.py: error importing: <TypeError> str expected, not bytes (error at encoding.py:*) (glob)
20 hgext/blackbox.py: error importing: <TypeError> str expected, not bytes (error at encoding.py:*) (glob)
20 hgext/blackbox.py: error importing: <TypeError> str expected, not bytes (error at encoding.py:*) (glob)
21 hgext/bugzilla.py: error importing: <TypeError> str expected, not bytes (error at encoding.py:*) (glob)
21 hgext/bugzilla.py: error importing: <TypeError> str expected, not bytes (error at encoding.py:*) (glob)
22 hgext/censor.py: error importing: <TypeError> str expected, not bytes (error at encoding.py:*) (glob)
22 hgext/censor.py: error importing: <TypeError> str expected, not bytes (error at encoding.py:*) (glob)
23 hgext/chgserver.py: error importing: <TypeError> str expected, not bytes (error at encoding.py:*) (glob)
23 hgext/chgserver.py: error importing: <TypeError> str expected, not bytes (error at encoding.py:*) (glob)
24 hgext/children.py: error importing: <TypeError> str expected, not bytes (error at encoding.py:*) (glob)
24 hgext/children.py: error importing: <TypeError> str expected, not bytes (error at encoding.py:*) (glob)
25 hgext/churn.py: error importing: <TypeError> str expected, not bytes (error at encoding.py:*) (glob)
25 hgext/churn.py: error importing: <TypeError> str expected, not bytes (error at encoding.py:*) (glob)
26 hgext/clonebundles.py: error importing: <TypeError> str expected, not bytes (error at encoding.py:*) (glob)
26 hgext/clonebundles.py: error importing: <TypeError> str expected, not bytes (error at encoding.py:*) (glob)
27 hgext/color.py: invalid syntax: invalid syntax (<unknown>, line *) (glob)
27 hgext/color.py: invalid syntax: invalid syntax (<unknown>, line *) (glob)
28 hgext/convert/bzr.py: error importing: <TypeError> str expected, not bytes (error at encoding.py:*) (glob)
28 hgext/convert/bzr.py: error importing: <TypeError> str expected, not bytes (error at encoding.py:*) (glob)
29 hgext/convert/common.py: error importing: <TypeError> str expected, not bytes (error at encoding.py:*) (glob)
29 hgext/convert/common.py: error importing: <TypeError> str expected, not bytes (error at encoding.py:*) (glob)
30 hgext/convert/convcmd.py: error importing: <TypeError> str expected, not bytes (error at encoding.py:*) (glob)
30 hgext/convert/convcmd.py: error importing: <TypeError> str expected, not bytes (error at encoding.py:*) (glob)
31 hgext/convert/cvs.py: error importing: <TypeError> str expected, not bytes (error at encoding.py:*) (glob)
31 hgext/convert/cvs.py: error importing: <TypeError> str expected, not bytes (error at encoding.py:*) (glob)
32 hgext/convert/cvsps.py: error importing: <TypeError> str expected, not bytes (error at encoding.py:*) (glob)
32 hgext/convert/cvsps.py: error importing: <TypeError> str expected, not bytes (error at encoding.py:*) (glob)
33 hgext/convert/darcs.py: error importing: <TypeError> str expected, not bytes (error at encoding.py:*) (glob)
33 hgext/convert/darcs.py: error importing: <TypeError> str expected, not bytes (error at encoding.py:*) (glob)
34 hgext/convert/filemap.py: error importing: <TypeError> str expected, not bytes (error at encoding.py:*) (glob)
34 hgext/convert/filemap.py: error importing: <TypeError> str expected, not bytes (error at encoding.py:*) (glob)
35 hgext/convert/git.py: error importing: <TypeError> str expected, not bytes (error at encoding.py:*) (glob)
35 hgext/convert/git.py: error importing: <TypeError> str expected, not bytes (error at encoding.py:*) (glob)
36 hgext/convert/gnuarch.py: error importing: <TypeError> str expected, not bytes (error at encoding.py:*) (glob)
36 hgext/convert/gnuarch.py: error importing: <TypeError> str expected, not bytes (error at encoding.py:*) (glob)
37 hgext/convert/hg.py: error importing: <TypeError> str expected, not bytes (error at encoding.py:*) (glob)
37 hgext/convert/hg.py: error importing: <TypeError> str expected, not bytes (error at encoding.py:*) (glob)
38 hgext/convert/monotone.py: error importing: <TypeError> str expected, not bytes (error at encoding.py:*) (glob)
38 hgext/convert/monotone.py: error importing: <TypeError> str expected, not bytes (error at encoding.py:*) (glob)
39 hgext/convert/p4.py: error importing: <TypeError> str expected, not bytes (error at encoding.py:*) (glob)
39 hgext/convert/p4.py: error importing: <TypeError> str expected, not bytes (error at encoding.py:*) (glob)
40 hgext/convert/subversion.py: error importing: <TypeError> str expected, not bytes (error at encoding.py:*) (glob)
40 hgext/convert/subversion.py: error importing: <TypeError> str expected, not bytes (error at encoding.py:*) (glob)
41 hgext/convert/transport.py: error importing module: <ImportError> No module named 'svn.client' (line *) (glob)
41 hgext/convert/transport.py: error importing module: <ImportError> No module named 'svn.client' (line *) (glob)
42 hgext/eol.py: error importing: <TypeError> str expected, not bytes (error at encoding.py:*) (glob)
42 hgext/eol.py: error importing: <TypeError> str expected, not bytes (error at encoding.py:*) (glob)
43 hgext/extdiff.py: error importing: <TypeError> str expected, not bytes (error at encoding.py:*) (glob)
43 hgext/extdiff.py: error importing: <TypeError> str expected, not bytes (error at encoding.py:*) (glob)
44 hgext/factotum.py: error importing: <TypeError> str expected, not bytes (error at encoding.py:*) (glob)
44 hgext/factotum.py: error importing: <TypeError> str expected, not bytes (error at encoding.py:*) (glob)
45 hgext/fetch.py: error importing: <TypeError> str expected, not bytes (error at encoding.py:*) (glob)
45 hgext/fetch.py: error importing: <TypeError> str expected, not bytes (error at encoding.py:*) (glob)
46 hgext/fsmonitor/state.py: error importing: <TypeError> str expected, not bytes (error at encoding.py:*) (glob)
46 hgext/fsmonitor/state.py: error importing: <TypeError> str expected, not bytes (error at encoding.py:*) (glob)
47 hgext/fsmonitor/watchmanclient.py: error importing: <TypeError> str expected, not bytes (error at encoding.py:*) (glob)
47 hgext/fsmonitor/watchmanclient.py: error importing: <TypeError> str expected, not bytes (error at encoding.py:*) (glob)
48 hgext/gpg.py: error importing: <TypeError> str expected, not bytes (error at encoding.py:*) (glob)
48 hgext/gpg.py: error importing: <TypeError> str expected, not bytes (error at encoding.py:*) (glob)
49 hgext/graphlog.py: error importing: <TypeError> str expected, not bytes (error at encoding.py:*) (glob)
49 hgext/graphlog.py: error importing: <TypeError> str expected, not bytes (error at encoding.py:*) (glob)
50 hgext/hgk.py: error importing: <TypeError> str expected, not bytes (error at encoding.py:*) (glob)
50 hgext/hgk.py: error importing: <TypeError> str expected, not bytes (error at encoding.py:*) (glob)
51 hgext/highlight/highlight.py: error importing: <TypeError> str expected, not bytes (error at encoding.py:*) (glob)
51 hgext/highlight/highlight.py: error importing: <TypeError> str expected, not bytes (error at encoding.py:*) (glob)
52 hgext/histedit.py: error importing: <TypeError> str expected, not bytes (error at encoding.py:*) (glob)
52 hgext/histedit.py: error importing: <TypeError> str expected, not bytes (error at encoding.py:*) (glob)
53 hgext/journal.py: error importing: <TypeError> str expected, not bytes (error at encoding.py:*) (glob)
53 hgext/journal.py: error importing: <TypeError> str expected, not bytes (error at encoding.py:*) (glob)
54 hgext/keyword.py: error importing: <TypeError> str expected, not bytes (error at encoding.py:*) (glob)
54 hgext/keyword.py: error importing: <TypeError> str expected, not bytes (error at encoding.py:*) (glob)
55 hgext/largefiles/basestore.py: error importing: <TypeError> str expected, not bytes (error at encoding.py:*) (glob)
55 hgext/largefiles/basestore.py: error importing: <TypeError> str expected, not bytes (error at encoding.py:*) (glob)
56 hgext/largefiles/lfcommands.py: error importing: <TypeError> str expected, not bytes (error at encoding.py:*) (glob)
56 hgext/largefiles/lfcommands.py: error importing: <TypeError> str expected, not bytes (error at encoding.py:*) (glob)
57 hgext/largefiles/lfutil.py: error importing: <TypeError> str expected, not bytes (error at encoding.py:*) (glob)
57 hgext/largefiles/lfutil.py: error importing: <TypeError> str expected, not bytes (error at encoding.py:*) (glob)
58 hgext/largefiles/localstore.py: error importing: <TypeError> str expected, not bytes (error at encoding.py:*) (glob)
58 hgext/largefiles/localstore.py: error importing: <TypeError> str expected, not bytes (error at encoding.py:*) (glob)
59 hgext/largefiles/overrides.py: error importing: <TypeError> str expected, not bytes (error at encoding.py:*) (glob)
59 hgext/largefiles/overrides.py: error importing: <TypeError> str expected, not bytes (error at encoding.py:*) (glob)
60 hgext/largefiles/proto.py: error importing: <TypeError> str expected, not bytes (error at encoding.py:*) (glob)
60 hgext/largefiles/proto.py: error importing: <TypeError> str expected, not bytes (error at encoding.py:*) (glob)
61 hgext/largefiles/remotestore.py: error importing: <TypeError> str expected, not bytes (error at encoding.py:*) (glob)
61 hgext/largefiles/remotestore.py: error importing: <TypeError> str expected, not bytes (error at encoding.py:*) (glob)
62 hgext/largefiles/reposetup.py: error importing: <TypeError> str expected, not bytes (error at encoding.py:*) (glob)
62 hgext/largefiles/reposetup.py: error importing: <TypeError> str expected, not bytes (error at encoding.py:*) (glob)
63 hgext/largefiles/storefactory.py: error importing: <TypeError> str expected, not bytes (error at encoding.py:*) (glob)
63 hgext/largefiles/storefactory.py: error importing: <TypeError> str expected, not bytes (error at encoding.py:*) (glob)
64 hgext/largefiles/uisetup.py: error importing: <TypeError> str expected, not bytes (error at encoding.py:*) (glob)
64 hgext/largefiles/uisetup.py: error importing: <TypeError> str expected, not bytes (error at encoding.py:*) (glob)
65 hgext/largefiles/wirestore.py: error importing module: <SystemError> Parent module 'hgext.largefiles' not loaded, cannot perform relative import (line *) (glob)
65 hgext/largefiles/wirestore.py: error importing module: <SystemError> Parent module 'hgext.largefiles' not loaded, cannot perform relative import (line *) (glob)
66 hgext/mq.py: error importing: <TypeError> str expected, not bytes (error at encoding.py:*) (glob)
66 hgext/mq.py: error importing: <TypeError> str expected, not bytes (error at encoding.py:*) (glob)
67 hgext/notify.py: error importing: <TypeError> str expected, not bytes (error at encoding.py:*) (glob)
67 hgext/notify.py: error importing: <TypeError> str expected, not bytes (error at encoding.py:*) (glob)
68 hgext/pager.py: error importing: <TypeError> str expected, not bytes (error at encoding.py:*) (glob)
68 hgext/pager.py: error importing: <TypeError> str expected, not bytes (error at encoding.py:*) (glob)
69 hgext/patchbomb.py: error importing: <TypeError> str expected, not bytes (error at encoding.py:*) (glob)
69 hgext/patchbomb.py: error importing: <TypeError> str expected, not bytes (error at encoding.py:*) (glob)
70 hgext/purge.py: error importing: <TypeError> str expected, not bytes (error at encoding.py:*) (glob)
70 hgext/purge.py: error importing: <TypeError> str expected, not bytes (error at encoding.py:*) (glob)
71 hgext/rebase.py: error importing: <TypeError> str expected, not bytes (error at encoding.py:*) (glob)
71 hgext/rebase.py: error importing: <TypeError> str expected, not bytes (error at encoding.py:*) (glob)
72 hgext/record.py: error importing: <TypeError> str expected, not bytes (error at encoding.py:*) (glob)
72 hgext/record.py: error importing: <TypeError> str expected, not bytes (error at encoding.py:*) (glob)
73 hgext/relink.py: error importing: <TypeError> str expected, not bytes (error at encoding.py:*) (glob)
73 hgext/relink.py: error importing: <TypeError> str expected, not bytes (error at encoding.py:*) (glob)
74 hgext/schemes.py: error importing: <TypeError> str expected, not bytes (error at encoding.py:*) (glob)
74 hgext/schemes.py: error importing: <TypeError> str expected, not bytes (error at encoding.py:*) (glob)
75 hgext/share.py: error importing: <TypeError> str expected, not bytes (error at encoding.py:*) (glob)
75 hgext/share.py: error importing: <TypeError> str expected, not bytes (error at encoding.py:*) (glob)
76 hgext/shelve.py: error importing: <TypeError> str expected, not bytes (error at encoding.py:*) (glob)
76 hgext/shelve.py: error importing: <TypeError> str expected, not bytes (error at encoding.py:*) (glob)
77 hgext/strip.py: error importing: <TypeError> str expected, not bytes (error at encoding.py:*) (glob)
77 hgext/strip.py: error importing: <TypeError> str expected, not bytes (error at encoding.py:*) (glob)
78 hgext/transplant.py: error importing: <TypeError> str expected, not bytes (error at encoding.py:*) (glob)
78 hgext/transplant.py: error importing: <TypeError> str expected, not bytes (error at encoding.py:*) (glob)
79 hgext/win32mbcs.py: error importing: <TypeError> str expected, not bytes (error at encoding.py:*) (glob)
79 hgext/win32mbcs.py: error importing: <TypeError> str expected, not bytes (error at encoding.py:*) (glob)
80 hgext/win32text.py: error importing: <TypeError> str expected, not bytes (error at encoding.py:*) (glob)
80 hgext/win32text.py: error importing: <TypeError> str expected, not bytes (error at encoding.py:*) (glob)
81 mercurial/archival.py: invalid syntax: invalid syntax (<unknown>, line *) (glob)
81 mercurial/archival.py: invalid syntax: invalid syntax (<unknown>, line *) (glob)
82 mercurial/bookmarks.py: error importing: <TypeError> str expected, not bytes (error at encoding.py:*) (glob)
82 mercurial/bookmarks.py: error importing: <TypeError> str expected, not bytes (error at encoding.py:*) (glob)
83 mercurial/branchmap.py: error importing: <TypeError> str expected, not bytes (error at encoding.py:*) (glob)
83 mercurial/branchmap.py: error importing: <TypeError> str expected, not bytes (error at encoding.py:*) (glob)
84 mercurial/bundle2.py: invalid syntax: invalid syntax (<unknown>, line *) (glob)
84 mercurial/bundle2.py: error importing: <TypeError> str expected, not bytes (error at encoding.py:*) (glob)
85 mercurial/bundlerepo.py: error importing: <TypeError> str expected, not bytes (error at encoding.py:*) (glob)
85 mercurial/bundlerepo.py: error importing: <TypeError> str expected, not bytes (error at encoding.py:*) (glob)
86 mercurial/byterange.py: error importing: <TypeError> str expected, not bytes (error at encoding.py:*) (glob)
86 mercurial/byterange.py: error importing: <TypeError> str expected, not bytes (error at encoding.py:*) (glob)
87 mercurial/changegroup.py: error importing: <TypeError> str expected, not bytes (error at encoding.py:*) (glob)
87 mercurial/changegroup.py: error importing: <TypeError> str expected, not bytes (error at encoding.py:*) (glob)
88 mercurial/changelog.py: error importing: <TypeError> str expected, not bytes (error at encoding.py:*) (glob)
88 mercurial/changelog.py: error importing: <TypeError> str expected, not bytes (error at encoding.py:*) (glob)
89 mercurial/cmdutil.py: error importing: <TypeError> str expected, not bytes (error at encoding.py:*) (glob)
89 mercurial/cmdutil.py: error importing: <TypeError> str expected, not bytes (error at encoding.py:*) (glob)
90 mercurial/commands.py: invalid syntax: invalid syntax (<unknown>, line *) (glob)
90 mercurial/commands.py: invalid syntax: invalid syntax (<unknown>, line *) (glob)
91 mercurial/commandserver.py: error importing: <TypeError> str expected, not bytes (error at encoding.py:*) (glob)
91 mercurial/commandserver.py: error importing: <TypeError> str expected, not bytes (error at encoding.py:*) (glob)
92 mercurial/config.py: error importing: <TypeError> str expected, not bytes (error at encoding.py:*) (glob)
92 mercurial/config.py: error importing: <TypeError> str expected, not bytes (error at encoding.py:*) (glob)
93 mercurial/context.py: error importing: <TypeError> str expected, not bytes (error at encoding.py:*) (glob)
93 mercurial/context.py: error importing: <TypeError> str expected, not bytes (error at encoding.py:*) (glob)
94 mercurial/copies.py: error importing: <TypeError> str expected, not bytes (error at encoding.py:*) (glob)
94 mercurial/copies.py: error importing: <TypeError> str expected, not bytes (error at encoding.py:*) (glob)
95 mercurial/crecord.py: error importing: <TypeError> str expected, not bytes (error at encoding.py:*) (glob)
95 mercurial/crecord.py: error importing: <TypeError> str expected, not bytes (error at encoding.py:*) (glob)
96 mercurial/dagparser.py: error importing: <TypeError> str expected, not bytes (error at encoding.py:*) (glob)
96 mercurial/dagparser.py: error importing: <TypeError> str expected, not bytes (error at encoding.py:*) (glob)
97 mercurial/dagutil.py: error importing: <TypeError> str expected, not bytes (error at encoding.py:*) (glob)
97 mercurial/dagutil.py: error importing: <TypeError> str expected, not bytes (error at encoding.py:*) (glob)
98 mercurial/destutil.py: error importing: <TypeError> str expected, not bytes (error at encoding.py:*) (glob)
98 mercurial/destutil.py: error importing: <TypeError> str expected, not bytes (error at encoding.py:*) (glob)
99 mercurial/dirstate.py: error importing: <TypeError> str expected, not bytes (error at encoding.py:*) (glob)
99 mercurial/dirstate.py: error importing: <TypeError> str expected, not bytes (error at encoding.py:*) (glob)
100 mercurial/discovery.py: error importing: <TypeError> str expected, not bytes (error at encoding.py:*) (glob)
100 mercurial/discovery.py: error importing: <TypeError> str expected, not bytes (error at encoding.py:*) (glob)
101 mercurial/dispatch.py: error importing: <TypeError> str expected, not bytes (error at encoding.py:*) (glob)
101 mercurial/dispatch.py: error importing: <TypeError> str expected, not bytes (error at encoding.py:*) (glob)
102 mercurial/exchange.py: error importing: <TypeError> str expected, not bytes (error at i18n.py:*) (glob)
102 mercurial/exchange.py: error importing: <TypeError> str expected, not bytes (error at i18n.py:*) (glob)
103 mercurial/extensions.py: error importing: <TypeError> str expected, not bytes (error at i18n.py:*) (glob)
103 mercurial/extensions.py: error importing: <TypeError> str expected, not bytes (error at i18n.py:*) (glob)
104 mercurial/fancyopts.py: error importing: <TypeError> str expected, not bytes (error at i18n.py:*) (glob)
104 mercurial/fancyopts.py: error importing: <TypeError> str expected, not bytes (error at i18n.py:*) (glob)
105 mercurial/filelog.py: error importing: <TypeError> str expected, not bytes (error at i18n.py:*) (glob)
105 mercurial/filelog.py: error importing: <TypeError> str expected, not bytes (error at i18n.py:*) (glob)
106 mercurial/filemerge.py: error importing: <TypeError> str expected, not bytes (error at i18n.py:*) (glob)
106 mercurial/filemerge.py: error importing: <TypeError> str expected, not bytes (error at i18n.py:*) (glob)
107 mercurial/fileset.py: error importing: <TypeError> str expected, not bytes (error at i18n.py:*) (glob)
107 mercurial/fileset.py: error importing: <TypeError> str expected, not bytes (error at i18n.py:*) (glob)
108 mercurial/formatter.py: error importing: <TypeError> str expected, not bytes (error at i18n.py:*) (glob)
108 mercurial/formatter.py: error importing: <TypeError> str expected, not bytes (error at i18n.py:*) (glob)
109 mercurial/graphmod.py: error importing: <TypeError> str expected, not bytes (error at i18n.py:*) (glob)
109 mercurial/graphmod.py: error importing: <TypeError> str expected, not bytes (error at i18n.py:*) (glob)
110 mercurial/hbisect.py: error importing: <TypeError> str expected, not bytes (error at i18n.py:*) (glob)
110 mercurial/hbisect.py: error importing: <TypeError> str expected, not bytes (error at i18n.py:*) (glob)
111 mercurial/help.py: error importing: <TypeError> str expected, not bytes (error at i18n.py:*) (glob)
111 mercurial/help.py: error importing: <TypeError> str expected, not bytes (error at i18n.py:*) (glob)
112 mercurial/hg.py: error importing: <TypeError> str expected, not bytes (error at i18n.py:*) (glob)
112 mercurial/hg.py: error importing: <TypeError> str expected, not bytes (error at i18n.py:*) (glob)
113 mercurial/hgweb/common.py: error importing module: <SystemError> Parent module 'mercurial.hgweb' not loaded, cannot perform relative import (line *) (glob)
113 mercurial/hgweb/common.py: error importing module: <SystemError> Parent module 'mercurial.hgweb' not loaded, cannot perform relative import (line *) (glob)
114 mercurial/hgweb/hgweb_mod.py: error importing module: <SystemError> Parent module 'mercurial.hgweb' not loaded, cannot perform relative import (line *) (glob)
114 mercurial/hgweb/hgweb_mod.py: error importing module: <SystemError> Parent module 'mercurial.hgweb' not loaded, cannot perform relative import (line *) (glob)
115 mercurial/hgweb/hgwebdir_mod.py: error importing module: <SystemError> Parent module 'mercurial.hgweb' not loaded, cannot perform relative import (line *) (glob)
115 mercurial/hgweb/hgwebdir_mod.py: error importing module: <SystemError> Parent module 'mercurial.hgweb' not loaded, cannot perform relative import (line *) (glob)
116 mercurial/hgweb/protocol.py: error importing module: <SystemError> Parent module 'mercurial.hgweb' not loaded, cannot perform relative import (line *) (glob)
116 mercurial/hgweb/protocol.py: error importing module: <SystemError> Parent module 'mercurial.hgweb' not loaded, cannot perform relative import (line *) (glob)
117 mercurial/hgweb/request.py: error importing module: <SystemError> Parent module 'mercurial.hgweb' not loaded, cannot perform relative import (line *) (glob)
117 mercurial/hgweb/request.py: error importing module: <SystemError> Parent module 'mercurial.hgweb' not loaded, cannot perform relative import (line *) (glob)
118 mercurial/hgweb/server.py: error importing module: <SystemError> Parent module 'mercurial.hgweb' not loaded, cannot perform relative import (line *) (glob)
118 mercurial/hgweb/server.py: error importing module: <SystemError> Parent module 'mercurial.hgweb' not loaded, cannot perform relative import (line *) (glob)
119 mercurial/hgweb/webcommands.py: error importing module: <SystemError> Parent module 'mercurial.hgweb' not loaded, cannot perform relative import (line *) (glob)
119 mercurial/hgweb/webcommands.py: error importing module: <SystemError> Parent module 'mercurial.hgweb' not loaded, cannot perform relative import (line *) (glob)
120 mercurial/hgweb/webutil.py: error importing module: <SystemError> Parent module 'mercurial.hgweb' not loaded, cannot perform relative import (line *) (glob)
120 mercurial/hgweb/webutil.py: error importing module: <SystemError> Parent module 'mercurial.hgweb' not loaded, cannot perform relative import (line *) (glob)
121 mercurial/hgweb/wsgicgi.py: error importing module: <SystemError> Parent module 'mercurial.hgweb' not loaded, cannot perform relative import (line *) (glob)
121 mercurial/hgweb/wsgicgi.py: error importing module: <SystemError> Parent module 'mercurial.hgweb' not loaded, cannot perform relative import (line *) (glob)
122 mercurial/hook.py: error importing: <TypeError> str expected, not bytes (error at i18n.py:*) (glob)
122 mercurial/hook.py: error importing: <TypeError> str expected, not bytes (error at i18n.py:*) (glob)
123 mercurial/httpconnection.py: error importing: <TypeError> str expected, not bytes (error at i18n.py:*) (glob)
123 mercurial/httpconnection.py: error importing: <TypeError> str expected, not bytes (error at i18n.py:*) (glob)
124 mercurial/httppeer.py: error importing: <TypeError> str expected, not bytes (error at i18n.py:*) (glob)
124 mercurial/httppeer.py: error importing: <TypeError> str expected, not bytes (error at i18n.py:*) (glob)
125 mercurial/keepalive.py: error importing: <TypeError> getattr(): attribute name must be string (error at pycompat.py:*) (glob)
125 mercurial/keepalive.py: error importing: <TypeError> getattr(): attribute name must be string (error at pycompat.py:*) (glob)
126 mercurial/localrepo.py: error importing: <TypeError> getattr(): attribute name must be string (error at pycompat.py:*) (glob)
126 mercurial/localrepo.py: error importing: <TypeError> getattr(): attribute name must be string (error at pycompat.py:*) (glob)
127 mercurial/lock.py: error importing: <TypeError> getattr(): attribute name must be string (error at pycompat.py:*) (glob)
127 mercurial/lock.py: error importing: <TypeError> getattr(): attribute name must be string (error at pycompat.py:*) (glob)
128 mercurial/mail.py: error importing: <TypeError> getattr(): attribute name must be string (error at pycompat.py:*) (glob)
128 mercurial/mail.py: error importing: <TypeError> getattr(): attribute name must be string (error at pycompat.py:*) (glob)
129 mercurial/manifest.py: error importing: <TypeError> getattr(): attribute name must be string (error at pycompat.py:*) (glob)
129 mercurial/manifest.py: error importing: <TypeError> getattr(): attribute name must be string (error at pycompat.py:*) (glob)
130 mercurial/match.py: error importing: <TypeError> getattr(): attribute name must be string (error at pycompat.py:*) (glob)
130 mercurial/match.py: error importing: <TypeError> getattr(): attribute name must be string (error at pycompat.py:*) (glob)
131 mercurial/mdiff.py: error importing: <TypeError> getattr(): attribute name must be string (error at pycompat.py:*) (glob)
131 mercurial/mdiff.py: error importing: <TypeError> getattr(): attribute name must be string (error at pycompat.py:*) (glob)
132 mercurial/merge.py: error importing: <TypeError> getattr(): attribute name must be string (error at pycompat.py:*) (glob)
132 mercurial/merge.py: error importing: <TypeError> getattr(): attribute name must be string (error at pycompat.py:*) (glob)
133 mercurial/minirst.py: error importing: <TypeError> getattr(): attribute name must be string (error at pycompat.py:*) (glob)
133 mercurial/minirst.py: error importing: <TypeError> getattr(): attribute name must be string (error at pycompat.py:*) (glob)
134 mercurial/namespaces.py: error importing: <TypeError> getattr(): attribute name must be string (error at pycompat.py:*) (glob)
134 mercurial/namespaces.py: error importing: <TypeError> getattr(): attribute name must be string (error at pycompat.py:*) (glob)
135 mercurial/obsolete.py: error importing: <TypeError> getattr(): attribute name must be string (error at pycompat.py:*) (glob)
135 mercurial/obsolete.py: error importing: <TypeError> getattr(): attribute name must be string (error at pycompat.py:*) (glob)
136 mercurial/patch.py: error importing: <TypeError> getattr(): attribute name must be string (error at pycompat.py:*) (glob)
136 mercurial/patch.py: error importing: <TypeError> getattr(): attribute name must be string (error at pycompat.py:*) (glob)
137 mercurial/pathutil.py: error importing: <TypeError> getattr(): attribute name must be string (error at pycompat.py:*) (glob)
137 mercurial/pathutil.py: error importing: <TypeError> getattr(): attribute name must be string (error at pycompat.py:*) (glob)
138 mercurial/peer.py: error importing: <TypeError> getattr(): attribute name must be string (error at pycompat.py:*) (glob)
138 mercurial/peer.py: error importing: <TypeError> getattr(): attribute name must be string (error at pycompat.py:*) (glob)
139 mercurial/pure/mpatch.py: error importing: <TypeError> getattr(): attribute name must be string (error at pycompat.py:*) (glob)
139 mercurial/pure/mpatch.py: error importing: <TypeError> getattr(): attribute name must be string (error at pycompat.py:*) (glob)
140 mercurial/pure/parsers.py: error importing: <TypeError> getattr(): attribute name must be string (error at pycompat.py:*) (glob)
140 mercurial/pure/parsers.py: error importing: <TypeError> getattr(): attribute name must be string (error at pycompat.py:*) (glob)
141 mercurial/pushkey.py: error importing: <TypeError> getattr(): attribute name must be string (error at pycompat.py:*) (glob)
141 mercurial/pushkey.py: error importing: <TypeError> getattr(): attribute name must be string (error at pycompat.py:*) (glob)
142 mercurial/pvec.py: error importing: <TypeError> getattr(): attribute name must be string (error at pycompat.py:*) (glob)
142 mercurial/pvec.py: error importing: <TypeError> getattr(): attribute name must be string (error at pycompat.py:*) (glob)
143 mercurial/registrar.py: error importing: <TypeError> getattr(): attribute name must be string (error at util.py:*) (glob)
143 mercurial/registrar.py: error importing: <TypeError> getattr(): attribute name must be string (error at util.py:*) (glob)
144 mercurial/repair.py: error importing module: <SyntaxError> invalid syntax (bundle2.py, line *) (line *) (glob)
144 mercurial/repair.py: error importing: <TypeError> getattr(): attribute name must be string (error at util.py:*) (glob)
145 mercurial/repoview.py: error importing: <TypeError> getattr(): attribute name must be string (error at util.py:*) (glob)
145 mercurial/repoview.py: error importing: <TypeError> getattr(): attribute name must be string (error at util.py:*) (glob)
146 mercurial/revlog.py: error importing: <TypeError> getattr(): attribute name must be string (error at util.py:*) (glob)
146 mercurial/revlog.py: error importing: <TypeError> getattr(): attribute name must be string (error at util.py:*) (glob)
147 mercurial/revset.py: error importing: <TypeError> getattr(): attribute name must be string (error at util.py:*) (glob)
147 mercurial/revset.py: error importing: <TypeError> getattr(): attribute name must be string (error at util.py:*) (glob)
148 mercurial/scmutil.py: error importing: <TypeError> getattr(): attribute name must be string (error at util.py:*) (glob)
148 mercurial/scmutil.py: error importing: <TypeError> getattr(): attribute name must be string (error at util.py:*) (glob)
149 mercurial/scmwindows.py: error importing module: <ImportError> No module named '_winreg' (line *) (glob)
149 mercurial/scmwindows.py: error importing module: <ImportError> No module named '_winreg' (line *) (glob)
150 mercurial/similar.py: error importing: <TypeError> getattr(): attribute name must be string (error at util.py:*) (glob)
150 mercurial/similar.py: error importing: <TypeError> getattr(): attribute name must be string (error at util.py:*) (glob)
151 mercurial/simplemerge.py: error importing: <TypeError> getattr(): attribute name must be string (error at util.py:*) (glob)
151 mercurial/simplemerge.py: error importing: <TypeError> getattr(): attribute name must be string (error at util.py:*) (glob)
152 mercurial/sshpeer.py: error importing: <TypeError> getattr(): attribute name must be string (error at util.py:*) (glob)
152 mercurial/sshpeer.py: error importing: <TypeError> getattr(): attribute name must be string (error at util.py:*) (glob)
153 mercurial/sshserver.py: error importing: <TypeError> getattr(): attribute name must be string (error at util.py:*) (glob)
153 mercurial/sshserver.py: error importing: <TypeError> getattr(): attribute name must be string (error at util.py:*) (glob)
154 mercurial/sslutil.py: error importing: <TypeError> getattr(): attribute name must be string (error at util.py:*) (glob)
154 mercurial/sslutil.py: error importing: <TypeError> getattr(): attribute name must be string (error at util.py:*) (glob)
155 mercurial/statichttprepo.py: error importing: <TypeError> getattr(): attribute name must be string (error at util.py:*) (glob)
155 mercurial/statichttprepo.py: error importing: <TypeError> getattr(): attribute name must be string (error at util.py:*) (glob)
156 mercurial/store.py: error importing: <TypeError> getattr(): attribute name must be string (error at util.py:*) (glob)
156 mercurial/store.py: error importing: <TypeError> getattr(): attribute name must be string (error at util.py:*) (glob)
157 mercurial/streamclone.py: error importing: <TypeError> getattr(): attribute name must be string (error at util.py:*) (glob)
157 mercurial/streamclone.py: error importing: <TypeError> getattr(): attribute name must be string (error at util.py:*) (glob)
158 mercurial/subrepo.py: error importing: <TypeError> getattr(): attribute name must be string (error at util.py:*) (glob)
158 mercurial/subrepo.py: error importing: <TypeError> getattr(): attribute name must be string (error at util.py:*) (glob)
159 mercurial/tagmerge.py: error importing: <TypeError> getattr(): attribute name must be string (error at util.py:*) (glob)
159 mercurial/tagmerge.py: error importing: <TypeError> getattr(): attribute name must be string (error at util.py:*) (glob)
160 mercurial/tags.py: error importing: <TypeError> getattr(): attribute name must be string (error at util.py:*) (glob)
160 mercurial/tags.py: error importing: <TypeError> getattr(): attribute name must be string (error at util.py:*) (glob)
161 mercurial/templatefilters.py: error importing: <TypeError> getattr(): attribute name must be string (error at util.py:*) (glob)
161 mercurial/templatefilters.py: error importing: <TypeError> getattr(): attribute name must be string (error at util.py:*) (glob)
162 mercurial/templatekw.py: error importing: <TypeError> getattr(): attribute name must be string (error at util.py:*) (glob)
162 mercurial/templatekw.py: error importing: <TypeError> getattr(): attribute name must be string (error at util.py:*) (glob)
163 mercurial/templater.py: error importing: <TypeError> getattr(): attribute name must be string (error at util.py:*) (glob)
163 mercurial/templater.py: error importing: <TypeError> getattr(): attribute name must be string (error at util.py:*) (glob)
164 mercurial/transaction.py: error importing: <TypeError> getattr(): attribute name must be string (error at util.py:*) (glob)
164 mercurial/transaction.py: error importing: <TypeError> getattr(): attribute name must be string (error at util.py:*) (glob)
165 mercurial/ui.py: error importing: <TypeError> getattr(): attribute name must be string (error at util.py:*) (glob)
165 mercurial/ui.py: error importing: <TypeError> getattr(): attribute name must be string (error at util.py:*) (glob)
166 mercurial/unionrepo.py: error importing: <TypeError> getattr(): attribute name must be string (error at util.py:*) (glob)
166 mercurial/unionrepo.py: error importing: <TypeError> getattr(): attribute name must be string (error at util.py:*) (glob)
167 mercurial/url.py: error importing: <TypeError> getattr(): attribute name must be string (error at util.py:*) (glob)
167 mercurial/url.py: error importing: <TypeError> getattr(): attribute name must be string (error at util.py:*) (glob)
168 mercurial/verify.py: error importing: <TypeError> attribute name must be string, not 'bytes' (error at mdiff.py:*) (glob)
168 mercurial/verify.py: error importing: <TypeError> attribute name must be string, not 'bytes' (error at mdiff.py:*) (glob)
169 mercurial/win32.py: error importing module: <ImportError> No module named 'msvcrt' (line *) (glob)
169 mercurial/win32.py: error importing module: <ImportError> No module named 'msvcrt' (line *) (glob)
170 mercurial/windows.py: error importing module: <ImportError> No module named '_winreg' (line *) (glob)
170 mercurial/windows.py: error importing module: <ImportError> No module named '_winreg' (line *) (glob)
171 mercurial/wireproto.py: error importing module: <SyntaxError> invalid syntax (bundle2.py, line *) (line *) (glob)
171 mercurial/wireproto.py: error importing module: <TypeError> a bytes-like object is required, not 'str' (line *) (glob)
172
172
173 #endif
173 #endif
General Comments 0
You need to be logged in to leave comments. Login now